Merge from mozilla-central.
authorDavid Anderson <danderson@mozilla.com>
Wed, 15 Feb 2012 13:45:39 -0800
changeset 105846 98a0b13b8a0cec8430394da7d323021ff8ee7800
parent 105842 132462b85b08292526d3c3d87ecffe552ac81727 (current diff)
parent 86895 3a90a3dedf6c3339ae8ecc74e07719a1ee38d9cc (diff)
child 105847 6ea8ff60d96ec4fd4126a9d26cd132d3c0b2a312
push id1075
push uservporof@mozilla.com
push dateThu, 13 Sep 2012 10:46:49 +0000
treeherderfx-team@f39786e8364d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone13.0a1
Merge from mozilla-central.
accessible/src/html/nsHTMLSelectAccessible.cpp
accessible/src/xul/nsXULListboxAccessible.cpp
accessible/src/xul/nsXULMenuAccessible.cpp
accessible/src/xul/nsXULTreeAccessible.cpp
b2g/app/nsBrowserApp.cpp
browser/app/nsBrowserApp.cpp
browser/base/content/browser-context.inc
browser/base/content/browser.js
build/mobile/devicemanagerSUT.py
configure.in
content/base/public/nsINode.h
content/base/src/Makefile.in
content/base/src/nsAttrAndChildArray.cpp
content/base/src/nsAttrValue.cpp
content/base/src/nsAttrValue.h
content/base/src/nsContentUtils.cpp
content/base/src/nsDOMAttribute.cpp
content/base/src/nsDocument.cpp
content/base/src/nsFrameMessageManager.cpp
content/base/src/nsGenericDOMDataNode.cpp
content/base/src/nsGenericElement.cpp
content/base/src/nsGenericElement.h
content/base/src/nsStyledElement.cpp
content/base/src/nsStyledElement.h
content/base/src/nsTextFragment.cpp
content/base/src/nsTextFragment.h
content/base/src/nsXMLHttpRequest.cpp
content/canvas/src/WebGLContext.cpp
content/html/content/public/nsHTMLMediaElement.h
content/html/content/src/nsGenericHTMLElement.cpp
content/html/content/src/nsGenericHTMLElement.h
content/html/content/src/nsHTMLButtonElement.cpp
content/html/content/src/nsHTMLFieldSetElement.cpp
content/html/content/src/nsHTMLFieldSetElement.h
content/html/content/src/nsHTMLFormElement.cpp
content/html/content/src/nsHTMLFormElement.h
content/html/content/src/nsHTMLInputElement.cpp
content/html/content/src/nsHTMLInputElement.h
content/html/content/src/nsHTMLMediaElement.cpp
content/html/content/src/nsHTMLMenuItemElement.cpp
content/html/content/src/nsHTMLMenuItemElement.h
content/html/content/src/nsHTMLOptionElement.cpp
content/html/content/src/nsHTMLOptionElement.h
content/html/content/src/nsHTMLScriptElement.cpp
content/html/content/src/nsHTMLSelectElement.cpp
content/html/content/src/nsHTMLSelectElement.h
content/html/content/src/nsHTMLTableElement.cpp
content/html/content/src/nsHTMLTableElement.h
content/html/content/src/nsHTMLTextAreaElement.cpp
content/html/content/src/nsHTMLVideoElement.cpp
content/media/MediaResource.cpp
content/media/MediaResource.h
content/media/nsAudioStream.cpp
content/media/nsAudioStream.h
content/media/nsBuiltinDecoder.cpp
content/media/nsBuiltinDecoder.h
content/media/nsBuiltinDecoderReader.h
content/media/nsBuiltinDecoderStateMachine.cpp
content/media/nsBuiltinDecoderStateMachine.h
content/media/nsMediaCache.cpp
content/media/nsMediaCache.h
content/media/nsMediaDecoder.cpp
content/media/nsMediaDecoder.h
content/media/nsMediaStream.cpp
content/media/nsMediaStream.h
content/media/ogg/nsOggReader.cpp
content/media/ogg/nsOggReader.h
content/media/raw/nsRawReader.cpp
content/media/raw/nsRawReader.h
content/media/wave/nsWaveReader.cpp
content/media/webm/nsWebMReader.cpp
content/media/webm/nsWebMReader.h
content/svg/content/src/nsSVGAnimationElement.cpp
content/svg/content/src/nsSVGAnimationElement.h
content/svg/content/src/nsSVGElement.cpp
content/svg/content/src/nsSVGElement.h
content/svg/content/src/nsSVGFilters.cpp
content/svg/content/src/nsSVGFilters.h
content/svg/content/src/nsSVGImageElement.cpp
content/svg/content/src/nsSVGImageElement.h
content/svg/content/src/nsSVGScriptElement.cpp
content/xul/content/src/nsXULElement.cpp
content/xul/content/src/nsXULElement.h
dom/base/nsGlobalWindow.cpp
dom/base/nsJSEnvironment.cpp
dom/plugins/base/nsPluginHost.cpp
dom/plugins/base/nsPluginTags.cpp
dom/plugins/base/nsPluginTags.h
dom/workers/RuntimeService.cpp
dom/workers/XMLHttpRequestPrivate.cpp
editor/libeditor/base/nsEditor.cpp
editor/libeditor/html/nsHTMLDataTransfer.cpp
editor/libeditor/html/nsHTMLEditRules.cpp
editor/libeditor/html/nsHTMLEditor.cpp
editor/libeditor/html/nsHTMLEditorStyle.cpp
editor/libeditor/html/nsTableEditor.cpp
editor/libeditor/text/nsPlaintextEditor.cpp
editor/txtsvc/src/nsTextServicesDocument.cpp
gfx/gl/GLContext.h
gfx/harfbuzz/src/gen-arabic-joining-table.py
gfx/harfbuzz/src/hb-blob-private.h
gfx/harfbuzz/src/hb-blob.c
gfx/harfbuzz/src/hb-common.c
gfx/harfbuzz/src/hb-font-private.h
gfx/harfbuzz/src/hb-ft.c
gfx/harfbuzz/src/hb-glib.c
gfx/harfbuzz/src/hb-graphite.cc
gfx/harfbuzz/src/hb-graphite.h
gfx/harfbuzz/src/hb-icu.c
gfx/harfbuzz/src/hb-language.c
gfx/harfbuzz/src/hb-language.h
gfx/harfbuzz/src/hb-object-private.h
gfx/harfbuzz/src/hb-ot-head-private.hh
gfx/harfbuzz/src/hb-ot-layout-gdef-private.hh
gfx/harfbuzz/src/hb-ot-layout-gpos-private.hh
gfx/harfbuzz/src/hb-ot-layout-gsub-private.hh
gfx/harfbuzz/src/hb-ot-shape-complex-arabic-table.h
gfx/harfbuzz/src/hb-ot-shape.cc
gfx/harfbuzz/src/hb-ot-tag.c
gfx/harfbuzz/src/hb-private.h
gfx/harfbuzz/src/hb-unicode-private.h
gfx/harfbuzz/src/hb-unicode.c
gfx/harfbuzz/src/test.c
gfx/layers/ImageLayers.h
gfx/layers/basic/BasicLayers.cpp
gfx/layers/opengl/ThebesLayerOGL.cpp
gfx/thebes/gfxContext.cpp
gfx/thebes/gfxDWriteFonts.cpp
gfx/thebes/gfxFT2Fonts.cpp
gfx/thebes/gfxFont.cpp
gfx/thebes/gfxFont.h
gfx/thebes/gfxFontUtils.cpp
gfx/thebes/gfxFontUtils.h
gfx/thebes/gfxGDIFont.cpp
gfx/thebes/gfxGDIFontList.cpp
gfx/thebes/gfxGDIFontList.h
gfx/thebes/gfxHarfBuzzShaper.cpp
gfx/thebes/gfxHarfBuzzShaper.h
gfx/thebes/gfxMacFont.cpp
gfx/thebes/gfxPangoFonts.cpp
gfx/thebes/gfxPlatformFontList.cpp
gfx/thebes/gfxScriptItemizer.cpp
gfx/thebes/gfxScriptItemizer.h
gfx/thebes/gfxUserFontSet.h
image/src/RasterImage.cpp
intl/chardet/src/nsDebugDetector.cpp
intl/chardet/src/nsDebugDetector.h
intl/chardet/src/nsObserverBase.cpp
intl/chardet/src/nsObserverBase.h
js/src/Makefile.in
js/src/assembler/jit/ExecutableAllocator.h
js/src/frontend/Parser.cpp
js/src/ion/Ion.cpp
js/src/ion/IonCompartment.h
js/src/ion/IonFrames.cpp
js/src/ion/MCallOptimize.cpp
js/src/ion/shared/Assembler-x86-shared.cpp
js/src/jsapi-tests/Makefile.in
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsatom.cpp
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jscompartment.cpp
js/src/jsgc.cpp
js/src/jsgcmark.cpp
js/src/jsgcmark.h
js/src/jsinfer.cpp
js/src/jsinferinlines.h
js/src/jsinterp.cpp
js/src/jsinterpinlines.h
js/src/jsobj.cpp
js/src/jsutil.h
js/src/jsxml.cpp
js/src/methodjit/MethodJIT.h
js/src/shell/js.cpp
js/src/vm/Stack.cpp
layout/base/nsDisplayList.cpp
layout/base/nsDisplayList.h
layout/base/nsFrameManager.cpp
layout/base/nsFrameManager.h
layout/generic/nsFrame.cpp
layout/generic/nsFrame.h
layout/generic/nsIFrame.h
layout/generic/nsPlaceholderFrame.cpp
layout/generic/nsPlaceholderFrame.h
layout/tables/nsTableFrame.cpp
layout/tables/nsTableFrame.h
layout/tables/nsTableOuterFrame.cpp
layout/tables/nsTableOuterFrame.h
mfbt/Util.h
mobile/android/base/GeckoApp.java
mobile/android/base/GeckoAppShell.java
mobile/android/base/LauncherShortcuts.java.in
mobile/xul/app/nsBrowserApp.cpp
modules/libpref/src/Preferences.cpp
mozglue/android/APKOpen.cpp
mozglue/android/Makefile.in
netwerk/system/win32/nsNotifyAddrListener.cpp
netwerk/system/win32/nsNotifyAddrListener.h
nsprpub/TAG-INFO
nsprpub/config/prdepend.h
toolkit/components/startup/nsAppStartup.cpp
toolkit/components/telemetry/Telemetry.cpp
toolkit/components/telemetry/TelemetryHistograms.h
toolkit/content/widgets/videocontrols.css
toolkit/content/widgets/videocontrols.xml
toolkit/devtools/debugger/server/dbg-server.js
toolkit/mozapps/extensions/AddonRepository.jsm
toolkit/mozapps/extensions/AddonUpdateChecker.jsm
toolkit/mozapps/extensions/LightweightThemeManager.jsm
toolkit/mozapps/extensions/XPIProvider.jsm
toolkit/mozapps/extensions/content/extensions.js
toolkit/mozapps/installer/packager.mk
toolkit/xre/nsXREDirProvider.cpp
widget/android/nsWindow.cpp
--- a/accessible/src/html/nsHTMLSelectAccessible.cpp
+++ b/accessible/src/html/nsHTMLSelectAccessible.cpp
@@ -426,26 +426,26 @@ nsHTMLSelectOptionAccessible::ContainerW
 ////////////////////////////////////////////////////////////////////////////////
 // nsHTMLSelectOptionAccessible: private methods
 
 nsIContent*
 nsHTMLSelectOptionAccessible::GetSelectState(PRUint64* aState)
 {
   *aState = 0;
 
-  nsIContent *content = mContent;
-  while (content && content->Tag() != nsGkAtoms::select) {
-    content = content->GetParent();
+  nsIContent* selectNode = mContent;
+  while (selectNode && selectNode->Tag() != nsGkAtoms::select) {
+    selectNode = selectNode->GetParent();
   }
 
-  if (content) {
-    nsAccessible* selAcc = GetAccService()->GetAccessible(content, nsnull);
-    if (selAcc) {
-      *aState = selAcc->State();
-      return content;
+  if (selectNode) {
+    nsAccessible* select = mDoc->GetAccessible(selectNode);
+    if (select) {
+      *aState = select->State();
+      return selectNode;
     }
   }
   return nsnull; 
 }
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsHTMLSelectOptGroupAccessible
--- a/accessible/src/xul/nsXULListboxAccessible.cpp
+++ b/accessible/src/xul/nsXULListboxAccessible.cpp
@@ -882,17 +882,17 @@ nsXULListboxAccessible::ContainerWidget(
       do_QueryInterface(mContent->GetParent());
     if (menuListElm) {
       nsCOMPtr<nsIDOMNode> inputElm;
       menuListElm->GetInputField(getter_AddRefs(inputElm));
       if (inputElm) {
         nsCOMPtr<nsINode> inputNode = do_QueryInterface(inputElm);
         if (inputNode) {
           nsAccessible* input = 
-            GetAccService()->GetAccessible(inputNode, nsnull);
+            mDoc->GetAccessible(inputNode);
           return input ? input->ContainerWidget() : nsnull;
         }
       }
     }
   }
   return nsnull;
 }
 
--- a/accessible/src/xul/nsXULMenuAccessible.cpp
+++ b/accessible/src/xul/nsXULMenuAccessible.cpp
@@ -642,17 +642,17 @@ nsXULMenubarAccessible::AreItemsOperable
 nsAccessible*
 nsXULMenubarAccessible::CurrentItem()
 {
   nsMenuBarFrame* menuBarFrame = do_QueryFrame(GetFrame());
   if (menuBarFrame) {
     nsMenuFrame* menuFrame = menuBarFrame->GetCurrentMenuItem();
     if (menuFrame) {
       nsIContent* menuItemNode = menuFrame->GetContent();
-      return GetAccService()->GetAccessible(menuItemNode, nsnull);
+      return mDoc->GetAccessible(menuItemNode);
     }
   }
   return nsnull;
 }
 
 void
 nsXULMenubarAccessible::SetCurrentItem(nsAccessible* aItem)
 {
--- a/accessible/src/xul/nsXULTreeAccessible.cpp
+++ b/accessible/src/xul/nsXULTreeAccessible.cpp
@@ -512,17 +512,17 @@ nsXULTreeAccessible::ContainerWidget() c
       do_QueryInterface(mContent->GetParent());
     if (menuListElm) {
       nsCOMPtr<nsIDOMNode> inputElm;
       menuListElm->GetInputField(getter_AddRefs(inputElm));
       if (inputElm) {
         nsCOMPtr<nsINode> inputNode = do_QueryInterface(inputElm);
         if (inputNode) {
           nsAccessible* input = 
-            GetAccService()->GetAccessible(inputNode, nsnull);
+            mDoc->GetAccessible(inputNode);
           return input ? input->ContainerWidget() : nsnull;
         }
       }
     }
   }
   return nsnull;
 }
 
--- a/b2g/app/nsBrowserApp.cpp
+++ b/b2g/app/nsBrowserApp.cpp
@@ -43,21 +43,16 @@
 #include <stdlib.h>
 #elif defined(XP_UNIX)
 #include <sys/time.h>
 #include <sys/resource.h>
 #endif
 
 #include <stdio.h>
 #include <stdarg.h>
-#include <string.h>
-
-#include "plstr.h"
-#include "prprf.h"
-#include "prenv.h"
 
 #include "nsCOMPtr.h"
 #include "nsILocalFile.h"
 #include "nsStringGlue.h"
 
 #ifdef XP_WIN
 // we want a wmain entry point
 #include "nsWindowsWMain.cpp"
@@ -132,17 +127,17 @@ static const nsDynamicFunctionLoad kXULF
 #ifdef XRE_HAS_DLL_BLOCKLIST
     { "XRE_SetupDllBlocklist", (NSFuncPtr*) &XRE_SetupDllBlocklist },
 #endif
     { "XRE_TelemetryAccumulate", (NSFuncPtr*) &XRE_TelemetryAccumulate },
     { "XRE_main", (NSFuncPtr*) &XRE_main },
     { nsnull, nsnull }
 };
 
-static int do_main(const char *exePath, int argc, char* argv[])
+static int do_main(int argc, char* argv[])
 {
   nsCOMPtr<nsILocalFile> appini;
   nsresult rv;
 
   // Allow firefox.exe to launch XULRunner apps via -app <application.ini>
   // Note that -app must be the *first* argument.
   const char *appDataFile = getenv("XUL_APP_FILE");
   if (appDataFile && *appDataFile) {
@@ -170,43 +165,29 @@ static int do_main(const char *exePath, 
       Output("Couldn't set %s.\n", appEnv);
       return 255;
     }
     argv[2] = argv[0];
     argv += 2;
     argc -= 2;
   }
 
-  int result;
   if (appini) {
     nsXREAppData *appData;
     rv = XRE_CreateAppData(appini, &appData);
     if (NS_FAILED(rv)) {
       Output("Couldn't read application.ini");
       return 255;
     }
-    result = XRE_main(argc, argv, appData);
+    int result = XRE_main(argc, argv, appData);
     XRE_FreeAppData(appData);
-  } else {
-#ifdef XP_WIN
-    // exePath comes from mozilla::BinaryPath::Get, which returns a UTF-8
-    // encoded path, so it is safe to convert it
-    rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(exePath), PR_FALSE,
-                         getter_AddRefs(appini));
-#else
-    rv = NS_NewNativeLocalFile(nsDependentCString(exePath), PR_FALSE,
-                               getter_AddRefs(appini));
-#endif
-    if (NS_FAILED(rv)) {
-      return 255;
-    }
-    result = XRE_main(argc, argv, &sAppData);
+    return result;
   }
 
-  return result;
+  return XRE_main(argc, argv, &sAppData);
 }
 
 int main(int argc, char* argv[])
 {
   char exePath[MAXPATHLEN];
 
   nsresult rv = mozilla::BinaryPath::Get(argv[0], exePath);
   if (NS_FAILED(rv)) {
@@ -279,14 +260,14 @@ int main(int argc, char* argv[])
                               int(newRUsage.ru_majflt - initialRUsage.ru_majflt));
     }
 #endif
   }
 
   int result;
   {
     ScopedLogging log;
-    result = do_main(exePath, argc, argv);
+    result = do_main(argc, argv);
   }
 
   XPCOMGlueShutdown();
   return result;
 }
--- a/browser/app/nsBrowserApp.cpp
+++ b/browser/app/nsBrowserApp.cpp
@@ -47,21 +47,16 @@
 #endif
 
 #ifdef XP_MACOSX
 #include "MacQuirks.h"
 #endif
 
 #include <stdio.h>
 #include <stdarg.h>
-#include <string.h>
-
-#include "plstr.h"
-#include "prprf.h"
-#include "prenv.h"
 
 #include "nsCOMPtr.h"
 #include "nsILocalFile.h"
 #include "nsStringGlue.h"
 
 #ifdef XP_WIN
 // we want a wmain entry point
 #include "nsWindowsWMain.cpp"
@@ -136,17 +131,17 @@ static const nsDynamicFunctionLoad kXULF
 #ifdef XRE_HAS_DLL_BLOCKLIST
     { "XRE_SetupDllBlocklist", (NSFuncPtr*) &XRE_SetupDllBlocklist },
 #endif
     { "XRE_TelemetryAccumulate", (NSFuncPtr*) &XRE_TelemetryAccumulate },
     { "XRE_main", (NSFuncPtr*) &XRE_main },
     { nsnull, nsnull }
 };
 
-static int do_main(const char *exePath, int argc, char* argv[])
+static int do_main(int argc, char* argv[])
 {
   nsCOMPtr<nsILocalFile> appini;
   nsresult rv;
 
   // Allow firefox.exe to launch XULRunner apps via -app <application.ini>
   // Note that -app must be the *first* argument.
   const char *appDataFile = getenv("XUL_APP_FILE");
   if (appDataFile && *appDataFile) {
@@ -174,43 +169,29 @@ static int do_main(const char *exePath, 
       Output("Couldn't set %s.\n", appEnv);
       return 255;
     }
     argv[2] = argv[0];
     argv += 2;
     argc -= 2;
   }
 
-  int result;
   if (appini) {
     nsXREAppData *appData;
     rv = XRE_CreateAppData(appini, &appData);
     if (NS_FAILED(rv)) {
       Output("Couldn't read application.ini");
       return 255;
     }
-    result = XRE_main(argc, argv, appData);
+    int result = XRE_main(argc, argv, appData);
     XRE_FreeAppData(appData);
-  } else {
-#ifdef XP_WIN
-    // exePath comes from mozilla::BinaryPath::Get, which returns a UTF-8
-    // encoded path, so it is safe to convert it
-    rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(exePath), PR_FALSE,
-                         getter_AddRefs(appini));
-#else
-    rv = NS_NewNativeLocalFile(nsDependentCString(exePath), PR_FALSE,
-                               getter_AddRefs(appini));
-#endif
-    if (NS_FAILED(rv)) {
-      return 255;
-    }
-    result = XRE_main(argc, argv, &sAppData);
+    return result;
   }
 
-  return result;
+  return XRE_main(argc, argv, &sAppData);
 }
 
 int main(int argc, char* argv[])
 {
   char exePath[MAXPATHLEN];
 
 #ifdef XP_MACOSX
   TriggerQuirks();
@@ -287,14 +268,14 @@ int main(int argc, char* argv[])
                               int(newRUsage.ru_majflt - initialRUsage.ru_majflt));
     }
 #endif
   }
 
   int result;
   {
     ScopedLogging log;
-    result = do_main(exePath, argc, argv);
+    result = do_main(argc, argv);
   }
 
   XPCOMGlueShutdown();
   return result;
 }
--- a/browser/app/profile/prefs.js
+++ b/browser/app/profile/prefs.js
@@ -1,10 +1,9 @@
 # Mozilla User Preferences
 
 /* Do not edit this file.
  *
  * If you make changes to this file while the browser is running,
  * the changes will be overwritten when the browser exits.
  *
  * To make a manual change to preferences, you can visit the URL about:config
- * For more information, see http://www.mozilla.org/unix/customizing.html#prefs
  */
--- a/browser/base/content/browser-context.inc
+++ b/browser/base/content/browser-context.inc
@@ -256,17 +256,18 @@
                 accesskey="&selectAllCmd.accesskey;"
                 command="cmd_selectAll"/>
       <menuseparator id="context-sep-selectall"/>
       <menuitem id="context-keywordfield"
                 label="&keywordfield.label;"
                 accesskey="&keywordfield.accesskey;"
                 oncommand="AddKeywordForSearchField();"/>
       <menuitem id="context-searchselect"
-                oncommand="BrowserSearch.loadSearch(getBrowserSelection(), true);"/>
+                oncommand="BrowserSearch.loadSearch(getBrowserSelection(), true,
+                                                    'application/x-moz-contextsearch');"/>
       <menuseparator id="frame-sep"/>
       <menu id="frame" label="&thisFrameMenu.label;" accesskey="&thisFrameMenu.accesskey;">
         <menupopup>
           <menuitem id="context-showonlythisframe"
                     label="&showOnlyThisFrameCmd.label;"
                     accesskey="&showOnlyThisFrameCmd.accesskey;"
                     oncommand="gContextMenu.showOnlyThisFrame();"/>
           <menuitem id="context-openframeintab"
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -3520,28 +3520,38 @@ const BrowserSearch = {
    * engine if the search bar is visible, or the default engine otherwise.
    *
    * @param searchText
    *        The search terms to use for the search.
    *
    * @param useNewTab
    *        Boolean indicating whether or not the search should load in a new
    *        tab.
+   *
+   * @param responseType [optional]
+   *        The MIME type that we'd like to receive in response
+   *        to this submission.  If null or the the response type is not supported
+   *        for the search engine, will fallback to "text/html".
    */
-  loadSearch: function BrowserSearch_search(searchText, useNewTab) {
+  loadSearch: function BrowserSearch_search(searchText, useNewTab, responseType) {
     var engine;
 
     // If the search bar is visible, use the current engine, otherwise, fall
     // back to the default engine.
     if (isElementVisible(this.searchBar))
       engine = Services.search.currentEngine;
     else
       engine = Services.search.defaultEngine;
 
-    var submission = engine.getSubmission(searchText); // HTML response
+    var submission = engine.getSubmission(searchText, responseType);
+
+    // If a response type was specified and getSubmission returned null,
+    // fallback to the default response type.
+    if (!submission && responseType)
+      submission = engine.getSubmission(searchText);
 
     // getSubmission can return null if the engine doesn't have a URL
     // with a text/html response type.  This is unlikely (since
     // SearchService._addEngineToStore() should fail for such an engine),
     // but let's be on the safe side.
     if (!submission)
       return;
 
--- a/browser/base/content/test/browser_canonizeURL.js
+++ b/browser/base/content/test/browser_canonizeURL.js
@@ -12,17 +12,17 @@ var pairs = [
   ["example.net", "http://example.net/"],
   ["http://example", "http://example/"],
   ["example:8080", "http://example:8080/"],
   ["ex-ample.foo", "http://ex-ample.foo/"],
   ["example.foo/bar ", "http://example.foo/bar"],
   ["1.1.1.1", "http://1.1.1.1/"],
   ["ftp://example", "ftp://example/"],
   ["ftp.example.bar", "ftp://ftp.example.bar/"],
-  ["ex ample", Services.search.originalDefaultEngine.getSubmission("ex ample").uri.spec],
+  ["ex ample", Services.search.originalDefaultEngine.getSubmission("ex ample", "application/x-moz-keywordsearch").uri.spec],
 ];
 
 function testNext() {
   if (!pairs.length) {
     finish();
     return;
   }
 
--- a/browser/base/content/test/browser_keywordSearch.js
+++ b/browser/base/content/test/browser_keywordSearch.js
@@ -2,22 +2,22 @@
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  **/
 
 var gTests = [
   {
     name: "normal search (search service)",
     testText: "test search",
-    searchURL: Services.search.originalDefaultEngine.getSubmission("test search").uri.spec
+    searchURL: Services.search.originalDefaultEngine.getSubmission("test search", "application/x-moz-keywordsearch").uri.spec
   },
   {
     name: "?-prefixed search (search service)",
     testText: "?   foo  ",
-    searchURL: Services.search.originalDefaultEngine.getSubmission("foo").uri.spec
+    searchURL: Services.search.originalDefaultEngine.getSubmission("foo", "application/x-moz-keywordsearch").uri.spec
   },
   {
     name: "normal search (keyword.url)",
     testText: "test search",
     keywordURLPref: "http://example.com/?q=",
     searchURL: "http://example.com/?q=test+search"
   },
   {
--- a/browser/components/search/test/Makefile.in
+++ b/browser/components/search/test/Makefile.in
@@ -41,17 +41,19 @@ srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir  =  browser/components/search/test
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _BROWSER_TEST_FILES = 	  browser_405664.js \
 	                  browser_addEngine.js \
+	                  browser_contextmenu.js \
 	                  testEngine.xml \
+	                  testEngine_mozsearch.xml \
 	                  testEngine.src \
 	                  browser_426329.js \
 	                  426329.xml \
 	                  browser_483086.js \
 	                  483086-1.xml \
 	                  483086-2.xml \
 	                  test.html \
 	                  $(NULL)
--- a/browser/components/search/test/browser_426329.js
+++ b/browser/components/search/test/browser_426329.js
@@ -3,16 +3,18 @@
 var chromeUtils = {};
 this._scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
                      getService(Ci.mozIJSSubScriptLoader);
 this._scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js", chromeUtils);
 
 function test() {
   waitForExplicitFinish();
 
+  const ENGINE_HTML_BASE = "http://mochi.test:8888/browser/browser/components/search/test/test.html";
+
   var searchEntries = ["test", "More Text", "Some Text"];
   var searchBar = BrowserSearch.searchBar;
   var searchButton = document.getAnonymousElementByAttribute(searchBar,
                      "anonid", "search-go-button");
   ok(searchButton, "got search-go-button");
 
   searchBar.value = "test";
 
@@ -52,31 +54,33 @@ function test() {
   function testReturn() {
     init();
     EventUtils.synthesizeKey("VK_RETURN", {});
     doOnloadOnce(function(event) {
 
       is(gBrowser.tabs.length, preTabNo, "Return key did not open new tab");
       is(event.originalTarget, preSelectedBrowser.contentDocument,
          "Return key loaded results in current tab");
+      is(event.originalTarget.URL, expectedURL(searchBar.value), "Check URL of search page opened");
 
       testAltReturn();
     });
   }
 
   function testAltReturn() {
     init();
     EventUtils.synthesizeKey("VK_RETURN", { altKey: true });
     doOnloadOnce(function(event) {
 
       is(gBrowser.tabs.length, preTabNo + 1, "Alt+Return key added new tab");
       isnot(event.originalTarget, preSelectedBrowser.contentDocument,
             "Alt+Return key loaded results in new tab");
       is(event.originalTarget, gBrowser.contentDocument,
          "Alt+Return key loaded results in foreground tab");
+      is(event.originalTarget.URL, expectedURL(searchBar.value), "Check URL of search page opened");
 
       //Shift key has no effect for now, so skip it
       //testShiftAltReturn();
       testLeftClick();
     });
   }
 
   function testShiftAltReturn() {
@@ -84,59 +88,63 @@ function test() {
     EventUtils.synthesizeKey("VK_RETURN", { shiftKey: true, altKey: true });
     doOnloadOnce(function(event) {
 
       is(gBrowser.tabs.length, preTabNo + 1, "Shift+Alt+Return key added new tab");
       isnot(event.originalTarget, preSelectedBrowser.contentDocument,
             "Shift+Alt+Return key loaded results in new tab");
       isnot(event.originalTarget, gBrowser.contentDocument,
             "Shift+Alt+Return key loaded results in background tab");
+      is(event.originalTarget.URL, expectedURL(searchBar.value), "Check URL of search page opened");
 
       testLeftClick();
     });
   }
 
   function testLeftClick() {
     init();
     simulateClick({ button: 0 }, searchButton);
     doOnloadOnce(function(event) {
 
       is(gBrowser.tabs.length, preTabNo, "LeftClick did not open new tab");
       is(event.originalTarget, preSelectedBrowser.contentDocument,
          "LeftClick loaded results in current tab");
+      is(event.originalTarget.URL, expectedURL(searchBar.value), "Check URL of search page opened");
 
       testMiddleClick();
     });
   }
 
   function testMiddleClick() {
     init();
     simulateClick({ button: 1 }, searchButton);
     doOnloadOnce(function(event) {
 
       is(gBrowser.tabs.length, preTabNo + 1, "MiddleClick added new tab");
       isnot(event.originalTarget, preSelectedBrowser.contentDocument,
             "MiddleClick loaded results in new tab");
       is(event.originalTarget, gBrowser.contentDocument,
          "MiddleClick loaded results in foreground tab");
+      is(event.originalTarget.URL, expectedURL(searchBar.value), "Check URL of search page opened");
 
       testShiftMiddleClick();
     });
   }
 
   function testShiftMiddleClick() {
     init();
     simulateClick({ button: 1, shiftKey: true }, searchButton);
     doOnloadOnce(function(event) {
 
       is(gBrowser.tabs.length, preTabNo + 1, "Shift+MiddleClick added new tab");
       isnot(event.originalTarget, preSelectedBrowser.contentDocument,
             "Shift+MiddleClick loaded results in new tab");
       isnot(event.originalTarget, gBrowser.contentDocument,
             "Shift+MiddleClick loaded results in background tab");
+      is(event.originalTarget.URL, expectedURL(searchBar.value), "Check URL of search page opened");
 
       testDropText();
      });
    }
  
   // prevent the search buttonmenu from opening during the drag tests
   function stopPopup(event) { event.preventDefault(); }
 
@@ -227,16 +235,23 @@ function test() {
     var buttonArg   = aEvent.button   || 0;
     event.initMouseEvent("click", true, true, window,
                           0, 0, 0, 0, 0,
                           ctrlKeyArg, altKeyArg, shiftKeyArg, metaKeyArg,
                           buttonArg, null); 
     aTarget.dispatchEvent(event);
   }
 
+  function expectedURL(aSearchTerms) {
+    var textToSubURI = Cc["@mozilla.org/intl/texttosuburi;1"].
+                       getService(Ci.nsITextToSubURI);
+    var searchArg = textToSubURI.ConvertAndEscape("utf-8", aSearchTerms);
+    return ENGINE_HTML_BASE + "?test=" + searchArg;
+  }
+
   // modified from toolkit/components/satchel/test/test_form_autocomplete.html
   function checkMenuEntries(expectedValues) {
     var actualValues = getMenuEntries();
     is(actualValues.length, expectedValues.length, "Checking length of expected menu");
     for (var i = 0; i < expectedValues.length; i++)
       is(actualValues[i], expectedValues[i], "Checking menu entry #"+i);
   }
 
new file mode 100644
--- /dev/null
+++ b/browser/components/search/test/browser_contextmenu.js
@@ -0,0 +1,97 @@
+/* Any copyright is dedicated to the Public Domain.
+ *  * http://creativecommons.org/publicdomain/zero/1.0/ */
+/*
+ * Test searching for the selected text using the context menu
+ */
+
+function test() {
+  waitForExplicitFinish();
+
+  const ss = Services.search;
+  const ENGINE_NAME = "Foo";
+  var contextMenu;
+
+  function observer(aSub, aTopic, aData) {
+    switch (aData) {
+      case "engine-added":
+        var engine = ss.getEngineByName(ENGINE_NAME);
+        ok(engine, "Engine was added.");
+        //XXX Bug 493051
+        //ss.currentEngine = engine;
+        break;
+      case "engine-current":
+        ok(ss.currentEngine.name == ENGINE_NAME, "currentEngine set");
+        startTest();
+        break;
+      case "engine-removed":
+        Services.obs.removeObserver(observer, "browser-search-engine-modified");
+        finish();
+        break;
+    }
+  }
+
+  Services.obs.addObserver(observer, "browser-search-engine-modified", false);
+  ss.addEngine("http://mochi.test:8888/browser/browser/components/search/test/testEngine_mozsearch.xml",
+               Ci.nsISearchEngine.DATA_XML, "data:image/x-icon,%00",
+               false);
+
+  function startTest() {
+    contextMenu = document.getElementById("contentAreaContextMenu");
+    ok(contextMenu, "Got context menu XUL");
+
+    doOnloadOnce(testContextMenu);
+    var tab = gBrowser.addTab("data:text/plain,test%20search");
+    gBrowser.selectedTab = tab;
+  }
+
+  function testContextMenu() {
+    function rightClickOnDocument(){
+      var clickTarget = content.document.body;
+      var eventDetails = { type: "contextmenu", button: 2 };
+      EventUtils.synthesizeMouseAtCenter(clickTarget, eventDetails, content);
+      SimpleTest.executeSoon(checkContextMenu);
+    }
+
+    // check the search menu item and then perform a search
+    function checkContextMenu() {
+      var searchItem = contextMenu.getElementsByAttribute("id", "context-searchselect")[0];
+      ok(searchItem, "Got search context menu item");
+      is(searchItem.label, 'Search ' + ENGINE_NAME + ' for "test search"', "Check context menu label");
+      is(searchItem.disabled, false, "Check that search context menu item is enabled");
+      searchItem.click();
+    }
+
+    function checkSearchURL(event){
+      is(event.originalTarget.URL,
+         "http://mochi.test:8888/browser/browser/components/search/test/?test=test+search&ie=utf-8&client=app&channel=contextsearch",
+         "Checking context menu search URL");
+      finalize();
+    }
+
+    doOnloadOnce(checkSearchURL);
+
+    // select the text on the page
+    var selectAllItem = contextMenu.getElementsByAttribute("id", "context-selectall")[0];
+    ok(selectAllItem, "Got select all context menu item");
+    selectAllItem.click();
+
+    // wait for the selection to take effect
+    SimpleTest.executeSoon(rightClickOnDocument);
+  }
+
+  function finalize() {
+    while (gBrowser.tabs.length != 1) {
+      gBrowser.removeTab(gBrowser.tabs[0]);
+    }
+    content.location.href = "about:blank";
+    var engine = ss.getEngineByName(ENGINE_NAME);
+    ss.removeEngine(engine);
+  }
+
+  function doOnloadOnce(callback) {
+    gBrowser.addEventListener("DOMContentLoaded", function handleLoad(event) {
+      gBrowser.removeEventListener("DOMContentLoaded", handleLoad, true);
+      callback(event);
+    }, true);
+  }
+}
copy from browser/components/search/test/testEngine.xml
copy to browser/components/search/test/testEngine_mozsearch.xml
--- a/browser/components/search/test/testEngine.xml
+++ b/browser/components/search/test/testEngine_mozsearch.xml
@@ -1,12 +1,25 @@
-<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/"
-                       xmlns:moz="http://www.mozilla.org/2006/browser/search/">
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
   <ShortName>Foo</ShortName>
   <Description>Foo Search</Description>
   <InputEncoding>utf-8</InputEncoding>
   <Image width="16" height="16">data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAABGklEQVQoz2NgGB6AnZ1dUlJSXl4eSDIyMhLW4Ovr%2B%2Fr168uXL69Zs4YoG%2BLi4i5dusTExMTGxsbNzd3f37937976%2BnpmZmagbHR09J49e5YvX66kpATVEBYW9ubNm2nTphkbG7e2tp44cQLIuHfvXm5urpaWFlDKysqqu7v73LlzECMYIiIiHj58mJCQoKKicvXq1bS0NKBgW1vbjh074uPjgeqAXE1NzSdPnvDz84M0AEUvXLgAsW379u1z5swBen3jxo2zZ892cHB4%2BvQp0KlAfwI1cHJyghQFBwfv2rULokFXV%2FfixYu7d%2B8GGqGgoMDKyrpu3br9%2B%2FcDuXl5eVA%2FAEWBfoWHAdAYoNuAYQ0XAeoUERFhGDYAAPoUaT2dfWJuAAAAAElFTkSuQmCC</Image>
-  <Url type="text/html" method="GET" template="http://mochi.test:8888/browser/browser/components/search/test/?search">
+  <Url type="application/x-suggestions+json" method="GET" template="http://mochi.test:8888/browser/browser/components/search/test/?suggestions&amp;locale={moz:locale}&amp;test={searchTerms}"/>
+  <Url type="text/html" method="GET" template="http://mochi.test:8888/browser/browser/components/search/test/">
     <Param name="test" value="{searchTerms}"/>
+    <Param name="ie" value="utf-8"/>
+    <MozParam name="client" condition="defaultEngine" trueValue="app-default" falseValue="app"/>
   </Url>
-  <moz:SearchForm>http://mochi.test:8888/browser/browser/components/search/test/</moz:SearchForm>
-  <moz:Alias>fooalias</moz:Alias>
-</OpenSearchDescription>
+  <Url type="application/x-moz-keywordsearch" method="GET" template="http://mochi.test:8888/browser/browser/components/search/test/">
+    <Param name="test" value="{searchTerms}"/>
+    <Param name="ie" value="utf-8"/>
+    <MozParam name="client" condition="defaultEngine" trueValue="app-default" falseValue="app"/>
+    <Param name="channel" value="keywordsearch"/>
+  </Url>
+  <Url type="application/x-moz-contextsearch" method="GET" template="http://mochi.test:8888/browser/browser/components/search/test/">
+    <Param name="test" value="{searchTerms}"/>
+    <Param name="ie" value="utf-8"/>
+    <MozParam name="client" condition="defaultEngine" trueValue="app-default" falseValue="app"/>
+    <Param name="channel" value="contextsearch"/>
+  </Url>
+  <SearchForm>http://mochi.test:8888/browser/browser/components/search/test/</SearchForm>
+</SearchPlugin>
new file mode 100644
--- /dev/null
+++ b/browser/locales/en-US/searchplugins/google-params.inc
@@ -0,0 +1,15 @@
+  <Param name="q" value="{searchTerms}"/>
+  <Param name="ie" value="utf-8"/>
+  <Param name="oe" value="utf-8"/>
+  <Param name="aq" value="t"/>
+  <!-- Dynamic parameters -->
+  <Param name="rls" value="{moz:distributionID}:{moz:locale}:{moz:official}"/>
+#if MOZ_UPDATE_CHANNEL == beta
+  <MozParam name="client" condition="defaultEngine" trueValue="firefox-beta" falseValue="firefox"/>
+#elif MOZ_UPDATE_CHANNEL == aurora
+  <MozParam name="client" condition="defaultEngine" trueValue="firefox-aurora" falseValue="firefox"/>
+#elif MOZ_UPDATE_CHANNEL == nightly
+  <MozParam name="client" condition="defaultEngine" trueValue="firefox-nightly" falseValue="firefox"/>
+#else
+  <MozParam name="client" condition="defaultEngine" trueValue="firefox-a" falseValue="firefox"/>
+#endif
--- a/browser/locales/en-US/searchplugins/google.xml
+++ b/browser/locales/en-US/searchplugins/google.xml
@@ -1,25 +1,21 @@
 <SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
 <ShortName>Google</ShortName>
 <Description>Google Search</Description>
 <InputEncoding>UTF-8</InputEncoding>
 <Image width="16" height="16">data:image/png;base64,AAABAAEAEBAAAAEAGABoAwAAFgAAACgAAAAQAAAAIAAAAAEAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADs9Pt8xetPtu9FsfFNtu%2BTzvb2%2B%2Fne4dFJeBw0egA%2FfAJAfAA8ewBBegAAAAD%2B%2FPtft98Mp%2BwWsfAVsvEbs%2FQeqvF8xO7%2F%2F%2F63yqkxdgM7gwE%2FggM%2BfQA%2BegBDeQDe7PIbotgQufcMufEPtfIPsvAbs%2FQvq%2Bfz%2Bf%2F%2B%2B%2FZKhR05hgBBhQI8hgBAgAI9ewD0%2B%2Fg3pswAtO8Cxf4Kw%2FsJvvYAqupKsNv%2B%2Fv7%2F%2FP5VkSU0iQA7jQA9hgBDgQU%2BfQH%2F%2Ff%2FQ6fM4sM4KsN8AteMCruIqqdbZ7PH8%2Fv%2Fg6Nc%2Fhg05kAA8jAM9iQI%2BhQA%2BgQDQu6b97uv%2F%2F%2F7V8Pqw3eiWz97q8%2Ff%2F%2F%2F%2F7%2FPptpkkqjQE4kwA7kAA5iwI8iAA8hQCOSSKdXjiyflbAkG7u2s%2F%2B%2F%2F39%2F%2F7r8utrqEYtjQE8lgA7kwA7kwA9jwA9igA9hACiWSekVRyeSgiYSBHx6N%2F%2B%2Fv7k7OFRmiYtlAA5lwI7lwI4lAA7kgI9jwE9iwI4iQCoVhWcTxCmb0K%2BooT8%2Fv%2F7%2F%2F%2FJ2r8fdwI1mwA3mQA3mgA8lAE8lAE4jwA9iwE%2BhwGfXifWvqz%2B%2Ff%2F58u%2Fev6Dt4tr%2B%2F%2F2ZuIUsggA7mgM6mAM3lgA5lgA6kQE%2FkwBChwHt4dv%2F%2F%2F728ei1bCi7VAC5XQ7kz7n%2F%2F%2F6bsZkgcB03lQA9lgM7kwA2iQktZToPK4r9%2F%2F%2F9%2F%2F%2FSqYK5UwDKZAS9WALIkFn%2B%2F%2F3%2F%2BP8oKccGGcIRJrERILYFEMwAAuEAAdX%2F%2Ff7%2F%2FP%2B%2BfDvGXQLIZgLEWgLOjlf7%2F%2F%2F%2F%2F%2F9QU90EAPQAAf8DAP0AAfMAAOUDAtr%2F%2F%2F%2F7%2B%2Fu2bCTIYwDPZgDBWQDSr4P%2F%2Fv%2F%2F%2FP5GRuABAPkAA%2FwBAfkDAPAAAesAAN%2F%2F%2B%2Fz%2F%2F%2F64g1C5VwDMYwK8Yg7y5tz8%2Fv%2FV1PYKDOcAAP0DAf4AAf0AAfYEAOwAAuAAAAD%2F%2FPvi28ymXyChTATRrIb8%2F%2F3v8fk6P8MAAdUCAvoAAP0CAP0AAfYAAO4AAACAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQAA</Image>
 <Url type="application/x-suggestions+json" method="GET" template="http://suggestqueries.google.com/complete/search?output=firefox&amp;client=firefox&amp;hl={moz:locale}&amp;q={searchTerms}"/>
 <Url type="text/html" method="GET" template="http://www.google.com/search">
-  <Param name="q" value="{searchTerms}"/>
-  <Param name="ie" value="utf-8"/>
-  <Param name="oe" value="utf-8"/>
-  <Param name="aq" value="t"/>
-  <!-- Dynamic parameters -->
-  <Param name="rls" value="{moz:distributionID}:{moz:locale}:{moz:official}"/>
-#if MOZ_UPDATE_CHANNEL == beta
-  <MozParam name="client" condition="defaultEngine" trueValue="firefox-beta" falseValue="firefox"/>
-#elif MOZ_UPDATE_CHANNEL == aurora
-  <MozParam name="client" condition="defaultEngine" trueValue="firefox-aurora" falseValue="firefox"/>
-#elif MOZ_UPDATE_CHANNEL == nightly
-  <MozParam name="client" condition="defaultEngine" trueValue="firefox-nightly" falseValue="firefox"/>
-#else
-  <MozParam name="client" condition="defaultEngine" trueValue="firefox-a" falseValue="firefox"/>
-#endif
+#include google-params.inc
+</Url>
+<!-- Keyword search URL is the same as the default, but with an additional parameter -->
+<Url type="application/x-moz-keywordsearch" method="GET" template="http://www.google.com/search">
+#include google-params.inc
+  <Param name="channel" value="fflb"/>
+</Url>
+<!-- Context/Right-click search URL is the same as the default, but with an additional parameter -->
+<Url type="application/x-moz-contextsearch" method="GET" template="http://www.google.com/search">
+#include google-params.inc
+  <Param name="channel" value="rcs"/>
 </Url>
 <SearchForm>http://www.google.com/</SearchForm>
 </SearchPlugin>
--- a/build/Makefile.in
+++ b/build/Makefile.in
@@ -79,23 +79,23 @@ endif
 APP_BUILDID := $(shell cat $(DEPTH)/config/buildid)
 APP_INI_DEPS += $(DEPTH)/config/buildid
 
 DEFINES += -DGRE_MILESTONE=$(GRE_MILESTONE) -DAPP_BUILDID=$(APP_BUILDID)
 
 DEFINES += -DMOZ_APP_VERSION="$(MOZ_APP_VERSION)"
 APP_INI_DEPS += $(DEPTH)/config/autoconf.mk
 
-MOZ_SOURCE_STAMP ?= $(firstword $(shell hg -R $(topsrcdir) parent --template="{node|short}\n" 2>/dev/null))
+MOZ_SOURCE_STAMP ?= $(firstword $(shell hg -R $(topsrcdir)/$(MOZ_BUILD_APP)/.. parent --template="{node|short}\n" 2>/dev/null))
 ifdef MOZ_SOURCE_STAMP
 DEFINES += -DMOZ_SOURCE_STAMP="$(MOZ_SOURCE_STAMP)"
 endif
 
 _dollar=$$
-SOURCE_REPO := $(shell cd $(topsrcdir) && hg showconfig paths.default 2>/dev/null | head -n1 | sed -e "s/^ssh:/http:/" -e "s/\/$(_dollar)//" )
+SOURCE_REPO := $(shell cd $(topsrcdir)/$(MOZ_BUILD_APP)/.. && hg showconfig paths.default 2>/dev/null | head -n1 | sed -e "s/^ssh:/http:/" -e "s/\/$(_dollar)//" )
 ifdef SOURCE_REPO
 DEFINES += -DMOZ_SOURCE_REPO="$(SOURCE_REPO)"
 endif
 
 DEFINES += \
   -DMOZ_APP_BASENAME="$(MOZ_APP_BASENAME)" \
   -DMOZ_APP_VENDOR="$(MOZ_APP_VENDOR)" \
   -DMOZ_APP_ID="$(MOZ_APP_ID)" \
--- a/build/mobile/devicemanagerSUT.py
+++ b/build/mobile/devicemanagerSUT.py
@@ -210,22 +210,21 @@ class DeviceManagerSUT(DeviceManager):
             return None
 
           # If something goes wrong in the agent it will send back a string that
           # starts with '##AGENT-ERROR##'
           if (self.agentErrorRE.match(temp)):
             data = temp
             break
 
-          lines = temp.split('\n')
+          data += temp
 
-          for line in lines:
+          for line in data.splitlines():
             if (promptre.match(line)):
               found = True
-          data += temp
 
           # If we violently lose the connection to the device, this loop tends to spin,
           # this guard prevents that
           if (temp == ''):
             loopguard += 1
 
     if (shouldCloseSocket == True):
       try:
@@ -525,49 +524,16 @@ class DeviceManagerSUT(DeviceManager):
       cmdline += " > " + outputFile
     
     # Prepend our env to the command 
     cmdline = '%s %s' % (self.formatEnvString(env), cmdline)
 
     if self.fireProcess(cmdline, failIfRunning) is None:
       return None
     return outputFile
-  
-  # iterates process list and returns pid if exists, otherwise None
-  # external function
-  # returns:
-  #  success: pid
-  #  failure: None
-  def processExist(self, appname):
-    pid = None
-
-    #filter out extra spaces
-    parts = filter(lambda x: x != '', appname.split(' '))
-    appname = ' '.join(parts)
-
-    #filter out the quoted env string if it exists
-    #ex: '"name=value;name2=value2;etc=..." process args' -> 'process args'
-    parts = appname.split('"')
-    if (len(parts) > 2):
-      appname = ' '.join(parts[2:]).strip()
-  
-    pieces = appname.split(' ')
-    parts = pieces[0].split('/')
-    app = parts[-1]
-
-    procList = self.getProcessList()
-    if (procList == []):
-      return None
-      
-    for proc in procList:
-      procName = proc[1].split('/')[-1]
-      if (procName == app):
-        pid = proc[0]
-        break
-    return pid
 
   # external function
   # returns:
   #  success: output from testagent
   #  failure: None
   def killProcess(self, appname):
     try:
       data = self.verifySendCMD(['kill ' + appname])
--- a/build/unix/build-toolchain/build-gcc.py
+++ b/build/unix/build-toolchain/build-gcc.py
@@ -141,17 +141,17 @@ base_dir = "/builds/slave/moz-toolschain
 
 source_dir = base_dir + "/src"
 build_dir  = base_dir + "/build"
 
 def build_source_dir(prefix, version):
     return source_dir + '/' + prefix + version
 
 binutils_version = "2.21.1"
-glibc_version = "2.13" #FIXME: should probably use 2.5.1
+glibc_version = "2.12.2" #FIXME: should probably use 2.5.1
 tar_version = "1.26"
 gcc_version = "4.5.2"
 mpfr_version = "2.4.2"
 gmp_version = "5.0.1"
 mpc_version = "0.8.1"
 
 binutils_source_uri = "http://ftp.gnu.org/gnu/binutils/binutils-%sa.tar.bz2" % \
     binutils_version
@@ -184,16 +184,17 @@ gmp_source_dir  = build_source_dir('gmp-
 gcc_source_dir  = build_source_dir('gcc-', gcc_version)
 
 if not os.path.exists(source_dir):
     os.makedirs(source_dir)
     extract(binutils_source_tar, source_dir)
     patch('binutils-deterministic.patch', 1, binutils_source_dir)
     extract(glibc_source_tar, source_dir)
     patch('glibc-deterministic.patch', 1, glibc_source_dir)
+    run_in(glibc_source_dir, ["autoconf"])
     extract(tar_source_tar, source_dir)
     extract(mpc_source_tar, source_dir)
     extract(mpfr_source_tar, source_dir)
     extract(gmp_source_tar, source_dir)
     extract(gcc_source_tar, source_dir)
     patch('plugin_finish_decl.diff', 0, gcc_source_dir)
     patch('pr49911.diff', 1, gcc_source_dir)
     patch('r159628-r163231-r171807.patch', 1, gcc_source_dir)
--- a/config/config.mk
+++ b/config/config.mk
@@ -251,17 +251,19 @@ endif # MOZ_DEBUG
 # the Makefile wants static CRT linking.
 ifeq ($(MOZ_MEMORY)_$(USE_STATIC_LIBS),1_1)
 # Disable default CRT libs and add the right lib path for the linker
 MOZ_GLUE_LDFLAGS=
 endif
 
 endif # WINNT && !GNU_CC
 
-ifndef MOZ_GLUE_PROGRAM_LDFLAGS
+ifdef MOZ_GLUE_PROGRAM_LDFLAGS
+DEFINES += -DMOZ_GLUE_IN_PROGRAM
+else
 MOZ_GLUE_PROGRAM_LDFLAGS=$(MOZ_GLUE_LDFLAGS)
 endif
 
 #
 # Build using PIC by default
 #
 _ENABLE_PIC=1
 
@@ -805,8 +807,26 @@ OBJ_SUFFIX := $(_OBJ_SUFFIX)
 # for the first pass.
 ifndef NO_PROFILE_GUIDED_OPTIMIZE
 ifdef MOZ_PROFILE_GENERATE
 ifdef GNU_CC
 OBJ_SUFFIX := i_o
 endif
 endif
 endif
+
+# EXPAND_LIBNAME - $(call EXPAND_LIBNAME,foo)
+# expands to $(LIB_PREFIX)foo.$(LIB_SUFFIX) or -lfoo, depending on linker
+# arguments syntax. Should only be used for system libraries
+
+# EXPAND_LIBNAME_PATH - $(call EXPAND_LIBNAME_PATH,foo,dir)
+# expands to dir/$(LIB_PREFIX)foo.$(LIB_SUFFIX)
+
+# EXPAND_MOZLIBNAME - $(call EXPAND_MOZLIBNAME,foo)
+# expands to $(DIST)/lib/$(LIB_PREFIX)foo.$(LIB_SUFFIX)
+
+ifdef GNU_CC
+EXPAND_LIBNAME = $(addprefix -l,$(1))
+else
+EXPAND_LIBNAME = $(foreach lib,$(1),$(LIB_PREFIX)$(lib).$(LIB_SUFFIX))
+endif
+EXPAND_LIBNAME_PATH = $(foreach lib,$(1),$(2)/$(LIB_PREFIX)$(lib).$(LIB_SUFFIX))
+EXPAND_MOZLIBNAME = $(foreach lib,$(1),$(DIST)/lib/$(LIB_PREFIX)$(lib).$(LIB_SUFFIX))
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -87,34 +87,16 @@ endif
 _VPATH_SRCS = $(abspath $<)
 
 # Add $(DIST)/lib to VPATH so that -lfoo dependencies are followed
 VPATH += $(DIST)/lib
 ifdef LIBXUL_SDK
 VPATH += $(LIBXUL_SDK)/lib
 endif
 
-# EXPAND_LIBNAME - $(call EXPAND_LIBNAME,foo)
-# expands to $(LIB_PREFIX)foo.$(LIB_SUFFIX) or -lfoo, depending on linker
-# arguments syntax. Should only be used for system libraries
-
-# EXPAND_LIBNAME_PATH - $(call EXPAND_LIBNAME_PATH,foo,dir)
-# expands to dir/$(LIB_PREFIX)foo.$(LIB_SUFFIX)
-
-# EXPAND_MOZLIBNAME - $(call EXPAND_MOZLIBNAME,foo)
-# expands to $(DIST)/lib/$(LIB_PREFIX)foo.$(LIB_SUFFIX)
-
-ifdef GNU_CC
-EXPAND_LIBNAME = $(addprefix -l,$(1))
-else
-EXPAND_LIBNAME = $(foreach lib,$(1),$(LIB_PREFIX)$(lib).$(LIB_SUFFIX))
-endif
-EXPAND_LIBNAME_PATH = $(foreach lib,$(1),$(2)/$(LIB_PREFIX)$(lib).$(LIB_SUFFIX))
-EXPAND_MOZLIBNAME = $(foreach lib,$(1),$(DIST)/lib/$(LIB_PREFIX)$(lib).$(LIB_SUFFIX))
-
 ifdef EXTRA_DSO_LIBS
 EXTRA_DSO_LIBS	:= $(call EXPAND_MOZLIBNAME,$(EXTRA_DSO_LIBS))
 endif
 
 ################################################################################
 # Testing frameworks support
 ################################################################################
 
--- a/configure.in
+++ b/configure.in
@@ -9122,22 +9122,22 @@ if test -z "$MOZ_NATIVE_NSPR"; then
     ac_configure_args="$ac_configure_args --with-nspr-cflags='$NSPR_CFLAGS'"
     ac_configure_args="$ac_configure_args --with-nspr-libs='$NSPR_LIBS'"
 fi
 ac_configure_args="$ac_configure_args --with-dist-dir=../../dist"
 ac_configure_args="$ac_configure_args --prefix=$dist"
 ac_configure_args="$ac_configure_args --with-sync-build-files=$_topsrcdir"
 if test "$MOZ_MEMORY"; then
    ac_configure_args="$ac_configure_args --enable-jemalloc"
-   if test -n "$MOZ_GLUE_LDFLAGS"; then
-     export MOZ_GLUE_LDFLAGS
-   fi
-   if test -n "$MOZ_GLUE_PROGRAM_LDFLAGS"; then
-     export MOZ_GLUE_PROGRAM_LDFLAGS
-   fi
+fi
+if test -n "$MOZ_GLUE_LDFLAGS"; then
+   export MOZ_GLUE_LDFLAGS
+fi
+if test -n "$MOZ_GLUE_PROGRAM_LDFLAGS"; then
+   export MOZ_GLUE_PROGRAM_LDFLAGS
 fi
 export MOZ_APP_NAME
 AC_OUTPUT_SUBDIRS(js/src)
 ac_configure_args="$_SUBDIR_CONFIG_ARGS"
 
 fi # COMPILE_ENVIRONMENT && !LIBXUL_SDK_DIR
 
 dnl Prevent the regeneration of autoconf.mk forcing rebuilds of the world
--- a/content/base/public/nsINode.h
+++ b/content/base/public/nsINode.h
@@ -306,28 +306,34 @@ public:
   NS_DECL_DOM_MEMORY_REPORTER_SIZEOF
 
   friend class nsNodeUtils;
   friend class nsNodeWeakReference;
   friend class nsNodeSupportsWeakRefTearoff;
   friend class nsAttrAndChildArray;
 
 #ifdef MOZILLA_INTERNAL_API
+  static nsINode *sOrphanNodeHead;
+
   nsINode(already_AddRefed<nsINodeInfo> aNodeInfo)
   : mNodeInfo(aNodeInfo),
     mParent(nsnull),
     mFlags(0),
-    mBoolFlags(0),
-    mNextSibling(nsnull),
-    mPreviousSibling(nsnull),
+    mBoolFlags(1 << NodeIsOrphan),
+    mNextOrphanNode(sOrphanNodeHead->mNextOrphanNode),
+    mPreviousOrphanNode(sOrphanNodeHead),
     mFirstChild(nsnull),
     mSlots(nsnull)
   {
+    NS_ASSERTION(GetBoolFlag(NodeIsOrphan),
+                 "mBoolFlags not initialized correctly!");
+
+    mNextOrphanNode->mPreviousOrphanNode = this;
+    sOrphanNodeHead->mNextOrphanNode = this;
   }
-
 #endif
 
   virtual ~nsINode();
 
   /**
    * Bit-flags to pass (or'ed together) to IsNodeOfType()
    */
   enum {
@@ -1098,18 +1104,73 @@ public:
     return NS_OK;
   }
   nsresult LookupNamespaceURI(const nsAString& aNamespacePrefix,
                               nsAString& aNamespaceURI);
 
   nsresult IsEqualNode(nsIDOMNode* aOther, bool* aReturn);
   bool IsEqualTo(nsINode* aOther);
 
-  nsIContent* GetNextSibling() const { return mNextSibling; }
-  nsIContent* GetPreviousSibling() const { return mPreviousSibling; }
+  nsIContent* GetNextSibling() const
+  {
+    return NS_UNLIKELY(IsOrphan()) ? nsnull : mNextSibling;
+  }
+
+  nsIContent* GetPreviousSibling() const
+  {
+    return NS_UNLIKELY(IsOrphan()) ? nsnull : mPreviousSibling;
+  }
+
+  // Returns true if this node is an orphan node
+  bool IsOrphan() const
+  {
+#ifdef MOZILLA_INTERNAL_API
+    NS_ASSERTION(this != sOrphanNodeHead, "Orphan node head orphan check?!");
+#endif
+
+    return GetBoolFlag(NodeIsOrphan);
+  }
+
+#ifdef MOZILLA_INTERNAL_API
+  // Mark this node as an orphan node. This marking is only relevant
+  // for this node itself, not its children. Its children are not
+  // considered orphan until they themselves are removed from their
+  // parent and get marked as orphans.
+  void MarkAsOrphan()
+  {
+    NS_ASSERTION(!IsOrphan(), "Orphan node orphaned again?");
+    NS_ASSERTION(this != sOrphanNodeHead, "Orphan node head orphaned?!");
+
+    mNextOrphanNode = sOrphanNodeHead->mNextOrphanNode;
+    mPreviousOrphanNode = sOrphanNodeHead;
+    mNextOrphanNode->mPreviousOrphanNode = this;
+    sOrphanNodeHead->mNextOrphanNode = this;
+
+    SetBoolFlag(NodeIsOrphan);
+  }
+
+  // Unmark this node as an orphan node. Do this before inserting this
+  // node into a parent or otherwise associating it with some other
+  // owner.
+  void MarkAsNonOrphan()
+  {
+    NS_ASSERTION(IsOrphan(), "Non-orphan node un-orphaned");
+    NS_ASSERTION(this != sOrphanNodeHead, "Orphan node head unorphaned?!");
+    NS_ASSERTION(!mParent, "Must not have a parent here!");
+
+    mPreviousOrphanNode->mNextOrphanNode = mNextOrphanNode;
+    mNextOrphanNode->mPreviousOrphanNode = mPreviousOrphanNode;
+    mPreviousOrphanNode = nsnull;
+    mNextOrphanNode = nsnull;
+
+    ClearBoolFlag(NodeIsOrphan);
+  }
+#endif
+
+  static void Init();
 
   /**
    * Get the next node in the pre-order tree traversal of the DOM.  If
    * aRoot is non-null, then it must be an ancestor of |this|
    * (possibly equal to |this|) and only nodes that are descendants of
    * aRoot, not including aRoot itself, will be returned.  Returns
    * null if there are no more nodes to traverse.
    */
@@ -1246,16 +1307,18 @@ private:
     NodeIsCCBlackTree,
     // Maybe set if the node is a root of a subtree 
     // which needs to be kept in the purple buffer.
     NodeIsPurpleRoot,
     // Set if the node has an explicit base URI stored
     NodeHasExplicitBaseURI,
     // Set if the element has some style states locked
     ElementHasLockedStyleStates,
+    // Set if the node is an orphan node.
+    NodeIsOrphan,
     // Guard value
     BooleanFlagCount
   };
 
   void SetBoolFlag(BooleanFlag name, bool value) {
     PR_STATIC_ASSERT(BooleanFlagCount <= 8*sizeof(mBoolFlags));
     mBoolFlags = (mBoolFlags & ~(1 << name)) | (value << name);
   }
@@ -1461,18 +1524,34 @@ protected:
 
   PRUint32 mFlags;
 
 private:
   // Boolean flags.
   PRUint32 mBoolFlags;
 
 protected:
-  nsIContent* mNextSibling;
-  nsIContent* mPreviousSibling;
+  union {
+    // mNextSibling is used when this node is part of a DOM tree
+    nsIContent* mNextSibling;
+
+    // mNextOrphanNode is used when this is in the linked list of
+    // orphan nodes.
+    nsINode *mNextOrphanNode;
+  };
+
+  union {
+    // mPreviousSibling is used when this node is part of a DOM tree
+    nsIContent* mPreviousSibling;
+
+    // mPreviousOrphanNode is used when this is in the linked list of
+    // orphan nodes.
+    nsINode* mPreviousOrphanNode;
+  };
+
   nsIContent* mFirstChild;
 
   // Storage for more members that are usually not needed; allocated lazily.
   nsSlots* mSlots;
 };
 
 
 extern const nsIID kThisPtrOffsetsSID;
--- a/content/base/src/Makefile.in
+++ b/content/base/src/Makefile.in
@@ -76,16 +76,17 @@ EXPORTS_mozilla/dom = \
 
 LOCAL_INCLUDES = -I$(srcdir)/js/xpconnect/src
 
 CPPSRCS		= \
 		mozSanitizingSerializer.cpp \
 		nsAtomListUtils.cpp \
 		nsAttrAndChildArray.cpp \
 		nsAttrValue.cpp \
+		nsAttrValueOrString.cpp \
 		nsCCUncollectableMarker.cpp \
 		nsChannelPolicy.cpp \
 		nsCommentNode.cpp \
 		nsContentAreaDragDrop.cpp \
 		nsContentIterator.cpp \
 		nsContentList.cpp \
 		nsContentPolicy.cpp \
 		nsContentSink.cpp \
--- a/content/base/src/nsAttrAndChildArray.cpp
+++ b/content/base/src/nsAttrAndChildArray.cpp
@@ -227,23 +227,29 @@ nsAttrAndChildArray::RemoveChildAt(PRUin
 already_AddRefed<nsIContent>
 nsAttrAndChildArray::TakeChildAt(PRUint32 aPos)
 {
   NS_ASSERTION(aPos < ChildCount(), "out-of-bounds");
 
   PRUint32 childCount = ChildCount();
   void** pos = mImpl->mBuffer + AttrSlotsSize() + aPos;
   nsIContent* child = static_cast<nsIContent*>(*pos);
+
+  MOZ_ASSERT(!child->IsOrphan(), "Child should not be an orphan here");
+
   if (child->mPreviousSibling) {
     child->mPreviousSibling->mNextSibling = child->mNextSibling;
   }
   if (child->mNextSibling) {
     child->mNextSibling->mPreviousSibling = child->mPreviousSibling;
   }
-  child->mPreviousSibling = child->mNextSibling = nsnull;
+
+  // Mark the child as an orphan now that it's no longer associated
+  // with its old parent.
+  child->MarkAsOrphan();
 
   memmove(pos, pos + 1, (childCount - aPos - 1) * sizeof(nsIContent*));
   SetChildCount(childCount - 1);
 
   return child;
 }
 
 PRInt32
@@ -652,28 +658,31 @@ nsAttrAndChildArray::Clear()
 
   nsAutoScriptBlocker scriptBlocker;
   PRUint32 end = slotCount * ATTRSIZE + ChildCount();
   for (i = slotCount * ATTRSIZE; i < end; ++i) {
     nsIContent* child = static_cast<nsIContent*>(mImpl->mBuffer[i]);
     // making this false so tree teardown doesn't end up being
     // O(N*D) (number of nodes times average depth of tree).
     child->UnbindFromTree(false); // XXX is it better to let the owner do this?
-    // Make sure to unlink our kids from each other, since someone
-    // else could stil be holding references to some of them.
+    // Mark the child as an orphan now that it's no longer a child of
+    // its old parent, and make sure to unlink our kids from each
+    // other, since someone else could stil be holding references to
+    // some of them.
+
+    child->MarkAsOrphan();
 
     // XXXbz We probably can't push this assignment down into the |aNullParent|
     // case of UnbindFromTree because we still need the assignment in
     // RemoveChildAt.  In particular, ContentRemoved fires between
     // RemoveChildAt and UnbindFromTree, and in ContentRemoved the sibling
     // chain needs to be correct.  Though maybe we could set the prev and next
     // to point to each other but keep the kid being removed pointing to them
     // through ContentRemoved so consumers can find where it used to be in the
     // list?
-    child->mPreviousSibling = child->mNextSibling = nsnull;
     NS_RELEASE(child);
   }
 
   SetAttrSlotAndChildCount(0, 0);
 }
 
 PRUint32
 nsAttrAndChildArray::NonMappedAttrCount() const
@@ -817,18 +826,26 @@ nsAttrAndChildArray::AddAttrSlot()
 
   return true;
 }
 
 inline void
 nsAttrAndChildArray::SetChildAtPos(void** aPos, nsIContent* aChild,
                                    PRUint32 aIndex, PRUint32 aChildCount)
 {
-  NS_PRECONDITION(!aChild->GetNextSibling(), "aChild with next sibling?");
-  NS_PRECONDITION(!aChild->GetPreviousSibling(), "aChild with prev sibling?");
+  MOZ_ASSERT(aChild->IsOrphan(), "aChild should be an orphan here");
+
+  NS_PRECONDITION(aChild->IsOrphan() || !aChild->GetNextSibling(),
+                  "aChild should be orphan and have no next sibling!");
+  NS_PRECONDITION(aChild->IsOrphan() || !aChild->GetPreviousSibling(),
+                  "aChild should be orphan and have no prev sibling!");
+
+  // Unmark this child as an orphan now that it's a child of its new
+  // parent.
+  aChild->MarkAsNonOrphan();
 
   *aPos = aChild;
   NS_ADDREF(aChild);
   if (aIndex != 0) {
     nsIContent* previous = static_cast<nsIContent*>(*(aPos - 1));
     aChild->mPreviousSibling = previous;
     previous->mNextSibling = aChild;
   }
--- a/content/base/src/nsAttrValue.cpp
+++ b/content/base/src/nsAttrValue.cpp
@@ -166,16 +166,20 @@ nsAttrValue::Reset()
   }
 
   mBits = 0;
 }
 
 void
 nsAttrValue::SetTo(const nsAttrValue& aOther)
 {
+  if (this == &aOther) {
+    return;
+  }
+
   switch (aOther.BaseType()) {
     case eStringBase:
     {
       ResetIfSet();
       nsStringBuffer* str = static_cast<nsStringBuffer*>(aOther.GetPtr());
       if (str) {
         str->AddRef();
         SetPtrValueAndType(str, eStringBase);
@@ -310,16 +314,29 @@ nsAttrValue::SetTo(const nsIntMargin& aV
   if (EnsureEmptyMiscContainer()) {
     MiscContainer* cont = GetMiscContainer();
     cont->mIntMargin = new nsIntMargin(aValue);
     cont->mType = eIntMarginValue;
   }
 }
 
 void
+nsAttrValue::SetToSerialized(const nsAttrValue& aOther)
+{
+  if (aOther.Type() != nsAttrValue::eString &&
+      aOther.Type() != nsAttrValue::eAtom) {
+    nsAutoString val;
+    aOther.ToString(val);
+    SetTo(val);
+  } else {
+    SetTo(aOther);
+  }
+}
+
+void
 nsAttrValue::SwapValueWith(nsAttrValue& aOther)
 {
   PtrBits tmp = aOther.mBits;
   aOther.mBits = mBits;
   mBits = tmp;
 }
 
 void
@@ -414,16 +431,39 @@ nsAttrValue::ToString(nsAString& aResult
     default:
     {
       aResult.Truncate();
       break;
     }
   }
 }
 
+already_AddRefed<nsIAtom>
+nsAttrValue::GetAsAtom() const
+{
+  switch (Type()) {
+    case eString:
+      return do_GetAtom(GetStringValue());
+
+    case eAtom:
+      {
+        nsIAtom* atom = GetAtomValue();
+        NS_ADDREF(atom);
+        return atom;
+      }
+
+    default:
+      {
+        nsAutoString val;
+        ToString(val);
+        return do_GetAtom(val);
+      }
+  }
+}
+
 const nsCheapString
 nsAttrValue::GetStringValue() const
 {
   NS_PRECONDITION(Type() == eString, "wrong type");
 
   return nsCheapString(static_cast<nsStringBuffer*>(GetPtr()));
 }
 
@@ -747,16 +787,46 @@ nsAttrValue::Equals(nsIAtom* aValue, nsC
   }
 
   nsAutoString val;
   ToString(val);
   return aValue->Equals(val);
 }
 
 bool
+nsAttrValue::EqualsAsStrings(const nsAttrValue& aOther) const
+{
+  if (Type() == aOther.Type()) {
+    return Equals(aOther);
+  }
+
+  // We need to serialize at least one nsAttrValue before passing to
+  // Equals(const nsAString&), but we can avoid unnecessarily serializing both
+  // by checking if one is already of a string type.
+  bool thisIsString = (BaseType() == eStringBase || BaseType() == eAtomBase);
+  const nsAttrValue& lhs = thisIsString ? *this : aOther;
+  const nsAttrValue& rhs = thisIsString ? aOther : *this;
+
+  switch (rhs.BaseType()) {
+    case eAtomBase:
+      return lhs.Equals(rhs.GetAtomValue(), eCaseMatters);
+
+    case eStringBase:
+      return lhs.Equals(rhs.GetStringValue(), eCaseMatters);
+
+    default:
+    {
+      nsAutoString val;
+      rhs.ToString(val);
+      return lhs.Equals(val, eCaseMatters);
+    }
+  }
+}
+
+bool
 nsAttrValue::Contains(nsIAtom* aValue, nsCaseTreatment aCaseSensitive) const
 {
   switch (BaseType()) {
     case eAtomBase:
     {
       nsIAtom* atom = GetAtomValue();
 
       if (aCaseSensitive == eCaseMatters) {
--- a/content/base/src/nsAttrValue.h
+++ b/content/base/src/nsAttrValue.h
@@ -101,16 +101,18 @@ public:
 
   nsAttrValue();
   nsAttrValue(const nsAttrValue& aOther);
   explicit nsAttrValue(const nsAString& aValue);
   nsAttrValue(mozilla::css::StyleRule* aValue, const nsAString* aSerialized);
   explicit nsAttrValue(const nsIntMargin& aValue);
   ~nsAttrValue();
 
+  inline const nsAttrValue& operator=(const nsAttrValue& aOther);
+
   static nsresult Init();
   static void Shutdown();
 
   // This has to be the same as in ValueBaseType
   enum ValueType {
     eString =       0x00, //   00
                           //   01  this value indicates an 'misc' struct
     eAtom =         0x02, //   10
@@ -131,19 +133,33 @@ public:
   void Reset();
 
   void SetTo(const nsAttrValue& aOther);
   void SetTo(const nsAString& aValue);
   void SetTo(PRInt16 aInt);
   void SetTo(mozilla::css::StyleRule* aValue, const nsAString* aSerialized);
   void SetTo(const nsIntMargin& aValue);
 
+  /**
+   * Sets this object with the string or atom representation of aValue.
+   *
+   * After calling this method, this object will have type eString unless the
+   * type of aValue is eAtom, in which case this object will also have type
+   * eAtom.
+   */
+  void SetToSerialized(const nsAttrValue& aValue);
+
   void SwapValueWith(nsAttrValue& aOther);
 
   void ToString(nsAString& aResult) const;
+  /**
+   * Returns the value of this object as an atom. If necessary, the value will
+   * first be serialised using ToString before converting to an atom.
+   */
+  already_AddRefed<nsIAtom> GetAsAtom() const;
 
   // Methods to get value. These methods do not convert so only use them
   // to retrieve the datatype that this nsAttrValue has.
   inline bool IsEmptyString() const;
   const nsCheapString GetStringValue() const;
   inline nsIAtom* GetAtomValue() const;
   inline PRInt32 GetIntegerValue() const;
   bool GetColorValue(nscolor& aColor) const;
@@ -171,16 +187,25 @@ public:
   nsIAtom* AtomAt(PRInt32 aIndex) const;
 
   PRUint32 HashValue() const;
   bool Equals(const nsAttrValue& aOther) const;
   bool Equals(const nsAString& aValue, nsCaseTreatment aCaseSensitive) const;
   bool Equals(nsIAtom* aValue, nsCaseTreatment aCaseSensitive) const;
 
   /**
+   * Compares this object with aOther according to their string representation.
+   *
+   * For example, when called on an object with type eInteger and value 4, and
+   * given aOther of type eString and value "4", EqualsAsStrings will return
+   * true (while Equals will return false).
+   */
+  bool EqualsAsStrings(const nsAttrValue& aOther) const;
+
+  /**
    * Returns true if this AttrValue is equal to the given atom, or is an
    * array which contains the given atom.
    */
   bool Contains(nsIAtom* aValue, nsCaseTreatment aCaseSensitive) const;
   /**
    * Returns true if this AttrValue is an atom equal to the given
    * string, or is an array of atoms which contains the given string.
    * This always does a case-sensitive comparison.
@@ -388,16 +413,23 @@ private:
 
   PtrBits mBits;
 };
 
 /**
  * Implementation of inline methods
  */
 
+inline const nsAttrValue&
+nsAttrValue::operator=(const nsAttrValue& aOther)
+{
+  SetTo(aOther);
+  return *this;
+}
+
 inline nsIAtom*
 nsAttrValue::GetAtomValue() const
 {
   NS_PRECONDITION(Type() == eAtom, "wrong type");
   return reinterpret_cast<nsIAtom*>(GetPtr());
 }
 
 inline PRInt32
new file mode 100644
--- /dev/null
+++ b/content/base/src/nsAttrValueOrString.cpp
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsAttrValueOrString.h"
+
+const nsAString&
+nsAttrValueOrString::String() const
+{
+  if (mStringPtr) {
+    return *mStringPtr;
+  }
+
+  if (mAttrValue->Type() == nsAttrValue::eString) {
+    mCheapString = mAttrValue->GetStringValue();
+    mStringPtr = &mCheapString;
+    return *mStringPtr;
+  }
+
+  mAttrValue->ToString(mCheapString);
+  mStringPtr = &mCheapString;
+  return *mStringPtr;
+}
new file mode 100644
--- /dev/null
+++ b/content/base/src/nsAttrValueOrString.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * A wrapper to contain either an nsAttrValue or an nsAString. This is useful
+ * because constructing an nsAttrValue from an nsAString can be expensive when
+ * the buffer of the string is not shared.
+ *
+ * Since a raw pointer to the passed-in string is kept, this class should only
+ * be used on the stack.
+ */
+
+#ifndef nsAttrValueOrString_h___
+#define nsAttrValueOrString_h___
+
+#include "nsString.h"
+#include "nsAttrValue.h"
+
+class NS_STACK_CLASS nsAttrValueOrString
+{
+public:
+  nsAttrValueOrString(const nsAString& aValue)
+    : mAttrValue(nsnull)
+    , mStringPtr(&aValue)
+    , mCheapString(nsnull)
+  { }
+  nsAttrValueOrString(const nsAttrValue& aValue)
+    : mAttrValue(&aValue)
+    , mStringPtr(nsnull)
+    , mCheapString(nsnull)
+  { }
+
+  /**
+   * Returns a reference to the string value of the contents of this object.
+   *
+   * When this object points to a string or an nsAttrValue of string or atom
+   * type this should be fairly cheap. Other nsAttrValue types will be
+   * serialized the first time this is called and cached from thereon.
+   */
+  const nsAString& String() const;
+
+  /**
+   * Compares the string representation of this object with the string
+   * representation of an nsAttrValue.
+   */
+  bool EqualsAsStrings(const nsAttrValue& aOther) const
+  {
+    if (mStringPtr) {
+      return aOther.Equals(*mStringPtr, eCaseMatters);
+    }
+    return aOther.EqualsAsStrings(*mAttrValue);
+  }
+
+protected:
+  const nsAttrValue*       mAttrValue;
+  mutable const nsAString* mStringPtr;
+  mutable nsCheapString    mCheapString;
+};
+
+#endif // nsAttrValueOrString_h___
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -357,16 +357,18 @@ nsresult
 nsContentUtils::Init()
 {
   if (sInitialized) {
     NS_WARNING("Init() called twice");
 
     return NS_OK;
   }
 
+  nsINode::Init();
+
   nsresult rv = NS_GetNameSpaceManager(&sNameSpaceManager);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsXPConnect* xpconnect = nsXPConnect::GetXPConnect();
   NS_ENSURE_TRUE(xpconnect, NS_ERROR_FAILURE);
 
   sXPConnect = xpconnect;
   sThreadJSContextStack = xpconnect;
--- a/content/base/src/nsDOMAttribute.cpp
+++ b/content/base/src/nsDOMAttribute.cpp
@@ -86,16 +86,17 @@ nsDOMAttribute::nsDOMAttribute(nsDOMAttr
     content->AddMutationObserver(this);
   }
 }
 
 nsDOMAttribute::~nsDOMAttribute()
 {
   if (mChild) {
     static_cast<nsTextNode*>(mChild)->UnbindFromAttribute();
+    mChild->MarkAsOrphan();
     NS_RELEASE(mChild);
     mFirstChild = nsnull;
   }
 
   nsIContent* content = GetContentInternal();
   if (content) {
     content->RemoveMutationObserver(this);
   }
@@ -116,16 +117,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDOMAttribute)
   nsINode::Trace(tmp, aCallback, aClosure);
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMAttribute)
   nsINode::Unlink(tmp);
   if (tmp->mChild) {
     static_cast<nsTextNode*>(tmp->mChild)->UnbindFromAttribute();
+    tmp->mChild->MarkAsOrphan();
     NS_RELEASE(tmp->mChild);
     tmp->mFirstChild = nsnull;
   }
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 DOMCI_NODE_DATA(Attr, nsDOMAttribute)
 
 // QueryInterface implementation for nsDOMAttribute
@@ -721,16 +723,17 @@ nsDOMAttribute::EnsureChildState()
   NS_PRECONDITION(!mChild, "Someone screwed up");
 
   nsAutoString value;
   GetValue(value);
 
   if (!value.IsEmpty()) {
     NS_NewTextNode(&mChild, mNodeInfo->NodeInfoManager());
 
+    mChild->MarkAsNonOrphan();
     static_cast<nsTextNode*>(mChild)->BindToAttribute(this);
     mFirstChild = mChild;
 
     mChild->SetText(value, false);
   }
 }
 
 void
@@ -788,10 +791,11 @@ nsDOMAttribute::doRemoveChild(bool aNoti
   NS_RELEASE(mChild);
   mFirstChild = nsnull;
 
   if (aNotify) {
     nsNodeUtils::AttributeChildRemoved(this, child);
   }
 
   child->UnbindFromAttribute();
+  child->MarkAsOrphan();
 }
 
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -1574,17 +1574,18 @@ nsDocument::~nsDocument()
     PR_LOG(gDocumentLeakPRLog, PR_LOG_DEBUG,
            ("DOCUMENT %p destroyed", this));
 #endif
 
 #ifdef DEBUG
   nsCycleCollector_DEBUG_wasFreed(static_cast<nsIDocument*>(this));
 #endif
 
-  NS_ASSERTION(!mIsShowing, "Destroying a currently-showing document");
+  NS_ASSERTION(!mIsShowing, "Deleting a currently-showing document");
+  NS_ASSERTION(IsOrphan(), "Deleted document not an orphan?");
 
   mInDestructor = true;
   mInUnlinkOrDeletion = true;
 
   // Clear mObservers to keep it in sync with the mutationobserver list
   mObservers.Clear();
 
   if (mStyleSheetSetList) {
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -1087,17 +1087,17 @@ nsFrameMessageManager::NewProcessMessage
   return mm;
 }
 
 nsresult
 NS_NewChildProcessMessageManager(nsISyncMessageSender** aResult)
 {
   NS_ASSERTION(!nsFrameMessageManager::sChildProcessManager,
                "Re-creating sChildProcessManager");
-  PRBool isChrome = IsChromeProcess();
+  bool isChrome = IsChromeProcess();
   nsFrameMessageManager* mm = new nsFrameMessageManager(false,
                                                         isChrome ? SendSyncMessageToSameProcessParent
                                                                  : SendSyncMessageToParentProcess,
                                                         isChrome ? SendAsyncMessageToSameProcessParent
                                                                  : SendAsyncMessageToParentProcess,
                                                         nsnull,
                                                         &nsFrameMessageManager::sChildProcessManager,
                                                         nsnull,
--- a/content/base/src/nsGenericElement.cpp
+++ b/content/base/src/nsGenericElement.cpp
@@ -81,16 +81,17 @@
 #include "nsXBLPrototypeBinding.h"
 #include "nsDOMError.h"
 #include "nsDOMString.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIDOMMutationEvent.h"
 #include "nsMutationEvent.h"
 #include "nsNodeUtils.h"
 #include "nsDocument.h"
+#include "nsAttrValueOrString.h"
 #ifdef MOZ_XUL
 #include "nsXULElement.h"
 #endif /* MOZ_XUL */
 #include "nsFrameManager.h"
 #include "nsFrameSelection.h"
 #ifdef DEBUG
 #include "nsRange.h"
 #endif
@@ -207,19 +208,26 @@ nsINode::nsSlots::Unlink()
   if (mChildNodes) {
     mChildNodes->DropReference();
     NS_RELEASE(mChildNodes);
   }
 }
 
 //----------------------------------------------------------------------
 
+nsINode *nsINode::sOrphanNodeHead = nsnull;
+
 nsINode::~nsINode()
 {
   NS_ASSERTION(!HasSlots(), "nsNodeUtils::LastRelease was not called?");
+
+  MOZ_ASSERT(IsOrphan(), "Node should be orphan by the time it's deleted!");
+
+  mPreviousOrphanNode->mNextOrphanNode = mNextOrphanNode;
+  mNextOrphanNode->mPreviousOrphanNode = mPreviousOrphanNode;
 }
 
 void*
 nsINode::GetProperty(PRUint16 aCategory, nsIAtom *aPropertyName,
                      nsresult *aStatus) const
 {
   return OwnerDoc()->PropertyTable(aCategory)->GetProperty(this, aPropertyName,
                                                            aStatus);
@@ -3222,17 +3230,17 @@ nsGenericElement::UnbindFromTree(bool aD
     DeleteProperty(nsGkAtoms::transitionsProperty);
     DeleteProperty(nsGkAtoms::animationsOfBeforeProperty);
     DeleteProperty(nsGkAtoms::animationsOfAfterProperty);
     DeleteProperty(nsGkAtoms::animationsProperty);
   }
 
   // Unset this since that's what the old code effectively did.
   UnsetFlags(NODE_FORCE_XBL_BINDINGS);
-  
+
 #ifdef MOZ_XUL
   nsXULElement* xulElem = nsXULElement::FromContent(this);
   if (xulElem) {
     xulElem->SetXULBindingParent(nsnull);
   }
   else
 #endif
   {
@@ -3816,17 +3824,32 @@ nsGenericElement::GetTextContent(nsAStri
 }
 
 NS_IMETHODIMP
 nsGenericElement::SetTextContent(const nsAString& aTextContent)
 {
   return nsContentUtils::SetNodeTextContent(this, aTextContent, false);
 }
 
-/* static */
+// static
+void
+nsINode::Init()
+{
+  // Allocate static storage for the head of the list of orphan nodes
+  static MOZ_ALIGNED_DECL(char orphanNodeListHead[sizeof(nsINode)], 8);
+  sOrphanNodeHead = reinterpret_cast<nsINode *>(&orphanNodeListHead[0]);
+
+  sOrphanNodeHead->mNextOrphanNode = sOrphanNodeHead;
+  sOrphanNodeHead->mPreviousOrphanNode = sOrphanNodeHead;
+
+  sOrphanNodeHead->mFirstChild = reinterpret_cast<nsIContent *>(0xdeadbeef);
+  sOrphanNodeHead->mParent = reinterpret_cast<nsIContent *>(0xdeadbeef);
+}
+
+// static
 nsresult
 nsGenericElement::DispatchEvent(nsPresContext* aPresContext,
                                 nsEvent* aEvent,
                                 nsIContent* aTarget,
                                 bool aFullDispatch,
                                 nsEventStatus* aStatus)
 {
   NS_PRECONDITION(aTarget, "Must have target");
@@ -4975,20 +4998,24 @@ nsGenericElement::CopyInnerTo(nsGenericE
                                 name->GetPrefix(), valStr, false);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
 bool
-nsGenericElement::MaybeCheckSameAttrVal(PRInt32 aNamespaceID, nsIAtom* aName,
-                                        nsIAtom* aPrefix, const nsAString& aValue,
-                                        bool aNotify, nsAutoString* aOldValue,
-                                        PRUint8* aModType, bool* aHasListeners)
+nsGenericElement::MaybeCheckSameAttrVal(PRInt32 aNamespaceID,
+                                        nsIAtom* aName,
+                                        nsIAtom* aPrefix,
+                                        const nsAttrValueOrString& aValue,
+                                        bool aNotify,
+                                        nsAttrValue& aOldValue,
+                                        PRUint8* aModType,
+                                        bool* aHasListeners)
 {
   bool modification = false;
   *aHasListeners = aNotify &&
     nsContentUtils::HasMutationListeners(this,
                                          NS_EVENT_BITS_MUTATION_ATTRMODIFIED,
                                          this);
 
   // If we have no listeners and aNotify is false, we are almost certainly
@@ -4996,26 +5023,29 @@ nsGenericElement::MaybeCheckSameAttrVal(
   // value.  Even if we do, setting the value is cheap when we have no
   // listeners and don't plan to notify.  The check for aNotify here is an
   // optimization, the check for *aHasListeners is a correctness issue.
   if (*aHasListeners || aNotify) {
     nsAttrInfo info(GetAttrInfo(aNamespaceID, aName));
     if (info.mValue) {
       // Check whether the old value is the same as the new one.  Note that we
       // only need to actually _get_ the old value if we have listeners.
-      bool valueMatches;
       if (*aHasListeners) {
-        // Need to store the old value
-        info.mValue->ToString(*aOldValue);
-        valueMatches = aValue.Equals(*aOldValue);
-      } else {
-        NS_ABORT_IF_FALSE(aNotify,
-                          "Either hasListeners or aNotify should be true.");
-        valueMatches = info.mValue->Equals(aValue, eCaseMatters);
+        // Need to store the old value.
+        //
+        // If the current attribute value contains a pointer to some other data
+        // structure that gets updated in the process of setting the attribute
+        // we'll no longer have the old value of the attribute. Therefore, we
+        // should serialize the attribute value now to keep a snapshot.
+        //
+        // We have to serialize the value anyway in order to create the
+        // mutation event so there's no cost in doing it now.
+        aOldValue.SetToSerialized(*info.mValue);
       }
+      bool valueMatches = aValue.EqualsAsStrings(*info.mValue);
       if (valueMatches && aPrefix == info.mName->GetPrefix()) {
         return true;
       }
       modification = true;
     }
   }
   *aModType = modification ?
     static_cast<PRUint8>(nsIDOMMutationEvent::MODIFICATION) :
@@ -5035,24 +5065,25 @@ nsGenericElement::SetAttr(PRInt32 aNames
                "Don't call SetAttr with unknown namespace");
 
   if (!mAttrsAndChildren.CanFitMoreAttrs()) {
     return NS_ERROR_FAILURE;
   }
 
   PRUint8 modType;
   bool hasListeners;
-  nsAutoString oldValue;
-
-  if (MaybeCheckSameAttrVal(aNamespaceID, aName, aPrefix, aValue, aNotify,
-                            &oldValue, &modType, &hasListeners)) {
+  nsAttrValueOrString value(aValue);
+  nsAttrValue oldValue;
+
+  if (MaybeCheckSameAttrVal(aNamespaceID, aName, aPrefix, value, aNotify,
+                            oldValue, &modType, &hasListeners)) {
     return NS_OK;
   }
 
-  nsresult rv = BeforeSetAttr(aNamespaceID, aName, &aValue, aNotify);
+  nsresult rv = BeforeSetAttr(aNamespaceID, aName, &value, aNotify);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (aNotify) {
     nsNodeUtils::AttributeWillChange(this, aNamespaceID, aName, modType);
   }
 
   // Hold a script blocker while calling ParseAttribute since that can call
   // out to id-observers
@@ -5060,17 +5091,17 @@ nsGenericElement::SetAttr(PRInt32 aNames
 
   nsAttrValue attrValue;
   if (!ParseAttribute(aNamespaceID, aName, aValue, attrValue)) {
     attrValue.SetTo(aValue);
   }
 
   return SetAttrAndNotify(aNamespaceID, aName, aPrefix, oldValue,
                           attrValue, modType, hasListeners, aNotify,
-                          &aValue);
+                          kCallAfterSetAttr);
 }
 
 nsresult
 nsGenericElement::SetParsedAttr(PRInt32 aNamespaceID, nsIAtom* aName,
                                 nsIAtom* aPrefix, nsAttrValue& aParsedValue,
                                 bool aNotify)
 {
   // Keep this in sync with SetAttr above
@@ -5078,58 +5109,64 @@ nsGenericElement::SetParsedAttr(PRInt32 
   NS_ENSURE_ARG_POINTER(aName);
   NS_ASSERTION(aNamespaceID != kNameSpaceID_Unknown,
                "Don't call SetAttr with unknown namespace");
 
   if (!mAttrsAndChildren.CanFitMoreAttrs()) {
     return NS_ERROR_FAILURE;
   }
 
-  nsAutoString value;
-  aParsedValue.ToString(value);
 
   PRUint8 modType;
   bool hasListeners;
-  nsAutoString oldValue;
+  nsAttrValueOrString value(aParsedValue);
+  nsAttrValue oldValue;
 
   if (MaybeCheckSameAttrVal(aNamespaceID, aName, aPrefix, value, aNotify,
-                            &oldValue, &modType, &hasListeners)) {
+                            oldValue, &modType, &hasListeners)) {
     return NS_OK;
   }
 
   nsresult rv = BeforeSetAttr(aNamespaceID, aName, &value, aNotify);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (aNotify) {
     nsNodeUtils::AttributeWillChange(this, aNamespaceID, aName, modType);
   }
 
   return SetAttrAndNotify(aNamespaceID, aName, aPrefix, oldValue,
                           aParsedValue, modType, hasListeners, aNotify,
-                          &value);
+                          kCallAfterSetAttr);
 }
 
 nsresult
 nsGenericElement::SetAttrAndNotify(PRInt32 aNamespaceID,
                                    nsIAtom* aName,
                                    nsIAtom* aPrefix,
-                                   const nsAString& aOldValue,
+                                   const nsAttrValue& aOldValue,
                                    nsAttrValue& aParsedValue,
                                    PRUint8 aModType,
                                    bool aFireMutation,
                                    bool aNotify,
-                                   const nsAString* aValueForAfterSetAttr)
+                                   bool aCallAfterSetAttr)
 {
   nsresult rv;
 
   nsIDocument* document = GetCurrentDoc();
   mozAutoDocUpdate updateBatch(document, UPDATE_CONTENT_MODEL, aNotify);
 
   nsMutationGuard::DidMutate();
 
+  // Copy aParsedValue for later use since it will be lost when we call
+  // SetAndTakeMappedAttr below
+  nsAttrValue aValueForAfterSetAttr;
+  if (aCallAfterSetAttr) {
+    aValueForAfterSetAttr.SetTo(aParsedValue);
+  }
+
   if (aNamespaceID == kNameSpaceID_None) {
     // XXXbz Perhaps we should push up the attribute mapping function
     // stuff to nsGenericElement?
     if (!IsAttributeMapped(aName) ||
         !SetMappedAttribute(document, aName, aParsedValue, &rv)) {
       rv = mAttrsAndChildren.SetAndTakeAttr(aName, aParsedValue);
     }
   }
@@ -5157,18 +5194,18 @@ nsGenericElement::SetAttrAndNotify(PRInt
   if (aNotify) {
     nsNodeUtils::AttributeChanged(this, aNamespaceID, aName, aModType);
   }
 
   if (aNamespaceID == kNameSpaceID_XMLEvents && 
       aName == nsGkAtoms::event && mNodeInfo->GetDocument()) {
     mNodeInfo->GetDocument()->AddXMLEventsContent(this);
   }
-  if (aValueForAfterSetAttr) {
-    rv = AfterSetAttr(aNamespaceID, aName, aValueForAfterSetAttr, aNotify);
+  if (aCallAfterSetAttr) {
+    rv = AfterSetAttr(aNamespaceID, aName, &aValueForAfterSetAttr, aNotify);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   if (aFireMutation) {
     nsMutationEvent mutation(true, NS_MUTATION_ATTRMODIFIED);
 
     nsCOMPtr<nsIDOMAttr> attrNode;
     nsAutoString ns;
@@ -5178,18 +5215,18 @@ nsGenericElement::SetAttrAndNotify(PRInt
     mutation.mRelatedNode = attrNode;
 
     mutation.mAttrName = aName;
     nsAutoString newValue;
     GetAttr(aNamespaceID, aName, newValue);
     if (!newValue.IsEmpty()) {
       mutation.mNewAttrValue = do_GetAtom(newValue);
     }
-    if (!aOldValue.IsEmpty()) {
-      mutation.mPrevAttrValue = do_GetAtom(aOldValue);
+    if (!aOldValue.IsEmptyString()) {
+      mutation.mPrevAttrValue = aOldValue.GetAsAtom();
     }
     mutation.mAttrChange = aModType;
 
     mozAutoSubtreeModified subtree(OwnerDoc(), this);
     (new nsAsyncDOMEvent(this, mutation))->RunDOMEventWhenSafe();
   }
 
   return NS_OK;
@@ -5329,18 +5366,18 @@ nsGenericElement::UnsetAttr(PRInt32 aNam
 
   PRInt32 index = mAttrsAndChildren.IndexOfAttr(aName, aNameSpaceID);
   if (index < 0) {
     return NS_OK;
   }
 
   nsresult rv = BeforeSetAttr(aNameSpaceID, aName, nsnull, aNotify);
   NS_ENSURE_SUCCESS(rv, rv);
-  
-  nsIDocument *document = GetCurrentDoc();    
+
+  nsIDocument *document = GetCurrentDoc();
   mozAutoDocUpdate updateBatch(document, UPDATE_CONTENT_MODEL, aNotify);
 
   if (aNotify) {
     nsNodeUtils::AttributeWillChange(this, aNameSpaceID, aName,
                                      nsIDOMMutationEvent::REMOVAL);
   }
 
   bool hasMutationListeners = aNotify &&
--- a/content/base/src/nsGenericElement.h
+++ b/content/base/src/nsGenericElement.h
@@ -74,16 +74,17 @@ class nsIFrame;
 class nsIDOMNamedNodeMap;
 class nsICSSDeclaration;
 class nsIDOMCSSStyleDeclaration;
 class nsIURI;
 class nsINodeInfo;
 class nsIControllers;
 class nsEventListenerManager;
 class nsIScrollableFrame;
+class nsAttrValueOrString;
 class nsContentList;
 class nsDOMTokenList;
 struct nsRect;
 
 typedef PRUptrdiff PtrBits;
 
 /**
  * Class that implements the nsIDOMNodeList interface (a list of children of
@@ -284,26 +285,28 @@ public:
    * Helper for SetAttr/SetParsedAttr. This method will return true if aNotify
    * is true or there are mutation listeners that must be triggered, the
    * attribute is currently set, and the new value that is about to be set is
    * different to the current value. As a perf optimization the new and old
    * values will not actually be compared if we aren't notifying and we don't
    * have mutation listeners (in which case it's cheap to just return false
    * and let the caller go ahead and set the value).
    * @param aOldValue Set to the old value of the attribute, but only if there
-   *   are event listeners
+   *   are event listeners. If set, the type of aOldValue will be either
+   *   nsAttrValue::eString or nsAttrValue::eAtom.
    * @param aModType Set to nsIDOMMutationEvent::MODIFICATION or to
    *   nsIDOMMutationEvent::ADDITION, but only if this helper returns true
    * @param aHasListeners Set to true if there are mutation event listeners
    *   listening for NS_EVENT_BITS_MUTATION_ATTRMODIFIED
    */
   bool MaybeCheckSameAttrVal(PRInt32 aNamespaceID, nsIAtom* aName,
-                               nsIAtom* aPrefix, const nsAString& aValue,
-                               bool aNotify, nsAutoString* aOldValue,
-                               PRUint8* aModType, bool* aHasListeners);
+                             nsIAtom* aPrefix,
+                             const nsAttrValueOrString& aValue,
+                             bool aNotify, nsAttrValue& aOldValue,
+                             PRUint8* aModType, bool* aHasListeners);
   virtual nsresult SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName, nsIAtom* aPrefix,
                            const nsAString& aValue, bool aNotify);
   virtual nsresult SetParsedAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
                                  nsIAtom* aPrefix, nsAttrValue& aParsedValue,
                                  bool aNotify);
   virtual bool GetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
                          nsAString& aResult) const;
   virtual bool HasAttr(PRInt32 aNameSpaceID, nsIAtom* aName) const;
@@ -628,45 +631,58 @@ public:
   static bool CanSkipThis(nsINode* aNode);
   static void MarkNodeChildren(nsINode* aNode);
   static void InitCCCallbacks();
   static void MarkUserData(void* aObject, nsIAtom* aKey, void* aChild,
                            void *aData);
   static void MarkUserDataHandler(void* aObject, nsIAtom* aKey, void* aChild,
                                   void* aData);
 protected:
+  /*
+   * Named-bools for use with SetAttrAndNotify to make call sites easier to
+   * read.
+   */
+  static const bool kFireMutationEvent           = true;
+  static const bool kDontFireMutationEvent       = false;
+  static const bool kNotifyDocumentObservers     = true;
+  static const bool kDontNotifyDocumentObservers = false;
+  static const bool kCallAfterSetAttr            = true;
+  static const bool kDontCallAfterSetAttr        = false;
+
   /**
    * Set attribute and (if needed) notify documentobservers and fire off
    * mutation events.  This will send the AttributeChanged notification.
    * Callers of this method are responsible for calling AttributeWillChange,
    * since that needs to happen before the new attr value has been set, and
    * in particular before it has been parsed.
    *
+   * For the boolean parameters, consider using the named bools above to aid
+   * code readability.
+   *
    * @param aNamespaceID  namespace of attribute
    * @param aAttribute    local-name of attribute
    * @param aPrefix       aPrefix of attribute
    * @param aOldValue     previous value of attribute. Only needed if
    *                      aFireMutation is true.
    * @param aParsedValue  parsed new value of attribute
    * @param aModType      nsIDOMMutationEvent::MODIFICATION or ADDITION.  Only
    *                      needed if aFireMutation or aNotify is true.
    * @param aFireMutation should mutation-events be fired?
    * @param aNotify       should we notify document-observers?
-   * @param aValueForAfterSetAttr If not null, AfterSetAttr will be called
-   *                      with the value pointed by this parameter.
+   * @param aCallAfterSetAttr should we call AfterSetAttr?
    */
   nsresult SetAttrAndNotify(PRInt32 aNamespaceID,
                             nsIAtom* aName,
                             nsIAtom* aPrefix,
-                            const nsAString& aOldValue,
+                            const nsAttrValue& aOldValue,
                             nsAttrValue& aParsedValue,
                             PRUint8 aModType,
                             bool aFireMutation,
                             bool aNotify,
-                            const nsAString* aValueForAfterSetAttr);
+                            bool aCallAfterSetAttr);
 
   /**
    * Convert an attribute string value to attribute type based on the type of
    * attribute.  Called by SetAttr().  Note that at the moment we only do this
    * for attributes in the null namespace (kNameSpaceID_None).
    *
    * @param aNamespaceID the namespace of the attribute to convert
    * @param aAttribute the attribute to convert
@@ -701,24 +717,26 @@ protected:
    * Hook that is called by nsGenericElement::SetAttr to allow subclasses to
    * deal with attribute sets.  This will only be called after we verify that
    * we're actually doing an attr set and will be called before
    * AttributeWillChange and before ParseAttribute and hence before we've set
    * the new value.
    *
    * @param aNamespaceID the namespace of the attr being set
    * @param aName the localname of the attribute being set
-   * @param aValue the value it's being set to.  If null, the attr is being
-   *        removed.
+   * @param aValue the value it's being set to represented as either a string or
+   *        a parsed nsAttrValue. Alternatively, if the attr is being removed it
+   *        will be null.
    * @param aNotify Whether we plan to notify document observers.
    */
   // Note that this is inlined so that when subclasses call it it gets
   // inlined.  Those calls don't go through a vtable.
   virtual nsresult BeforeSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
-                                 const nsAString* aValue, bool aNotify)
+                                 const nsAttrValueOrString* aValue,
+                                 bool aNotify)
   {
     return NS_OK;
   }
 
   /**
    * Hook that is called by nsGenericElement::SetAttr to allow subclasses to
    * deal with attribute sets.  This will only be called after we have called
    * SetAndTakeAttr and AttributeChanged (that is, after we have actually set
@@ -728,17 +746,17 @@ protected:
    * @param aName the localname of the attribute being set
    * @param aValue the value it's being set to.  If null, the attr is being
    *        removed.
    * @param aNotify Whether we plan to notify document observers.
    */
   // Note that this is inlined so that when subclasses call it it gets
   // inlined.  Those calls don't go through a vtable.
   virtual nsresult AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
-                                const nsAString* aValue, bool aNotify)
+                                const nsAttrValue* aValue, bool aNotify)
   {
     return NS_OK;
   }
 
   /**
    * Hook to allow subclasses to produce a different nsEventListenerManager if
    * needed for attachment of attribute-defined handlers
    */
--- a/content/base/src/nsStyledElement.cpp
+++ b/content/base/src/nsStyledElement.cpp
@@ -141,17 +141,17 @@ nsStyledElementNotElementCSSInlineStyle:
   }
 
   return nsGenericElement::UnsetAttr(aNameSpaceID, aAttribute, aNotify);
 }
 
 nsresult
 nsStyledElementNotElementCSSInlineStyle::AfterSetAttr(PRInt32 aNamespaceID,
                                                       nsIAtom* aAttribute,
-                                                      const nsAString* aValue,
+                                                      const nsAttrValue* aValue,
                                                       bool aNotify)
 {
   if (aNamespaceID == kNameSpaceID_None && !aValue &&
       aAttribute == nsGkAtoms::id) {
     // The id has been removed when calling UnsetAttr but we kept it because
     // the id is used for some layout stuff between UnsetAttr and AfterSetAttr.
     // Now. the id is really removed so it would not be safe to keep this flag.
     ClearHasID();
@@ -162,48 +162,52 @@ nsStyledElementNotElementCSSInlineStyle:
 }
 
 NS_IMETHODIMP
 nsStyledElementNotElementCSSInlineStyle::SetInlineStyleRule(css::StyleRule* aStyleRule,
                                                             bool aNotify)
 {
   SetMayHaveStyle();
   bool modification = false;
-  nsAutoString oldValueStr;
+  nsAttrValue oldValue;
 
   bool hasListeners = aNotify &&
     nsContentUtils::HasMutationListeners(this,
                                          NS_EVENT_BITS_MUTATION_ATTRMODIFIED,
                                          this);
 
   // There's no point in comparing the stylerule pointers since we're always
   // getting a new stylerule here. And we can't compare the stringvalues of
   // the old and the new rules since both will point to the same declaration
   // and thus will be the same.
   if (hasListeners) {
     // save the old attribute so we can set up the mutation event properly
     // XXXbz if the old rule points to the same declaration as the new one,
     // this is getting the new attr value, not the old one....
+    nsAutoString oldValueStr;
     modification = GetAttr(kNameSpaceID_None, nsGkAtoms::style,
                            oldValueStr);
+    if (modification) {
+      oldValue.SetTo(oldValueStr);
+    }
   }
   else if (aNotify && IsInDoc()) {
     modification = !!mAttrsAndChildren.GetAttr(nsGkAtoms::style);
   }
 
   nsAttrValue attrValue(aStyleRule, nsnull);
 
   // XXXbz do we ever end up with ADDITION here?  I doubt it.
   PRUint8 modType = modification ?
     static_cast<PRUint8>(nsIDOMMutationEvent::MODIFICATION) :
     static_cast<PRUint8>(nsIDOMMutationEvent::ADDITION);
 
   return SetAttrAndNotify(kNameSpaceID_None, nsGkAtoms::style, nsnull,
-                          oldValueStr, attrValue, modType, hasListeners,
-                          aNotify, nsnull);
+                          oldValue, attrValue, modType, hasListeners,
+                          aNotify, kDontCallAfterSetAttr);
 }
 
 css::StyleRule*
 nsStyledElementNotElementCSSInlineStyle::GetInlineStyleRule()
 {
   if (!MayHaveStyle()) {
     return nsnull;
   }
--- a/content/base/src/nsStyledElement.h
+++ b/content/base/src/nsStyledElement.h
@@ -84,17 +84,17 @@ public:
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent,
                               bool aCompileEventHandlers);
   virtual void UnbindFromTree(bool aDeep, bool aNullParent);
 
   virtual nsresult UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
                              bool aNotify);
   virtual nsresult AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
-                                const nsAString* aValue, bool aNotify);
+                                const nsAttrValue* aValue, bool aNotify);
 
   nsIDOMCSSStyleDeclaration* GetStyle(nsresult* retval);
 
 protected:
 
   /**
    * Parse a style attr value into a CSS rulestruct (or, if there is no
    * document, leave it as a string) and return as nsAttrValue.
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -140,16 +140,17 @@ using namespace mozilla;
 #define XML_HTTP_REQUEST_BACKGROUND     (1 << 13) // Internal
 // This is set when we've got the headers for a multipart XMLHttpRequest,
 // but haven't yet started to process the first part.
 #define XML_HTTP_REQUEST_MPART_HEADERS  (1 << 14) // Internal
 #define XML_HTTP_REQUEST_USE_XSITE_AC   (1 << 15) // Internal
 #define XML_HTTP_REQUEST_NEED_AC_PREFLIGHT (1 << 16) // Internal
 #define XML_HTTP_REQUEST_AC_WITH_CREDENTIALS (1 << 17) // Internal
 #define XML_HTTP_REQUEST_TIMED_OUT (1 << 18) // Internal
+#define XML_HTTP_REQUEST_DELETED (1 << 19) // Internal
 
 #define XML_HTTP_REQUEST_LOADSTATES         \
   (XML_HTTP_REQUEST_UNSENT |                \
    XML_HTTP_REQUEST_OPENED |                \
    XML_HTTP_REQUEST_HEADERS_RECEIVED |      \
    XML_HTTP_REQUEST_LOADING |               \
    XML_HTTP_REQUEST_DONE |                  \
    XML_HTTP_REQUEST_SENT |                  \
@@ -455,16 +456,18 @@ nsXMLHttpRequest::nsXMLHttpRequest()
     mResultJSON(JSVAL_VOID),
     mResultArrayBuffer(nsnull)
 {
   nsLayoutStatics::AddRef();
 }
 
 nsXMLHttpRequest::~nsXMLHttpRequest()
 {
+  mState |= XML_HTTP_REQUEST_DELETED;
+
   if (mState & (XML_HTTP_REQUEST_STOPPED |
                 XML_HTTP_REQUEST_SENT |
                 XML_HTTP_REQUEST_LOADING)) {
     Abort();
   }
 
   NS_ABORT_IF_FALSE(!(mState & XML_HTTP_REQUEST_SYNCLOOPING), "we rather crash than hang");
   mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
@@ -1264,17 +1267,21 @@ nsXMLHttpRequest::CloseRequestWithError(
     mCORSPreflightChannel->Cancel(NS_BINDING_ABORTED);
   }
   if (mTimeoutTimer) {
     mTimeoutTimer->Cancel();
   }
   PRUint32 responseLength = mResponseBody.Length();
   ResetResponse();
   mState |= aFlag;
-  
+
+  // If we're in the destructor, don't risk dispatching an event.
+  if (mState & XML_HTTP_REQUEST_DELETED)
+    return;
+
   if (!(mState & (XML_HTTP_REQUEST_UNSENT |
                   XML_HTTP_REQUEST_OPENED |
                   XML_HTTP_REQUEST_DONE))) {
     ChangeState(XML_HTTP_REQUEST_DONE, true);
 
     if (!(mState & XML_HTTP_REQUEST_SYNCLOOPING)) {
       DispatchProgressEvent(this, aType, mLoadLengthComputable, responseLength,
                             mLoadTotal);
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -1135,18 +1135,18 @@ WebGLContext::Notify(nsITimer* timer)
     TerminateRobustnessTimer();
     // If the context has been lost and we're waiting for it to be restored, do
     // that now.
     if (mContextStatus == ContextLostAwaitingEvent) {
         bool defaultAction;
         nsContentUtils::DispatchTrustedEvent(HTMLCanvasElement()->OwnerDoc(),
                                              (nsIDOMHTMLCanvasElement*) HTMLCanvasElement(),
                                              NS_LITERAL_STRING("webglcontextlost"),
-                                             PR_TRUE,
-                                             PR_TRUE,
+                                             true,
+                                             true,
                                              &defaultAction);
 
         // If the script didn't handle the event, we don't allow restores.
         if (defaultAction)
             mAllowRestore = false;
 
         // If the script handled the event and we are allowing restores, then
         // mark it to be restored. Otherwise, leave it as context lost
@@ -1164,18 +1164,18 @@ WebGLContext::Notify(nsITimer* timer)
         if (NS_FAILED(SetDimensions(mWidth, mHeight))) {
             SetupRobustnessTimer();
             return NS_OK;
         }
         mContextStatus = ContextStable;
         nsContentUtils::DispatchTrustedEvent(HTMLCanvasElement()->OwnerDoc(),
                                              (nsIDOMHTMLCanvasElement*) HTMLCanvasElement(),
                                              NS_LITERAL_STRING("webglcontextrestored"),
-                                             PR_TRUE,
-                                             PR_TRUE);
+                                             true,
+                                             true);
         // Set all flags back to the state they were in before the context was
         // lost.
         mContextLostErrorSet = false;
         mContextLostDueToTest = false;
         mAllowRestore = true;
     }
 
     MaybeRestoreContext();
--- a/content/html/content/public/nsHTMLMediaElement.h
+++ b/content/html/content/public/nsHTMLMediaElement.h
@@ -43,34 +43,32 @@
 #include "nsMediaDecoder.h"
 #include "nsIChannel.h"
 #include "nsIHttpChannel.h"
 #include "nsThreadUtils.h"
 #include "nsIDOMRange.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsILoadGroup.h"
 #include "nsIObserver.h"
-#include "ImageLayers.h"
 #include "nsAudioStream.h"
+#include "VideoFrameContainer.h"
 
 // Define to output information on decoding and painting framerate
 /* #define DEBUG_FRAME_RATE 1 */
 
 typedef PRUint16 nsMediaNetworkState;
 typedef PRUint16 nsMediaReadyState;
 
 class nsHTMLMediaElement : public nsGenericHTMLElement,
                            public nsIObserver
 {
+public:
+  typedef mozilla::TimeStamp TimeStamp;
   typedef mozilla::layers::ImageContainer ImageContainer;
-
-public:
-
-  typedef mozilla::TimeStamp TimeStamp;
-  typedef mozilla::TimeDuration TimeDuration;
+  typedef mozilla::VideoFrameContainer VideoFrameContainer;
 
   enum CanPlayStatus {
     CANPLAY_NO,
     CANPLAY_MAYBE,
     CANPLAY_YES
   };
 
   CORSMode GetCORSMode() {
@@ -187,17 +185,22 @@ public:
   // element will then notify its decoder that it needs to make a copy of
   // the audio data sent to hardware and dispatch it in "mozaudioavailable"
   // events. This allows us to not perform the copy and thus reduce overhead
   // in the common case where we don't have a "MozAudioAvailable" listener.
   void NotifyAudioAvailableListener();
 
   // Called by the media decoder and the video frame to get the
   // ImageContainer containing the video data.
-  ImageContainer* GetImageContainer();
+  VideoFrameContainer* GetVideoFrameContainer();
+  ImageContainer* GetImageContainer()
+  {
+    VideoFrameContainer* container = GetVideoFrameContainer();
+    return container ? container->GetImageContainer() : nsnull;
+  }
 
   // Called by the video frame to get the print surface, if this is
   // a static document and we're not actually playing video
   gfxASurface* GetPrintSurface() { return mPrintSurface; }
 
   // Dispatch events
   using nsGenericHTMLElement::DispatchEvent;
   nsresult DispatchEvent(const nsAString& aName);
@@ -564,19 +567,19 @@ protected:
   /**
    * Process any media fragment entries in the URI
    */
   void ProcessMediaFragmentURI();
 
   // The current decoder. Load() has been called on this decoder.
   nsRefPtr<nsMediaDecoder> mDecoder;
 
-  // A reference to the ImageContainer which contains the current frame
+  // A reference to the VideoFrameContainer which contains the current frame
   // of video to display.
-  nsRefPtr<ImageContainer> mImageContainer;
+  nsRefPtr<VideoFrameContainer> mVideoFrameContainer;
 
   // Holds a reference to the first channel we open to the media resource.
   // Once the decoder is created, control over the channel passes to the
   // decoder, and we null out this reference. We must store this in case
   // we need to cancel the channel before control of it passes to the decoder.
   nsCOMPtr<nsIChannel> mChannel;
 
   // Error attribute
@@ -637,17 +640,20 @@ protected:
   nsCOMPtr<nsIURI> mLoadingSrc;
   
   // Stores the current preload action for this element. Initially set to
   // PRELOAD_UNDEFINED, its value is changed by calling
   // UpdatePreloadAction().
   PreloadAction mPreloadAction;
 
   // Size of the media. Updated by the decoder on the main thread if
-  // it changes. Defaults to a width and height of -1 inot set.
+  // it changes. Defaults to a width and height of -1 if not set.
+  // We keep this separate from the intrinsic size stored in the
+  // VideoFrameContainer so that it doesn't change unexpectedly under us
+  // due to decoder activity.
   nsIntSize mMediaSize;
 
   // Time that the last timeupdate event was fired. Read/Write from the
   // main thread only.
   TimeStamp mTimeUpdateTime;
 
   // Media 'currentTime' value when the last timeupdate event occurred.
   // Read/Write from the main thread only.
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -828,17 +828,17 @@ nsGenericHTMLElement::SetOuterHTML(const
     NS_ENSURE_SUCCESS(rv, rv);
     nsCOMPtr<nsIContent> fragment = do_QueryInterface(df);
     nsContentUtils::ParseFragmentHTML(aOuterHTML,
                                       fragment,
                                       localName,
                                       namespaceID,
                                       OwnerDoc()->GetCompatibilityMode() ==
                                         eCompatibility_NavQuirks,
-                                      PR_TRUE);
+                                      true);
     parent->ReplaceChild(fragment, this, &rv);
     return rv;
   }
 
   nsCOMPtr<nsINode> context;
   if (parent->IsElement()) {
     context = parent;
   } else {
@@ -850,17 +850,17 @@ nsGenericHTMLElement::SetOuterHTML(const
                                                  kNameSpaceID_XHTML,
                                                  nsIDOMNode::ELEMENT_NODE);
     context = NS_NewHTMLBodyElement(info.forget(), FROM_PARSER_FRAGMENT);
   }
 
   nsCOMPtr<nsIDOMDocumentFragment> df;
   nsresult rv = nsContentUtils::CreateContextualFragment(context,
                                                          aOuterHTML,
-                                                         PR_TRUE,
+                                                         true,
                                                          getter_AddRefs(df));
   NS_ENSURE_SUCCESS(rv, rv);
   nsCOMPtr<nsINode> fragment = do_QueryInterface(df);
   parent->ReplaceChild(fragment, this, &rv);
   return rv;
 }
 
 enum nsAdjacentPosition {
@@ -1278,21 +1278,24 @@ nsGenericHTMLElement::GetHrefURIForAncho
   nsCOMPtr<nsIURI> uri;
   GetURIAttr(nsGkAtoms::href, nsnull, getter_AddRefs(uri));
 
   return uri.forget();
 }
 
 nsresult
 nsGenericHTMLElement::AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
-                                   const nsAString* aValue, bool aNotify)
+                                   const nsAttrValue* aValue, bool aNotify)
 {
   if (aNamespaceID == kNameSpaceID_None) {
-    if (nsContentUtils::IsEventAttributeName(aName, EventNameType_HTML) && aValue) {
-      nsresult rv = AddScriptEventListener(aName, *aValue);
+    if (nsContentUtils::IsEventAttributeName(aName, EventNameType_HTML) &&
+        aValue) {
+      NS_ABORT_IF_FALSE(aValue->Type() == nsAttrValue::eString,
+        "Expected string value for script body");
+      nsresult rv = AddScriptEventListener(aName, aValue->GetStringValue());
       NS_ENSURE_SUCCESS(rv, rv);
     }
     else if (aNotify && aName == nsGkAtoms::spellcheck) {
       SyncEditorsOnSubtree(this);
     }
   }
 
   return nsGenericHTMLElementBase::AfterSetAttr(aNamespaceID, aName,
@@ -2738,17 +2741,18 @@ nsGenericHTMLFormElement::UnbindFromTree
   nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
 
   // The element might not have a fieldset anymore.
   UpdateFieldSet(false);
 }
 
 nsresult
 nsGenericHTMLFormElement::BeforeSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
-                                        const nsAString* aValue, bool aNotify)
+                                        const nsAttrValueOrString* aValue,
+                                        bool aNotify)
 {
   if (aNameSpaceID == kNameSpaceID_None) {
     nsAutoString tmp;
 
     // remove the control from the hashtable as needed
 
     if (mForm && (aName == nsGkAtoms::name || aName == nsGkAtoms::id)) {
       GetAttr(kNameSpaceID_None, aName, tmp);
@@ -2794,26 +2798,27 @@ nsGenericHTMLFormElement::BeforeSetAttr(
   }
 
   return nsGenericHTMLElement::BeforeSetAttr(aNameSpaceID, aName,
                                              aValue, aNotify);
 }
 
 nsresult
 nsGenericHTMLFormElement::AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
-                                       const nsAString* aValue, bool aNotify)
+                                       const nsAttrValue* aValue, bool aNotify)
 {
   if (aNameSpaceID == kNameSpaceID_None) {
     // add the control to the hashtable as needed
 
     if (mForm && (aName == nsGkAtoms::name || aName == nsGkAtoms::id) &&
-        aValue) {
-      if (!aValue->IsEmpty()) {
-        mForm->AddElementToTable(this, *aValue);
-      }
+        aValue && !aValue->IsEmptyString()) {
+      NS_ABORT_IF_FALSE(aValue->Type() == nsAttrValue::eAtom,
+        "Expected atom value for name/id");
+      mForm->AddElementToTable(this,
+        nsDependentAtomString(aValue->GetAtomValue()));
     }
 
     if (mForm && aName == nsGkAtoms::type) {
       nsAutoString tmp;
 
       GetAttr(kNameSpaceID_None, nsGkAtoms::name, tmp);
 
       if (!tmp.IsEmpty()) {
@@ -2835,17 +2840,17 @@ nsGenericHTMLFormElement::AfterSetAttr(P
       UpdateState(aNotify);
     }
 
     if (aName == nsGkAtoms::form) {
       // We need a new form id observer.
       nsIDocument* doc = GetCurrentDoc();
       if (doc) {
         Element* formIdElement = nsnull;
-        if (aValue && !aValue->IsEmpty()) {
+        if (aValue && !aValue->IsEmptyString()) {
           formIdElement = AddFormIdObserver();
         }
 
         // Because we have a new @form value (or no more @form), we have to
         // update our form owner.
         UpdateFormOwner(false, formIdElement);
       }
     }
--- a/content/html/content/src/nsGenericHTMLElement.h
+++ b/content/html/content/src/nsGenericHTMLElement.h
@@ -626,17 +626,17 @@ protected:
   /**
    * Determine whether an attribute is an event (onclick, etc.)
    * @param aName the attribute
    * @return whether the name is an event handler name
    */
   bool IsEventName(nsIAtom* aName);
 
   virtual nsresult AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
-                                const nsAString* aValue, bool aNotify);
+                                const nsAttrValue* aValue, bool aNotify);
 
   virtual nsEventListenerManager*
     GetEventListenerManagerForAttr(nsIAtom* aAttrName, bool* aDefer);
 
   virtual const nsAttrName* InternalGetExistingAttrNameFromQName(const nsAString& aStr) const;
 
   /**
    * Helper method for NS_IMPL_STRING_ATTR macro.
@@ -945,20 +945,21 @@ public:
    */
   bool CanBeDisabled() const;
 
   virtual bool IsHTMLFocusable(bool aWithMouse, bool* aIsFocusable,
                                  PRInt32* aTabIndex);
 
 protected:
   virtual nsresult BeforeSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
-                                 const nsAString* aValue, bool aNotify);
+                                 const nsAttrValueOrString* aValue,
+                                 bool aNotify);
 
   virtual nsresult AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
-                                const nsAString* aValue, bool aNotify);
+                                const nsAttrValue* aValue, bool aNotify);
 
   void UpdateEditableFormControlState(bool aNotify);
 
   /**
    * This method will update the form owner, using @form or looking to a parent.
    *
    * @param aBindToTree Whether the element is being attached to the tree.
    * @param aFormIdElement The element associated with the id in @form. If
--- a/content/html/content/src/nsHTMLButtonElement.cpp
+++ b/content/html/content/src/nsHTMLButtonElement.cpp
@@ -130,23 +130,24 @@ public:
   bool RestoreState(nsPresState* aState);
 
   nsEventStates IntrinsicState() const;
 
   /**
    * Called when an attribute is about to be changed
    */
   virtual nsresult BeforeSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
-                                 const nsAString* aValue, bool aNotify);
+                                 const nsAttrValueOrString* aValue,
+                                 bool aNotify);
   /**
    * Called when an attribute has just been changed
    */
   nsresult AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
-                        const nsAString* aValue, bool aNotify);
-  
+                        const nsAttrValue* aValue, bool aNotify);
+
   // nsIContent overrides...
   virtual bool IsHTMLFocusable(bool aWithMouse, bool *aIsFocusable, PRInt32 *aTabIndex);
   virtual bool ParseAttribute(PRInt32 aNamespaceID,
                                 nsIAtom* aAttribute,
                                 const nsAString& aValue,
                                 nsAttrValue& aResult);
   virtual nsresult PreHandleEvent(nsEventChainPreVisitor& aVisitor);
   virtual nsresult PostHandleEvent(nsEventChainPostVisitor& aVisitor);
@@ -593,30 +594,31 @@ nsHTMLButtonElement::DoneCreatingElement
   if (!mInhibitStateRestoration) {
     // Restore state as needed.
     RestoreFormControlState(this, this);
   }
 }
 
 nsresult
 nsHTMLButtonElement::BeforeSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
-                                   const nsAString* aValue, bool aNotify)
+                                   const nsAttrValueOrString* aValue,
+                                   bool aNotify)
 {
   if (aNotify && aName == nsGkAtoms::disabled &&
       aNameSpaceID == kNameSpaceID_None) {
     mDisabledChanged = true;
   }
 
   return nsGenericHTMLFormElement::BeforeSetAttr(aNameSpaceID, aName,
                                                  aValue, aNotify);
 }
 
 nsresult
 nsHTMLButtonElement::AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
-                                  const nsAString* aValue, bool aNotify)
+                                  const nsAttrValue* aValue, bool aNotify)
 {
   if (aNameSpaceID == kNameSpaceID_None) {
     if (aName == nsGkAtoms::type) {
       if (!aValue) {
         mType = kButtonDefaultType->value;
       }
 
       UpdateState(aNotify);
--- a/content/html/content/src/nsHTMLFieldSetElement.cpp
+++ b/content/html/content/src/nsHTMLFieldSetElement.cpp
@@ -114,17 +114,17 @@ nsHTMLFieldSetElement::PreHandleEvent(ns
     return NS_OK;
   }
 
   return nsGenericHTMLFormElement::PreHandleEvent(aVisitor);
 }
 
 nsresult
 nsHTMLFieldSetElement::AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
-                                    const nsAString* aValue, bool aNotify)
+                                    const nsAttrValue* aValue, bool aNotify)
 {
   if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::disabled &&
       nsINode::GetFirstChild()) {
     if (!mElements) {
       mElements = new nsContentList(this, MatchListedElements, nsnull, nsnull,
                                     true);
     }
 
--- a/content/html/content/src/nsHTMLFieldSetElement.h
+++ b/content/html/content/src/nsHTMLFieldSetElement.h
@@ -75,17 +75,17 @@ public:
   NS_FORWARD_NSIDOMHTMLELEMENT(nsGenericHTMLFormElement::)
 
   // nsIDOMHTMLFieldSetElement
   NS_DECL_NSIDOMHTMLFIELDSETELEMENT
 
   // nsIContent
   virtual nsresult PreHandleEvent(nsEventChainPreVisitor& aVisitor);
   virtual nsresult AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
-                                const nsAString* aValue, bool aNotify);
+                                const nsAttrValue* aValue, bool aNotify);
 
   virtual nsresult InsertChildAt(nsIContent* aChild, PRUint32 aIndex,
                                      bool aNotify);
   virtual nsresult RemoveChildAt(PRUint32 aIndex, bool aNotify);
 
   // nsIFormControl
   NS_IMETHOD_(PRUint32) GetType() const { return NS_FORM_FIELDSET; }
   NS_IMETHOD Reset();
--- a/content/html/content/src/nsHTMLFormElement.cpp
+++ b/content/html/content/src/nsHTMLFormElement.cpp
@@ -373,17 +373,17 @@ nsHTMLFormElement::SetAttr(PRInt32 aName
     mNotifiedObservers = notifiedObservers;
   }
   return nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, aValue,
                                        aNotify);
 }
 
 nsresult
 nsHTMLFormElement::AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
-                                const nsAString* aValue, bool aNotify)
+                                const nsAttrValue* aValue, bool aNotify)
 {
   if (aName == nsGkAtoms::novalidate && aNameSpaceID == kNameSpaceID_None) {
     // Update all form elements states because they might be [no longer]
     // affected by :-moz-ui-valid or :-moz-ui-invalid.
     for (PRUint32 i = 0, length = mControls->mElements.Length();
          i < length; ++i) {
       mControls->mElements[i]->UpdateState(true);
     }
--- a/content/html/content/src/nsHTMLFormElement.h
+++ b/content/html/content/src/nsHTMLFormElement.h
@@ -171,17 +171,17 @@ public:
                    const nsAString& aValue, bool aNotify)
   {
     return SetAttr(aNameSpaceID, aName, nsnull, aValue, aNotify);
   }
   virtual nsresult SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
                            nsIAtom* aPrefix, const nsAString& aValue,
                            bool aNotify);
   virtual nsresult AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
-                                const nsAString* aValue, bool aNotify);
+                                const nsAttrValue* aValue, bool aNotify);
 
   /**
    * Forget all information about the current submission (and the fact that we
    * are currently submitting at all).
    */
   void ForgetCurrentSubmission();
 
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
--- a/content/html/content/src/nsHTMLInputElement.cpp
+++ b/content/html/content/src/nsHTMLInputElement.cpp
@@ -70,16 +70,17 @@
 #include "nsIServiceManager.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsDOMError.h"
 #include "nsIPrivateDOMEvent.h"
 #include "nsIEditor.h"
 #include "nsGUIEvent.h"
 #include "nsIIOService.h"
 #include "nsDocument.h"
+#include "nsAttrValueOrString.h"
 
 #include "nsPresState.h"
 #include "nsLayoutErrors.h"
 #include "nsIDOMEvent.h"
 #include "nsIDOMNSEvent.h"
 #include "nsIDOMNodeList.h"
 #include "nsIDOMHTMLCollection.h"
 #include "nsLinebreakConverter.h" //to strip out carriage returns
@@ -717,51 +718,50 @@ nsHTMLInputElement::Clone(nsINodeInfo *a
   }
 
   it.forget(aResult);
   return NS_OK;
 }
 
 nsresult
 nsHTMLInputElement::BeforeSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
-                                  const nsAString* aValue,
+                                  const nsAttrValueOrString* aValue,
                                   bool aNotify)
 {
   if (aNameSpaceID == kNameSpaceID_None) {
     //
     // When name or type changes, radio should be removed from radio group.
     // (type changes are handled in the form itself currently)
     // If the parser is not done creating the radio, we also should not do it.
     //
     if ((aName == nsGkAtoms::name ||
          (aName == nsGkAtoms::type && !mForm)) &&
         mType == NS_FORM_INPUT_RADIO &&
         (mForm || !mParserCreating)) {
       WillRemoveFromRadioGroup();
     } else if (aNotify && aName == nsGkAtoms::src &&
                mType == NS_FORM_INPUT_IMAGE) {
       if (aValue) {
-        LoadImage(*aValue, true, aNotify);
+        LoadImage(aValue->String(), true, aNotify);
       } else {
         // Null value means the attr got unset; drop the image
         CancelImageRequests(aNotify);
       }
     } else if (aNotify && aName == nsGkAtoms::disabled) {
       mDisabledChanged = true;
     }
   }
 
   return nsGenericHTMLFormElement::BeforeSetAttr(aNameSpaceID, aName,
                                                  aValue, aNotify);
 }
 
 nsresult
 nsHTMLInputElement::AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
-                                 const nsAString* aValue,
-                                 bool aNotify)
+                                 const nsAttrValue* aValue, bool aNotify)
 {
   if (aNameSpaceID == kNameSpaceID_None) {
     //
     // When name or type changes, radio should be added to radio group.
     // (type changes are handled in the form itself currently)
     // If the parser is not done creating the radio, we also should not do it.
     //
     if ((aName == nsGkAtoms::name ||
--- a/content/html/content/src/nsHTMLInputElement.h
+++ b/content/html/content/src/nsHTMLInputElement.h
@@ -374,22 +374,23 @@ protected:
                                     bool aShouldInvalidate);
 
   nsresult GetSelectionRange(PRInt32* aSelectionStart, PRInt32* aSelectionEnd);
 
   /**
    * Called when an attribute is about to be changed
    */
   virtual nsresult BeforeSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
-                                 const nsAString* aValue, bool aNotify);
+                                 const nsAttrValueOrString* aValue,
+                                 bool aNotify);
   /**
    * Called when an attribute has just been changed
    */
   virtual nsresult AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
-                                const nsAString* aValue, bool aNotify);
+                                const nsAttrValue* aValue, bool aNotify);
 
   /**
    * Dispatch a select event. Returns true if the event was not cancelled.
    */
   bool DispatchSelectEvent(nsPresContext* aPresContext);
 
   void SelectAll(nsPresContext* aPresContext);
   bool IsImage() const
--- a/content/html/content/src/nsHTMLMediaElement.cpp
+++ b/content/html/content/src/nsHTMLMediaElement.cpp
@@ -63,17 +63,17 @@
 #include "jsapi.h"
 
 #include "nsITimer.h"
 
 #include "nsEventDispatcher.h"
 #include "nsMediaError.h"
 #include "nsICategoryManager.h"
 #include "nsCharSeparatedTokenizer.h"
-#include "nsMediaStream.h"
+#include "MediaResource.h"
 
 #include "nsIDOMHTMLVideoElement.h"
 #include "nsIContentPolicy.h"
 #include "nsContentPolicyUtils.h"
 #include "nsContentErrors.h"
 #include "nsCrossSiteListenerProxy.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsICachingChannel.h"
@@ -923,17 +923,17 @@ void nsHTMLMediaElement::UpdatePreloadAc
   }
 }
 
 nsresult nsHTMLMediaElement::LoadResource()
 {
   NS_ASSERTION(mDelayingLoadEvent,
                "Should delay load event (if in document) during load");
 
-  // If a previous call to mozSetup() was made, kill that media stream
+  // If a previous call to mozSetup() was made, kill that media resource
   // in order to use this new src instead.
   if (mAudioStream) {
     mAudioStream->Shutdown();
     mAudioStream = nsnull;
   }
 
   if (mChannel) {
     mChannel->Cancel(NS_BINDING_ABORTED);
@@ -1349,17 +1349,17 @@ MediaElementTableCount(nsHTMLMediaElemen
   }
   return count;
 }
 #endif
 
 void
 nsHTMLMediaElement::AddMediaElementToURITable()
 {
-  NS_ASSERTION(mDecoder && mDecoder->GetStream(), "Call this only with decoder Load called");
+  NS_ASSERTION(mDecoder && mDecoder->GetResource(), "Call this only with decoder Load called");
   NS_ASSERTION(MediaElementTableCount(this, mLoadingSrc) == 0,
     "Should not have entry for element in element table before addition");
   if (!gElementTable) {
     gElementTable = new MediaElementURITable();
     gElementTable->Init();
   }
   MediaElementSetForURI* entry = gElementTable->PutEntry(mLoadingSrc);
   entry->mElements.AppendElement(this);
@@ -1401,17 +1401,17 @@ nsHTMLMediaElement::LookupMediaElementUR
     return nsnull;
   for (PRUint32 i = 0; i < entry->mElements.Length(); ++i) {
     nsHTMLMediaElement* elem = entry->mElements[i];
     bool equal;
     // Look for elements that have the same principal and CORS mode.
     // Ditto for anything else that could cause us to send different headers.
     if (NS_SUCCEEDED(elem->NodePrincipal()->Equals(NodePrincipal(), &equal)) && equal &&
         elem->mCORSMode == mCORSMode) {
-      NS_ASSERTION(elem->mDecoder && elem->mDecoder->GetStream(), "Decoder gone");
+      NS_ASSERTION(elem->mDecoder && elem->mDecoder->GetResource(), "Decoder gone");
       return elem;
     }
   }
   return nsnull;
 }
 
 nsHTMLMediaElement::nsHTMLMediaElement(already_AddRefed<nsINodeInfo> aNodeInfo)
   : nsGenericHTMLElement(aNodeInfo),
@@ -1463,16 +1463,19 @@ nsHTMLMediaElement::nsHTMLMediaElement(a
   NotifyOwnerDocumentActivityChanged();
 }
 
 nsHTMLMediaElement::~nsHTMLMediaElement()
 {
   NS_ASSERTION(!mHasSelfReference,
                "How can we be destroyed if we're still holding a self reference?");
 
+  if (mVideoFrameContainer) {
+    mVideoFrameContainer->ForgetElement();
+  }
   UnregisterFreezableElement();
   if (mDecoder) {
     RemoveMediaElementFromURITable();
     mDecoder->Shutdown();
   }
 
   NS_ASSERTION(MediaElementTableCount(this, mLoadingSrc) == 0,
     "Destroyed media element should no longer be in element table");
@@ -2002,18 +2005,18 @@ nsHTMLMediaElement::CreateDecoder(const 
   return nsnull;
 }
 
 nsresult nsHTMLMediaElement::InitializeDecoderAsClone(nsMediaDecoder* aOriginal)
 {
   NS_ASSERTION(mLoadingSrc, "mLoadingSrc must already be set");
   NS_ASSERTION(mDecoder == nsnull, "Shouldn't have a decoder");
 
-  nsMediaStream* originalStream = aOriginal->GetStream();
-  if (!originalStream)
+  MediaResource* originalResource = aOriginal->GetResource();
+  if (!originalResource)
     return NS_ERROR_FAILURE;
   nsRefPtr<nsMediaDecoder> decoder = aOriginal->Clone();
   if (!decoder)
     return NS_ERROR_FAILURE;
 
   LOG(PR_LOG_DEBUG, ("%p Cloned decoder %p from %p", this, decoder.get(), aOriginal));
 
   if (!decoder->Init(this)) {
@@ -2022,25 +2025,25 @@ nsresult nsHTMLMediaElement::InitializeD
   }
 
   double duration = aOriginal->GetDuration();
   if (duration >= 0) {
     decoder->SetDuration(duration);
     decoder->SetSeekable(aOriginal->IsSeekable());
   }
 
-  nsMediaStream* stream = originalStream->CloneData(decoder);
-  if (!stream) {
+  MediaResource* resource = originalResource->CloneData(decoder);
+  if (!resource) {
     LOG(PR_LOG_DEBUG, ("%p Failed to cloned stream for decoder %p", this, decoder.get()));
     return NS_ERROR_FAILURE;
   }
 
   mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING;
 
-  nsresult rv = decoder->Load(stream, nsnull, aOriginal);
+  nsresult rv = decoder->Load(resource, nsnull, aOriginal);
   if (NS_FAILED(rv)) {
     LOG(PR_LOG_DEBUG, ("%p Failed to load decoder/stream for decoder %p", this, decoder.get()));
     return rv;
   }
 
   return FinishDecoderSetup(decoder);
 }
 
@@ -2062,43 +2065,43 @@ nsresult nsHTMLMediaElement::InitializeD
     ReportLoadError("MediaLoadUnsupportedMimeType", params, ArrayLength(params));
     return NS_ERROR_FAILURE;
   }
 
   LOG(PR_LOG_DEBUG, ("%p Created decoder %p for type %s", this, decoder.get(), mimeType.get()));
 
   mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING;
 
-  nsMediaStream* stream = nsMediaStream::Create(decoder, aChannel);
-  if (!stream)
+  MediaResource* resource = MediaResource::Create(decoder, aChannel);
+  if (!resource)
     return NS_ERROR_OUT_OF_MEMORY;
 
-  nsresult rv = decoder->Load(stream, aListener, nsnull);
+  nsresult rv = decoder->Load(resource, aListener, nsnull);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  // Decoder successfully created, the decoder now owns the nsMediaStream
+  // Decoder successfully created, the decoder now owns the MediaResource
   // which owns the channel.
   mChannel = nsnull;
 
   return FinishDecoderSetup(decoder);
 }
 
 nsresult nsHTMLMediaElement::FinishDecoderSetup(nsMediaDecoder* aDecoder)
 {
   NS_ASSERTION(mLoadingSrc, "mLoadingSrc set up");
 
   mDecoder = aDecoder;
   AddMediaElementToURITable();
 
   // Force a same-origin check before allowing events for this media resource.
   mMediaSecurityVerified = false;
 
-  // The new stream has not been suspended by us.
+  // The new resource has not been suspended by us.
   mPausedForInactiveDocument = false;
   // But we may want to suspend it now.
   // This will also do an AddRemoveSelfReference.
   NotifyOwnerDocumentActivityChanged();
 
   nsresult rv = NS_OK;
 
   mDecoder->SetVolume(mMuted ? 0.0 : mVolume);
@@ -2284,17 +2287,17 @@ void nsHTMLMediaElement::Error(PRUint16 
 
 void nsHTMLMediaElement::PlaybackEnded()
 {
   NS_ASSERTION(mDecoder->IsEnded(), "Decoder fired ended, but not in ended state");
   // We changed the state of IsPlaybackEnded which can affect AddRemoveSelfReference
   AddRemoveSelfReference();
 
   if (mDecoder && mDecoder->IsInfinite()) {
-    LOG(PR_LOG_DEBUG, ("%p, got duration by reaching the end of the stream", this));
+    LOG(PR_LOG_DEBUG, ("%p, got duration by reaching the end of the resource", this));
     DispatchAsyncEvent(NS_LITERAL_STRING("durationchange"));
   }
 
   if (HasAttr(kNameSpaceID_None, nsGkAtoms::loop)) {
     SetCurrentTime(0);
     return;
   }
 
@@ -2466,33 +2469,34 @@ void nsHTMLMediaElement::NotifyAutoplayD
     if (mDecoder) {
       SetPlayedOrSeeked(true);
       mDecoder->Play();
     }
     DispatchAsyncEvent(NS_LITERAL_STRING("play"));
   }
 }
 
-ImageContainer* nsHTMLMediaElement::GetImageContainer()
+VideoFrameContainer* nsHTMLMediaElement::GetVideoFrameContainer()
 {
-  if (mImageContainer)
-    return mImageContainer;
+  if (mVideoFrameContainer)
+    return mVideoFrameContainer;
 
   // If we have a print surface, this is just a static image so
   // no image container is required
   if (mPrintSurface)
     return nsnull;
 
   // Only video frames need an image container.
   nsCOMPtr<nsIDOMHTMLVideoElement> video = do_QueryObject(this);
   if (!video)
     return nsnull;
 
-  mImageContainer = LayerManager::CreateImageContainer();
-  return mImageContainer;
+  mVideoFrameContainer =
+    new VideoFrameContainer(this, LayerManager::CreateImageContainer());
+  return mVideoFrameContainer;
 }
 
 nsresult nsHTMLMediaElement::DispatchAudioAvailableEvent(float* aFrameBuffer,
                                                          PRUint32 aFrameBufferLength,
                                                          float aTime)
 {
   // Auto manage the memory for the frame buffer. If we fail and return
   // an error, this ensures we free the memory in the frame buffer. Otherwise
--- a/content/html/content/src/nsHTMLMenuItemElement.cpp
+++ b/content/html/content/src/nsHTMLMenuItemElement.cpp
@@ -412,17 +412,17 @@ nsHTMLMenuItemElement::GetText(nsAString
   nsContentUtils::GetNodeTextContent(this, false, text);
 
   text.CompressWhitespace(true, true);
   aText = text;
 }
 
 nsresult
 nsHTMLMenuItemElement::AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
-                                    const nsAString* aValue, bool aNotify)
+                                    const nsAttrValue* aValue, bool aNotify)
 {
   if (aNameSpaceID == kNameSpaceID_None) {
     if ((aName == nsGkAtoms::radiogroup || aName == nsGkAtoms::type) &&
         mType == CMD_TYPE_RADIO &&
         !mParserCreating) {
       if (IsInDoc() && GetParent()) {
         AddedToRadioGroup();
       }
--- a/content/html/content/src/nsHTMLMenuItemElement.h
+++ b/content/html/content/src/nsHTMLMenuItemElement.h
@@ -101,17 +101,17 @@ public:
    */
   bool IsChecked() const { return mChecked; }
   bool IsCheckedDirty() const { return mCheckedDirty; }
 
   void GetText(nsAString& aText);
 
 protected:
   virtual nsresult AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
-                                const nsAString* aValue, bool aNotify);
+                                const nsAttrValue* aValue, bool aNotify);
 
   void WalkRadioGroup(Visitor* aVisitor);
 
   nsHTMLMenuItemElement* GetSelectedRadio();
 
   void AddedToRadioGroup();
 
   void InitChecked();
--- a/content/html/content/src/nsHTMLOptionElement.cpp
+++ b/content/html/content/src/nsHTMLOptionElement.cpp
@@ -258,17 +258,18 @@ nsHTMLOptionElement::GetAttributeChangeH
       aAttribute == nsGkAtoms::text) {
     NS_UpdateHint(retval, NS_STYLE_HINT_REFLOW);
   }
   return retval;
 }
 
 nsresult
 nsHTMLOptionElement::BeforeSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
-                                   const nsAString* aValue, bool aNotify)
+                                   const nsAttrValueOrString* aValue,
+                                   bool aNotify)
 {
   nsresult rv = nsGenericHTMLElement::BeforeSetAttr(aNamespaceID, aName,
                                                     aValue, aNotify);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (aNamespaceID != kNameSpaceID_None || aName != nsGkAtoms::selected ||
       mSelectedChanged) {
     return NS_OK;
--- a/content/html/content/src/nsHTMLOptionElement.h
+++ b/content/html/content/src/nsHTMLOptionElement.h
@@ -86,18 +86,19 @@ public:
   // nsIJSNativeInitializer
   NS_IMETHOD Initialize(nsISupports* aOwner, JSContext* aContext,
                         JSObject *aObj, PRUint32 argc, jsval *argv);
 
   virtual nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute,
                                               PRInt32 aModType) const;
 
   virtual nsresult BeforeSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
-                                 const nsAString* aValue, bool aNotify);
-  
+                                 const nsAttrValueOrString* aValue,
+                                 bool aNotify);
+
   void SetSelectedInternal(bool aValue, bool aNotify);
 
   // nsIContent
   virtual nsEventStates IntrinsicState() const;
 
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
 
   nsresult CopyInnerTo(nsGenericElement* aDest) const;
--- a/content/html/content/src/nsHTMLScriptElement.cpp
+++ b/content/html/content/src/nsHTMLScriptElement.cpp
@@ -113,17 +113,17 @@ public:
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent,
                               bool aCompileEventHandlers);
 
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
 
   // nsGenericElement
   virtual nsresult AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
-                                const nsAString* aValue, bool aNotify);
+                                const nsAttrValue* aValue, bool aNotify);
 
   virtual nsXPCClassInfo* GetClassInfo();
 protected:
   // nsScriptElement
   virtual bool HasScriptContent();
 };
 
 
@@ -236,17 +236,17 @@ nsresult
 nsHTMLScriptElement::SetAsync(bool aValue)
 {
   mForceAsync = false;
   return SetBoolAttr(nsGkAtoms::async, aValue);
 }
 
 nsresult
 nsHTMLScriptElement::AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
-                                  const nsAString* aValue, bool aNotify)
+                                  const nsAttrValue* aValue, bool aNotify)
 {
   if (nsGkAtoms::async == aName && kNameSpaceID_None == aNamespaceID) {
     mForceAsync = false;
   }
   return nsGenericHTMLElement::AfterSetAttr(aNamespaceID, aName, aValue,
                                             aNotify);
 }
 
--- a/content/html/content/src/nsHTMLSelectElement.cpp
+++ b/content/html/content/src/nsHTMLSelectElement.cpp
@@ -1366,30 +1366,31 @@ nsHTMLSelectElement::UnbindFromTree(bool
   UpdateBarredFromConstraintValidation();
 
   // And now make sure our state is up to date
   UpdateState(false);
 }
 
 nsresult
 nsHTMLSelectElement::BeforeSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
-                                   const nsAString* aValue, bool aNotify)
+                                   const nsAttrValueOrString* aValue,
+                                   bool aNotify)
 {
   if (aNotify && aName == nsGkAtoms::disabled &&
       aNameSpaceID == kNameSpaceID_None) {
     mDisabledChanged = true;
   }
 
   return nsGenericHTMLFormElement::BeforeSetAttr(aNameSpaceID, aName,
                                                  aValue, aNotify);
 }
 
 nsresult
 nsHTMLSelectElement::AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
-                                  const nsAString* aValue, bool aNotify)
+                                  const nsAttrValue* aValue, bool aNotify)
 {
   if (aNameSpaceID == kNameSpaceID_None) {
     if (aName == nsGkAtoms::disabled) {
       UpdateBarredFromConstraintValidation();
     } else if (aName == nsGkAtoms::required) {
       UpdateValueMissingValidityState();
     }
 
--- a/content/html/content/src/nsHTMLSelectElement.h
+++ b/content/html/content/src/nsHTMLSelectElement.h
@@ -387,19 +387,20 @@ public:
   /**
    * Called when an attribute is about to be changed
    */
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                                nsIContent* aBindingParent,
                                bool aCompileEventHandlers);
   virtual void UnbindFromTree(bool aDeep, bool aNullParent);
   virtual nsresult BeforeSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
-                                 const nsAString* aValue, bool aNotify);
+                                 const nsAttrValueOrString* aValue,
+                                 bool aNotify);
   virtual nsresult AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
-                                const nsAString* aValue, bool aNotify);
+                                const nsAttrValue* aValue, bool aNotify);
   virtual nsresult UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
                              bool aNotify);
   
   virtual void DoneAddingChildren(bool aHaveNotified);
   virtual bool IsDoneAddingChildren() {
     return mIsDoneAddingChildren;
   }
 
--- a/content/html/content/src/nsHTMLTableElement.cpp
+++ b/content/html/content/src/nsHTMLTableElement.cpp
@@ -1260,29 +1260,29 @@ void
 nsHTMLTableElement::UnbindFromTree(bool aDeep, bool aNullParent)
 {
   ReleaseInheritedAttributes();
   nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
 }
 
 nsresult
 nsHTMLTableElement::BeforeSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
-                                  const nsAString* aValue,
+                                  const nsAttrValueOrString* aValue,
                                   bool aNotify)
 {
   if (aName == nsGkAtoms::cellpadding && aNameSpaceID == kNameSpaceID_None) {
     ReleaseInheritedAttributes();
   }
   return nsGenericHTMLElement::BeforeSetAttr(aNameSpaceID, aName, aValue,
                                              aNotify);
 }
 
 nsresult
 nsHTMLTableElement::AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
-                                 const nsAString* aValue,
+                                 const nsAttrValue* aValue,
                                  bool aNotify)
 {
   if (aName == nsGkAtoms::cellpadding && aNameSpaceID == kNameSpaceID_None) {
     BuildInheritedAttributes();
   }
   return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, aValue,
                                             aNotify);
 }
--- a/content/html/content/src/nsHTMLTableElement.h
+++ b/content/html/content/src/nsHTMLTableElement.h
@@ -79,22 +79,23 @@ public:
                               nsIContent* aBindingParent,
                               bool aCompileEventHandlers);
   virtual void UnbindFromTree(bool aDeep = true,
                               bool aNullParent = true);
   /**
    * Called when an attribute is about to be changed
    */
   virtual nsresult BeforeSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
-                                 const nsAString* aValue, bool aNotify);
+                                 const nsAttrValueOrString* aValue,
+                                 bool aNotify);
   /**
    * Called when an attribute has just been changed
    */
   virtual nsresult AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
-                                const nsAString* aValue, bool aNotify);
+                                const nsAttrValue* aValue, bool aNotify);
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsHTMLTableElement,
                                            nsGenericHTMLElement)
   nsMappedAttributes* GetAttributesMappedForCell();
   already_AddRefed<nsIDOMHTMLTableSectionElement> GetTHead() {
     return GetSection(nsGkAtoms::thead);
   }
   already_AddRefed<nsIDOMHTMLTableSectionElement> GetTFoot() {
--- a/content/html/content/src/nsHTMLTextAreaElement.cpp
+++ b/content/html/content/src/nsHTMLTextAreaElement.cpp
@@ -207,17 +207,18 @@ public:
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
 
   nsresult CopyInnerTo(nsGenericElement* aDest) const;
 
   /**
    * Called when an attribute is about to be changed
    */
   virtual nsresult BeforeSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
-                                 const nsAString* aValue, bool aNotify);
+                                 const nsAttrValueOrString* aValue,
+                                 bool aNotify);
 
   // nsIMutationObserver
   NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
 
   virtual void UpdateEditableState(bool aNotify)
@@ -279,17 +280,17 @@ protected:
   /**
    * Common method to call from the various mutation observer methods.
    * aContent is a content node that's either the one that changed or its
    * parent; we should only respond to the change if aContent is non-anonymous.
    */
   void ContentChanged(nsIContent* aContent);
 
   virtual nsresult AfterSetAttr(PRInt32 aNamespaceID, nsIAtom *aName,
-                                const nsAString* aValue, bool aNotify);
+                                const nsAttrValue* aValue, bool aNotify);
 
   /**
    * Return if an element should have a specific validity UI
    * (with :-moz-ui-invalid and :-moz-ui-valid pseudo-classes).
    *
    * @return Whether the elemnet should have a validity UI.
    */
   bool ShouldShowValidityUI() const {
@@ -1210,17 +1211,18 @@ nsHTMLTextAreaElement::UnbindFromTree(bo
   UpdateBarredFromConstraintValidation();
 
   // And now make sure our state is up to date
   UpdateState(false);
 }
 
 nsresult
 nsHTMLTextAreaElement::BeforeSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
-                                     const nsAString* aValue, bool aNotify)
+                                     const nsAttrValueOrString* aValue,
+                                     bool aNotify)
 {
   if (aNotify && aName == nsGkAtoms::disabled &&
       aNameSpaceID == kNameSpaceID_None) {
     mDisabledChanged = true;
   }
 
   return nsGenericHTMLFormElement::BeforeSetAttr(aNameSpaceID, aName,
                                                  aValue, aNotify);
@@ -1271,17 +1273,17 @@ nsHTMLTextAreaElement::ContentChanged(ns
     // further auditing.
     nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
     Reset();
   }
 }
 
 nsresult
 nsHTMLTextAreaElement::AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
-                                    const nsAString* aValue, bool aNotify)
+                                    const nsAttrValue* aValue, bool aNotify)
 {
   if (aNameSpaceID == kNameSpaceID_None) {
     if (aName == nsGkAtoms::required || aName == nsGkAtoms::disabled ||
         aName == nsGkAtoms::readonly) {
       UpdateValueMissingValidityState();
 
       // This *has* to be called *after* validity has changed.
       if (aName == nsGkAtoms::readonly || aName == nsGkAtoms::disabled) {
--- a/content/html/content/src/nsHTMLVideoElement.cpp
+++ b/content/html/content/src/nsHTMLVideoElement.cpp
@@ -204,17 +204,19 @@ NS_IMETHODIMP nsHTMLVideoElement::GetMoz
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   *aMozPresentedFrames = mDecoder ? mDecoder->GetFrameStatistics().GetPresentedFrames() : 0;
   return NS_OK;
 }
 
 NS_IMETHODIMP nsHTMLVideoElement::GetMozPaintedFrames(PRUint32 *aMozPaintedFrames)
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
-  *aMozPaintedFrames = (!mDecoder || !GetImageContainer()) ? 0 : GetImageContainer()->GetPaintCount();
+  ImageContainer* container = GetImageContainer();
+  *aMozPaintedFrames = container ? container->GetPaintCount() : 0;
   return NS_OK;
 }
 
 NS_IMETHODIMP nsHTMLVideoElement::GetMozFrameDelay(double *aMozFrameDelay) {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
-  *aMozFrameDelay = mDecoder ? mDecoder->GetFrameDelay() : 0;
+  VideoFrameContainer* container = GetVideoFrameContainer();
+  *aMozFrameDelay = container ?  container->GetFrameDelay() : 0;
   return NS_OK;
 }
--- a/content/media/Makefile.in
+++ b/content/media/Makefile.in
@@ -41,35 +41,37 @@ VPATH     = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE = content
 LIBRARY_NAME = gkconmedia_s
 LIBXUL_LIBRARY = 1
 
 EXPORTS = \
+  nsAudioAvailableEventManager.h \
   nsMediaDecoder.h \
-  nsMediaStream.h \
   nsMediaCache.h \
   nsBuiltinDecoder.h \
   nsBuiltinDecoderStateMachine.h \
   nsBuiltinDecoderReader.h \
+  MediaResource.h \
+  VideoFrameContainer.h \
   VideoUtils.h \
-  nsAudioAvailableEventManager.h \
   $(NULL)
 
 CPPSRCS = \
+  nsAudioAvailableEventManager.cpp \
   nsMediaDecoder.cpp \
   nsMediaCache.cpp \
-  nsMediaStream.cpp \
   nsBuiltinDecoder.cpp \
   nsBuiltinDecoderStateMachine.cpp \
   nsBuiltinDecoderReader.cpp \
+  MediaResource.cpp \
+  VideoFrameContainer.cpp \
   VideoUtils.cpp \
-  nsAudioAvailableEventManager.cpp \
   $(NULL)
 
 ifdef MOZ_SYDNEYAUDIO
 EXPORTS += \
   nsAudioStream.h \
   $(NULL)
 CPPSRCS += \
   nsAudioStream.cpp \
rename from content/media/nsMediaStream.cpp
rename to content/media/MediaResource.cpp
--- a/content/media/nsMediaStream.cpp
+++ b/content/media/MediaResource.cpp
@@ -31,17 +31,17 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-#include "nsMediaStream.h"
+#include "MediaResource.h"
 
 #include "mozilla/Mutex.h"
 #include "nsDebug.h"
 #include "nsMediaDecoder.h"
 #include "nsNetUtil.h"
 #include "nsThreadUtils.h"
 #include "nsIFile.h"
 #include "nsIFileChannel.h"
@@ -62,101 +62,101 @@
 #include "mozilla/Util.h" // for DebugOnly
 #include "nsContentUtils.h"
 
 static const PRUint32 HTTP_OK_CODE = 200;
 static const PRUint32 HTTP_PARTIAL_RESPONSE_CODE = 206;
 
 using namespace mozilla;
 
-nsMediaChannelStream::nsMediaChannelStream(nsMediaDecoder* aDecoder,
+ChannelMediaResource::ChannelMediaResource(nsMediaDecoder* aDecoder,
     nsIChannel* aChannel, nsIURI* aURI)
-  : nsMediaStream(aDecoder, aChannel, aURI),
+  : MediaResource(aDecoder, aChannel, aURI),
     mOffset(0), mSuspendCount(0),
     mReopenOnError(false), mIgnoreClose(false),
     mCacheStream(this),
-    mLock("nsMediaChannelStream.mLock"),
+    mLock("ChannelMediaResource.mLock"),
     mIgnoreResume(false)
 {
 }
 
-nsMediaChannelStream::~nsMediaChannelStream()
+ChannelMediaResource::~ChannelMediaResource()
 {
   if (mListener) {
     // Kill its reference to us since we're going away
     mListener->Revoke();
   }
 }
 
-// nsMediaChannelStream::Listener just observes the channel and
-// forwards notifications to the nsMediaChannelStream. We use multiple
+// ChannelMediaResource::Listener just observes the channel and
+// forwards notifications to the ChannelMediaResource. We use multiple
 // listener objects so that when we open a new stream for a seek we can
-// disconnect the old listener from the nsMediaChannelStream and hook up
+// disconnect the old listener from the ChannelMediaResource and hook up
 // a new listener, so notifications from the old channel are discarded
 // and don't confuse us.
-NS_IMPL_ISUPPORTS4(nsMediaChannelStream::Listener,
+NS_IMPL_ISUPPORTS4(ChannelMediaResource::Listener,
                    nsIRequestObserver, nsIStreamListener, nsIChannelEventSink,
                    nsIInterfaceRequestor)
 
 nsresult
-nsMediaChannelStream::Listener::OnStartRequest(nsIRequest* aRequest,
+ChannelMediaResource::Listener::OnStartRequest(nsIRequest* aRequest,
                                                nsISupports* aContext)
 {
-  if (!mStream)
+  if (!mResource)
     return NS_OK;
-  return mStream->OnStartRequest(aRequest);
+  return mResource->OnStartRequest(aRequest);
 }
 
 nsresult
-nsMediaChannelStream::Listener::OnStopRequest(nsIRequest* aRequest,
+ChannelMediaResource::Listener::OnStopRequest(nsIRequest* aRequest,
                                               nsISupports* aContext,
                                               nsresult aStatus)
 {
-  if (!mStream)
+  if (!mResource)
     return NS_OK;
-  return mStream->OnStopRequest(aRequest, aStatus);
+  return mResource->OnStopRequest(aRequest, aStatus);
 }
 
 nsresult
-nsMediaChannelStream::Listener::OnDataAvailable(nsIRequest* aRequest,
+ChannelMediaResource::Listener::OnDataAvailable(nsIRequest* aRequest,
                                                 nsISupports* aContext,
                                                 nsIInputStream* aStream,
                                                 PRUint32 aOffset,
                                                 PRUint32 aCount)
 {
-  if (!mStream)
+  if (!mResource)
     return NS_OK;
-  return mStream->OnDataAvailable(aRequest, aStream, aCount);
+  return mResource->OnDataAvailable(aRequest, aStream, aCount);
 }
 
 nsresult
-nsMediaChannelStream::Listener::AsyncOnChannelRedirect(nsIChannel* aOldChannel,
+ChannelMediaResource::Listener::AsyncOnChannelRedirect(nsIChannel* aOldChannel,
                                                        nsIChannel* aNewChannel,
                                                        PRUint32 aFlags,
                                                        nsIAsyncVerifyRedirectCallback* cb)
 {
   nsresult rv = NS_OK;
-  if (mStream)
-    rv = mStream->OnChannelRedirect(aOldChannel, aNewChannel, aFlags);
+  if (mResource)
+    rv = mResource->OnChannelRedirect(aOldChannel, aNewChannel, aFlags);
 
   if (NS_FAILED(rv))
     return rv;
 
   cb->OnRedirectVerifyCallback(NS_OK);
   return NS_OK;
 }
 
 nsresult
-nsMediaChannelStream::Listener::GetInterface(const nsIID & aIID, void **aResult)
+ChannelMediaResource::Listener::GetInterface(const nsIID & aIID, void **aResult)
 {
   return QueryInterface(aIID, aResult);
 }
 
 nsresult
-nsMediaChannelStream::OnStartRequest(nsIRequest* aRequest)
+ChannelMediaResource::OnStartRequest(nsIRequest* aRequest)
 {
   NS_ASSERTION(mChannel.get() == aRequest, "Wrong channel!");
 
   nsHTMLMediaElement* element = mDecoder->GetMediaElement();
   NS_ENSURE_TRUE(element, NS_ERROR_FAILURE);
   nsresult status;
   nsresult rv = aRequest->GetStatus(&status);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -295,17 +295,17 @@ nsMediaChannelStream::OnStartRequest(nsI
   // Fires an initial progress event and sets up the stall counter so stall events
   // fire if no download occurs within the required time frame.
   mDecoder->Progress(false);
 
   return NS_OK;
 }
 
 nsresult
-nsMediaChannelStream::OnStopRequest(nsIRequest* aRequest, nsresult aStatus)
+ChannelMediaResource::OnStopRequest(nsIRequest* aRequest, nsresult aStatus)
 {
   NS_ASSERTION(mChannel.get() == aRequest, "Wrong channel!");
   NS_ASSERTION(mSuspendCount == 0,
                "How can OnStopRequest fire while we're suspended?");
 
   {
     MutexAutoLock lock(mLock);
     mChannelStatistics.Stop(TimeStamp::Now());
@@ -347,83 +347,83 @@ nsMediaChannelStream::OnStopRequest(nsIR
       ModifyLoadFlags(loadFlags);
     }
   }
 
   return NS_OK;
 }
 
 nsresult
-nsMediaChannelStream::OnChannelRedirect(nsIChannel* aOld, nsIChannel* aNew,
+ChannelMediaResource::OnChannelRedirect(nsIChannel* aOld, nsIChannel* aNew,
                                         PRUint32 aFlags)
 {
   mChannel = aNew;
   SetupChannelHeaders();
   return NS_OK;
 }
 
 struct CopySegmentClosure {
   nsCOMPtr<nsIPrincipal> mPrincipal;
-  nsMediaChannelStream*  mStream;
+  ChannelMediaResource*  mResource;
 };
 
 NS_METHOD
-nsMediaChannelStream::CopySegmentToCache(nsIInputStream *aInStream,
+ChannelMediaResource::CopySegmentToCache(nsIInputStream *aInStream,
                                          void *aClosure,
                                          const char *aFromSegment,
                                          PRUint32 aToOffset,
                                          PRUint32 aCount,
                                          PRUint32 *aWriteCount)
 {
   CopySegmentClosure* closure = static_cast<CopySegmentClosure*>(aClosure);
 
-  closure->mStream->mDecoder->NotifyDataArrived(aFromSegment, aCount, closure->mStream->mOffset);
+  closure->mResource->mDecoder->NotifyDataArrived(aFromSegment, aCount, closure->mResource->mOffset);
 
   // Keep track of where we're up to
-  closure->mStream->mOffset += aCount;
-  closure->mStream->mCacheStream.NotifyDataReceived(aCount, aFromSegment,
-                                                    closure->mPrincipal);
+  closure->mResource->mOffset += aCount;
+  closure->mResource->mCacheStream.NotifyDataReceived(aCount, aFromSegment,
+                                                      closure->mPrincipal);
   *aWriteCount = aCount;
   return NS_OK;
 }
 
 nsresult
-nsMediaChannelStream::OnDataAvailable(nsIRequest* aRequest,
+ChannelMediaResource::OnDataAvailable(nsIRequest* aRequest,
                                       nsIInputStream* aStream,
                                       PRUint32 aCount)
 {
   NS_ASSERTION(mChannel.get() == aRequest, "Wrong channel!");
 
   {
     MutexAutoLock lock(mLock);
     mChannelStatistics.AddBytes(aCount);
   }
 
   CopySegmentClosure closure;
   nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
   if (secMan && mChannel) {
     secMan->GetChannelPrincipal(mChannel, getter_AddRefs(closure.mPrincipal));
   }
-  closure.mStream = this;
+  closure.mResource = this;
 
   PRUint32 count = aCount;
   while (count > 0) {
     PRUint32 read;
     nsresult rv = aStream->ReadSegments(CopySegmentToCache, &closure, count,
                                         &read);
     if (NS_FAILED(rv))
       return rv;
     NS_ASSERTION(read > 0, "Read 0 bytes while data was available?");
     count -= read;
   }
 
   return NS_OK;
 }
 
-nsresult nsMediaChannelStream::Open(nsIStreamListener **aStreamListener)
+nsresult ChannelMediaResource::Open(nsIStreamListener **aStreamListener)
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
   nsresult rv = mCacheStream.Init();
   if (NS_FAILED(rv))
     return rv;
   NS_ASSERTION(mOffset == 0, "Who set mOffset already?");
 
@@ -433,17 +433,17 @@ nsresult nsMediaChannelStream::Open(nsIS
     NS_ASSERTION(!aStreamListener,
                  "Should have already been given a channel if we're to return a stream listener");
     return NS_OK;
   }
 
   return OpenChannel(aStreamListener);
 }
 
-nsresult nsMediaChannelStream::OpenChannel(nsIStreamListener** aStreamListener)
+nsresult ChannelMediaResource::OpenChannel(nsIStreamListener** aStreamListener)
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
   NS_ENSURE_TRUE(mChannel, NS_ERROR_NULL_POINTER);
   NS_ASSERTION(!mListener, "Listener should have been removed by now");
 
   if (aStreamListener) {
     *aStreamListener = nsnull;
   }
@@ -486,17 +486,17 @@ nsresult nsMediaChannelStream::OpenChann
 
     nsresult rv = mChannel->AsyncOpen(listener, nsnull);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
-void nsMediaChannelStream::SetupChannelHeaders()
+void ChannelMediaResource::SetupChannelHeaders()
 {
   // Always use a byte range request even if we're reading from the start
   // of the resource.
   // This enables us to detect if the stream supports byte range
   // requests, and therefore seeking, early.
   nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(mChannel);
   if (hc) {
     nsCAutoString rangeString("bytes=");
@@ -511,54 +511,54 @@ void nsMediaChannelStream::SetupChannelH
       return;
     }
     element->SetRequestHeaders(hc);
   } else {
     NS_ASSERTION(mOffset == 0, "Don't know how to seek on this channel type");
   }
 }
 
-nsresult nsMediaChannelStream::Close()
+nsresult ChannelMediaResource::Close()
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
   mCacheStream.Close();
   CloseChannel();
   return NS_OK;
 }
 
-already_AddRefed<nsIPrincipal> nsMediaChannelStream::GetCurrentPrincipal()
+already_AddRefed<nsIPrincipal> ChannelMediaResource::GetCurrentPrincipal()
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
   nsCOMPtr<nsIPrincipal> principal = mCacheStream.GetCurrentPrincipal();
   return principal.forget();
 }
 
-nsMediaStream* nsMediaChannelStream::CloneData(nsMediaDecoder* aDecoder)
+MediaResource* ChannelMediaResource::CloneData(nsMediaDecoder* aDecoder)
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
-  nsMediaChannelStream* stream = new nsMediaChannelStream(aDecoder, nsnull, mURI);
-  if (stream) {
+  ChannelMediaResource* resource = new ChannelMediaResource(aDecoder, nsnull, mURI);
+  if (resource) {
     // Initially the clone is treated as suspended by the cache, because
     // we don't have a channel. If the cache needs to read data from the clone
     // it will call CacheClientResume (or CacheClientSeek with aResume true)
     // which will recreate the channel. This way, if all of the media data
     // is already in the cache we don't create an unneccesary HTTP channel
     // and perform a useless HTTP transaction.
-    stream->mSuspendCount = 1;
-    stream->mCacheStream.InitAsClone(&mCacheStream);
-    stream->mChannelStatistics = mChannelStatistics;
-    stream->mChannelStatistics.Stop(TimeStamp::Now());
+    resource->mSuspendCount = 1;
+    resource->mCacheStream.InitAsClone(&mCacheStream);
+    resource->mChannelStatistics = mChannelStatistics;
+    resource->mChannelStatistics.Stop(TimeStamp::Now());
   }
-  return stream;
+  return resource;
 }
 
-void nsMediaChannelStream::CloseChannel()
+void ChannelMediaResource::CloseChannel()
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
   {
     MutexAutoLock lock(mLock);
     mChannelStatistics.Stop(TimeStamp::Now());
   }
 
@@ -579,52 +579,52 @@ void nsMediaChannelStream::CloseChannel(
     // document load to think there was an error.
     // NS_ERROR_PARSED_DATA_CACHED is the best thing we have for that
     // at the moment.
     mChannel->Cancel(NS_ERROR_PARSED_DATA_CACHED);
     mChannel = nsnull;
   }
 }
 
-nsresult nsMediaChannelStream::ReadFromCache(char* aBuffer,
+nsresult ChannelMediaResource::ReadFromCache(char* aBuffer,
                                              PRInt64 aOffset,
                                              PRUint32 aCount)
 {
   return mCacheStream.ReadFromCache(aBuffer, aOffset, aCount);
 }
 
-nsresult nsMediaChannelStream::Read(char* aBuffer,
+nsresult ChannelMediaResource::Read(char* aBuffer,
                                     PRUint32 aCount,
                                     PRUint32* aBytes)
 {
   NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
 
   return mCacheStream.Read(aBuffer, aCount, aBytes);
 }
 
-nsresult nsMediaChannelStream::Seek(PRInt32 aWhence, PRInt64 aOffset)
+nsresult ChannelMediaResource::Seek(PRInt32 aWhence, PRInt64 aOffset)
 {
   NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
 
   return mCacheStream.Seek(aWhence, aOffset);
 }
 
-PRInt64 nsMediaChannelStream::Tell()
+PRInt64 ChannelMediaResource::Tell()
 {
   NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
 
   return mCacheStream.Tell();
 }
 
-nsresult nsMediaChannelStream::GetCachedRanges(nsTArray<nsByteRange>& aRanges)
+nsresult ChannelMediaResource::GetCachedRanges(nsTArray<MediaByteRange>& aRanges)
 {
   return mCacheStream.GetCachedRanges(aRanges);
 }
 
-void nsMediaChannelStream::Suspend(bool aCloseImmediately)
+void ChannelMediaResource::Suspend(bool aCloseImmediately)
 {
   NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread");
 
   nsHTMLMediaElement* element = mDecoder->GetMediaElement();
   if (!element) {
     // Shutting down; do nothing.
     return;
   }
@@ -643,17 +643,17 @@ void nsMediaChannelStream::Suspend(bool 
       PossiblySuspend();
       element->DownloadSuspended();
     }
   }
 
   ++mSuspendCount;
 }
 
-void nsMediaChannelStream::Resume()
+void ChannelMediaResource::Resume()
 {
   NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread");
   NS_ASSERTION(mSuspendCount > 0, "Too many resumes!");
 
   nsHTMLMediaElement* element = mDecoder->GetMediaElement();
   if (!element) {
     // Shutting down; do nothing.
     return;
@@ -686,17 +686,17 @@ void nsMediaChannelStream::Resume()
         CacheClientSeek(mOffset, false);
       }
       element->DownloadResumed();
     }
   }
 }
 
 nsresult
-nsMediaChannelStream::RecreateChannel()
+ChannelMediaResource::RecreateChannel()
 {
   nsLoadFlags loadFlags =
     nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY |
     (mLoadInBackground ? nsIRequest::LOAD_BACKGROUND : 0);
 
   nsHTMLMediaElement* element = mDecoder->GetMediaElement();
   if (!element) {
     // The decoder is being shut down, so don't bother opening a new channel
@@ -709,34 +709,34 @@ nsMediaChannelStream::RecreateChannel()
                        mURI,
                        nsnull,
                        loadGroup,
                        nsnull,
                        loadFlags);
 }
 
 void
-nsMediaChannelStream::DoNotifyDataReceived()
+ChannelMediaResource::DoNotifyDataReceived()
 {
   mDataReceivedEvent.Revoke();
   mDecoder->NotifyBytesDownloaded();
 }
 
 void
-nsMediaChannelStream::CacheClientNotifyDataReceived()
+ChannelMediaResource::CacheClientNotifyDataReceived()
 {
   NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread");
   // NOTE: this can be called with the media cache lock held, so don't
   // block or do anything which might try to acquire a lock!
 
   if (mDataReceivedEvent.IsPending())
     return;
 
   mDataReceivedEvent =
-    NS_NewNonOwningRunnableMethod(this, &nsMediaChannelStream::DoNotifyDataReceived);
+    NS_NewNonOwningRunnableMethod(this, &ChannelMediaResource::DoNotifyDataReceived);
   NS_DispatchToMainThread(mDataReceivedEvent.get(), NS_DISPATCH_NORMAL);
 }
 
 class DataEnded : public nsRunnable {
 public:
   DataEnded(nsMediaDecoder* aDecoder, nsresult aStatus) :
     mDecoder(aDecoder), mStatus(aStatus) {}
   NS_IMETHOD Run() {
@@ -744,28 +744,28 @@ public:
     return NS_OK;
   }
 private:
   nsRefPtr<nsMediaDecoder> mDecoder;
   nsresult                 mStatus;
 };
 
 void
-nsMediaChannelStream::CacheClientNotifyDataEnded(nsresult aStatus)
+ChannelMediaResource::CacheClientNotifyDataEnded(nsresult aStatus)
 {
   NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread");
   // NOTE: this can be called with the media cache lock held, so don't
   // block or do anything which might try to acquire a lock!
 
   nsCOMPtr<nsIRunnable> event = new DataEnded(mDecoder, aStatus);
   NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
 }
 
 nsresult
-nsMediaChannelStream::CacheClientSeek(PRInt64 aOffset, bool aResume)
+ChannelMediaResource::CacheClientSeek(PRInt64 aOffset, bool aResume)
 {
   NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread");
 
   CloseChannel();
 
   if (aResume) {
     NS_ASSERTION(mSuspendCount > 0, "Too many resumes!");
     // No need to mess with the channel, since we're making a new one
@@ -776,149 +776,149 @@ nsMediaChannelStream::CacheClientSeek(PR
   if (NS_FAILED(rv))
     return rv;
 
   mOffset = aOffset;
   return OpenChannel(nsnull);
 }
 
 nsresult
-nsMediaChannelStream::CacheClientSuspend()
+ChannelMediaResource::CacheClientSuspend()
 {
   Suspend(false);
 
   mDecoder->NotifySuspendedStatusChanged();
   return NS_OK;
 }
 
 nsresult
-nsMediaChannelStream::CacheClientResume()
+ChannelMediaResource::CacheClientResume()
 {
   Resume();
 
   mDecoder->NotifySuspendedStatusChanged();
   return NS_OK;
 }
 
 PRInt64
-nsMediaChannelStream::GetNextCachedData(PRInt64 aOffset)
+ChannelMediaResource::GetNextCachedData(PRInt64 aOffset)
 {
   return mCacheStream.GetNextCachedData(aOffset);
 }
 
 PRInt64
-nsMediaChannelStream::GetCachedDataEnd(PRInt64 aOffset)
+ChannelMediaResource::GetCachedDataEnd(PRInt64 aOffset)
 {
   return mCacheStream.GetCachedDataEnd(aOffset);
 }
 
 bool
-nsMediaChannelStream::IsDataCachedToEndOfStream(PRInt64 aOffset)
+ChannelMediaResource::IsDataCachedToEndOfResource(PRInt64 aOffset)
 {
   return mCacheStream.IsDataCachedToEndOfStream(aOffset);
 }
 
 void
-nsMediaChannelStream::EnsureCacheUpToDate()
+ChannelMediaResource::EnsureCacheUpToDate()
 {
   mCacheStream.EnsureCacheUpdate();
 }
 
 bool
-nsMediaChannelStream::IsSuspendedByCache(nsMediaStream** aActiveStream)
+ChannelMediaResource::IsSuspendedByCache(MediaResource** aActiveResource)
 {
-  return mCacheStream.AreAllStreamsForResourceSuspended(aActiveStream);
+  return mCacheStream.AreAllStreamsForResourceSuspended(aActiveResource);
 }
 
 bool
-nsMediaChannelStream::IsSuspended()
+ChannelMediaResource::IsSuspended()
 {
   MutexAutoLock lock(mLock);
   return mSuspendCount > 0;
 }
 
 void
-nsMediaChannelStream::SetReadMode(nsMediaCacheStream::ReadMode aMode)
+ChannelMediaResource::SetReadMode(nsMediaCacheStream::ReadMode aMode)
 {
   mCacheStream.SetReadMode(aMode);
 }
 
 void
-nsMediaChannelStream::SetPlaybackRate(PRUint32 aBytesPerSecond)
+ChannelMediaResource::SetPlaybackRate(PRUint32 aBytesPerSecond)
 {
   mCacheStream.SetPlaybackRate(aBytesPerSecond);
 }
 
 void
-nsMediaChannelStream::Pin()
+ChannelMediaResource::Pin()
 {
   mCacheStream.Pin();
 }
 
 void
-nsMediaChannelStream::Unpin()
+ChannelMediaResource::Unpin()
 {
   mCacheStream.Unpin();
 }
 
 double
-nsMediaChannelStream::GetDownloadRate(bool* aIsReliable)
+ChannelMediaResource::GetDownloadRate(bool* aIsReliable)
 {
   MutexAutoLock lock(mLock);
   return mChannelStatistics.GetRate(TimeStamp::Now(), aIsReliable);
 }
 
 PRInt64
-nsMediaChannelStream::GetLength()
+ChannelMediaResource::GetLength()
 {
   return mCacheStream.GetLength();
 }
 
 void
-nsMediaChannelStream::PossiblySuspend()
+ChannelMediaResource::PossiblySuspend()
 {
   bool isPending = false;
   nsresult rv = mChannel->IsPending(&isPending);
   if (NS_SUCCEEDED(rv) && isPending) {
     mChannel->Suspend();
     mIgnoreResume = false;
   } else {
     mIgnoreResume = true;
   }
 }
 
 void
-nsMediaChannelStream::PossiblyResume()
+ChannelMediaResource::PossiblyResume()
 {
   if (!mIgnoreResume) {
     mChannel->Resume();
   } else {
     mIgnoreResume = false;
   }
 }
 
-class nsMediaFileStream : public nsMediaStream
+class FileMediaResource : public MediaResource
 {
 public:
-  nsMediaFileStream(nsMediaDecoder* aDecoder, nsIChannel* aChannel, nsIURI* aURI) :
-    nsMediaStream(aDecoder, aChannel, aURI), mSize(-1),
-    mLock("nsMediaFileStream.mLock")
+  FileMediaResource(nsMediaDecoder* aDecoder, nsIChannel* aChannel, nsIURI* aURI) :
+    MediaResource(aDecoder, aChannel, aURI), mSize(-1),
+    mLock("FileMediaResource.mLock")
   {
   }
-  ~nsMediaFileStream()
+  ~FileMediaResource()
   {
   }
 
   // Main thread
   virtual nsresult Open(nsIStreamListener** aStreamListener);
   virtual nsresult Close();
   virtual void     Suspend(bool aCloseImmediately) {}
   virtual void     Resume() {}
   virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal();
-  virtual nsMediaStream* CloneData(nsMediaDecoder* aDecoder);
+  virtual MediaResource* CloneData(nsMediaDecoder* aDecoder);
   virtual nsresult ReadFromCache(char* aBuffer, PRInt64 aOffset, PRUint32 aCount);
 
   // These methods are called off the main thread.
 
   // Other thread
   virtual void     SetReadMode(nsMediaCacheStream::ReadMode aMode) {}
   virtual void     SetPlaybackRate(PRUint32 aBytesPerSecond) {}
   virtual nsresult Read(char* aBuffer, PRUint32 aCount, PRUint32* aBytes);
@@ -935,27 +935,27 @@ public:
     return 100*1024*1024; // arbitray, use 100MB/s
   }
   virtual PRInt64 GetLength() { return mSize; }
   virtual PRInt64 GetNextCachedData(PRInt64 aOffset)
   {
     return (aOffset < mSize) ? aOffset : -1;
   }
   virtual PRInt64 GetCachedDataEnd(PRInt64 aOffset) { return NS_MAX(aOffset, mSize); }
-  virtual bool    IsDataCachedToEndOfStream(PRInt64 aOffset) { return true; }
-  virtual bool    IsSuspendedByCache(nsMediaStream** aActiveStream)
+  virtual bool    IsDataCachedToEndOfResource(PRInt64 aOffset) { return true; }
+  virtual bool    IsSuspendedByCache(MediaResource** aActiveResource)
   {
-    if (aActiveStream) {
-      *aActiveStream = nsnull;
+    if (aActiveResource) {
+      *aActiveResource = nsnull;
     }
     return false;
   }
   virtual bool    IsSuspended() { return false; }
 
-  nsresult GetCachedRanges(nsTArray<nsByteRange>& aRanges);
+  nsresult GetCachedRanges(nsTArray<MediaByteRange>& aRanges);
 
 private:
   // The file size, or -1 if not known. Immutable after Open().
   PRInt64 mSize;
 
   // This lock handles synchronisation between calls to Close() and
   // the Read, Seek, etc calls. Close must not be called while a
   // Read or Seek is in progress since it resets various internal
@@ -989,26 +989,26 @@ public:
     mDecoder->NotifyDownloadEnded(NS_OK);
     return NS_OK;
   }
 
 private:
   nsRefPtr<nsMediaDecoder> mDecoder;
 };
 
-nsresult nsMediaFileStream::GetCachedRanges(nsTArray<nsByteRange>& aRanges)
+nsresult FileMediaResource::GetCachedRanges(nsTArray<MediaByteRange>& aRanges)
 {
   if (mSize == -1) {
     return NS_ERROR_FAILURE;
   }
-  aRanges.AppendElement(nsByteRange(0, mSize));
+  aRanges.AppendElement(MediaByteRange(0, mSize));
   return NS_OK;
 }
 
-nsresult nsMediaFileStream::Open(nsIStreamListener** aStreamListener)
+nsresult FileMediaResource::Open(nsIStreamListener** aStreamListener)
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
   if (aStreamListener) {
     *aStreamListener = nsnull;
   }
 
   nsresult rv = NS_OK;
@@ -1058,44 +1058,44 @@ nsresult nsMediaFileStream::Open(nsIStre
     mSize = size;
   }
 
   nsCOMPtr<nsIRunnable> event = new LoadedEvent(mDecoder);
   NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
   return NS_OK;
 }
 
-nsresult nsMediaFileStream::Close()
+nsresult FileMediaResource::Close()
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
   MutexAutoLock lock(mLock);
   if (mChannel) {
     mChannel->Cancel(NS_ERROR_PARSED_DATA_CACHED);
     mChannel = nsnull;
     mInput = nsnull;
     mSeekable = nsnull;
   }
 
   return NS_OK;
 }
 
-already_AddRefed<nsIPrincipal> nsMediaFileStream::GetCurrentPrincipal()
+already_AddRefed<nsIPrincipal> FileMediaResource::GetCurrentPrincipal()
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
   nsCOMPtr<nsIPrincipal> principal;
   nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
   if (!secMan || !mChannel)
     return nsnull;
   secMan->GetChannelPrincipal(mChannel, getter_AddRefs(principal));
   return principal.forget();
 }
 
-nsMediaStream* nsMediaFileStream::CloneData(nsMediaDecoder* aDecoder)
+MediaResource* FileMediaResource::CloneData(nsMediaDecoder* aDecoder)
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
   nsHTMLMediaElement* element = aDecoder->GetMediaElement();
   if (!element) {
     // The decoder is being shut down, so we can't clone
     return nsnull;
   }
@@ -1103,20 +1103,20 @@ nsMediaStream* nsMediaFileStream::CloneD
   NS_ENSURE_TRUE(loadGroup, nsnull);
 
   nsCOMPtr<nsIChannel> channel;
   nsresult rv =
     NS_NewChannel(getter_AddRefs(channel), mURI, nsnull, loadGroup, nsnull, 0);
   if (NS_FAILED(rv))
     return nsnull;
 
-  return new nsMediaFileStream(aDecoder, channel, mURI);
+  return new FileMediaResource(aDecoder, channel, mURI);
 }
 
-nsresult nsMediaFileStream::ReadFromCache(char* aBuffer, PRInt64 aOffset, PRUint32 aCount)
+nsresult FileMediaResource::ReadFromCache(char* aBuffer, PRInt64 aOffset, PRUint32 aCount)
 {
   MutexAutoLock lock(mLock);
   if (!mInput || !mSeekable)
     return NS_ERROR_FAILURE;
   PRInt64 offset = 0;
   nsresult res = mSeekable->Tell(&offset);
   NS_ENSURE_SUCCESS(res,res);
   res = mSeekable->Seek(nsISeekableStream::NS_SEEK_SET, aOffset);
@@ -1135,95 +1135,95 @@ nsresult nsMediaFileStream::ReadFromCach
 
   // If a read failed in the loop above, we want to return its failure code.
   NS_ENSURE_SUCCESS(res,res);
 
   // Else we succeed if the reset-seek succeeds.
   return seekres;
 }
 
-nsresult nsMediaFileStream::Read(char* aBuffer, PRUint32 aCount, PRUint32* aBytes)
+nsresult FileMediaResource::Read(char* aBuffer, PRUint32 aCount, PRUint32* aBytes)
 {
   MutexAutoLock lock(mLock);
   if (!mInput)
     return NS_ERROR_FAILURE;
   return mInput->Read(aBuffer, aCount, aBytes);
 }
 
-nsresult nsMediaFileStream::Seek(PRInt32 aWhence, PRInt64 aOffset)
+nsresult FileMediaResource::Seek(PRInt32 aWhence, PRInt64 aOffset)
 {
   NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
 
   MutexAutoLock lock(mLock);
   if (!mSeekable)
     return NS_ERROR_FAILURE;
   return mSeekable->Seek(aWhence, aOffset);
 }
 
-PRInt64 nsMediaFileStream::Tell()
+PRInt64 FileMediaResource::Tell()
 {
   NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
 
   MutexAutoLock lock(mLock);
   if (!mSeekable)
     return 0;
 
   PRInt64 offset = 0;
   mSeekable->Tell(&offset);
   return offset;
 }
 
-nsMediaStream*
-nsMediaStream::Create(nsMediaDecoder* aDecoder, nsIChannel* aChannel)
+MediaResource*
+MediaResource::Create(nsMediaDecoder* aDecoder, nsIChannel* aChannel)
 {
   NS_ASSERTION(NS_IsMainThread(),
-	             "nsMediaStream::Open called on non-main thread");
+	             "MediaResource::Open called on non-main thread");
 
   // If the channel was redirected, we want the post-redirect URI;
   // but if the URI scheme was expanded, say from chrome: to jar:file:,
   // we want the original URI.
   nsCOMPtr<nsIURI> uri;
   nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
   NS_ENSURE_SUCCESS(rv, nsnull);
 
   nsCOMPtr<nsIFileChannel> fc = do_QueryInterface(aChannel);
   if (fc) {
-    return new nsMediaFileStream(aDecoder, aChannel, uri);
+    return new FileMediaResource(aDecoder, aChannel, uri);
   }
-  return new nsMediaChannelStream(aDecoder, aChannel, uri);
+  return new ChannelMediaResource(aDecoder, aChannel, uri);
 }
 
-void nsMediaStream::MoveLoadsToBackground() {
+void MediaResource::MoveLoadsToBackground() {
   NS_ASSERTION(!mLoadInBackground, "Why are you calling this more than once?");
   mLoadInBackground = true;
   if (!mChannel) {
     // No channel, resource is probably already loaded.
     return;
   }
 
   nsresult rv;
   nsHTMLMediaElement* element = mDecoder->GetMediaElement();
   if (!element) {
-    NS_WARNING("Null element in nsMediaStream::MoveLoadsToBackground()");
+    NS_WARNING("Null element in MediaResource::MoveLoadsToBackground()");
     return;
   }
 
   bool isPending = false;
   if (NS_SUCCEEDED(mChannel->IsPending(&isPending)) &&
       isPending) {
     nsLoadFlags loadFlags;
     rv = mChannel->GetLoadFlags(&loadFlags);
     NS_ASSERTION(NS_SUCCEEDED(rv), "GetLoadFlags() failed!");
 
     loadFlags |= nsIRequest::LOAD_BACKGROUND;
     ModifyLoadFlags(loadFlags);
   }
 }
 
-void nsMediaStream::ModifyLoadFlags(nsLoadFlags aFlags)
+void MediaResource::ModifyLoadFlags(nsLoadFlags aFlags)
 {
   nsCOMPtr<nsILoadGroup> loadGroup;
   nsresult rv = mChannel->GetLoadGroup(getter_AddRefs(loadGroup));
   NS_ASSERTION(NS_SUCCEEDED(rv), "GetLoadGroup() failed!");
 
   nsresult status;
   mChannel->GetStatus(&status);
 
rename from content/media/nsMediaStream.h
rename to content/media/MediaResource.h
--- a/content/media/nsMediaStream.h
+++ b/content/media/MediaResource.h
@@ -30,18 +30,18 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-#if !defined(nsMediaStream_h_)
-#define nsMediaStream_h_
+#if !defined(MediaResource_h_)
+#define MediaResource_h_
 
 #include "mozilla/Mutex.h"
 #include "mozilla/XPCOM.h"
 #include "nsIChannel.h"
 #include "nsIPrincipal.h"
 #include "nsIURI.h"
 #include "nsIStreamListener.h"
 #include "nsIChannelEventSink.h"
@@ -52,35 +52,34 @@
 // seeked forward is less than this value then a read is
 // done rather than a byte range request.
 static const PRInt64 SEEK_VS_READ_THRESHOLD = 32*1024;
 
 static const PRUint32 HTTP_REQUESTED_RANGE_NOT_SATISFIABLE_CODE = 416;
 
 class nsMediaDecoder;
 
+namespace mozilla {
+
 /**
  * This class is useful for estimating rates of data passing through
  * some channel. The idea is that activity on the channel "starts"
  * and "stops" over time. At certain times data passes through the
  * channel (usually while the channel is active; data passing through
  * an inactive channel is ignored). The GetRate() function computes
  * an estimate of the "current rate" of the channel, which is some
  * kind of average of the data passing through over the time the
  * channel is active.
  * 
  * All methods take "now" as a parameter so the user of this class can
  * control the timeline used.
  */
-class nsChannelStatistics {
+class MediaChannelStatistics {
 public:
-  typedef mozilla::TimeStamp TimeStamp;
-  typedef mozilla::TimeDuration TimeDuration;
-
-  nsChannelStatistics() { Reset(); }
+  MediaChannelStatistics() { Reset(); }
   void Reset() {
     mLastStartTime = TimeStamp();
     mAccumulatedTime = TimeDuration(0);
     mAccumulatedBytes = 0;
     mIsStarted = false;
   }
   void Start(TimeStamp aNow) {
     if (mIsStarted)
@@ -119,80 +118,88 @@ public:
     if (seconds <= 0.0)
       return 0.0;
     return static_cast<double>(mAccumulatedBytes)/seconds;
   }
 private:
   PRInt64      mAccumulatedBytes;
   TimeDuration mAccumulatedTime;
   TimeStamp    mLastStartTime;
-  bool mIsStarted;
+  bool         mIsStarted;
 };
 
 // Represents a section of contiguous media, with a start and end offset.
 // Used to denote ranges of data which are cached.
-class nsByteRange {
+class MediaByteRange {
 public:
-  nsByteRange() : mStart(0), mEnd(0) {}
+  MediaByteRange() : mStart(0), mEnd(0) {}
 
-  nsByteRange(PRInt64 aStart, PRInt64 aEnd)
+  MediaByteRange(PRInt64 aStart, PRInt64 aEnd)
     : mStart(aStart), mEnd(aEnd)
   {
     NS_ASSERTION(mStart < mEnd, "Range should end after start!");
   }
 
   bool IsNull() const {
     return mStart == 0 && mEnd == 0;
   }
 
   PRInt64 mStart, mEnd;
 };
 
-/*
-   Provides the ability to open, read and seek into a media stream
-   (audio, video). Handles the underlying machinery to do range
-   requests, etc as needed by the actual stream type. Instances of
-   this class must be created on the main thread. 
-
-   Most methods must be called on the main thread only. Read, Seek and
-   Tell must only be called on non-main threads. In the case of the Ogg
-   Decoder they are called on the Decode thread for example. You must
-   ensure that no threads are calling these methods once Close is called.
-
-   Instances of this class are explicitly managed. 'delete' it when done.
-*/
-class nsMediaStream 
+/**
+ * Provides a thread-safe, seek/read interface to resources
+ * loaded from a URI. Uses nsMediaCache to cache data received over
+ * Necko's async channel API, thus resolving the mismatch between clients
+ * that need efficient random access to the data and protocols that do not
+ * support efficient random access, such as HTTP.
+ *
+ * Instances of this class must be created on the main thread.
+ * Most methods must be called on the main thread only. Read, Seek and
+ * Tell must only be called on non-main threads. In the case of the Ogg
+ * Decoder they are called on the Decode thread for example. You must
+ * ensure that no threads are calling these methods once Close is called.
+ *
+ * Instances of this class are explicitly managed. 'delete' it when done.
+ *
+ * The generic implementation of this class is ChannelMediaResource, which can
+ * handle any URI for which Necko supports AsyncOpen.
+ * The 'file:' protocol can be implemented efficiently with direct random
+ * access, so the FileMediaResource implementation class bypasses the cache.
+ * MediaResource::Create automatically chooses the best implementation class.
+ */
+class MediaResource
 {
 public:
-  virtual ~nsMediaStream()
+  virtual ~MediaResource()
   {
-    MOZ_COUNT_DTOR(nsMediaStream);
+    MOZ_COUNT_DTOR(MediaResource);
   }
 
   // The following can be called on the main thread only:
   // Get the URI
   nsIURI* URI() const { return mURI; }
-  // Close the stream, stop any listeners, channels, etc.
+  // Close the resource, stop any listeners, channels, etc.
   // Cancels any currently blocking Read request and forces that request to
   // return an error.
   virtual nsresult Close() = 0;
   // Suspend any downloads that are in progress.
   // If aCloseImmediately is set, resources should be released immediately
   // since we don't expect to resume again any time soon. Otherwise we
   // may resume again soon so resources should be held for a little
   // while.
   virtual void Suspend(bool aCloseImmediately) = 0;
   // Resume any downloads that have been suspended.
   virtual void Resume() = 0;
   // Get the current principal for the channel
   virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal() = 0;
   // Create a new stream of the same type that refers to the same URI
   // with a new channel. Any cached data associated with the original
   // stream should be accessible in the new stream too.
-  virtual nsMediaStream* CloneData(nsMediaDecoder* aDecoder) = 0;
+  virtual MediaResource* CloneData(nsMediaDecoder* aDecoder) = 0;
 
   // These methods are called off the main thread.
   // The mode is initially MODE_PLAYBACK.
   virtual void SetReadMode(nsMediaCacheStream::ReadMode aMode) = 0;
   // This is the client's estimate of the playback rate assuming
   // the media plays continuously. The cache can't guess this itself
   // because it doesn't know when the decoder was paused, buffering, etc.
   virtual void SetPlaybackRate(PRUint32 aBytesPerSecond) = 0;
@@ -259,66 +266,65 @@ public:
   // Returns the offset of the first byte of cached data at or after aOffset,
   // or -1 if there is no such cached data.
   virtual PRInt64 GetNextCachedData(PRInt64 aOffset) = 0;
   // Returns the end of the bytes starting at the given offset
   // which are in cache.
   virtual PRInt64 GetCachedDataEnd(PRInt64 aOffset) = 0;
   // Returns true if all the data from aOffset to the end of the stream
   // is in cache. If the end of the stream is not known, we return false.
-  virtual bool IsDataCachedToEndOfStream(PRInt64 aOffset) = 0;
+  virtual bool IsDataCachedToEndOfResource(PRInt64 aOffset) = 0;
   // Returns true if this stream is suspended by the cache because the
   // cache is full. If true then the decoder should try to start consuming
   // data, otherwise we may not be able to make progress.
   // nsMediaDecoder::NotifySuspendedStatusChanged is called when this
   // changes.
   // For resources using the media cache, this returns true only when all
   // streams for the same resource are all suspended.
-  // If aActiveStream is non-null, fills it with a pointer to a stream
+  // If aActiveResource is non-null, fills it with a pointer to a stream
   // for this resource that is not suspended or ended.
-  virtual bool IsSuspendedByCache(nsMediaStream** aActiveStream) = 0;
+  virtual bool IsSuspendedByCache(MediaResource** aActiveResource) = 0;
   // Returns true if this stream has been suspended.
   virtual bool IsSuspended() = 0;
   // Reads only data which is cached in the media cache. If you try to read
   // any data which overlaps uncached data, or if aCount bytes otherwise can't
   // be read, this function will return failure. This function be called from
   // any thread, and it is the only read operation which is safe to call on
   // the main thread, since it's guaranteed to be non blocking.
   virtual nsresult ReadFromCache(char* aBuffer,
                                  PRInt64 aOffset,
                                  PRUint32 aCount) = 0;
 
   /**
-   * Create a stream, reading data from the media resource via the
-   * channel. Call on main thread only.
-   * The caller must follow up by calling aStream->Open.
+   * Create a resource, reading data from the channel. Call on main thread only.
+   * The caller must follow up by calling resource->Open().
    */
-  static nsMediaStream* Create(nsMediaDecoder* aDecoder, nsIChannel* aChannel);
+  static MediaResource* Create(nsMediaDecoder* aDecoder, nsIChannel* aChannel);
 
   /**
    * Open the stream. This creates a stream listener and returns it in
    * aStreamListener; this listener needs to be notified of incoming data.
    */
   virtual nsresult Open(nsIStreamListener** aStreamListener) = 0;
 
   /**
-   * Fills aRanges with ByteRanges representing the data which is cached
+   * Fills aRanges with MediaByteRanges representing the data which is cached
    * in the media cache. Stream should be pinned during call and while
    * aRanges is being used.
    */
-  virtual nsresult GetCachedRanges(nsTArray<nsByteRange>& aRanges) = 0;
+  virtual nsresult GetCachedRanges(nsTArray<MediaByteRange>& aRanges) = 0;
 
 protected:
-  nsMediaStream(nsMediaDecoder* aDecoder, nsIChannel* aChannel, nsIURI* aURI) :
+  MediaResource(nsMediaDecoder* aDecoder, nsIChannel* aChannel, nsIURI* aURI) :
     mDecoder(aDecoder),
     mChannel(aChannel),
     mURI(aURI),
     mLoadInBackground(false)
   {
-    MOZ_COUNT_CTOR(nsMediaStream);
+    MOZ_COUNT_CTOR(MediaResource);
   }
 
   // Set the request's load flags to aFlags.  If the request is part of a
   // load group, the request is removed from the group, the flags are set, and
   // then the request is added back to the load group.
   void ModifyLoadFlags(nsLoadFlags aFlags);
 
   // This is not an nsCOMPointer to prevent a circular reference
@@ -335,30 +341,28 @@ protected:
   nsCOMPtr<nsIURI> mURI;
 
   // True if MoveLoadsToBackground() has been called, i.e. the load event
   // has been fired, and all channel loads will be in the background.
   bool mLoadInBackground;
 };
 
 /**
- * This is the nsMediaStream implementation that wraps Necko channels.
+ * This is the MediaResource implementation that wraps Necko channels.
  * Much of its functionality is actually delegated to nsMediaCache via
  * an underlying nsMediaCacheStream.
  *
  * All synchronization is performed by nsMediaCacheStream; all off-main-
  * thread operations are delegated directly to that object.
  */
-class nsMediaChannelStream : public nsMediaStream
+class ChannelMediaResource : public MediaResource
 {
-  typedef mozilla::Mutex Mutex;
-
 public:
-  nsMediaChannelStream(nsMediaDecoder* aDecoder, nsIChannel* aChannel, nsIURI* aURI);
-  ~nsMediaChannelStream();
+  ChannelMediaResource(nsMediaDecoder* aDecoder, nsIChannel* aChannel, nsIURI* aURI);
+  ~ChannelMediaResource();
 
   // These are called on the main thread by nsMediaCache. These must
   // not block or grab locks, because the media cache is holding its lock.
   // Notify that data is available from the cache. This can happen even
   // if this stream didn't read any data, since another stream might have
   // received data for the same resource.
   void CacheClientNotifyDataReceived();
   // Notify that we reached the end of the stream. This can happen even
@@ -382,17 +386,17 @@ public:
   // Main thread
   virtual nsresult Open(nsIStreamListener** aStreamListener);
   virtual nsresult Close();
   virtual void     Suspend(bool aCloseImmediately);
   virtual void     Resume();
   virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal();
   // Return true if the stream has been closed.
   bool IsClosed() const { return mCacheStream.IsClosed(); }
-  virtual nsMediaStream* CloneData(nsMediaDecoder* aDecoder);
+  virtual MediaResource* CloneData(nsMediaDecoder* aDecoder);
   virtual nsresult ReadFromCache(char* aBuffer, PRInt64 aOffset, PRUint32 aCount);
   virtual void     EnsureCacheUpToDate();
 
   // Other thread
   virtual void     SetReadMode(nsMediaCacheStream::ReadMode aMode);
   virtual void     SetPlaybackRate(PRUint32 aBytesPerSecond);
   virtual nsresult Read(char* aBuffer, PRUint32 aCount, PRUint32* aBytes);
   virtual nsresult Seek(PRInt32 aWhence, PRInt64 aOffset);
@@ -400,41 +404,41 @@ public:
 
   // Any thread
   virtual void    Pin();
   virtual void    Unpin();
   virtual double  GetDownloadRate(bool* aIsReliable);
   virtual PRInt64 GetLength();
   virtual PRInt64 GetNextCachedData(PRInt64 aOffset);
   virtual PRInt64 GetCachedDataEnd(PRInt64 aOffset);
-  virtual bool    IsDataCachedToEndOfStream(PRInt64 aOffset);
-  virtual bool    IsSuspendedByCache(nsMediaStream** aActiveStream);
+  virtual bool    IsDataCachedToEndOfResource(PRInt64 aOffset);
+  virtual bool    IsSuspendedByCache(MediaResource** aActiveResource);
   virtual bool    IsSuspended();
 
   class Listener : public nsIStreamListener,
                    public nsIInterfaceRequestor,
                    public nsIChannelEventSink
   {
   public:
-    Listener(nsMediaChannelStream* aStream) : mStream(aStream) {}
+    Listener(ChannelMediaResource* aResource) : mResource(aResource) {}
 
     NS_DECL_ISUPPORTS
     NS_DECL_NSIREQUESTOBSERVER
     NS_DECL_NSISTREAMLISTENER
     NS_DECL_NSICHANNELEVENTSINK
     NS_DECL_NSIINTERFACEREQUESTOR
 
-    void Revoke() { mStream = nsnull; }
+    void Revoke() { mResource = nsnull; }
 
   private:
-    nsMediaChannelStream* mStream;
+    ChannelMediaResource* mResource;
   };
   friend class Listener;
 
-  nsresult GetCachedRanges(nsTArray<nsByteRange>& aRanges);
+  nsresult GetCachedRanges(nsTArray<MediaByteRange>& aRanges);
 
 protected:
   // These are called on the main thread by Listener.
   nsresult OnStartRequest(nsIRequest* aRequest);
   nsresult OnStopRequest(nsIRequest* aRequest, nsresult aStatus);
   nsresult OnDataAvailable(nsIRequest* aRequest,
                            nsIInputStream* aStream,
                            PRUint32 aCount);
@@ -466,31 +470,33 @@ protected:
   // Resume from a suspend if we actually suspended (See PossiblySuspend).
   void PossiblyResume();
 
   // Main thread access only
   PRInt64            mOffset;
   nsRefPtr<Listener> mListener;
   // A data received event for the decoder that has been dispatched but has
   // not yet been processed.
-  nsRevocableEventPtr<nsRunnableMethod<nsMediaChannelStream, void, false> > mDataReceivedEvent;
+  nsRevocableEventPtr<nsRunnableMethod<ChannelMediaResource, void, false> > mDataReceivedEvent;
   PRUint32           mSuspendCount;
   // When this flag is set, if we get a network error we should silently
   // reopen the stream.
   bool               mReopenOnError;
   // When this flag is set, we should not report the next close of the
   // channel.
   bool               mIgnoreClose;
 
   // Any thread access
   nsMediaCacheStream mCacheStream;
 
   // This lock protects mChannelStatistics
   Mutex               mLock;
-  nsChannelStatistics mChannelStatistics;
+  MediaChannelStatistics mChannelStatistics;
 
   // True if we couldn't suspend the stream and we therefore don't want
   // to resume later. This is usually due to the channel not being in the
   // isPending state at the time of the suspend request.
   bool mIgnoreResume;
 };
 
+}
+
 #endif
new file mode 100644
--- /dev/null
+++ b/content/media/VideoFrameContainer.cpp
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "VideoFrameContainer.h"
+
+#include "nsHTMLMediaElement.h"
+#include "nsIFrame.h"
+#include "nsDisplayList.h"
+#include "nsSVGEffects.h"
+
+using namespace mozilla::layers;
+
+namespace mozilla {
+
+void VideoFrameContainer::SetCurrentFrame(const gfxIntSize& aIntrinsicSize,
+                                          Image* aImage,
+                                          TimeStamp aTargetTime)
+{
+  MutexAutoLock lock(mMutex);
+
+  if (aIntrinsicSize != mIntrinsicSize) {
+    mIntrinsicSize = aIntrinsicSize;
+    mIntrinsicSizeChanged = true;
+  }
+
+  gfxIntSize oldFrameSize = mImageContainer->GetCurrentSize();
+  TimeStamp lastPaintTime = mImageContainer->GetPaintTime();
+  if (!lastPaintTime.IsNull() && !mPaintTarget.IsNull()) {
+    mPaintDelay = lastPaintTime - mPaintTarget;
+  }
+  mImageContainer->SetCurrentImage(aImage);
+  gfxIntSize newFrameSize = mImageContainer->GetCurrentSize();
+  if (oldFrameSize != newFrameSize) {
+    mImageSizeChanged = true;
+  }
+
+  mPaintTarget = aTargetTime;
+}
+
+double VideoFrameContainer::GetFrameDelay()
+{
+  MutexAutoLock lock(mMutex);
+  return mPaintDelay.ToSeconds();
+}
+
+void VideoFrameContainer::Invalidate()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Must call on main thread");
+  if (!mElement) {
+    // Element has been destroyed
+    return;
+  }
+
+  nsIFrame* frame = mElement->GetPrimaryFrame();
+  bool invalidateFrame = false;
+
+  {
+    MutexAutoLock lock(mMutex);
+
+    // Get mImageContainerSizeChanged while holding the lock.
+    invalidateFrame = mImageSizeChanged;
+    mImageSizeChanged = false;
+
+    if (mIntrinsicSizeChanged) {
+      mElement->UpdateMediaSize(mIntrinsicSize);
+      mIntrinsicSizeChanged = false;
+
+      if (frame) {
+        nsPresContext* presContext = frame->PresContext();
+        nsIPresShell *presShell = presContext->PresShell();
+        presShell->FrameNeedsReflow(frame,
+                                    nsIPresShell::eStyleChange,
+                                    NS_FRAME_IS_DIRTY);
+      }
+    }
+  }
+
+  if (frame) {
+    nsRect contentRect = frame->GetContentRect() - frame->GetPosition();
+    if (invalidateFrame) {
+      frame->Invalidate(contentRect);
+    } else {
+      frame->InvalidateLayer(contentRect, nsDisplayItem::TYPE_VIDEO);
+    }
+  }
+
+  nsSVGEffects::InvalidateDirectRenderingObservers(mElement);
+}
+
+}
new file mode 100644
--- /dev/null
+++ b/content/media/VideoFrameContainer.h
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef VIDEOFRAMECONTAINER_H_
+#define VIDEOFRAMECONTAINER_H_
+
+#include "ImageLayers.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/TimeStamp.h"
+#include "nsISupportsImpl.h"
+#include "gfxPoint.h"
+
+class nsHTMLMediaElement;
+
+namespace mozilla {
+
+/**
+ * This object is used in the decoder backend threads and the main thread
+ * to manage the "current video frame" state. This state includes timing data
+ * and an intrinsic size (see below).
+ * This has to be a thread-safe object since it's accessed by resource decoders
+ * and other off-main-thread components. So we can't put this state in the media
+ * element itself ... well, maybe we could, but it could be risky and/or
+ * confusing.
+ */
+class VideoFrameContainer {
+public:
+  typedef mozilla::layers::ImageContainer ImageContainer;
+  typedef mozilla::layers::Image Image;
+
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VideoFrameContainer)
+
+  VideoFrameContainer(nsHTMLMediaElement* aElement,
+                      already_AddRefed<ImageContainer> aContainer)
+    : mElement(aElement),
+      mImageContainer(aContainer), mMutex("nsVideoFrameContainer"),
+      mIntrinsicSizeChanged(false), mImageSizeChanged(false)
+  {
+    NS_ASSERTION(aElement, "aElement must not be null");
+    NS_ASSERTION(mImageContainer, "aContainer must not be null");
+  }
+  // Call on any thread
+  void SetCurrentFrame(const gfxIntSize& aIntrinsicSize, Image* aImage,
+                       TimeStamp aTargetTime);
+  // Time in seconds by which the last painted video frame was late by.
+  // E.g. if the last painted frame should have been painted at time t,
+  // but was actually painted at t+n, this returns n in seconds. Threadsafe.
+  double GetFrameDelay();
+  // Call on main thread
+  void Invalidate();
+  ImageContainer* GetImageContainer() { return mImageContainer; }
+  void ForgetElement() { mElement = nsnull; }
+
+protected:
+  // Non-addreffed pointer to the element. The element calls ForgetElement
+  // to clear this reference when the element is destroyed.
+  nsHTMLMediaElement* mElement;
+  nsRefPtr<ImageContainer> mImageContainer;
+
+  // mMutex protects all the fields below.
+  Mutex mMutex;
+  // The intrinsic size is the ideal size which we should render the
+  // ImageContainer's current Image at.
+  // This can differ from the Image's actual size when the media resource
+  // specifies that the Image should be stretched to have the correct aspect
+  // ratio.
+  gfxIntSize mIntrinsicSize;
+  // The time at which the current video frame should have been painted.
+  // Access protected by mVideoUpdateLock.
+  TimeStamp mPaintTarget;
+  // The delay between the last video frame being presented and it being
+  // painted. This is time elapsed after mPaintTarget until the most recently
+  // painted frame appeared on screen.
+  TimeDuration mPaintDelay;
+  // True when the intrinsic size has been changed by SetCurrentFrame() since
+  // the last call to Invalidate().
+  // The next call to Invalidate() will recalculate
+  // and update the intrinsic size on the element, request a frame reflow and
+  // then reset this flag.
+  bool mIntrinsicSizeChanged;
+  // True when the Image size has changed since the last time Invalidate() was
+  // called. When set, the next call to Invalidate() will ensure that the
+  // frame is fully invalidated instead of just invalidating for the image change
+  // in the ImageLayer.
+  bool mImageSizeChanged;
+};
+
+}
+
+#endif /* VIDEOFRAMECONTAINER_H_ */
--- a/content/media/nsAudioStream.cpp
+++ b/content/media/nsAudioStream.cpp
@@ -112,20 +112,16 @@ class nsNativeAudioStream : public nsAud
   PRInt64 GetPositionInFrames();
   bool IsPaused();
   PRInt32 GetMinWriteSize();
 
  private:
 
   double mVolume;
   void* mAudioHandle;
-  int mRate;
-  int mChannels;
-
-  SampleFormat mFormat;
 
   // True if this audio stream is paused.
   bool mPaused;
 
   // True if this stream has encountered an error.
   bool mInError;
 
 };
@@ -150,20 +146,16 @@ class nsRemotedAudioStream : public nsAu
   PRInt64 GetPosition();
   PRInt64 GetPositionInFrames();
   bool IsPaused();
   PRInt32 GetMinWriteSize();
 
 private:
   nsRefPtr<AudioChild> mAudioChild;
 
-  SampleFormat mFormat;
-  int mRate;
-  int mChannels;
-
   PRInt32 mBytesPerFrame;
 
   // True if this audio stream is paused.
   bool mPaused;
 
   friend class AudioInitEvent;
 };
 
@@ -425,19 +417,16 @@ nsAudioStream::~nsAudioStream()
     nsCOMPtr<nsIRunnable> event = new AsyncShutdownPlaybackThread(mAudioPlaybackThread);
     NS_DispatchToMainThread(event);
   }
 }
 
 nsNativeAudioStream::nsNativeAudioStream() :
   mVolume(1.0),
   mAudioHandle(0),
-  mRate(0),
-  mChannels(0),
-  mFormat(FORMAT_S16_LE),
   mPaused(false),
   mInError(false)
 {
 }
 
 nsNativeAudioStream::~nsNativeAudioStream()
 {
   Shutdown();
@@ -649,19 +638,16 @@ PRInt32 nsNativeAudioStream::GetMinWrite
     return -1;
 
   return static_cast<PRInt32>(size / mChannels / sizeof(short));
 }
 
 #if defined(REMOTE_AUDIO)
 nsRemotedAudioStream::nsRemotedAudioStream()
  : mAudioChild(nsnull),
-   mFormat(FORMAT_S16_LE),
-   mRate(0),
-   mChannels(0),
    mBytesPerFrame(0),
    mPaused(false)
 {}
 
 nsRemotedAudioStream::~nsRemotedAudioStream()
 {
   Shutdown();
 }
@@ -871,19 +857,16 @@ private:
 
   // Software volume level.  Applied during the servicing of DataCallback().
   double mVolume;
 
   // Owning reference to a cubeb_stream.  cubeb_stream_destroy is called by
   // nsAutoRef's destructor.
   nsAutoRef<cubeb_stream> mCubebStream;
 
-  PRInt32 mRate;
-  PRInt32 mChannels;
-  SampleFormat mFormat;
   PRUint32 mBytesPerFrame;
 
   enum StreamState {
     INITIALIZED, // Initialized, playback has not begun.
     STARTED,     // Started by a call to Write() (iff INITIALIZED) or Resume().
     STOPPED,     // Stopped by a call to Pause().
     DRAINING,    // Drain requested.  DataCallback will indicate end of stream
                  // once the remaining contents of mBuffer are requested by
@@ -912,17 +895,17 @@ nsAudioStream* nsAudioStream::AllocateSt
     return new nsBufferedAudioStream();
   }
 #endif
   return new nsNativeAudioStream();
 }
 
 #if defined(MOZ_CUBEB)
 nsBufferedAudioStream::nsBufferedAudioStream()
-  : mMonitor("nsBufferedAudioStream"), mLostFrames(0), mVolume(1.0), mRate(0), mChannels(0),
+  : mMonitor("nsBufferedAudioStream"), mLostFrames(0), mVolume(1.0),
     mBytesPerFrame(0), mState(INITIALIZED)
 {
 }
 
 nsBufferedAudioStream::~nsBufferedAudioStream()
 {
   Shutdown();
 }
--- a/content/media/nsAudioStream.h
+++ b/content/media/nsAudioStream.h
@@ -49,16 +49,22 @@ public:
 
   enum SampleFormat
   {
     FORMAT_U8,
     FORMAT_S16_LE,
     FORMAT_FLOAT32
   };
 
+  nsAudioStream()
+    : mRate(0),
+      mChannels(0),
+      mFormat(FORMAT_S16_LE)
+  {}
+
   virtual ~nsAudioStream();
 
   // Initialize Audio Library. Some Audio backends require initializing the
   // library before using it.
   static void InitLibrary();
 
   // Shutdown Audio Library. Some Audio backends require shutting down the
   // library after using it.
@@ -117,13 +123,20 @@ public:
   // Returns true when the audio stream is paused.
   virtual bool IsPaused() = 0;
 
   // Returns the minimum number of audio frames which must be written before
   // you can be sure that something will be played.
   // Unsafe to call with the decoder monitor held.
   virtual PRInt32 GetMinWriteSize() = 0;
 
+  int GetRate() { return mRate; }
+  int GetChannels() { return mChannels; }
+  SampleFormat GetFormat() { return mFormat; }
+
 protected:
   nsCOMPtr<nsIThread> mAudioPlaybackThread;
+  int mRate;
+  int mChannels;
+  SampleFormat mFormat;
 };
 
 #endif
--- a/content/media/nsBuiltinDecoder.cpp
+++ b/content/media/nsBuiltinDecoder.cpp
@@ -132,17 +132,16 @@ nsBuiltinDecoder::nsBuiltinDecoder() :
 
 bool nsBuiltinDecoder::Init(nsHTMLMediaElement* aElement)
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   if (!nsMediaDecoder::Init(aElement))
     return false;
 
   nsContentUtils::RegisterShutdownObserver(this);
-  mImageContainer = aElement->GetImageContainer();
   return true;
 }
 
 void nsBuiltinDecoder::Shutdown()
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   
   if (mShuttingDown)
@@ -154,56 +153,56 @@ void nsBuiltinDecoder::Shutdown()
   // necessary to unblock the state machine thread if it's blocked, so
   // the asynchronous shutdown in nsDestroyStateMachine won't deadlock.
   if (mDecoderStateMachine) {
     mDecoderStateMachine->Shutdown();
   }
 
   // Force any outstanding seek and byterange requests to complete
   // to prevent shutdown from deadlocking.
-  if (mStream) {
-    mStream->Close();
+  if (mResource) {
+    mResource->Close();
   }
 
   ChangeState(PLAY_STATE_SHUTDOWN);
   nsMediaDecoder::Shutdown();
 
   nsContentUtils::UnregisterShutdownObserver(this);
 }
 
 nsBuiltinDecoder::~nsBuiltinDecoder()
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   UnpinForSeek();
   MOZ_COUNT_DTOR(nsBuiltinDecoder);
 }
 
-nsresult nsBuiltinDecoder::Load(nsMediaStream* aStream,
+nsresult nsBuiltinDecoder::Load(MediaResource* aResource,
                                 nsIStreamListener** aStreamListener,
                                 nsMediaDecoder* aCloneDonor)
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   if (aStreamListener) {
     *aStreamListener = nsnull;
   }
 
   {
     // Hold the lock while we do this to set proper lock ordering
     // expectations for dynamic deadlock detectors: decoder lock(s)
     // should be grabbed before the cache lock
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
 
-    nsresult rv = aStream->Open(aStreamListener);
+    nsresult rv = aResource->Open(aStreamListener);
     if (NS_FAILED(rv)) {
       LOG(PR_LOG_DEBUG, ("%p Failed to open stream!", this));
-      delete aStream;
+      delete aResource;
       return rv;
     }
 
-    mStream = aStream;
+    mResource = aResource;
   }
 
   mDecoderStateMachine = CreateStateMachine();
   if (!mDecoderStateMachine) {
     LOG(PR_LOG_DEBUG, ("%p Failed to create state machine!", this));
     return NS_ERROR_FAILURE;
   }
 
@@ -379,25 +378,20 @@ nsresult nsBuiltinDecoder::PlaybackRateC
 }
 
 double nsBuiltinDecoder::GetCurrentTime()
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   return mCurrentTime;
 }
 
-nsMediaStream* nsBuiltinDecoder::GetStream()
-{
-  return mStream;
-}
-
 already_AddRefed<nsIPrincipal> nsBuiltinDecoder::GetCurrentPrincipal()
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
-  return mStream ? mStream->GetCurrentPrincipal() : nsnull;
+  return mResource ? mResource->GetCurrentPrincipal() : nsnull;
 }
 
 void nsBuiltinDecoder::AudioAvailable(float* aFrameBuffer,
                                       PRUint32 aFrameBufferLength,
                                       float aTime)
 {
   // Auto manage the frame buffer's memory. If we return due to an error
   // here, this ensures we free the memory. Otherwise, we pass off ownership
@@ -447,24 +441,24 @@ void nsBuiltinDecoder::MetadataLoaded(PR
     // Resource was loaded during metadata loading, when progress
     // events are being ignored. Fire the final progress event.
     mElement->DispatchAsyncEvent(NS_LITERAL_STRING("progress"));
   }
 
   // Only inform the element of FirstFrameLoaded if not doing a load() in order
   // to fulfill a seek, otherwise we'll get multiple loadedfirstframe events.
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-  bool resourceIsLoaded = !mResourceLoaded && mStream &&
-    mStream->IsDataCachedToEndOfStream(mDecoderPosition);
+  bool resourceIsLoaded = !mResourceLoaded && mResource &&
+    mResource->IsDataCachedToEndOfResource(mDecoderPosition);
   if (mElement && notifyElement) {
     mElement->FirstFrameLoaded(resourceIsLoaded);
   }
 
   // This can run cache callbacks.
-  mStream->EnsureCacheUpToDate();
+  mResource->EnsureCacheUpToDate();
 
   // The element can run javascript via events
   // before reaching here, so only change the
   // state if we're still set to the original
   // loading state.
   if (mPlayState == PLAY_STATE_LOADING) {
     if (mRequestedSeekTime >= 0.0) {
       ChangeState(PLAY_STATE_SEEKING);
@@ -582,22 +576,22 @@ NS_IMETHODIMP nsBuiltinDecoder::Observe(
 nsMediaDecoder::Statistics
 nsBuiltinDecoder::GetStatistics()
 {
   NS_ASSERTION(NS_IsMainThread() || OnStateMachineThread(),
                "Should be on main or state machine thread.");
   Statistics result;
 
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-  if (mStream) {
+  if (mResource) {
     result.mDownloadRate = 
-      mStream->GetDownloadRate(&result.mDownloadRateReliable);
+      mResource->GetDownloadRate(&result.mDownloadRateReliable);
     result.mDownloadPosition =
-      mStream->GetCachedDataEnd(mDecoderPosition);
-    result.mTotalBytes = mStream->GetLength();
+      mResource->GetCachedDataEnd(mDecoderPosition);
+    result.mTotalBytes = mResource->GetLength();
     result.mPlaybackRate = ComputePlaybackRate(&result.mPlaybackRateReliable);
     result.mDecoderPosition = mDecoderPosition;
     result.mPlaybackPosition = mPlaybackPosition;
   }
   else {
     result.mDownloadRate = 0;
     result.mDownloadRateReliable = true;
     result.mPlaybackRate = 0;
@@ -612,52 +606,52 @@ nsBuiltinDecoder::GetStatistics()
 }
 
 double nsBuiltinDecoder::ComputePlaybackRate(bool* aReliable)
 {
   GetReentrantMonitor().AssertCurrentThreadIn();
   NS_ASSERTION(NS_IsMainThread() || OnStateMachineThread(),
                "Should be on main or state machine thread.");
 
-  PRInt64 length = mStream ? mStream->GetLength() : -1;
+  PRInt64 length = mResource ? mResource->GetLength() : -1;
   if (mDuration >= 0 && length >= 0) {
     *aReliable = true;
     return length * static_cast<double>(USECS_PER_S) / mDuration;
   }
   return mPlaybackStatistics.GetRateAtLastStop(aReliable);
 }
 
 void nsBuiltinDecoder::UpdatePlaybackRate()
 {
   NS_ASSERTION(NS_IsMainThread() || OnStateMachineThread(),
                "Should be on main or state machine thread.");
   GetReentrantMonitor().AssertCurrentThreadIn();
-  if (!mStream)
+  if (!mResource)
     return;
   bool reliable;
   PRUint32 rate = PRUint32(ComputePlaybackRate(&reliable));
   if (reliable) {
     // Avoid passing a zero rate
     rate = NS_MAX(rate, 1u);
   }
   else {
     // Set a minimum rate of 10,000 bytes per second ... sometimes we just
     // don't have good data
     rate = NS_MAX(rate, 10000u);
   }
-  mStream->SetPlaybackRate(rate);
+  mResource->SetPlaybackRate(rate);
 }
 
 void nsBuiltinDecoder::NotifySuspendedStatusChanged()
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
-  if (!mStream)
+  if (!mResource)
     return;
-  nsMediaStream* activeStream;
-  bool suspended = mStream->IsSuspendedByCache(&activeStream);
+  MediaResource* activeStream;
+  bool suspended = mResource->IsSuspendedByCache(&activeStream);
   
   if (suspended && mElement) {
     // if this is an autoplay element, we need to kick off its autoplaying
     // now so we consume data and hopefully free up cache space
     mElement->NotifyAutoplayDataReady();
   }
 }
 
@@ -860,17 +854,26 @@ void nsBuiltinDecoder::PlaybackPositionC
 
   double lastTime = mCurrentTime;
 
   // Control the scope of the monitor so it is not
   // held while the timeupdate and the invalidate is run.
   {
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     if (mDecoderStateMachine) {
-      mCurrentTime = mDecoderStateMachine->GetCurrentTime();
+      if (!IsSeeking()) {
+        // Only update the current playback position if we're not seeking.
+        // If we are seeking, the update could have been scheduled on the
+        // state machine thread while we were playing but after the seek
+        // algorithm set the current playback position on the main thread,
+        // and we don't want to override the seek algorithm and change the
+        // current time after the seek has started but before it has
+        // completed.
+        mCurrentTime = mDecoderStateMachine->GetCurrentTime();
+      }
       mDecoderStateMachine->ClearPositionChangeFlag();
     }
   }
 
   // Invalidate the frame so any video data is displayed.
   // Do this before the timeupdate event so that if that
   // event runs JavaScript that queries the media size, the
   // frame has reflowed and the size updated beforehand.
@@ -948,63 +951,63 @@ void nsBuiltinDecoder::SetEndTime(double
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     mDecoderStateMachine->SetFragmentEndTime(static_cast<PRInt64>(aTime * USECS_PER_S));
   }
 }
 
 void nsBuiltinDecoder::Suspend()
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
-  if (mStream) {
-    mStream->Suspend(true);
+  if (mResource) {
+    mResource->Suspend(true);
   }
 }
 
 void nsBuiltinDecoder::Resume(bool aForceBuffering)
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
-  if (mStream) {
-    mStream->Resume();
+  if (mResource) {
+    mResource->Resume();
   }
   if (aForceBuffering) {
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     if (mDecoderStateMachine) {
       mDecoderStateMachine->StartBuffering();
     }
   }
 }
 
 void nsBuiltinDecoder::StopProgressUpdates()
 {
   NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
                "Should be on state machine or decode thread.");
   GetReentrantMonitor().AssertCurrentThreadIn();
   mIgnoreProgressData = true;
-  if (mStream) {
-    mStream->SetReadMode(nsMediaCacheStream::MODE_METADATA);
+  if (mResource) {
+    mResource->SetReadMode(nsMediaCacheStream::MODE_METADATA);
   }
 }
 
 void nsBuiltinDecoder::StartProgressUpdates()
 {
   NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
                "Should be on state machine or decode thread.");
   GetReentrantMonitor().AssertCurrentThreadIn();
   mIgnoreProgressData = false;
-  if (mStream) {
-    mStream->SetReadMode(nsMediaCacheStream::MODE_PLAYBACK);
-    mDecoderPosition = mPlaybackPosition = mStream->Tell();
+  if (mResource) {
+    mResource->SetReadMode(nsMediaCacheStream::MODE_PLAYBACK);
+    mDecoderPosition = mPlaybackPosition = mResource->Tell();
   }
 }
 
 void nsBuiltinDecoder::MoveLoadsToBackground()
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
-  if (mStream) {
-    mStream->MoveLoadsToBackground();
+  if (mResource) {
+    mResource->MoveLoadsToBackground();
   }
 }
 
 void nsBuiltinDecoder::UpdatePlaybackOffset(PRInt64 aOffset)
 {
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
   mPlaybackPosition = NS_MAX(aOffset, mPlaybackPosition);
 }
--- a/content/media/nsBuiltinDecoder.h
+++ b/content/media/nsBuiltinDecoder.h
@@ -220,17 +220,17 @@ destroying the nsBuiltinDecoder object.
 #include "nsIChannel.h"
 #include "nsIObserver.h"
 #include "nsIFrame.h"
 #include "nsAutoPtr.h"
 #include "nsSize.h"
 #include "prlog.h"
 #include "gfxContext.h"
 #include "gfxRect.h"
-#include "nsMediaStream.h"
+#include "MediaResource.h"
 #include "nsMediaDecoder.h"
 #include "nsHTMLMediaElement.h"
 #include "mozilla/ReentrantMonitor.h"
 
 class nsAudioStream;
 
 static inline bool IsCurrentThread(nsIThread* aThread) {
   return NS_GetCurrentThread() == aThread;
@@ -347,25 +347,22 @@ public:
 
   // Called when a "MozAudioAvailable" event listener is added to the media
   // element. Called on the main thread.
   virtual void NotifyAudioAvailableListener() = 0;
 };
 
 class nsBuiltinDecoder : public nsMediaDecoder
 {
-  // ISupports
-  NS_DECL_ISUPPORTS
+public:
+  typedef mozilla::MediaChannelStatistics MediaChannelStatistics;
 
-  // nsIObserver
+  NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
 
- public:
-  typedef mozilla::ReentrantMonitor ReentrantMonitor;
-
   // Enumeration for the valid play states (see mPlayState)
   enum PlayState {
     PLAY_STATE_START,
     PLAY_STATE_LOADING,
     PLAY_STATE_PAUSED,
     PLAY_STATE_PLAYING,
     PLAY_STATE_SEEKING,
     PLAY_STATE_ENDED,
@@ -378,17 +375,17 @@ class nsBuiltinDecoder : public nsMediaD
   virtual bool Init(nsHTMLMediaElement* aElement);
 
   // This method must be called by the owning object before that
   // object disposes of this decoder object.
   virtual void Shutdown();
   
   virtual double GetCurrentTime();
 
-  virtual nsresult Load(nsMediaStream* aStream,
+  virtual nsresult Load(MediaResource* aResource,
                         nsIStreamListener** aListener,
                         nsMediaDecoder* aCloneDonor);
 
   virtual nsDecoderStateMachine* CreateStateMachine() = 0;
 
   // Start playback of a video. 'Load' must have previously been
   // called.
   virtual nsresult Play();
@@ -400,17 +397,17 @@ class nsBuiltinDecoder : public nsMediaD
 
   virtual void Pause();
   virtual void SetVolume(double aVolume);
   virtual double GetDuration();
 
   virtual void SetInfinite(bool aInfinite);
   virtual bool IsInfinite();
 
-  virtual nsMediaStream* GetStream();
+  virtual MediaResource* GetResource() { return mResource; }
   virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal();
 
   virtual void NotifySuspendedStatusChanged();
   virtual void NotifyBytesDownloaded();
   virtual void NotifyDownloadEnded(nsresult aStatus);
   // Called by the decode thread to keep track of the number of bytes read
   // from the resource.
   void NotifyBytesConsumed(PRInt64 aBytes);
@@ -455,17 +452,17 @@ class nsBuiltinDecoder : public nsMediaD
   // thread only.
   virtual void Suspend();
 
   // Resume any media downloads that have been suspended. Called by the
   // media element when it is restored from the bfcache. Call on the
   // main thread only.
   virtual void Resume(bool aForceBuffering);
 
-  // Tells our nsMediaStream to put all loads in the background.
+  // Tells our MediaResource to put all loads in the background.
   virtual void MoveLoadsToBackground();
 
   void AudioAvailable(float* aFrameBuffer, PRUint32 aFrameBufferLength, float aTime);
 
   // Called by the state machine to notify the decoder that the duration
   // has changed.
   void DurationChanged();
 
@@ -509,17 +506,16 @@ class nsBuiltinDecoder : public nsMediaD
       mDecoderStateMachine->NotifyDataArrived(aBuffer, aLength, aOffset);
     }
   }
 
   // Sets the length of the framebuffer used in MozAudioAvailable events.
   // The new size must be between 512 and 16384.
   virtual nsresult RequestFrameBufferLength(PRUint32 aLength);
 
- public:
   // Return the current state. Can be called on any thread. If called from
   // a non-main thread, the decoder monitor must be held.
   PlayState GetState() {
     return mPlayState;
   }
 
   // Stop updating the bytes downloaded for progress notifications. Called
   // when seeking to prevent wild changes to the progress notification.
@@ -613,17 +609,16 @@ class nsBuiltinDecoder : public nsMediaD
 
   // Drop reference to state machine.  Only called during shutdown dance.
   void ReleaseStateMachine() { mDecoderStateMachine = nsnull; }
 
    // Called when a "MozAudioAvailable" event listener is added to the media
    // element. Called on the main thread.
    virtual void NotifyAudioAvailableListener();
 
-public:
   // Notifies the element that decoding has failed.
   void DecodeError();
 
   // Schedules the state machine to run one cycle on the shared state
   // machine thread. Main thread only.
   nsresult ScheduleStateMachineThread();
 
   /******
@@ -638,17 +633,17 @@ public:
   // Current playback position in the stream. This is (approximately)
   // where we're up to playing back the stream. This is not adjusted
   // during decoder seek operations, but it's updated at the end when we
   // start playing back again.
   PRInt64 mPlaybackPosition;
   // Data needed to estimate playback data rate. The timeline used for
   // this estimate is "decode time" (where the "current time" is the
   // time of the last decoded video frame).
-  nsChannelStatistics mPlaybackStatistics;
+  MediaChannelStatistics mPlaybackStatistics;
 
   // The current playback position of the media resource in units of
   // seconds. This is updated approximately at the framerate of the
   // video (if it is a video) or the callback period of the audio.
   // It is read and written from the main thread only.
   double mCurrentTime;
 
   // Volume that playback should start at.  0.0 = muted. 1.0 = full
@@ -677,18 +672,18 @@ public:
 
   // The state machine object for handling the decoding. It is safe to
   // call methods of this object from other threads. Its internal data
   // is synchronised on a monitor. The lifetime of this object is
   // after mPlayState is LOADING and before mPlayState is SHUTDOWN. It
   // is safe to access it during this period.
   nsCOMPtr<nsDecoderStateMachine> mDecoderStateMachine;
 
-  // Stream of media data.
-  nsAutoPtr<nsMediaStream> mStream;
+  // Media data resource.
+  nsAutoPtr<MediaResource> mResource;
 
   // ReentrantMonitor for detecting when the video play state changes. A call
   // to Wait on this monitor will block the thread until the next
   // state change.
   ReentrantMonitor mReentrantMonitor;
 
   // Set to one of the valid play states. It is protected by the
   // monitor mReentrantMonitor. This monitor must be acquired when reading or
--- a/content/media/nsBuiltinDecoderReader.h
+++ b/content/media/nsBuiltinDecoderReader.h
@@ -387,16 +387,17 @@ private:
 // Encapsulates the decoding and reading of media data. Reading can only be
 // done on the decode thread. Never hold the decoder monitor when
 // calling into this class. Unless otherwise specified, methods and fields of
 // this class can only be accessed on the decode thread.
 class nsBuiltinDecoderReader : public nsRunnable {
 public:
   typedef mozilla::ReentrantMonitor ReentrantMonitor;
   typedef mozilla::ReentrantMonitorAutoEnter ReentrantMonitorAutoEnter;
+  typedef mozilla::VideoFrameContainer VideoFrameContainer;
 
   nsBuiltinDecoderReader(nsBuiltinDecoder* aDecoder);
   ~nsBuiltinDecoderReader();
 
   // Initializes the reader, returns NS_OK on success, or NS_ERROR_FAILURE
   // on failure.
   virtual nsresult Init(nsBuiltinDecoderReader* aCloneDonor) = 0;
 
--- a/content/media/nsBuiltinDecoderStateMachine.cpp
+++ b/content/media/nsBuiltinDecoderStateMachine.cpp
@@ -1646,17 +1646,17 @@ void nsBuiltinDecoderStateMachine::Decod
   LOG(PR_LOG_DEBUG, ("%p Seek completed, mCurrentFrameTime=%lld\n",
       mDecoder.get(), mCurrentFrameTime));
 
   // Change state to DECODING or COMPLETED now. SeekingStopped will
   // call nsBuiltinDecoderStateMachine::Seek to reset our state to SEEKING
   // if we need to seek again.
 
   nsCOMPtr<nsIRunnable> stopEvent;
-  bool isLiveStream = mDecoder->GetStream()->GetLength() == -1;
+  bool isLiveStream = mDecoder->GetResource()->GetLength() == -1;
   if (GetMediaTime() == mEndTime && !isLiveStream) {
     // Seeked to end of media, move to COMPLETED state. Note we don't do
     // this if we're playing a live stream, since the end of media will advance
     // once we download more data!
     LOG(PR_LOG_DEBUG, ("%p Changed state from SEEKING (to %lld) to COMPLETED",
                         mDecoder.get(), seekTime));
     stopEvent = NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::SeekingStoppedAtEnd);
     mState = DECODER_STATE_COMPLETED;
@@ -1716,18 +1716,18 @@ private:
   nsRefPtr<nsBuiltinDecoder> mDecoder;
   nsCOMPtr<nsBuiltinDecoderStateMachine> mStateMachine;
 };
 
 nsresult nsBuiltinDecoderStateMachine::RunStateMachine()
 {
   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
 
-  nsMediaStream* stream = mDecoder->GetStream();
-  NS_ENSURE_TRUE(stream, NS_ERROR_NULL_POINTER);
+  MediaResource* resource = mDecoder->GetResource();
+  NS_ENSURE_TRUE(resource, NS_ERROR_NULL_POINTER);
 
   switch (mState) {
     case DECODER_STATE_SHUTDOWN: {
       if (IsPlaying()) {
         StopPlayback();
       }
       StopAudioThread();
       StopDecodeThread();
@@ -1797,23 +1797,23 @@ nsresult nsBuiltinDecoderStateMachine::R
 
       TimeStamp now = TimeStamp::Now();
       NS_ASSERTION(!mBufferingStart.IsNull(), "Must know buffering start time.");
 
       // We will remain in the buffering state if we've not decoded enough
       // data to begin playback, or if we've not downloaded a reasonable
       // amount of data inside our buffering time.
       TimeDuration elapsed = now - mBufferingStart;
-      bool isLiveStream = mDecoder->GetStream()->GetLength() == -1;
+      bool isLiveStream = mDecoder->GetResource()->GetLength() == -1;
       if ((isLiveStream || !mDecoder->CanPlayThrough()) &&
             elapsed < TimeDuration::FromSeconds(mBufferingWait) &&
             (mQuickBuffering ? HasLowDecodedData(QUICK_BUFFERING_LOW_DATA_USECS)
                             : (GetUndecodedData() < mBufferingWait * USECS_PER_S / 1000)) &&
-            !stream->IsDataCachedToEndOfStream(mDecoder->mDecoderPosition) &&
-            !stream->IsSuspended())
+            !resource->IsDataCachedToEndOfResource(mDecoder->mDecoderPosition) &&
+            !resource->IsSuspended())
       {
         LOG(PR_LOG_DEBUG,
             ("%p Buffering: %.3lfs/%ds, timeout in %.3lfs %s",
               mDecoder.get(),
               GetUndecodedData() / static_cast<double>(USECS_PER_S),
               mBufferingWait,
               mBufferingWait - elapsed.ToSeconds(),
               (mQuickBuffering ? "(quick exit)" : "")));
@@ -1903,19 +1903,19 @@ void nsBuiltinDecoderStateMachine::Rende
   NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
                "Should be on state machine or decode thread.");
   mDecoder->GetReentrantMonitor().AssertNotCurrentThreadIn();
 
   if (aData->mDuplicate) {
     return;
   }
 
-  nsRefPtr<Image> image = aData->mImage;
-  if (image) {
-    mDecoder->SetVideoData(aData->mDisplay, image, aTarget);
+  VideoFrameContainer* container = mDecoder->GetVideoFrameContainer();
+  if (container) {
+    container->SetCurrentFrame(aData->mDisplay, aData->mImage, aTarget);
   }
 }
 
 PRInt64
 nsBuiltinDecoderStateMachine::GetAudioClock()
 {
   NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread.");
   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
@@ -1995,22 +1995,22 @@ void nsBuiltinDecoderStateMachine::Advan
         ? (DurationToUsecs(TimeStamp::Now() - mPlayStartTime) + mPlayDuration)
         : mPlayDuration;
       remainingTime = frame->mTime - mStartTime - now;
     }
   }
 
   // Check to see if we don't have enough data to play up to the next frame.
   // If we don't, switch to buffering mode.
-  nsMediaStream* stream = mDecoder->GetStream();
+  MediaResource* resource = mDecoder->GetResource();
   if (mState == DECODER_STATE_DECODING &&
       mDecoder->GetState() == nsBuiltinDecoder::PLAY_STATE_PLAYING &&
       HasLowDecodedData(remainingTime + EXHAUSTED_DATA_MARGIN_USECS) &&
-      !stream->IsDataCachedToEndOfStream(mDecoder->mDecoderPosition) &&
-      !stream->IsSuspended() &&
+      !resource->IsDataCachedToEndOfResource(mDecoder->mDecoderPosition) &&
+      !resource->IsSuspended() &&
       (JustExitedQuickBuffering() || HasLowUndecodedData()))
   {
     if (currentFrame) {
       mReader->mVideoQueue.PushFront(currentFrame.forget());
     }
     StartBuffering();
     ScheduleStateMachine();
     return;
@@ -2173,21 +2173,21 @@ void nsBuiltinDecoderStateMachine::Start
   nsMediaDecoder::Statistics stats = mDecoder->GetStatistics();
   LOG(PR_LOG_DEBUG, ("%p Playback rate: %.1lfKB/s%s download rate: %.1lfKB/s%s",
     mDecoder.get(),
     stats.mPlaybackRate/1024, stats.mPlaybackRateReliable ? "" : " (unreliable)",
     stats.mDownloadRate/1024, stats.mDownloadRateReliable ? "" : " (unreliable)"));
 }
 
 nsresult nsBuiltinDecoderStateMachine::GetBuffered(nsTimeRanges* aBuffered) {
-  nsMediaStream* stream = mDecoder->GetStream();
-  NS_ENSURE_TRUE(stream, NS_ERROR_FAILURE);
-  stream->Pin();
+  MediaResource* resource = mDecoder->GetResource();
+  NS_ENSURE_TRUE(resource, NS_ERROR_FAILURE);
+  resource->Pin();
   nsresult res = mReader->GetBuffered(aBuffered, mStartTime);
-  stream->Unpin();
+  resource->Unpin();
   return res;
 }
 
 bool nsBuiltinDecoderStateMachine::IsPausedAndDecoderWaiting() {
   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
   NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread.");
 
   return
--- a/content/media/nsBuiltinDecoderStateMachine.h
+++ b/content/media/nsBuiltinDecoderStateMachine.h
@@ -131,16 +131,17 @@ hardware (via nsAudioStream and libsydne
   See nsBuiltinDecoder.h for more details.
 */
 class nsBuiltinDecoderStateMachine : public nsDecoderStateMachine
 {
 public:
   typedef mozilla::ReentrantMonitor ReentrantMonitor;
   typedef mozilla::TimeStamp TimeStamp;
   typedef mozilla::TimeDuration TimeDuration;
+  typedef mozilla::VideoFrameContainer VideoFrameContainer;
 
   nsBuiltinDecoderStateMachine(nsBuiltinDecoder* aDecoder, nsBuiltinDecoderReader* aReader, bool aRealTime = false);
   ~nsBuiltinDecoderStateMachine();
 
   // nsDecoderStateMachine interface
   virtual nsresult Init(nsDecoderStateMachine* aCloneDonor);
   State GetState()
   { 
--- a/content/media/nsMediaCache.cpp
+++ b/content/media/nsMediaCache.cpp
@@ -40,17 +40,17 @@
 #include "mozilla/XPCOM.h"
 
 #include "nsMediaCache.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsNetUtil.h"
 #include "prio.h"
 #include "nsThreadUtils.h"
-#include "nsMediaStream.h"
+#include "MediaResource.h"
 #include "nsMathUtils.h"
 #include "prlog.h"
 #include "nsIPrivateBrowsingService.h"
 #include "mozilla/Preferences.h"
 
 using namespace mozilla;
 
 #ifdef PR_LOGGING
@@ -1903,27 +1903,27 @@ nsMediaCacheStream::SetSeekable(bool aIs
 bool
 nsMediaCacheStream::IsSeekable()
 {
   ReentrantMonitorAutoEnter mon(gMediaCache->GetReentrantMonitor());
   return mIsSeekable;
 }
 
 bool
-nsMediaCacheStream::AreAllStreamsForResourceSuspended(nsMediaStream** aActiveStream)
+nsMediaCacheStream::AreAllStreamsForResourceSuspended(MediaResource** aActiveStream)
 {
   ReentrantMonitorAutoEnter mon(gMediaCache->GetReentrantMonitor());
   nsMediaCache::ResourceStreamIterator iter(mResourceID);
   while (nsMediaCacheStream* stream = iter.Next()) {
     if (!stream->mCacheSuspended && !stream->mChannelEnded && !stream->mClosed) {
       if (aActiveStream) {
         *aActiveStream = stream->mClient;
       }
       return false;
-	}
+    }
   }
   if (aActiveStream) {
     *aActiveStream = nsnull;
   }
   return true;
 }
 
 void
@@ -2357,30 +2357,30 @@ nsMediaCacheStream::InitAsClone(nsMediaC
     // Every block is a readahead block for the clone because the clone's initial
     // stream offset is zero
     gMediaCache->AddBlockOwnerAsReadahead(cacheBlockIndex, this, i);
   }
 
   return NS_OK;
 }
 
-nsresult nsMediaCacheStream::GetCachedRanges(nsTArray<nsByteRange>& aRanges)
+nsresult nsMediaCacheStream::GetCachedRanges(nsTArray<MediaByteRange>& aRanges)
 {
   // Take the monitor, so that the cached data ranges can't grow while we're
   // trying to loop over them.
   ReentrantMonitorAutoEnter mon(gMediaCache->GetReentrantMonitor());
 
   // We must be pinned while running this, otherwise the cached data ranges may
   // shrink while we're trying to loop over them.
   NS_ASSERTION(mPinCount > 0, "Must be pinned");
 
   PRInt64 startOffset = GetNextCachedData(0);
   while (startOffset >= 0) {
     PRInt64 endOffset = GetCachedDataEnd(startOffset);
     NS_ASSERTION(startOffset < endOffset, "Buffered range must end after its start");
     // Bytes [startOffset..endOffset] are cached.
-    aRanges.AppendElement(nsByteRange(startOffset, endOffset));
+    aRanges.AppendElement(MediaByteRange(startOffset, endOffset));
     startOffset = GetNextCachedData(endOffset);
     NS_ASSERTION(startOffset == -1 || startOffset > endOffset,
       "Must have advanced to start of next range, or hit end of stream");
   }
   return NS_OK;
 }
--- a/content/media/nsMediaCache.h
+++ b/content/media/nsMediaCache.h
@@ -38,20 +38,21 @@
 
 #ifndef nsMediaCache_h_
 #define nsMediaCache_h_
 
 #include "nsTArray.h"
 #include "nsIPrincipal.h"
 #include "nsCOMPtr.h"
 
-class nsMediaStream;
-class nsByteRange;
-
 namespace mozilla {
+// defined in MediaResource.h
+class ChannelMediaResource;
+class MediaByteRange;
+class MediaResource;
 class ReentrantMonitorAutoEnter;
 }
 
 /**
  * Media applications want fast, "on demand" random access to media data,
  * for pausing, seeking, etc. But we are primarily interested
  * in transporting media data using HTTP over the Internet, which has
  * high latency to open a connection, requires a new connection for every
@@ -164,75 +165,76 @@ class ReentrantMonitorAutoEnter;
  * 
  * All changes to the actual contents of the cache happen on the main
  * thread, since that's where Necko's notifications happen.
  *
  * The media cache maintains at most one Necko channel for each stream.
  * (In the future it might be advantageous to relax this, e.g. so that a
  * seek to near the end of the file can happen without disturbing
  * the loading of data from the beginning of the file.) The Necko channel
- * is managed through nsMediaChannelStream; nsMediaCache does not
+ * is managed through ChannelMediaResource; nsMediaCache does not
  * depend on Necko directly.
  * 
  * Every time something changes that might affect whether we want to
  * read from a Necko channel, or whether we want to seek on the Necko
  * channel --- such as data arriving or data being consumed by the
  * decoder --- we asynchronously trigger nsMediaCache::Update on the main
  * thread. That method implements most cache policy. It evaluates for
  * each stream whether we want to suspend or resume the stream and what
  * offset we should seek to, if any. It is also responsible for trimming
  * back the cache size to its desired limit by moving overflowing blocks
  * into the main part of the cache.
  * 
  * Streams can be opened in non-seekable mode. In non-seekable mode,
- * the cache will only call nsMediaChannelStream::CacheClientSeek with
+ * the cache will only call ChannelMediaResource::CacheClientSeek with
  * a 0 offset. The cache tries hard not to discard readahead data
  * for non-seekable streams, since that could trigger a potentially
  * disastrous re-read of the entire stream. It's up to cache clients
  * to try to avoid requesting seeks on such streams.
  * 
  * nsMediaCache has a single internal monitor for all synchronization.
  * This is treated as the lowest level monitor in the media code. So,
- * we must not acquire any nsMediaDecoder locks or nsMediaStream locks
+ * we must not acquire any nsMediaDecoder locks or MediaResource locks
  * while holding the nsMediaCache lock. But it's OK to hold those locks
  * and then get the nsMediaCache lock.
  * 
  * nsMediaCache associates a principal with each stream. CacheClientSeek
  * can trigger new HTTP requests; due to redirects to other domains,
  * each HTTP load can return data with a different principal. This
  * principal must be passed to NotifyDataReceived, and nsMediaCache
  * will detect when different principals are associated with data in the
  * same stream, and replace them with a null principal.
  */
 class nsMediaCache;
-// defined in nsMediaStream.h
-class nsMediaChannelStream;
 
 /**
  * If the cache fails to initialize then Init will fail, so nonstatic
  * methods of this class can assume gMediaCache is non-null.
  * 
  * This class can be directly embedded as a value.
  */
 class nsMediaCacheStream {
+public:
+  typedef mozilla::ChannelMediaResource ChannelMediaResource;
+  typedef mozilla::MediaByteRange MediaByteRange;
+  typedef mozilla::MediaResource MediaResource;
   typedef mozilla::ReentrantMonitorAutoEnter ReentrantMonitorAutoEnter;
 
-public:
   enum {
     // This needs to be a power of two
     BLOCK_SIZE = 32768
   };
   enum ReadMode {
     MODE_METADATA,
     MODE_PLAYBACK
   };
 
   // aClient provides the underlying transport that cache will use to read
   // data for this stream.
-  nsMediaCacheStream(nsMediaChannelStream* aClient)
+  nsMediaCacheStream(ChannelMediaResource* aClient)
     : mClient(aClient), mResourceID(0), mInitialized(false),
       mHasHadUpdate(false),
       mIsSeekable(false), mCacheSuspended(false),
       mChannelEnded(false), mDidNotifyDataEnded(false),
       mUsingNullPrincipal(false),
       mChannelOffset(0), mStreamLength(-1),  
       mStreamOffset(0), mPlaybackBytesPerSecond(10000),
       mPinCount(0), mCurrentMode(MODE_PLAYBACK),
@@ -254,17 +256,17 @@ public:
   // These are called on the main thread.
   // Tell us whether the stream is seekable or not. Non-seekable streams
   // will always pass 0 for aOffset to CacheClientSeek. This should only
   // be called while the stream is at channel offset 0. Seekability can
   // change during the lifetime of the nsMediaCacheStream --- every time
   // we do an HTTP load the seekability may be different (and sometimes
   // is, in practice, due to the effects of caching proxies).
   void SetSeekable(bool aIsSeekable);
-  // This must be called (and return) before the nsMediaChannelStream
+  // This must be called (and return) before the ChannelMediaResource
   // used to create this nsMediaCacheStream is deleted.
   void Close();
   // This returns true when the stream has been closed
   bool IsClosed() const { return mClosed; }
   // Get the principal for this stream.
   nsIPrincipal* GetCurrentPrincipal() { return mPrincipal; }
   // Ensure a global media cache update has run with this stream present.
   // This ensures the cache has had a chance to suspend or unsuspend this stream.
@@ -286,24 +288,24 @@ public:
   // EOF also depend on the reported length if we haven't managed to
   // read the whole stream yet.
   void NotifyDataLength(PRInt64 aLength);
   // Notifies the cache that a load has begun. We pass the offset
   // because in some cases the offset might not be what the cache
   // requested. In particular we might unexpectedly start providing
   // data at offset 0. This need not be called if the offset is the
   // offset that the cache requested in
-  // nsMediaChannelStream::CacheClientSeek. This can be called at any
+  // ChannelMediaResource::CacheClientSeek. This can be called at any
   // time by the client, not just after a CacheClientSeek.
   void NotifyDataStarted(PRInt64 aOffset);
   // Notifies the cache that data has been received. The stream already
   // knows the offset because data is received in sequence and
   // the starting offset is known via NotifyDataStarted or because
   // the cache requested the offset in
-  // nsMediaChannelStream::CacheClientSeek, or because it defaulted to 0.
+  // ChannelMediaResource::CacheClientSeek, or because it defaulted to 0.
   // We pass in the principal that was used to load this data.
   void NotifyDataReceived(PRInt64 aSize, const char* aData,
                           nsIPrincipal* aPrincipal);
   // Notifies the cache that the channel has closed with the given status.
   void NotifyDataEnded(nsresult aStatus);
 
   // These methods can be called on any thread.
   // Cached blocks associated with this stream will not be evicted
@@ -324,17 +326,17 @@ public:
   PRInt64 GetCachedDataEnd(PRInt64 aOffset);
   // Returns the offset of the first byte of cached data at or after aOffset,
   // or -1 if there is no such cached data.
   PRInt64 GetNextCachedData(PRInt64 aOffset);
   // Fills aRanges with the ByteRanges representing the data which is currently
   // cached. Locks the media cache while running, to prevent any ranges
   // growing. The stream should be pinned while this runs and while its results
   // are used, to ensure no data is evicted.
-  nsresult GetCachedRanges(nsTArray<nsByteRange>& aRanges);
+  nsresult GetCachedRanges(nsTArray<MediaByteRange>& aRanges);
 
   // Reads from buffered data only. Will fail if not all data to be read is
   // in the cache. Will not mark blocks as read. Can be called from the main
   // thread. It's the caller's responsibility to wrap the call in a pin/unpin,
   // and also to check that the range they want is cached before calling this.
   nsresult ReadFromCache(char* aBuffer,
                          PRInt64 aOffset,
                          PRInt64 aCount);
@@ -351,19 +353,19 @@ public:
   // because it doesn't know when the decoder was paused, buffering, etc.
   // Do not pass zero.
   void SetPlaybackRate(PRUint32 aBytesPerSecond);
   // Returns the last set value of SetSeekable.
   bool IsSeekable();
 
   // Returns true when all streams for this resource are suspended or their
   // channel has ended.
-  // If aActiveStream is non-null, fills it with a pointer to a stream
+  // If aActiveResource is non-null, fills it with a pointer to a stream
   // for this resource that is not suspended or ended.
-  bool AreAllStreamsForResourceSuspended(nsMediaStream** aActiveStream);
+  bool AreAllStreamsForResourceSuspended(MediaResource** aActiveResource);
 
   // These methods must be called on a different thread from the main
   // thread. They should always be called on the same thread for a given
   // stream.
   // This can fail when aWhence is NS_SEEK_END and no stream length
   // is known.
   nsresult Seek(PRInt32 aWhence, PRInt64 aOffset);
   PRInt64 Tell();
@@ -448,17 +450,17 @@ private:
   // aReentrantMonitor is the nsAutoReentrantMonitor wrapper holding the cache monitor.
   // This is used to NotifyAll to wake up threads that might be
   // blocked on reading from this stream.
   void CloseInternal(ReentrantMonitorAutoEnter& aReentrantMonitor);
   // Update mPrincipal given that data has been received from aPrincipal
   void UpdatePrincipal(nsIPrincipal* aPrincipal);
 
   // These fields are main-thread-only.
-  nsMediaChannelStream*  mClient;
+  ChannelMediaResource*  mClient;
   nsCOMPtr<nsIPrincipal> mPrincipal;
   // This is a unique ID representing the resource we're loading.
   // All streams with the same mResourceID are loading the same
   // underlying resource and should share data.
   PRInt64                mResourceID;
   // Set to true when Init or InitAsClone has been called
   bool                   mInitialized;
   // Set to true when nsMediaCache::Update() has finished while this stream
--- a/content/media/nsMediaDecoder.cpp
+++ b/content/media/nsMediaDecoder.cpp
@@ -32,17 +32,17 @@
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsMediaDecoder.h"
-#include "nsMediaStream.h"
+#include "MediaResource.h"
 
 #include "prlog.h"
 #include "prmem.h"
 #include "nsIFrame.h"
 #include "nsIDocument.h"
 #include "nsThreadUtils.h"
 #include "nsIDOMHTMLMediaElement.h"
 #include "nsNetUtil.h"
@@ -67,38 +67,34 @@ static const PRUint32 STALL_MS = 3000;
 // to report that it can play through the entire media without the decode
 // catching up with the download. Having this margin make the
 // nsMediaDecoder::CanPlayThrough() calculation more stable in the case of
 // fluctuating bitrates.
 static const PRInt64 CAN_PLAY_THROUGH_MARGIN = 10;
 
 nsMediaDecoder::nsMediaDecoder() :
   mElement(nsnull),
-  mRGBWidth(-1),
-  mRGBHeight(-1),
-  mVideoUpdateLock("nsMediaDecoder.mVideoUpdateLock"),
   mFrameBufferLength(0),
   mPinnedForSeek(false),
-  mSizeChanged(false),
-  mImageContainerSizeChanged(false),
   mShuttingDown(false)
 {
   MOZ_COUNT_CTOR(nsMediaDecoder);
   MediaMemoryReporter::AddMediaDecoder(this);
 }
 
 nsMediaDecoder::~nsMediaDecoder()
 {
   MOZ_COUNT_DTOR(nsMediaDecoder);
   MediaMemoryReporter::RemoveMediaDecoder(this);
 }
 
 bool nsMediaDecoder::Init(nsHTMLMediaElement* aElement)
 {
   mElement = aElement;
+  mVideoFrameContainer = aElement->GetVideoFrameContainer();
   return true;
 }
 
 void nsMediaDecoder::Shutdown()
 {
   StopProgress();
   mElement = nsnull;
 }
@@ -113,57 +109,16 @@ nsresult nsMediaDecoder::RequestFrameBuf
   if (aLength < FRAMEBUFFER_LENGTH_MIN || aLength > FRAMEBUFFER_LENGTH_MAX) {
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
   }
 
   mFrameBufferLength = aLength;
   return NS_OK;
 }
 
-void nsMediaDecoder::Invalidate()
-{
-  if (!mElement)
-    return;
-
-  nsIFrame* frame = mElement->GetPrimaryFrame();
-  bool invalidateFrame = false;
-
-  {
-    MutexAutoLock lock(mVideoUpdateLock);
-
-    // Get mImageContainerSizeChanged while holding the lock.
-    invalidateFrame = mImageContainerSizeChanged;
-    mImageContainerSizeChanged = false;
-
-    if (mSizeChanged) {
-      mElement->UpdateMediaSize(nsIntSize(mRGBWidth, mRGBHeight));
-      mSizeChanged = false;
-
-      if (frame) {
-        nsPresContext* presContext = frame->PresContext();
-        nsIPresShell *presShell = presContext->PresShell();
-        presShell->FrameNeedsReflow(frame,
-                                    nsIPresShell::eStyleChange,
-                                    NS_FRAME_IS_DIRTY);
-      }
-    }
-  }
-
-  if (frame) {
-    nsRect contentRect = frame->GetContentRect() - frame->GetPosition();
-    if (invalidateFrame) {
-      frame->Invalidate(contentRect);
-    } else {
-      frame->InvalidateLayer(contentRect, nsDisplayItem::TYPE_VIDEO);
-    }
-  }
-
-  nsSVGEffects::InvalidateDirectRenderingObservers(mElement);
-}
-
 static void ProgressCallback(nsITimer* aTimer, void* aClosure)
 {
   nsMediaDecoder* decoder = static_cast<nsMediaDecoder*>(aClosure);
   decoder->Progress(true);
 }
 
 void nsMediaDecoder::Progress(bool aTimer)
 {
@@ -219,69 +174,34 @@ nsresult nsMediaDecoder::StopProgress()
 
 void nsMediaDecoder::FireTimeUpdate()
 {
   if (!mElement)
     return;
   mElement->FireTimeUpdate(true);
 }
 
-void nsMediaDecoder::SetVideoData(const gfxIntSize& aSize,
-                                  Image* aImage,
-                                  TimeStamp aTarget)
-{
-  MutexAutoLock lock(mVideoUpdateLock);
-
-  if (mRGBWidth != aSize.width || mRGBHeight != aSize.height) {
-    mRGBWidth = aSize.width;
-    mRGBHeight = aSize.height;
-    mSizeChanged = true;
-  }
-  if (mImageContainer && aImage) {
-    gfxIntSize oldFrameSize = mImageContainer->GetCurrentSize();
-
-    TimeStamp paintTime = mImageContainer->GetPaintTime();
-    if (!paintTime.IsNull() && !mPaintTarget.IsNull()) {
-      mPaintDelay = paintTime - mPaintTarget;
-    }
-
-    mImageContainer->SetCurrentImage(aImage);
-    gfxIntSize newFrameSize = mImageContainer->GetCurrentSize();
-    if (oldFrameSize != newFrameSize) {
-      mImageContainerSizeChanged = true;
-    }
-  }
-
-  mPaintTarget = aTarget;
-}
-
-double nsMediaDecoder::GetFrameDelay()
-{
-  MutexAutoLock lock(mVideoUpdateLock);
-  return mPaintDelay.ToSeconds();
-}
-
 void nsMediaDecoder::PinForSeek()
 {
-  nsMediaStream* stream = GetStream();
-  if (!stream || mPinnedForSeek) {
+  MediaResource* resource = GetResource();
+  if (!resource || mPinnedForSeek) {
     return;
   }
   mPinnedForSeek = true;
-  stream->Pin();
+  resource->Pin();
 }
 
 void nsMediaDecoder::UnpinForSeek()
 {
-  nsMediaStream* stream = GetStream();
-  if (!stream || !mPinnedForSeek) {
+  MediaResource* resource = GetResource();
+  if (!resource || !mPinnedForSeek) {
     return;
   }
   mPinnedForSeek = false;
-  stream->Unpin();
+  resource->Unpin();
 }
 
 bool nsMediaDecoder::CanPlayThrough()
 {
   Statistics stats = GetStatistics();
   if (!stats.mDownloadRateReliable || !stats.mPlaybackRateReliable) {
     return false;
   }
--- a/content/media/nsMediaDecoder.h
+++ b/content/media/nsMediaDecoder.h
@@ -43,61 +43,65 @@
 #include "nsIPrincipal.h"
 #include "nsSize.h"
 #include "prlog.h"
 #include "gfxContext.h"
 #include "gfxRect.h"
 #include "nsITimer.h"
 #include "ImageLayers.h"
 #include "mozilla/ReentrantMonitor.h"
-#include "mozilla/Mutex.h"
 #include "nsIMemoryReporter.h"
+#include "VideoFrameContainer.h"
 
 class nsHTMLMediaElement;
-class nsMediaStream;
 class nsIStreamListener;
 class nsTimeRanges;
 
+namespace mozilla {
+class MediaResource;
+}
+
 // The size to use for audio data frames in MozAudioAvailable events.
 // This value is per channel, and is chosen to give ~43 fps of events,
 // for example, 44100 with 2 channels, 2*1024 = 2048.
 static const PRUint32 FRAMEBUFFER_LENGTH_PER_CHANNEL = 1024;
 
 // The total size of the framebuffer used for MozAudioAvailable events
 // has to be within the following range.
 static const PRUint32 FRAMEBUFFER_LENGTH_MIN = 512;
 static const PRUint32 FRAMEBUFFER_LENGTH_MAX = 16384;
 
 // All methods of nsMediaDecoder must be called from the main thread only
-// with the exception of GetImageContainer, SetVideoData and GetStatistics,
+// with the exception of GetVideoFrameContainer and GetStatistics,
 // which can be called from any thread.
 class nsMediaDecoder : public nsIObserver
 {
 public:
+  typedef mozilla::MediaResource MediaResource;
+  typedef mozilla::ReentrantMonitor ReentrantMonitor;
   typedef mozilla::TimeStamp TimeStamp;
   typedef mozilla::TimeDuration TimeDuration;
-  typedef mozilla::layers::ImageContainer ImageContainer;
+  typedef mozilla::VideoFrameContainer VideoFrameContainer;
   typedef mozilla::layers::Image Image;
-  typedef mozilla::ReentrantMonitor ReentrantMonitor;
-  typedef mozilla::Mutex Mutex;
+  typedef mozilla::layers::ImageContainer ImageContainer;
 
   nsMediaDecoder();
   virtual ~nsMediaDecoder();
 
   // Create a new decoder of the same type as this one.
   virtual nsMediaDecoder* Clone() = 0;
 
   // Perform any initialization required for the decoder.
   // Return true on successful initialisation, false
   // on failure.
   virtual bool Init(nsHTMLMediaElement* aElement);
 
-  // Get the current nsMediaStream being used. Its URI will be returned
+  // Get the current MediaResource being used. Its URI will be returned
   // by currentSrc. Returns what was passed to Load(), if Load() has been called.
-  virtual nsMediaStream* GetStream() = 0;
+  virtual MediaResource* GetResource() = 0;
 
   // Return the principal of the current URI being played or downloaded.
   virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal() = 0;
 
   // Return the time position in the video stream being
   // played measured in seconds.
   virtual double GetCurrentTime() = 0;
 
@@ -132,20 +136,20 @@ public:
   virtual void SetVolume(double aVolume) = 0;
 
   // Start playback of a video. 'Load' must have previously been
   // called.
   virtual nsresult Play() = 0;
 
   // Start downloading the media. Decode the downloaded data up to the
   // point of the first frame of data.
-  // aStream is the media stream to use. Ownership of aStream passes to
+  // aResource is the media stream to use. Ownership of aResource passes to
   // the decoder, even if Load returns an error.
   // This is called at most once per decoder, after Init().
-  virtual nsresult Load(nsMediaStream* aStream,
+  virtual nsresult Load(MediaResource* aResource,
                         nsIStreamListener **aListener,
                         nsMediaDecoder* aCloneDonor) = 0;
 
   // Called when the video file has completed downloading.
   virtual void ResourceLoaded() = 0;
 
   // Called if the media file encounters a network error.
   virtual void NetworkError() = 0;
@@ -268,21 +272,16 @@ public:
       mDecoder->GetFrameStatistics().NotifyDecodedFrames(mParsed, mDecoded);
     }
   private:
     nsMediaDecoder* mDecoder;
     PRUint32& mParsed;
     PRUint32& mDecoded;
   };
 
-  // Time in seconds by which the last painted video frame was late by.
-  // E.g. if the last painted frame should have been painted at time t,
-  // but was actually painted at t+n, this returns n in seconds. Threadsafe.
-  double GetFrameDelay();
-
   // Return statistics. This is used for progress events and other things.
   // This can be called from any thread. It's only a snapshot of the
   // current state, since other threads might be changing the state
   // at any time.
   virtual Statistics GetStatistics() = 0;
   
   // Return the frame decode/paint related statistics.
   FrameStatistics& GetFrameStatistics() { return mFrameStats; }
@@ -301,39 +300,44 @@ public:
   // Return the time ranges that can be seeked into.
   virtual nsresult GetSeekable(nsTimeRanges* aSeekable) = 0;
 
   // Set the end time of the media resource. When playback reaches
   // this point the media pauses. aTime is in seconds.
   virtual void SetEndTime(double aTime) = 0;
 
   // Invalidate the frame.
-  virtual void Invalidate();
+  void Invalidate()
+  {
+    if (mVideoFrameContainer) {
+      mVideoFrameContainer->Invalidate();
+    }
+  }
 
   // Fire progress events if needed according to the time and byte
   // constraints outlined in the specification. aTimer is true
   // if the method is called as a result of the progress timer rather
   // than the result of downloaded data.
   virtual void Progress(bool aTimer);
 
   // Fire timeupdate events if needed according to the time constraints
   // outlined in the specification.
   virtual void FireTimeUpdate();
 
-  // Called by nsMediaStream when the "cache suspended" status changes.
-  // If nsMediaStream::IsSuspendedByCache returns true, then the decoder
+  // Called by MediaResource when the "cache suspended" status changes.
+  // If MediaResource::IsSuspendedByCache returns true, then the decoder
   // should stop buffering or otherwise waiting for download progress and
   // start consuming data, if possible, because the cache is full.
   virtual void NotifySuspendedStatusChanged() = 0;
 
-  // Called by nsMediaStream when some data has been received.
+  // Called by MediaResource when some data has been received.
   // Call on the main thread only.
   virtual void NotifyBytesDownloaded() = 0;
 
-  // Called by nsChannelToPipeListener or nsMediaStream when the
+  // Called by nsChannelToPipeListener or MediaResource when the
   // download has ended. Called on the main thread only. aStatus is
   // the result from OnStopRequest.
   virtual void NotifyDownloadEnded(nsresult aStatus) = 0;
 
   // Called as data arrives on the stream and is read into the cache.  Called
   // on the main thread only.
   virtual void NotifyDataArrived(const char* aBuffer, PRUint32 aLength, PRInt64 aOffset) = 0;
 
@@ -367,44 +371,38 @@ public:
   // Sets the length of the framebuffer used in MozAudioAvailable events.
   // The new size must be between 512 and 16384.
   virtual nsresult RequestFrameBufferLength(PRUint32 aLength);
 
   // Moves any existing channel loads into the background, so that they don't
   // block the load event. This is called when we stop delaying the load
   // event. Any new loads initiated (for example to seek) will also be in the
   // background. Implementations of this must call MoveLoadsToBackground() on
-  // their nsMediaStream.
+  // their MediaResource.
   virtual void MoveLoadsToBackground()=0;
 
-  // Gets the image container for the media element. Will return null if
-  // the element is not a video element. This can be called from any
-  // thread; ImageContainers can be used from any thread.
-  ImageContainer* GetImageContainer() { return mImageContainer; }
-
-  // Set the video width, height, pixel aspect ratio, current image and
-  // target paint time of the next video frame to be displayed.
-  // Ownership of the image is transferred to the layers subsystem.
-  void SetVideoData(const gfxIntSize& aSize,
-                    Image* aImage,
-                    TimeStamp aTarget);
-
   // Constructs the time ranges representing what segments of the media
   // are buffered and playable.
   virtual nsresult GetBuffered(nsTimeRanges* aBuffered) = 0;
 
   // Returns true if we can play the entire media through without stopping
   // to buffer, given the current download and playback rates.
   bool CanPlayThrough();
 
   // Returns the size, in bytes, of the heap memory used by the currently
   // queued decoded video and audio data.
   virtual PRInt64 VideoQueueMemoryInUse() = 0;
   virtual PRInt64 AudioQueueMemoryInUse() = 0;
 
+  VideoFrameContainer* GetVideoFrameContainer() { return mVideoFrameContainer; }
+  ImageContainer* GetImageContainer()
+  {
+    return mVideoFrameContainer ? mVideoFrameContainer->GetImageContainer() : nsnull;
+  }
+
 protected:
 
   // Start timer to update download progress information.
   nsresult StartProgress();
 
   // Stop progress information timer.
   nsresult StopProgress();
 
@@ -417,76 +415,39 @@ protected:
   // Timer used for updating progress events
   nsCOMPtr<nsITimer> mProgressTimer;
 
   // This should only ever be accessed from the main thread.
   // It is set in Init and cleared in Shutdown when the element goes away.
   // The decoder does not add a reference the element.
   nsHTMLMediaElement* mElement;
 
-  PRInt32 mRGBWidth;
-  PRInt32 mRGBHeight;
-
   // Counters related to decode and presentation of frames.
   FrameStatistics mFrameStats;
 
-  // The time at which the current video frame should have been painted.
-  // Access protected by mVideoUpdateLock.
-  TimeStamp mPaintTarget;
-
-  // The delay between the last video frame being presented and it being
-  // painted. This is time elapsed after mPaintTarget until the most recently
-  // painted frame appeared on screen. Access protected by mVideoUpdateLock.
-  TimeDuration mPaintDelay;
-
-  nsRefPtr<ImageContainer> mImageContainer;
+  nsRefPtr<VideoFrameContainer> mVideoFrameContainer;
 
   // Time that the last progress event was fired. Read/Write from the
   // main thread only.
   TimeStamp mProgressTime;
 
   // Time that data was last read from the media resource. Used for
   // computing if the download has stalled and to rate limit progress events
   // when data is arriving slower than PROGRESS_MS. A value of null indicates
   // that a stall event has already fired and not to fire another one until
   // more data is received. Read/Write from the main thread only.
   TimeStamp mDataTime;
 
-  // Lock around the video RGB, width and size data. This
-  // is used in the decoder backend threads and the main thread
-  // to ensure that repainting the video does not use these
-  // values while they are out of sync (width changed but
-  // not height yet, etc).
-  // Backends that are updating the height, width or writing
-  // to the RGB buffer must obtain this lock first to ensure that
-  // the video element does not use video data or sizes that are
-  // in the midst of being changed.
-  Mutex mVideoUpdateLock;
-
   // The framebuffer size to use for audioavailable events.
   PRUint32 mFrameBufferLength;
 
   // True when our media stream has been pinned. We pin the stream
   // while seeking.
   bool mPinnedForSeek;
 
-  // Set to true when the video width, height or pixel aspect ratio is
-  // changed by SetVideoData().  The next call to Invalidate() will recalculate
-  // and update the intrinsic size on the element, request a frame reflow and
-  // then reset this flag.
-  bool mSizeChanged;
-
-  // Set to true in SetVideoData() if the new image has a different size
-  // than the current image.  The image size is also affected by transforms
-  // so this can be true even if mSizeChanged is false, for example when
-  // zooming.  The next call to Invalidate() will call nsIFrame::Invalidate
-  // when this flag is set, rather than just InvalidateLayer, and then reset
-  // this flag.
-  bool mImageContainerSizeChanged;
-
   // True if the decoder is being shutdown. At this point all events that
   // are currently queued need to return immediately to prevent javascript
   // being run that operates on the element and decoder during shutdown.
   // Read/Write from the main thread only.
   bool mShuttingDown;
 };
 
 namespace mozilla {
--- a/content/media/ogg/nsOggReader.cpp
+++ b/content/media/ogg/nsOggReader.cpp
@@ -72,19 +72,19 @@ extern PRLogModuleInfo* gBuiltinDecoderL
 static const PRUint32 SEEK_FUZZ_USECS = 500000;
 
 enum PageSyncResult {
   PAGE_SYNC_ERROR = 1,
   PAGE_SYNC_END_OF_RANGE= 2,
   PAGE_SYNC_OK = 3
 };
 
-// Reads a page from the media stream.
+// Reads a page from the media resource.
 static PageSyncResult
-PageSync(nsMediaStream* aStream,
+PageSync(MediaResource* aResource,
          ogg_sync_state* aState,
          bool aCachedDataOnly,
          PRInt64 aOffset,
          PRInt64 aEndOffset,
          ogg_page* aPage,
          int& aSkippedBytes);
 
 // Chunk size to read when reading Ogg files. Average Ogg page length
@@ -261,19 +261,22 @@ nsresult nsOggReader::ReadMetadata(nsVid
     nsIntSize frameSize(mTheoraState->mInfo.frame_width,
                         mTheoraState->mInfo.frame_height);
     if (nsVideoInfo::ValidateVideoRegion(frameSize, picture, displaySize)) {
       // Video track's frame sizes will not overflow. Activate the video track.
       mInfo.mHasVideo = true;
       mInfo.mDisplay = displaySize;
       mPicture = picture;
 
-      mDecoder->SetVideoData(gfxIntSize(displaySize.width, displaySize.height),
-                             nsnull,
-                             TimeStamp::Now());
+      VideoFrameContainer* container = mDecoder->GetVideoFrameContainer();
+      if (container) {
+        container->SetCurrentFrame(gfxIntSize(displaySize.width, displaySize.height),
+                                   nsnull,
+                                   TimeStamp::Now());
+      }
 
       // Copy Theora info data for time computations on other threads.
       memcpy(&mTheoraInfo, &mTheoraState->mInfo, sizeof(mTheoraInfo));
       mTheoraSerial = mTheoraState->mSerial;
     }
   }
 
   if (mVorbisState && ReadHeaders(mVorbisState)) {
@@ -290,17 +293,17 @@ nsresult nsOggReader::ReadMetadata(nsVid
 
   if (mSkeletonState) {
     if (!HasAudio() && !HasVideo()) {
       // We have a skeleton track, but no audio or video, may as well disable
       // the skeleton, we can't do anything useful with this media.
       mSkeletonState->Deactivate();
     } else if (ReadHeaders(mSkeletonState) && mSkeletonState->HasIndex()) {
       // Extract the duration info out of the index, so we don't need to seek to
-      // the end of stream to get it.
+      // the end of resource to get it.
       nsAutoTArray<PRUint32, 2> tracks;
       if (HasVideo()) {
         tracks.AppendElement(mTheoraState->mSerial);
       }
       if (HasAudio()) {
         tracks.AppendElement(mVorbisState->mSerial);
       }
       PRInt64 duration = 0;
@@ -310,25 +313,25 @@ nsresult nsOggReader::ReadMetadata(nsVid
         LOG(PR_LOG_DEBUG, ("Got duration from Skeleton index %lld", duration));
       }
     }
   }
 
   {
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
 
-    nsMediaStream* stream = mDecoder->GetStream();
+    MediaResource* resource = mDecoder->GetResource();
     if (mDecoder->GetStateMachine()->GetDuration() == -1 &&
         mDecoder->GetStateMachine()->GetState() != nsDecoderStateMachine::DECODER_STATE_SHUTDOWN &&
-        stream->GetLength() >= 0 &&
+        resource->GetLength() >= 0 &&
         mDecoder->GetStateMachine()->IsSeekable())
     {
       // We didn't get a duration from the index or a Content-Duration header.
       // Seek to the end of file to find the end time.
-      PRInt64 length = stream->GetLength();
+      PRInt64 length = resource->GetLength();
       NS_ASSERTION(length > 0, "Must have a content length to get end time");
 
       PRInt64 endTime = 0;
       {
         ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
         endTime = RangeEndTime(length);
       }
       if (endTime != -1) {
@@ -544,20 +547,20 @@ PRInt64 nsOggReader::ReadOggPage(ogg_pag
       continue;
     }
     // Returns a buffer that can be written too
     // with the given size. This buffer is stored
     // in the ogg synchronisation structure.
     char* buffer = ogg_sync_buffer(&mOggState, 4096);
     NS_ASSERTION(buffer, "ogg_sync_buffer failed");
 
-    // Read from the stream into the buffer
+    // Read from the resource into the buffer
     PRUint32 bytesRead = 0;
 
-    nsresult rv = mDecoder->GetStream()->Read(buffer, 4096, &bytesRead);
+    nsresult rv = mDecoder->GetResource()->Read(buffer, 4096, &bytesRead);
     if (NS_FAILED(rv) || (bytesRead == 0 && ret == 0)) {
       // End of file.
       return -1;
     }
 
     mDecoder->NotifyBytesConsumed(bytesRead);
     // Update the synchronisation layer with the number
     // of bytes written to the buffer
@@ -611,19 +614,19 @@ GetChecksum(ogg_page* page)
                (p[2] << 16) +
                (p[3] << 24);
   return c;
 }
 
 PRInt64 nsOggReader::RangeStartTime(PRInt64 aOffset)
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
-  nsMediaStream* stream = mDecoder->GetStream();
-  NS_ENSURE_TRUE(stream != nsnull, nsnull);
-  nsresult res = stream->Seek(nsISeekableStream::NS_SEEK_SET, aOffset);
+  MediaResource* resource = mDecoder->GetResource();
+  NS_ENSURE_TRUE(resource != nsnull, nsnull);
+  nsresult res = resource->Seek(nsISeekableStream::NS_SEEK_SET, aOffset);
   NS_ENSURE_SUCCESS(res, nsnull);
   PRInt64 startTime = 0;
   nsBuiltinDecoderReader::FindStartTime(startTime);
   return startTime;
 }
 
 struct nsAutoOggSyncState {
   nsAutoOggSyncState() {
@@ -635,30 +638,30 @@ struct nsAutoOggSyncState {
   ogg_sync_state mState;
 };
 
 PRInt64 nsOggReader::RangeEndTime(PRInt64 aEndOffset)
 {
   NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(),
                "Should be on state machine or decode thread.");
 
-  nsMediaStream* stream = mDecoder->GetStream();
-  NS_ENSURE_TRUE(stream != nsnull, -1);
-  PRInt64 position = stream->Tell();
+  MediaResource* resource = mDecoder->GetResource();
+  NS_ENSURE_TRUE(resource != nsnull, -1);
+  PRInt64 position = resource->Tell();
   PRInt64 endTime = RangeEndTime(0, aEndOffset, false);
-  nsresult res = stream->Seek(nsISeekableStream::NS_SEEK_SET, position);
+  nsresult res = resource->Seek(nsISeekableStream::NS_SEEK_SET, position);
   NS_ENSURE_SUCCESS(res, -1);
   return endTime;
 }
 
 PRInt64 nsOggReader::RangeEndTime(PRInt64 aStartOffset,
                                   PRInt64 aEndOffset,
                                   bool aCachedDataOnly)
 {
-  nsMediaStream* stream = mDecoder->GetStream();
+  MediaResource* resource = mDecoder->GetResource();
   nsAutoOggSyncState sync;
 
   // We need to find the last page which ends before aEndOffset that
   // has a granulepos that we can convert to a timestamp. We do this by
   // backing off from aEndOffset until we encounter a page on which we can
   // interpret the granulepos. If while backing off we encounter a page which
   // we've previously encountered before, we'll either backoff again if we
   // haven't found an end time yet, or return the last end time found.
@@ -693,25 +696,25 @@ PRInt64 nsOggReader::RangeEndTime(PRInt6
       limit = NS_MAX(static_cast<PRInt64>(0), limit);
       limit = NS_MIN(limit, static_cast<PRInt64>(step));
       PRUint32 bytesToRead = static_cast<PRUint32>(limit);
       PRUint32 bytesRead = 0;
       char* buffer = ogg_sync_buffer(&sync.mState, bytesToRead);
       NS_ASSERTION(buffer, "Must have buffer");
       nsresult res;
       if (aCachedDataOnly) {
-        res = stream->ReadFromCache(buffer, readHead, bytesToRead);
+        res = resource->ReadFromCache(buffer, readHead, bytesToRead);
         NS_ENSURE_SUCCESS(res, -1);
         bytesRead = bytesToRead;
       } else {
         NS_ASSERTION(readHead < aEndOffset,
-                     "Stream pos must be before range end");
-        res = stream->Seek(nsISeekableStream::NS_SEEK_SET, readHead);
+                     "resource pos must be before range end");
+        res = resource->Seek(nsISeekableStream::NS_SEEK_SET, readHead);
         NS_ENSURE_SUCCESS(res, -1);
-        res = stream->Read(buffer, bytesToRead, &bytesRead);
+        res = resource->Read(buffer, bytesToRead, &bytesRead);
         NS_ENSURE_SUCCESS(res, -1);
       }
       readHead += bytesRead;
 
       // Update the synchronisation layer with the number
       // of bytes written to the buffer
       ret = ogg_sync_wrote(&sync.mState, bytesRead);
       if (ret != 0) {
@@ -764,22 +767,22 @@ PRInt64 nsOggReader::RangeEndTime(PRInt6
   }
 
   return endTime;
 }
 
 nsresult nsOggReader::GetSeekRanges(nsTArray<SeekRange>& aRanges)
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
-  nsTArray<nsByteRange> cached;
-  nsresult res = mDecoder->GetStream()->GetCachedRanges(cached);
+  nsTArray<MediaByteRange> cached;
+  nsresult res = mDecoder->GetResource()->GetCachedRanges(cached);
   NS_ENSURE_SUCCESS(res, res);
 
   for (PRUint32 index = 0; index < cached.Length(); index++) {
-    nsByteRange& range = cached[index];
+    MediaByteRange& range = cached[index];
     PRInt64 startTime = -1;
     PRInt64 endTime = -1;
     if (NS_FAILED(ResetDecode())) {
       return NS_ERROR_FAILURE;
     }
     PRInt64 startOffset = range.mStart;
     PRInt64 endOffset = range.mEnd;
     startTime = RangeStartTime(startOffset);
@@ -804,17 +807,17 @@ nsOggReader::SeekRange
 nsOggReader::SelectSeekRange(const nsTArray<SeekRange>& ranges,
                              PRInt64 aTarget,
                              PRInt64 aStartTime,
                              PRInt64 aEndTime,
                              bool aExact)
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
   PRInt64 so = 0;
-  PRInt64 eo = mDecoder->GetStream()->GetLength();
+  PRInt64 eo = mDecoder->GetResource()->GetLength();
   PRInt64 st = aStartTime;
   PRInt64 et = aEndTime;
   for (PRUint32 i = 0; i < ranges.Length(); i++) {
     const SeekRange &r = ranges[i];
     if (r.mTimeStart < aTarget) {
       so = r.mOffsetStart;
       st = r.mTimeStart;
     }
@@ -832,27 +835,27 @@ nsOggReader::SelectSeekRange(const nsTAr
     return SeekRange();
   }
   return SeekRange(so, eo, st, et);
 }
 
 nsOggReader::IndexedSeekResult nsOggReader::RollbackIndexedSeek(PRInt64 aOffset)
 {
   mSkeletonState->Deactivate();
-  nsMediaStream* stream = mDecoder->GetStream();
-  NS_ENSURE_TRUE(stream != nsnull, SEEK_FATAL_ERROR);
-  nsresult res = stream->Seek(nsISeekableStream::NS_SEEK_SET, aOffset);
+  MediaResource* resource = mDecoder->GetResource();
+  NS_ENSURE_TRUE(resource != nsnull, SEEK_FATAL_ERROR);
+  nsresult res = resource->Seek(nsISeekableStream::NS_SEEK_SET, aOffset);
   NS_ENSURE_SUCCESS(res, SEEK_FATAL_ERROR);
   return SEEK_INDEX_FAIL;
 }
  
 nsOggReader::IndexedSeekResult nsOggReader::SeekToKeyframeUsingIndex(PRInt64 aTarget)
 {
-  nsMediaStream* stream = mDecoder->GetStream();
-  NS_ENSURE_TRUE(stream != nsnull, SEEK_FATAL_ERROR);
+  MediaResource* resource = mDecoder->GetResource();
+  NS_ENSURE_TRUE(resource != nsnull, SEEK_FATAL_ERROR);
   if (!HasSkeleton() || !mSkeletonState->HasIndex()) {
     return SEEK_INDEX_FAIL;
   }
   // We have an index from the Skeleton track, try to use it to seek.
   nsAutoTArray<PRUint32, 2> tracks;
   if (HasVideo()) {
     tracks.AppendElement(mTheoraState->mSerial);
   }
@@ -863,46 +866,46 @@ nsOggReader::IndexedSeekResult nsOggRead
   if (NS_FAILED(mSkeletonState->IndexedSeekTarget(aTarget,
                                                   tracks,
                                                   keyframe)))
   {
     // Could not locate a keypoint for the target in the index.
     return SEEK_INDEX_FAIL;
   }
 
-  // Remember original stream read cursor position so we can rollback on failure.
-  PRInt64 tell = stream->Tell();
+  // Remember original resource read cursor position so we can rollback on failure.
+  PRInt64 tell = resource->Tell();
 
   // Seek to the keypoint returned by the index.
-  if (keyframe.mKeyPoint.mOffset > stream->GetLength() ||
+  if (keyframe.mKeyPoint.mOffset > resource->GetLength() ||
       keyframe.mKeyPoint.mOffset < 0)
   {
     // Index must be invalid.
     return RollbackIndexedSeek(tell);
   }
   LOG(PR_LOG_DEBUG, ("Seeking using index to keyframe at offset %lld\n",
                      keyframe.mKeyPoint.mOffset));
-  nsresult res = stream->Seek(nsISeekableStream::NS_SEEK_SET,
+  nsresult res = resource->Seek(nsISeekableStream::NS_SEEK_SET,
                               keyframe.mKeyPoint.mOffset);
   NS_ENSURE_SUCCESS(res, SEEK_FATAL_ERROR);
   mPageOffset = keyframe.mKeyPoint.mOffset;
 
   // We've moved the read set, so reset decode.
   res = ResetDecode();
   NS_ENSURE_SUCCESS(res, SEEK_FATAL_ERROR);
 
   // Check that the page the index thinks is exactly here is actually exactly
   // here. If not, the index is invalid.
   ogg_page page;
   int skippedBytes = 0;
-  PageSyncResult syncres = PageSync(stream,
+  PageSyncResult syncres = PageSync(resource,
                                     &mOggState,
                                     false,
                                     mPageOffset,
-                                    stream->GetLength(),
+                                    resource->GetLength(),
                                     &page,
                                     skippedBytes);
   NS_ENSURE_TRUE(syncres != PAGE_SYNC_ERROR, SEEK_FATAL_ERROR);
   if (syncres != PAGE_SYNC_OK || skippedBytes != 0) {
     LOG(PR_LOG_DEBUG, ("Indexed-seek failure: Ogg Skeleton Index is invalid "
                        "or sync error after seek"));
     return RollbackIndexedSeek(tell);
   }
@@ -913,17 +916,17 @@ nsOggReader::IndexedSeekResult nsOggRead
     return RollbackIndexedSeek(tell);
   }
   nsOggCodecState* codecState = nsnull;
   mCodecStates.Get(serial, &codecState);
   if (codecState &&
       codecState->mActive &&
       ogg_stream_pagein(&codecState->mState, &page) != 0)
   {
-    // Couldn't insert page into the ogg stream, or somehow the stream
+    // Couldn't insert page into the ogg resource, or somehow the resource
     // is no longer active.
     return RollbackIndexedSeek(tell);
   }      
   mPageOffset = keyframe.mKeyPoint.mOffset + page.header_len + page.body_len;
   return SEEK_OK;
 }
 
 nsresult nsOggReader::SeekInBufferedRange(PRInt64 aTarget,
@@ -1009,23 +1012,23 @@ nsresult nsOggReader::SeekInUnbuffered(P
 nsresult nsOggReader::Seek(PRInt64 aTarget,
                            PRInt64 aStartTime,
                            PRInt64 aEndTime,
                            PRInt64 aCurrentTime)
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
   LOG(PR_LOG_DEBUG, ("%p About to seek to %lld", mDecoder, aTarget));
   nsresult res;
-  nsMediaStream* stream = mDecoder->GetStream();
-  NS_ENSURE_TRUE(stream != nsnull, NS_ERROR_FAILURE);
+  MediaResource* resource = mDecoder->GetResource();
+  NS_ENSURE_TRUE(resource != nsnull, NS_ERROR_FAILURE);
 
   if (aTarget == aStartTime) {
     // We've seeked to the media start. Just seek to the offset of the first
     // content page.
-    res = stream->Seek(nsISeekableStream::NS_SEEK_SET, 0);
+    res = resource->Seek(nsISeekableStream::NS_SEEK_SET, 0);
     NS_ENSURE_SUCCESS(res,res);
 
     mPageOffset = 0;
     res = ResetDecode();
     NS_ENSURE_SUCCESS(res,res);
 
     NS_ASSERTION(aStartTime != -1, "mStartTime should be known");
     {
@@ -1062,19 +1065,19 @@ nsresult nsOggReader::Seek(PRInt64 aTarg
   }
 
   // The decode position must now be either close to the seek target, or
   // we've seeked to before the keyframe before the seek target. Decode
   // forward to the seek target frame.
   return DecodeToTarget(aTarget);
 }
 
-// Reads a page from the media stream.
+// Reads a page from the media resource.
 static PageSyncResult
-PageSync(nsMediaStream* aStream,
+PageSync(MediaResource* aResource,
          ogg_sync_state* aState,
          bool aCachedDataOnly,
          PRInt64 aOffset,
          PRInt64 aEndOffset,
          ogg_page* aPage,
          int& aSkippedBytes)
 {
   aSkippedBytes = 0;
@@ -1092,26 +1095,26 @@ PageSync(nsMediaStream* aStream,
       PRInt64 bytesToRead = NS_MIN(static_cast<PRInt64>(PAGE_STEP),
                                    aEndOffset - readHead);
       NS_ASSERTION(bytesToRead <= PR_UINT32_MAX, "bytesToRead range check");
       if (bytesToRead <= 0) {
         return PAGE_SYNC_END_OF_RANGE;
       }
       nsresult rv = NS_OK;
       if (aCachedDataOnly) {
-        rv = aStream->ReadFromCache(buffer, readHead,
-                                    static_cast<PRUint32>(bytesToRead));
+        rv = aResource->ReadFromCache(buffer, readHead,
+                                      static_cast<PRUint32>(bytesToRead));
         NS_ENSURE_SUCCESS(rv,PAGE_SYNC_ERROR);
         bytesRead = static_cast<PRUint32>(bytesToRead);
       } else {
-        rv = aStream->Seek(nsISeekableStream::NS_SEEK_SET, readHead);
+        rv = aResource->Seek(nsISeekableStream::NS_SEEK_SET, readHead);
         NS_ENSURE_SUCCESS(rv,PAGE_SYNC_ERROR);
-        rv = aStream->Read(buffer,
-                           static_cast<PRUint32>(bytesToRead),
-                           &bytesRead);
+        rv = aResource->Read(buffer,
+                             static_cast<PRUint32>(bytesToRead),
+                             &bytesRead);
         NS_ENSURE_SUCCESS(rv,PAGE_SYNC_ERROR);
       }
       if (bytesRead == 0 && NS_SUCCEEDED(rv)) {
         // End of file.
         return PAGE_SYNC_END_OF_RANGE;
       }
       readHead += bytesRead;
 
@@ -1134,23 +1137,23 @@ PageSync(nsMediaStream* aStream,
 }
 
 nsresult nsOggReader::SeekBisection(PRInt64 aTarget,
                                     const SeekRange& aRange,
                                     PRUint32 aFuzz)
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
   nsresult res;
-  nsMediaStream* stream = mDecoder->GetStream();
+  MediaResource* resource = mDecoder->GetResource();
 
   if (aTarget == aRange.mTimeStart) {
     if (NS_FAILED(ResetDecode())) {
       return NS_ERROR_FAILURE;
     }
-    res = stream->Seek(nsISeekableStream::NS_SEEK_SET, 0);
+    res = resource->Seek(nsISeekableStream::NS_SEEK_SET, 0);
     NS_ENSURE_SUCCESS(res,res);
     mPageOffset = 0;
     return NS_OK;
   }
 
   // Bisection search, find start offset of last page with end time less than
   // the seek target.
   ogg_int64_t startOffset = aRange.mOffsetStart;
@@ -1247,17 +1250,17 @@ nsresult nsOggReader::SeekBisection(PRIn
       NS_ASSERTION(guess != previousGuess, "Guess should be different to previous");
       previousGuess = guess;
 
       hops++;
     
       // Locate the next page after our seek guess, and then figure out the
       // granule time of the audio and video bitstreams there. We can then
       // make a bisection decision based on our location in the media.
-      PageSyncResult res = PageSync(stream,
+      PageSyncResult res = PageSync(resource,
                                     &mOggState,
                                     false,
                                     guess,
                                     endOffset,
                                     &page,
                                     skippedBytes);
       NS_ENSURE_TRUE(res != PAGE_SYNC_ERROR, NS_ERROR_FAILURE);
 
@@ -1347,29 +1350,29 @@ nsresult nsOggReader::SeekBisection(PRIn
       break;
     } // End of "until we determine time at guess offset" loop.
 
     if (interval == 0) {
       // Seek termination condition; we've found the page boundary of the
       // last page before the target, and the first page after the target.
       SEEK_LOG(PR_LOG_DEBUG, ("Terminating seek at offset=%lld", startOffset));
       NS_ASSERTION(startTime < aTarget, "Start time must always be less than target");
-      res = stream->Seek(nsISeekableStream::NS_SEEK_SET, startOffset);
+      res = resource->Seek(nsISeekableStream::NS_SEEK_SET, startOffset);
       NS_ENSURE_SUCCESS(res,res);
       mPageOffset = startOffset;
       if (NS_FAILED(ResetDecode())) {
         return NS_ERROR_FAILURE;
       }
       break;
     }
 
     SEEK_LOG(PR_LOG_DEBUG, ("Time at offset %lld is %lld", guess, granuleTime));
     if (granuleTime < seekTarget && granuleTime > seekLowerBound) {
       // We're within the fuzzy region in which we want to terminate the search.
-      res = stream->Seek(nsISeekableStream::NS_SEEK_SET, pageOffset);
+      res = resource->Seek(nsISeekableStream::NS_SEEK_SET, pageOffset);
       NS_ENSURE_SUCCESS(res,res);
       mPageOffset = pageOffset;
       if (NS_FAILED(ResetDecode())) {
         return NS_ERROR_FAILURE;
       }
       SEEK_LOG(PR_LOG_DEBUG, ("Terminating seek at offset=%lld", mPageOffset));
       break;
     }
@@ -1402,24 +1405,24 @@ nsresult nsOggReader::GetBuffered(nsTime
   // a deadlock. Accessing mInfo doesn't require a lock - it doesn't change
   // after metadata is read and GetBuffered isn't called before metadata is
   // read.
   if (!mInfo.mHasVideo && !mInfo.mHasAudio) {
     // No need to search through the file if there are no audio or video tracks
     return NS_OK;
   }
 
-  nsMediaStream* stream = mDecoder->GetStream();
-  nsTArray<nsByteRange> ranges;
-  nsresult res = stream->GetCachedRanges(ranges);
+  MediaResource* resource = mDecoder->GetResource();
+  nsTArray<MediaByteRange> ranges;
+  nsresult res = resource->GetCachedRanges(ranges);
   NS_ENSURE_SUCCESS(res, res);
 
   // Traverse across the buffered byte ranges, determining the time ranges
-  // they contain. nsMediaStream::GetNextCachedData(offset) returns -1 when
-  // offset is after the end of the media stream, or there's no more cached
+  // they contain. MediaResource::GetNextCachedData(offset) returns -1 when
+  // offset is after the end of the media resource, or there's no more cached
   // data after the offset. This loop will run until we've checked every
   // buffered range in the media, in increasing order of offset.
   nsAutoOggSyncState sync;
   for (PRUint32 index = 0; index < ranges.Length(); index++) {
     // Ensure the offsets are after the header pages.
     PRInt64 startOffset = ranges[index].mStart;
     PRInt64 endOffset = ranges[index].mEnd;
 
@@ -1431,17 +1434,17 @@ nsresult nsOggReader::GetBuffered(nsTime
 
     // Find the start time of the range. Read pages until we find one with a
     // granulepos which we can convert into a timestamp to use as the time of
     // the start of the buffered range.
     ogg_sync_reset(&sync.mState);
     while (startTime == -1) {
       ogg_page page;
       PRInt32 discard;
-      PageSyncResult res = PageSync(stream,
+      PageSyncResult res = PageSync(resource,
                                     &sync.mState,
                                     true,
                                     startOffset,
                                     endOffset,
                                     &page,
                                     discard);
       if (res == PAGE_SYNC_ERROR) {
         return NS_ERROR_FAILURE;
--- a/content/media/ogg/nsOggReader.h
+++ b/content/media/ogg/nsOggReader.h
@@ -188,17 +188,17 @@ private:
                          PRUint32 aFuzz);
 
   // Returns true if the serial number is for a stream we encountered
   // while reading metadata. Call on the main thread only.
   bool IsKnownStream(PRUint32 aSerial);
 
   // Fills aRanges with SeekRanges denoting the sections of the media which
   // have been downloaded and are stored in the media cache. The reader
-  // monitor must must be held with exactly one lock count. The nsMediaStream
+  // monitor must must be held with exactly one lock count. The MediaResource
   // must be pinned while calling this.
   nsresult GetSeekRanges(nsTArray<SeekRange>& aRanges);
 
   // Returns the range in which you should perform a seek bisection if
   // you wish to seek to aTarget usecs, given the known (buffered) byte ranges
   // in aRanges. If aExact is true, we only return an exact copy of a
   // range in which aTarget lies, or a null range if aTarget isn't contained
   // in any of the (buffered) ranges. Otherwise, when aExact is false,
@@ -213,18 +213,18 @@ private:
 private:
 
   // Decodes a packet of Vorbis data, and inserts its samples into the 
   // audio queue.
   nsresult DecodeVorbis(ogg_packet* aPacket);
 
   // Decodes a packet of Theora data, and inserts its frame into the
   // video queue. May return NS_ERROR_OUT_OF_MEMORY. Caller must have obtained
-  // the reader's monitor. aTimeThreshold is the current playback position
-  // in media time in microseconds. Frames with an end time before this will
+  // the reader's monitor. aTimeThreshold is the current playback position
+  // in media time in microseconds. Frames with an end time before this will
   // not be enqueued.
   nsresult DecodeTheora(ogg_packet* aPacket, PRInt64 aTimeThreshold);
 
   // Read a page of data from the Ogg file. Returns the offset of the start
   // of the page, or -1 if the page read failed.
   PRInt64 ReadOggPage(ogg_page* aPage);
 
   // Reads and decodes header packets for aState, until either header decode
--- a/content/media/raw/nsRawReader.cpp
+++ b/content/media/raw/nsRawReader.cpp
@@ -66,21 +66,21 @@ nsresult nsRawReader::ResetDecode()
   return nsBuiltinDecoderReader::ResetDecode();
 }
 
 nsresult nsRawReader::ReadMetadata(nsVideoInfo* aInfo)
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(),
                "Should be on decode thread.");
 
-  nsMediaStream* stream = mDecoder->GetStream();
-  NS_ASSERTION(stream, "Decoder has no media stream");
+  MediaResource* resource = mDecoder->GetResource();
+  NS_ASSERTION(resource, "Decoder has no media resource");
 
-  if (!ReadFromStream(stream, reinterpret_cast<PRUint8*>(&mMetadata),
-                      sizeof(mMetadata)))
+  if (!ReadFromResource(resource, reinterpret_cast<PRUint8*>(&mMetadata),
+                        sizeof(mMetadata)))
     return NS_ERROR_FAILURE;
 
   // Validate the header
   if (!(mMetadata.headerPacketID == 0 /* Packet ID of 0 for the header*/ &&
         mMetadata.codecID == RAW_ID /* "YUV" */ &&
         mMetadata.majorVersion == 0 &&
         mMetadata.minorVersion == 1))
     return NS_ERROR_FAILURE;
@@ -123,17 +123,17 @@ nsresult nsRawReader::ReadMetadata(nsVid
       mMetadata.lumaChannelBpp != 8 ||
       mMetadata.colorspace != 1 /* 4:2:0 */)
     return NS_ERROR_FAILURE;
 
   mFrameSize = mMetadata.frameWidth * mMetadata.frameHeight *
     (mMetadata.lumaChannelBpp + mMetadata.chromaChannelBpp) / 8.0 +
     sizeof(nsRawPacketHeader);
 
-  PRInt64 length = stream->GetLength();
+  PRInt64 length = resource->GetLength();
   if (length != -1) {
     mozilla::ReentrantMonitorAutoEnter autoMonitor(mDecoder->GetReentrantMonitor());
     mDecoder->GetStateMachine()->SetDuration(USECS_PER_S *
                                            (length - sizeof(nsRawVideoHeader)) /
                                            (mFrameSize * mFrameRate));
   }
 
   *aInfo = mInfo;
@@ -145,24 +145,24 @@ nsresult nsRawReader::ReadMetadata(nsVid
 {
   NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(),
                "Should be on state machine thread or decode thread.");
   return false;
 }
 
 // Helper method that either reads until it gets aLength bytes 
 // or returns false
-bool nsRawReader::ReadFromStream(nsMediaStream *aStream, PRUint8* aBuf,
+bool nsRawReader::ReadFromResource(MediaResource *aResource, PRUint8* aBuf,
                                    PRUint32 aLength)
 {
   while (aLength > 0) {
     PRUint32 bytesRead = 0;
     nsresult rv;
 
-    rv = aStream->Read(reinterpret_cast<char*>(aBuf), aLength, &bytesRead);
+    rv = aResource->Read(reinterpret_cast<char*>(aBuf), aLength, &bytesRead);
     NS_ENSURE_SUCCESS(rv, false);
 
     if (bytesRead == 0) {
       return false;
     }
 
     aLength -= bytesRead;
     aBuf += bytesRead;
@@ -184,31 +184,31 @@ bool nsRawReader::DecodeVideoFrame(bool 
 
   if (!mFrameSize)
     return false; // Metadata read failed.  We should refuse to play.
 
   PRInt64 currentFrameTime = USECS_PER_S * mCurrentFrame / mFrameRate;
   PRUint32 length = mFrameSize - sizeof(nsRawPacketHeader);
 
   nsAutoArrayPtr<PRUint8> buffer(new PRUint8[length]);
-  nsMediaStream* stream = mDecoder->GetStream();
-  NS_ASSERTION(stream, "Decoder has no media stream");
+  MediaResource* resource = mDecoder->GetResource();
+  NS_ASSERTION(resource, "Decoder has no media resource");
 
   // We're always decoding one frame when called
   while(true) {
     nsRawPacketHeader header;
 
     // Read in a packet header and validate
-    if (!(ReadFromStream(stream, reinterpret_cast<PRUint8*>(&header),
-                         sizeof(header))) ||
+    if (!(ReadFromResource(resource, reinterpret_cast<PRUint8*>(&header),
+                           sizeof(header))) ||
         !(header.packetID == 0xFF && header.codecID == RAW_ID /* "YUV" */)) {
       return false;
     }
 
-    if (!ReadFromStream(stream, buffer, length)) {
+    if (!ReadFromResource(resource, buffer, length)) {
       return false;
     }
 
     parsed++;
 
     if (currentFrameTime >= aTimeThreshold)
       break;
 
@@ -254,31 +254,31 @@ bool nsRawReader::DecodeVideoFrame(bool 
   return true;
 }
 
 nsresult nsRawReader::Seek(PRInt64 aTime, PRInt64 aStartTime, PRInt64 aEndTime, PRInt64 aCurrentTime)
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(),
                "Should be on decode thread.");
 
-  nsMediaStream *stream = mDecoder->GetStream();
-  NS_ASSERTION(stream, "Decoder has no media stream");
+  MediaResource *resource = mDecoder->GetResource();
+  NS_ASSERTION(resource, "Decoder has no media resource");
 
   PRUint32 frame = mCurrentFrame;
   if (aTime >= UINT_MAX)
     return NS_ERROR_FAILURE;
   mCurrentFrame = aTime * mFrameRate / USECS_PER_S;
 
   PRUint32 offset;
   if (!MulOverflow32(mCurrentFrame, mFrameSize, offset))
     return NS_ERROR_FAILURE;
 
   offset += sizeof(nsRawVideoHeader);
 
-  nsresult rv = stream->Seek(nsISeekableStream::NS_SEEK_SET, offset);
+  nsresult rv = resource->Seek(nsISeekableStream::NS_SEEK_SET, offset);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mVideoQueue.Erase();
 
   while(mVideoQueue.GetSize() == 0) {
     bool keyframeSkip = false;
     if (!DecodeVideoFrame(keyframeSkip, 0)) {
       mCurrentFrame = frame;
--- a/content/media/raw/nsRawReader.h
+++ b/content/media/raw/nsRawReader.h
@@ -66,18 +66,17 @@ public:
     return true;
   }
 
   virtual nsresult ReadMetadata(nsVideoInfo* aInfo);
   virtual nsresult Seek(PRInt64 aTime, PRInt64 aStartTime, PRInt64 aEndTime, PRInt64 aCurrentTime);
   virtual nsresult GetBuffered(nsTimeRanges* aBuffered, PRInt64 aStartTime);
 
 private:
-  bool ReadFromStream(nsMediaStream *aStream, PRUint8 *aBuf,
-                        PRUint32 aLength);
+  bool ReadFromResource(MediaResource *aResource, PRUint8 *aBuf, PRUint32 aLength);
 
   nsRawVideoHeader mMetadata;
   PRUint32 mCurrentFrame;
   double mFrameRate;
   PRUint32 mFrameSize;
   nsIntRect mPicture;
 };
 
--- a/content/media/wave/nsWaveReader.cpp
+++ b/content/media/wave/nsWaveReader.cpp
@@ -33,17 +33,17 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 #include "nsError.h"
 #include "nsBuiltinDecoderStateMachine.h"
 #include "nsBuiltinDecoder.h"
-#include "nsMediaStream.h"
+#include "MediaResource.h"
 #include "nsWaveReader.h"
 #include "nsTimeRanges.h"
 #include "VideoUtils.h"
 
 #include "mozilla/StdInt.h"
 
 using namespace mozilla;
 
@@ -258,53 +258,53 @@ nsresult nsWaveReader::Seek(PRInt64 aTar
   }
   double d = BytesToTime(GetDataLength());
   NS_ASSERTION(d < INT64_MAX / USECS_PER_S, "Duration overflow"); 
   PRInt64 duration = static_cast<PRInt64>(d * USECS_PER_S);
   double seekTime = NS_MIN(aTarget, duration) / static_cast<double>(USECS_PER_S);
   PRInt64 position = RoundDownToFrame(static_cast<PRInt64>(TimeToBytes(seekTime)));
   NS_ASSERTION(INT64_MAX - mWavePCMOffset > position, "Integer overflow during wave seek");
   position += mWavePCMOffset;
-  return mDecoder->GetStream()->Seek(nsISeekableStream::NS_SEEK_SET, position);
+  return mDecoder->GetResource()->Seek(nsISeekableStream::NS_SEEK_SET, position);
 }
 
 static double RoundToUsecs(double aSeconds) {
   return floor(aSeconds * USECS_PER_S) / USECS_PER_S;
 }
 
 nsresult nsWaveReader::GetBuffered(nsTimeRanges* aBuffered, PRInt64 aStartTime)
 {
-  PRInt64 startOffset = mDecoder->GetStream()->GetNextCachedData(mWavePCMOffset);
+  PRInt64 startOffset = mDecoder->GetResource()->GetNextCachedData(mWavePCMOffset);
   while (startOffset >= 0) {
-    PRInt64 endOffset = mDecoder->GetStream()->GetCachedDataEnd(startOffset);
+    PRInt64 endOffset = mDecoder->GetResource()->GetCachedDataEnd(startOffset);
     // Bytes [startOffset..endOffset] are cached.
     NS_ASSERTION(startOffset >= mWavePCMOffset, "Integer underflow in GetBuffered");
     NS_ASSERTION(endOffset >= mWavePCMOffset, "Integer underflow in GetBuffered");
 
     // We need to round the buffered ranges' times to microseconds so that they
     // have the same precision as the currentTime and duration attribute on 
     // the media element.
     aBuffered->Add(RoundToUsecs(BytesToTime(startOffset - mWavePCMOffset)),
                    RoundToUsecs(BytesToTime(endOffset - mWavePCMOffset)));
-    startOffset = mDecoder->GetStream()->GetNextCachedData(endOffset);
+    startOffset = mDecoder->GetResource()->GetNextCachedData(endOffset);
   }
   return NS_OK;
 }
 
 bool
 nsWaveReader::ReadAll(char* aBuf, PRInt64 aSize, PRInt64* aBytesRead)
 {
   PRUint32 got = 0;
   if (aBytesRead) {
     *aBytesRead = 0;
   }
   do {
     PRUint32 read = 0;
-    if (NS_FAILED(mDecoder->GetStream()->Read(aBuf + got, PRUint32(aSize - got), &read))) {
-      NS_WARNING("Stream read failed");
+    if (NS_FAILED(mDecoder->GetResource()->Read(aBuf + got, PRUint32(aSize - got), &read))) {
+      NS_WARNING("Resource read failed");
       return false;
     }
     if (read == 0) {
       return false;
     }
     mDecoder->NotifyBytesConsumed(read);
     got += read;
     if (aBytesRead) {
@@ -315,26 +315,26 @@ nsWaveReader::ReadAll(char* aBuf, PRInt6
 }
 
 bool
 nsWaveReader::LoadRIFFChunk()
 {
   char riffHeader[RIFF_INITIAL_SIZE];
   const char* p = riffHeader;
 
-  NS_ABORT_IF_FALSE(mDecoder->GetStream()->Tell() == 0,
-                    "LoadRIFFChunk called when stream in invalid state");
+  NS_ABORT_IF_FALSE(mDecoder->GetResource()->Tell() == 0,
+                    "LoadRIFFChunk called when resource in invalid state");
 
   if (!ReadAll(riffHeader, sizeof(riffHeader))) {
     return false;
   }
 
   PR_STATIC_ASSERT(sizeof(PRUint32) * 2 <= RIFF_INITIAL_SIZE);
   if (ReadUint32BE(&p) != RIFF_CHUNK_MAGIC) {
-    NS_WARNING("Stream data not in RIFF format");
+    NS_WARNING("resource data not in RIFF format");
     return false;
   }
 
   // Skip over RIFF size field.
   p += 4;
 
   if (ReadUint32BE(&p) != WAVE_CHUNK_MAGIC) {
     NS_WARNING("Expected WAVE chunk");
@@ -387,18 +387,18 @@ nsWaveReader::ScanForwardUntil(PRUint32 
 bool
 nsWaveReader::LoadFormatChunk()
 {
   PRUint32 fmtSize, rate, channels, frameSize, sampleFormat;
   char waveFormat[WAVE_FORMAT_CHUNK_SIZE];
   const char* p = waveFormat;
 
   // RIFF chunks are always word (two byte) aligned.
-  NS_ABORT_IF_FALSE(mDecoder->GetStream()->Tell() % 2 == 0,
-                    "LoadFormatChunk called with unaligned stream");
+  NS_ABORT_IF_FALSE(mDecoder->GetResource()->Tell() % 2 == 0,
+                    "LoadFormatChunk called with unaligned resource");
 
   // The "format" chunk may not directly follow the "riff" chunk, so skip
   // over any intermediate chunks.
   if (!ScanForwardUntil(FRMT_CHUNK_MAGIC, &fmtSize)) {
     return false;
   }
 
   if (!ReadAll(waveFormat, sizeof(waveFormat))) {
@@ -452,18 +452,18 @@ nsWaveReader::LoadFormatChunk()
       nsAutoArrayPtr<char> chunkExtension(new char[extra]);
       if (!ReadAll(chunkExtension.get(), extra)) {
         return false;
       }
     }
   }
 
   // RIFF chunks are always word (two byte) aligned.
-  NS_ABORT_IF_FALSE(mDecoder->GetStream()->Tell() % 2 == 0,
-                    "LoadFormatChunk left stream unaligned");
+  NS_ABORT_IF_FALSE(mDecoder->GetResource()->Tell() % 2 == 0,
+                    "LoadFormatChunk left resource unaligned");
 
   // Make sure metadata is fairly sane.  The rate check is fairly arbitrary,
   // but the channels check is intentionally limited to mono or stereo
   // because that's what the audio backend currently supports.
   if (rate < 100 || rate > 96000 ||
       channels < 1 || channels > MAX_CHANNELS ||
       (frameSize != 1 && frameSize != 2 && frameSize != 4) ||
       (sampleFormat != 8 && sampleFormat != 16)) {
@@ -482,27 +482,27 @@ nsWaveReader::LoadFormatChunk()
   }
   return true;
 }
 
 bool
 nsWaveReader::FindDataOffset()
 {
   // RIFF chunks are always word (two byte) aligned.
-  NS_ABORT_IF_FALSE(mDecoder->GetStream()->Tell() % 2 == 0,
-                    "FindDataOffset called with unaligned stream");
+  NS_ABORT_IF_FALSE(mDecoder->GetResource()->Tell() % 2 == 0,
+                    "FindDataOffset called with unaligned resource");
 
   // The "data" chunk may not directly follow the "format" chunk, so skip
   // over any intermediate chunks.
   PRUint32 length;
   if (!ScanForwardUntil(DATA_CHUNK_MAGIC, &length)) {
     return false;
   }
 
-  PRInt64 offset = mDecoder->GetStream()->Tell();
+  PRInt64 offset = mDecoder->GetResource()->Tell();
   if (offset <= 0 || offset > PR_UINT32_MAX) {
     NS_WARNING("PCM data offset out of range");
     return false;
   }
 
   ReentrantMonitorAutoEnter monitor(mDecoder->GetReentrantMonitor());
   mWaveLength = length;
   mWavePCMOffset = PRUint32(offset);
@@ -532,21 +532,21 @@ nsWaveReader::RoundDownToFrame(PRInt64 a
 
 PRInt64
 nsWaveReader::GetDataLength()
 {
   PRInt64 length = mWaveLength;
   // If the decoder has a valid content length, and it's shorter than the
   // expected length of the PCM data, calculate the playback duration from
   // the content length rather than the expected PCM data length.
-  PRInt64 streamLength = mDecoder->GetStream()->GetLength();
+  PRInt64 streamLength = mDecoder->GetResource()->GetLength();
   if (streamLength >= 0) {
     PRInt64 dataLength = NS_MAX<PRInt64>(0, streamLength - mWavePCMOffset);
     length = NS_MIN(dataLength, length);
   }
   return length;
 }
 
 PRInt64
 nsWaveReader::GetPosition()
 {
-  return mDecoder->GetStream()->Tell();
+  return mDecoder->GetResource()->Tell();
 }
--- a/content/media/webm/nsWebMReader.cpp
+++ b/content/media/webm/nsWebMReader.cpp
@@ -34,17 +34,17 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 #include "nsError.h"
 #include "nsBuiltinDecoderStateMachine.h"
 #include "nsBuiltinDecoder.h"
-#include "nsMediaStream.h"
+#include "MediaResource.h"
 #include "nsWebMReader.h"
 #include "VideoUtils.h"
 #include "nsTimeRanges.h"
 #include "mozilla/Preferences.h"
 
 using namespace mozilla;
 using namespace mozilla::layers;
 
@@ -74,80 +74,84 @@ static const int SEEK_DECODE_MARGIN = 25
 
 template <>
 class nsAutoRefTraits<NesteggPacketHolder> : public nsPointerRefTraits<NesteggPacketHolder>
 {
 public:
   static void Release(NesteggPacketHolder* aHolder) { delete aHolder; }
 };
 
-// Functions for reading and seeking using nsMediaStream required for
+// Functions for reading and seeking using MediaResource required for
 // nestegg_io. The 'user data' passed to these functions is the
-// decoder from which the media stream is obtained.
+// decoder from which the media resource is obtained.
 static int webm_read(void *aBuffer, size_t aLength, void *aUserData)
 {
   NS_ASSERTION(aUserData, "aUserData must point to a valid nsBuiltinDecoder");
   nsBuiltinDecoder* decoder = reinterpret_cast<nsBuiltinDecoder*>(aUserData);
-  nsMediaStream* stream = decoder->GetStream();
-  NS_ASSERTION(stream, "Decoder has no media stream");
+  MediaResource* resource = decoder->GetResource();
+  NS_ASSERTION(resource, "Decoder has no media resource");
 
   nsresult rv = NS_OK;
   bool eof = false;
 
   char *p = static_cast<char *>(aBuffer);
   while (NS_SUCCEEDED(rv) && aLength > 0) {
     PRUint32 bytes = 0;
-    rv = stream->Read(p, aLength, &bytes);
+    rv = resource->Read(p, aLength, &bytes);
     if (bytes == 0) {
       eof = true;
       break;
     }
     decoder->NotifyBytesConsumed(bytes);
     aLength -= bytes;
     p += bytes;
   }
 
   return NS_FAILED(rv) ? -1 : eof ? 0 : 1;
 }
 
 static int webm_seek(int64_t aOffset, int aWhence, void *aUserData)
 {
   NS_ASSERTION(aUserData, "aUserData must point to a valid nsBuiltinDecoder");
   nsBuiltinDecoder* decoder = reinterpret_cast<nsBuiltinDecoder*>(aUserData);
-  nsMediaStream* stream = decoder->GetStream();
-  NS_ASSERTION(stream, "Decoder has no media stream");
-  nsresult rv = stream->Seek(aWhence, aOffset);
+  MediaResource* resource = decoder->GetResource();
+  NS_ASSERTION(resource, "Decoder has no media resource");
+  nsresult rv = resource->Seek(aWhence, aOffset);
   return NS_SUCCEEDED(rv) ? 0 : -1;
 }
 
 static int64_t webm_tell(void *aUserData)
 {
   NS_ASSERTION(aUserData, "aUserData must point to a valid nsBuiltinDecoder");
   nsBuiltinDecoder* decoder = reinterpret_cast<nsBuiltinDecoder*>(aUserData);
-  nsMediaStream* stream = decoder->GetStream();
-  NS_ASSERTION(stream, "Decoder has no media stream");
-  return stream->Tell();
+  MediaResource* resource = decoder->GetResource();
+  NS_ASSERTION(resource, "Decoder has no media resource");
+  return resource->Tell();
 }
 
 nsWebMReader::nsWebMReader(nsBuiltinDecoder* aDecoder)
   : nsBuiltinDecoderReader(aDecoder),
   mContext(nsnull),
   mPacketCount(0),
   mChannels(0),
   mVideoTrack(0),
   mAudioTrack(0),
   mAudioStartUsec(-1),
   mAudioFrames(0),
+  mForceStereoMode(0),
   mHasVideo(false),
   mHasAudio(false),
-  mForceStereoMode(0)
+  mStereoModeForced(false)
 {
   MOZ_COUNT_CTOR(nsWebMReader);
 
-  Preferences::GetInt("media.webm.force_stereo_mode", &mForceStereoMode);
+  mStereoModeForced =
+    NS_SUCCEEDED(Preferences::GetInt(
+          "media.webm.force_stereo_mode",
+          &mForceStereoMode));
 }
 
 nsWebMReader::~nsWebMReader()
 {
   Cleanup();
 
   mVideoPackets.Reset();
   mAudioPackets.Reset();
@@ -304,31 +308,34 @@ nsresult nsWebMReader::ReadMetadata(nsVi
       case NESTEGG_VIDEO_STEREO_TOP_BOTTOM:
         mInfo.mStereoMode = STEREO_MODE_TOP_BOTTOM;
         break;
       case NESTEGG_VIDEO_STEREO_RIGHT_LEFT:
         mInfo.mStereoMode = STEREO_MODE_RIGHT_LEFT;
         break;
       }
 
-      switch (mForceStereoMode) {
-      case 1:
-        mInfo.mStereoMode = STEREO_MODE_LEFT_RIGHT;
-        break;
-      case 2:
-        mInfo.mStereoMode = STEREO_MODE_RIGHT_LEFT;
-        break;
-      case 3:
-        mInfo.mStereoMode = STEREO_MODE_TOP_BOTTOM;
-        break;
-      case 4:
-        mInfo.mStereoMode = STEREO_MODE_BOTTOM_TOP;
-        break;
-      default:
-        mInfo.mStereoMode = STEREO_MODE_MONO;
+      // Switch only when stereo mode is explicitly set.
+      if (mStereoModeForced) {
+        switch (mForceStereoMode) {
+        case 1:
+          mInfo.mStereoMode = STEREO_MODE_LEFT_RIGHT;
+          break;
+        case 2:
+          mInfo.mStereoMode = STEREO_MODE_RIGHT_LEFT;
+          break;
+        case 3:
+          mInfo.mStereoMode = STEREO_MODE_TOP_BOTTOM;
+          break;
+        case 4:
+          mInfo.mStereoMode = STEREO_MODE_BOTTOM_TOP;
+          break;
+        default:
+          mInfo.mStereoMode = STEREO_MODE_MONO;
+        }
       }
     }
     else if (!mHasAudio && type == NESTEGG_TRACK_AUDIO) {
       nestegg_audio_params params;
       r = nestegg_track_audio_params(mContext, track, &params);
       if (r == -1) {
         Cleanup();
         return NS_ERROR_FAILURE;
@@ -553,17 +560,17 @@ nsReturnRef<NesteggPacketHolder> nsWebMR
     // Keep reading packets until we find a packet
     // for the track we want.
     do {
       nestegg_packet* packet;
       int r = nestegg_read_packet(mContext, &packet);
       if (r <= 0) {
         return nsReturnRef<NesteggPacketHolder>();
       }
-      PRInt64 offset = mDecoder->GetStream()->Tell();
+      PRInt64 offset = mDecoder->GetResource()->Tell();
       holder.own(new NesteggPacketHolder(packet, offset));
 
       unsigned int track = 0;
       r = nestegg_packet_track(packet, &track);
       if (r == -1) {
         return nsReturnRef<NesteggPacketHolder>();
       }
 
@@ -628,17 +635,17 @@ bool nsWebMReader::DecodeVideoFrame(bool
   uint64_t tstamp = 0;
   r = nestegg_packet_tstamp(packet, &tstamp);
   if (r == -1) {
     return false;
   }
 
   // The end time of this frame is the start time of the next frame.  Fetch
   // the timestamp of the next packet for this track.  If we've reached the
-  // end of the stream, use the file's duration as the end time of this
+  // end of the resource, use the file's duration as the end time of this
   // video frame.
   uint64_t next_tstamp = 0;
   {
     nsAutoRef<NesteggPacketHolder> next_holder(NextPacket(VIDEO));
     if (next_holder) {
       r = nestegg_packet_tstamp(next_holder->mPacket, &next_tstamp);
       if (r == -1) {
         return false;
@@ -763,33 +770,33 @@ nsresult nsWebMReader::Seek(PRInt64 aTar
   if (r != 0) {
     return NS_ERROR_FAILURE;
   }
   return DecodeToTarget(aTarget);
 }
 
 nsresult nsWebMReader::GetBuffered(nsTimeRanges* aBuffered, PRInt64 aStartTime)
 {
-  nsMediaStream* stream = mDecoder->GetStream();
+  MediaResource* resource = mDecoder->GetResource();
 
   uint64_t timecodeScale;
   if (!mContext || nestegg_tstamp_scale(mContext, &timecodeScale) == -1) {
     return NS_OK;
   }
 
   // Special case completely cached files.  This also handles local files.
-  if (stream->IsDataCachedToEndOfStream(0)) {
+  if (resource->IsDataCachedToEndOfResource(0)) {
     uint64_t duration = 0;
     if (nestegg_duration(mContext, &duration) == 0) {
       aBuffered->Add(0, duration / NS_PER_S);
     }
   } else {
-    nsMediaStream* stream = mDecoder->GetStream();
-    nsTArray<nsByteRange> ranges;
-    nsresult res = stream->GetCachedRanges(ranges);
+    MediaResource* resource = mDecoder->GetResource();
+    nsTArray<MediaByteRange> ranges;
+    nsresult res = resource->GetCachedRanges(ranges);
     NS_ENSURE_SUCCESS(res, res);
 
     PRInt64 startTimeOffsetNS = aStartTime * NS_PER_USEC;
     for (PRUint32 index = 0; index < ranges.Length(); index++) {
       mBufferedState->CalculateBufferedForRange(aBuffered,
                                                 ranges[index].mStart,
                                                 ranges[index].mEnd,
                                                 timecodeScale,
--- a/content/media/webm/nsWebMReader.h
+++ b/content/media/webm/nsWebMReader.h
@@ -196,17 +196,17 @@ private:
   void Cleanup();
 
 private:
   // libnestegg context for webm container. Access on state machine thread
   // or decoder thread only.
   nestegg* mContext;
 
   // VP8 decoder state
-  vpx_codec_ctx_t  mVP8;
+  vpx_codec_ctx_t mVP8;
 
   // Vorbis decoder state
   vorbis_info mVorbisInfo;
   vorbis_comment mVorbisComment;
   vorbis_dsp_state mVorbisDsp;
   vorbis_block mVorbisBlock;
   PRUint32 mPacketCount;
   PRUint32 mChannels;
@@ -232,18 +232,22 @@ private:
 
   // Size of the frame initially present in the stream. The picture region
   // is defined as a ratio relative to this.
   nsIntSize mInitialFrame;
 
   // Picture region, as relative to the initial frame size.
   nsIntRect mPicture;
 
+  // Value of the "media.webm.force_stereo_mode" pref, which we need off the
+  // main thread.
+  PRInt32 mForceStereoMode;
+
   // Booleans to indicate if we have audio and/or video data
   bool mHasVideo;
   bool mHasAudio;
 
-  // Value of the "media.webm.force_stereo_mode" pref, which we need off the
-  // main thread.
-  PRInt32 mForceStereoMode;
+  // Boolean which is set to true when the "media.webm.force_stereo_mode"
+  // pref is explicitly set.
+  bool mStereoModeForced;
 };
 
 #endif
--- a/content/svg/content/src/nsSVGAnimationElement.cpp
+++ b/content/svg/content/src/nsSVGAnimationElement.cpp
@@ -371,30 +371,32 @@ nsSVGAnimationElement::ParseAttribute(PR
   }
 
   return nsSVGAnimationElementBase::ParseAttribute(aNamespaceID, aAttribute,
                                                    aValue, aResult);
 }
 
 nsresult
 nsSVGAnimationElement::AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
-                                    const nsAString* aValue, bool aNotify)
+                                    const nsAttrValue* aValue, bool aNotify)
 {
   nsresult rv =
     nsSVGAnimationElementBase::AfterSetAttr(aNamespaceID, aName, aValue,
                                             aNotify);
 
   if (aNamespaceID != kNameSpaceID_XLink || aName != nsGkAtoms::href)
     return rv;
 
   if (!aValue) {
     mHrefTarget.Unlink();
     AnimationTargetChanged();
   } else if (IsInDoc()) {
-    UpdateHrefTarget(this, *aValue);
+    NS_ABORT_IF_FALSE(aValue->Type() == nsAttrValue::eString,
+                      "Expected href attribute to be string type");
+    UpdateHrefTarget(this, aValue->GetStringValue());
   } // else: we're not yet in a document -- we'll update the target on
     // next BindToTree call.
 
   return rv;
 }
 
 nsresult
 nsSVGAnimationElement::UnsetAttr(PRInt32 aNamespaceID,
--- a/content/svg/content/src/nsSVGAnimationElement.h
+++ b/content/svg/content/src/nsSVGAnimationElement.h
@@ -79,17 +79,17 @@ public:
   virtual bool IsNodeOfType(PRUint32 aFlags) const;
 
   // nsGenericElement specializations
   virtual bool ParseAttribute(PRInt32 aNamespaceID,
                                 nsIAtom* aAttribute,
                                 const nsAString& aValue,
                                 nsAttrValue& aResult);
   virtual nsresult AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
-                                const nsAString* aValue, bool aNotify);
+                                const nsAttrValue* aValue, bool aNotify);
 
   // nsISMILAnimationElement interface
   virtual const Element& AsElement() const;
   virtual Element& AsElement();
   virtual bool PassesConditionalProcessingTests();
   virtual const nsAttrValue* GetAnimAttr(nsIAtom* aName) const;
   virtual bool GetAnimAttr(nsIAtom* aAttName, nsAString& aResult) const;
   virtual bool HasAnimAttr(nsIAtom* aAttName) const;
--- a/content/svg/content/src/nsSVGElement.cpp
+++ b/content/svg/content/src/nsSVGElement.cpp
@@ -255,29 +255,32 @@ nsSVGElement::BindToTree(nsIDocument* aD
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
 nsresult
 nsSVGElement::AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
-                           const nsAString* aValue, bool aNotify)
-{  
+                           const nsAttrValue* aValue, bool aNotify)
+{
   // If this is an svg presentation attribute we need to map it into
   // the content stylerule.
   // XXX For some reason incremental mapping doesn't work, so for now
   // just delete the style rule and lazily reconstruct it in
   // GetContentStyleRule()
   if (aNamespaceID == kNameSpaceID_None && IsAttributeMapped(aName)) {
     mContentStyleRule = nsnull;
   }
 
   if (IsEventName(aName) && aValue) {
-    nsresult rv = AddScriptEventListener(GetEventNameForAttr(aName), *aValue);
+    NS_ABORT_IF_FALSE(aValue->Type() == nsAttrValue::eString,
+      "Expected string value for script body");
+    nsresult rv = AddScriptEventListener(GetEventNameForAttr(aName),
+                                         aValue->GetStringValue());
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return nsSVGElementBase::AfterSetAttr(aNamespaceID, aName, aValue, aNotify);
 }
 
 bool
 nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
--- a/content/svg/content/src/nsSVGElement.h
+++ b/content/svg/content/src/nsSVGElement.h
@@ -245,17 +245,17 @@ public:
     return nsnull;
   }
   virtual nsIAtom* GetTransformListAttrName() const {
     return nsnull;
   }
 
 protected:
   virtual nsresult AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
-                                const nsAString* aValue, bool aNotify);
+                                const nsAttrValue* aValue, bool aNotify);
   virtual bool ParseAttribute(PRInt32 aNamespaceID, nsIAtom* aAttribute,
                                 const nsAString& aValue, nsAttrValue& aResult);
   static nsresult ReportAttributeParseFailure(nsIDocument* aDocument,
                                               nsIAtom* aAttribute,
                                               const nsAString& aValue);
 
   // Hooks for subclasses
   virtual bool IsEventName(nsIAtom* aName);
--- a/content/svg/content/src/nsSVGFilters.cpp
+++ b/content/svg/content/src/nsSVGFilters.cpp
@@ -5556,17 +5556,17 @@ nsSVGFEImageElement::IsAttributeMapped(c
   };
   
   return FindAttributeDependence(name, map) ||
     nsSVGFEImageElementBase::IsAttributeMapped(name);
 }
 
 nsresult
 nsSVGFEImageElement::AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
-                                  const nsAString* aValue, bool aNotify)
+                                  const nsAttrValue* aValue, bool aNotify)
 {
   if (aNamespaceID == kNameSpaceID_XLink && aName == nsGkAtoms::href) {
 
     // If there isn't a frame we still need to load the image in case
     // the frame is created later e.g. by attaching to a document.
     // If there is a frame then it should deal with loading as the image
     // url may be animated.
     if (!GetPrimaryFrame()) {
--- a/content/svg/content/src/nsSVGFilters.h
+++ b/content/svg/content/src/nsSVGFilters.h
@@ -283,17 +283,17 @@ public:
   NS_FORWARD_NSIDOMELEMENT(nsSVGFEImageElementBase::)
 
   // nsIContent
   NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const;
 
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
 
   virtual nsresult AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
-                                const nsAString* aValue, bool aNotify);
+                                const nsAttrValue* aValue, bool aNotify);
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent,
                               bool aCompileEventHandlers);
   virtual nsEventStates IntrinsicState() const;
 
   // imgIDecoderObserver
   NS_IMETHOD OnStopDecode(imgIRequest *aRequest, nsresult status,
                           const PRUnichar *statusArg);
--- a/content/svg/content/src/nsSVGImageElement.cpp
+++ b/content/svg/content/src/nsSVGImageElement.cpp
@@ -165,17 +165,17 @@ nsSVGImageElement::LoadSVGImage(bool aFo
   return LoadImage(href, aForce, aNotify);
 }
 
 //----------------------------------------------------------------------
 // nsIContent methods:
 
 nsresult
 nsSVGImageElement::AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
-                                const nsAString* aValue, bool aNotify)
+                                const nsAttrValue* aValue, bool aNotify)
 {
   if (aNamespaceID == kNameSpaceID_XLink && aName == nsGkAtoms::href) {
 
     // If there isn't a frame we still need to load the image in case
     // the frame is created later e.g. by attaching to a document.
     // If there is a frame then it should deal with loading as the image
     // url may be animated
     if (!GetPrimaryFrame()) {
--- a/content/svg/content/src/nsSVGImageElement.h
+++ b/content/svg/content/src/nsSVGImageElement.h
@@ -73,17 +73,17 @@ public:
 
   // xxx I wish we could use virtual inheritance
   NS_FORWARD_NSIDOMNODE(nsSVGImageElementBase::)
   NS_FORWARD_NSIDOMELEMENT(nsSVGImageElementBase::)
   NS_FORWARD_NSIDOMSVGELEMENT(nsSVGImageElementBase::)
 
   // nsIContent interface
   virtual nsresult AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
-                                const nsAString* aValue, bool aNotify);
+                                const nsAttrValue* aValue, bool aNotify);
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent,
                               bool aCompileEventHandlers);
 
   virtual nsEventStates IntrinsicState() const;
 
   NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* name) const;
 
--- a/content/svg/content/src/nsSVGScriptElement.cpp
+++ b/content/svg/content/src/nsSVGScriptElement.cpp
@@ -90,17 +90,17 @@ public:
   // nsScriptElement
   virtual bool HasScriptContent();
 
   // nsIContent specializations:
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent,
                               bool aCompileEventHandlers);
   virtual nsresult AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
-                                const nsAString* aValue, bool aNotify);
+                                const nsAttrValue* aValue, bool aNotify);
 
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
 
   virtual nsXPCClassInfo* GetClassInfo();
 protected:
   virtual StringAttributesInfo GetStringInfo();
 
   enum { HREF };
@@ -275,16 +275,16 @@ nsSVGScriptElement::BindToTree(nsIDocume
     MaybeProcessScript();
   }
 
   return NS_OK;
 }
 
 nsresult
 nsSVGScriptElement::AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
-                                 const nsAString* aValue, bool aNotify)
+                                 const nsAttrValue* aValue, bool aNotify)
 {
   if (aNamespaceID == kNameSpaceID_XLink && aName == nsGkAtoms::href) {
     MaybeProcessScript();
   }
   return nsSVGScriptElementBase::AfterSetAttr(aNamespaceID, aName,
                                               aValue, aNotify);
 }
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -121,16 +121,17 @@
 #include "nsDOMAttributeMap.h"
 #include "nsGkAtoms.h"
 #include "nsXULContentUtils.h"
 #include "nsNodeUtils.h"
 #include "nsFrameLoader.h"
 #include "prlog.h"
 #include "rdf.h"
 #include "nsIControllers.h"
+#include "nsAttrValueOrString.h"
 
 // The XUL doc interface
 #include "nsIDOMXULDocument.h"
 
 #include "nsReadableUtils.h"
 #include "nsIFrame.h"
 #include "nsNodeInfoManager.h"
 #include "nsXBLBinding.h"
@@ -1050,17 +1051,17 @@ nsXULElement::UnregisterAccessKey(const 
                     UnregisterAccessKey(content, aOldValue.First());
             }
         }
     }
 }
 
 nsresult
 nsXULElement::BeforeSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
-                            const nsAString* aValue, bool aNotify)
+                            const nsAttrValueOrString* aValue, bool aNotify)
 {
     if (aNamespaceID == kNameSpaceID_None && aName == nsGkAtoms::accesskey &&
         IsInDoc()) {
         const nsAttrValue* attrVal = FindLocalOrProtoAttr(aNamespaceID, aName);
         if (attrVal) {
             nsAutoString oldValue;
             attrVal->ToString(oldValue);
             UnregisterAccessKey(oldValue);
@@ -1079,75 +1080,88 @@ nsXULElement::BeforeSetAttr(PRInt32 aNam
           RemoveBroadcaster(oldValue);
         }
     }
     else if (aNamespaceID == kNameSpaceID_None &&
              aValue &&
              mNodeInfo->Equals(nsGkAtoms::window) &&
              aName == nsGkAtoms::chromemargin) {
       nsAttrValue attrValue;
-      nsIntMargin margins;
       // Make sure the margin format is valid first
-      if (!attrValue.ParseIntMarginValue(*aValue)) {
-          return NS_ERROR_INVALID_ARG;
+      if (!attrValue.ParseIntMarginValue(aValue->String())) {
+        return NS_ERROR_INVALID_ARG;
       }
     }
 
     return nsStyledElement::BeforeSetAttr(aNamespaceID, aName,
                                           aValue, aNotify);
 }
 
 nsresult
 nsXULElement::AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
-                           const nsAString* aValue, bool aNotify)
+                           const nsAttrValue* aValue, bool aNotify)
 {
     if (aNamespaceID == kNameSpaceID_None) {
         // XXX UnsetAttr handles more attributes than we do. See bug 233642.
 
         // Add popup and event listeners. We can't call AddListenerFor since
         // the attribute isn't set yet.
         MaybeAddPopupListener(aName);
         if (nsContentUtils::IsEventAttributeName(aName, EventNameType_XUL) && aValue) {
             // If mPrototype->mScriptTypeID != GetScriptTypeID(), it means
             // we are resolving an overlay with a different default script
             // language.  We can't defer compilation of those handlers as
             // we will have lost the script language (storing it on each
             // nsXULPrototypeAttribute is expensive!)
             bool defer = mPrototype == nsnull ||
                            mPrototype->mScriptTypeID == GetScriptTypeID();
-            AddScriptEventListener(aName, *aValue, defer);
+            if (aValue->Type() == nsAttrValue::eString) {
+                AddScriptEventListener(aName, aValue->GetStringValue(), defer);
+            } else {
+                nsAutoString body;
+                aValue->ToString(body);
+                AddScriptEventListener(aName, body, defer);
+            }
         }
 
         // Hide chrome if needed
         if (mNodeInfo->Equals(nsGkAtoms::window) && aValue) {
-          if (aName == nsGkAtoms::hidechrome) {
-              HideWindowChrome(aValue->EqualsLiteral("true"));
-          }
-          else if (aName == nsGkAtoms::chromemargin) {
-              SetChromeMargins(aValue);
-          }
+            if (aName == nsGkAtoms::hidechrome) {
+                HideWindowChrome(
+                  aValue->Equals(NS_LITERAL_STRING("true"), eCaseMatters));
+            }
+            else if (aName == nsGkAtoms::chromemargin) {
+                SetChromeMargins(aValue);
+            }
         }
 
         // title, (in)activetitlebarcolor and drawintitlebar are settable on
         // any root node (windows, dialogs, etc)
         nsIDocument *document = GetCurrentDoc();
         if (document && document->GetRootElement() == this) {
             if (aName == nsGkAtoms::title) {
                 document->NotifyPossibleTitleChange(false);
             }
             else if ((aName == nsGkAtoms::activetitlebarcolor ||
-                      aName == nsGkAtoms::inactivetitlebarcolor)) {
+                      aName == nsGkAtoms::inactivetitlebarcolor) && aValue) {
                 nscolor color = NS_RGBA(0, 0, 0, 0);
-                nsAttrValue attrValue;
-                attrValue.ParseColor(*aValue);
-                attrValue.GetColorValue(color);
+                if (aValue->Type() == nsAttrValue::eColor) {
+                    aValue->GetColorValue(color);
+                } else {
+                    nsAutoString tmp;
+                    nsAttrValue attrValue;
+                    aValue->ToString(tmp);
+                    attrValue.ParseColor(tmp);
+                    attrValue.GetColorValue(color);
+                }
                 SetTitlebarColor(color, aName == nsGkAtoms::activetitlebarcolor);
             }
             else if (aName == nsGkAtoms::drawintitlebar) {
-                SetDrawsInTitlebar(aValue && aValue->EqualsLiteral("true"));
+                SetDrawsInTitlebar(aValue &&
+                    aValue->Equals(NS_LITERAL_STRING("true"), eCaseMatters));
             }
             else if (aName == nsGkAtoms::localedir) {
                 // if the localedir changed on the root element, reset the document direction
                 nsCOMPtr<nsIXULDocument> xuldoc = do_QueryInterface(document);
                 if (xuldoc) {
                     xuldoc->ResetDocumentDirection();
                 }
             }
@@ -2430,33 +2444,37 @@ public:
     }
 
 private:
     nsCOMPtr<nsIWidget> mWidget;
     nsIntMargin mMargin;
 };
 
 void
-nsXULElement::SetChromeMargins(const nsAString* aValue)
+nsXULElement::SetChromeMargins(const nsAttrValue* aValue)
 {
     if (!aValue)
         return;
 
     nsIWidget* mainWidget = GetWindowWidget();
     if (!mainWidget)
         return;
 
     // top, right, bottom, left - see nsAttrValue
-    nsAttrValue attrValue;
     nsIntMargin margins;
-
-    nsAutoString data;
-    data.Assign(*aValue);
-    if (attrValue.ParseIntMarginValue(data) &&
-        attrValue.GetIntMarginValue(margins)) {
+    bool gotMargins = false;
+
+    if (aValue->Type() == nsAttrValue::eIntMarginValue) {
+        gotMargins = aValue->GetIntMarginValue(margins);
+    } else {
+        nsAutoString tmp;
+        aValue->ToString(tmp);
+        gotMargins = nsContentUtils::ParseIntMarginValue(tmp, margins);
+    }
+    if (gotMargins) {
         nsContentUtils::AddScriptRunner(new MarginSetter(mainWidget, margins));
     }
 }
 
 void
 nsXULElement::ResetChromeMargins()
 {
     nsIWidget* mainWidget = GetWindowWidget();
--- a/content/xul/content/src/nsXULElement.h
+++ b/content/xul/content/src/nsXULElement.h
@@ -621,19 +621,20 @@ protected:
     nsresult MakeHeavyweight();
 
     const nsAttrValue* FindLocalOrProtoAttr(PRInt32 aNameSpaceID,
                                             nsIAtom *aName) const {
         return nsXULElement::GetAttrInfo(aNameSpaceID, aName).mValue;
     }
 
     virtual nsresult BeforeSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
-                                   const nsAString* aValue, bool aNotify);
+                                   const nsAttrValueOrString* aValue,
+                                   bool aNotify);
     virtual nsresult AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
-                                  const nsAString* aValue, bool aNotify);
+                                  const nsAttrValue* aValue, bool aNotify);
 
     virtual void UpdateEditableState(bool aNotify);
 
     virtual bool ParseAttribute(PRInt32 aNamespaceID,
                                   nsIAtom* aAttribute,
                                   const nsAString& aValue,
                                   nsAttrValue& aResult);
 
@@ -651,17 +652,17 @@ protected:
     void AddListenerFor(const nsAttrName& aName,
                         bool aCompileEventHandlers);
     void MaybeAddPopupListener(nsIAtom* aLocalName);
 
     nsIWidget* GetWindowWidget();
 
     // attribute setters for widget
     nsresult HideWindowChrome(bool aShouldHide);
-    void SetChromeMargins(const nsAString* aValue);
+    void SetChromeMargins(const nsAttrValue* aValue);
     void ResetChromeMargins();
     void SetTitlebarColor(nscolor aColor, bool aActive);
 
     void SetDrawsInTitlebar(bool aState);
 
     const nsAttrName* InternalGetExistingAttrNameFromQName(const nsAString& aStr) const;
 
     void RemoveBroadcaster(const nsAString & broadcasterId);
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -1044,16 +1044,20 @@ nsGlobalWindow::~nsGlobalWindow()
     // If our outer window's inner window is this window, null out the
     // outer window's reference to this window that's being deleted.
     nsGlobalWindow *outer = GetOuterWindowInternal();
     if (outer && outer->mInnerWindow == this) {
       outer->mInnerWindow = nsnull;
     }
   }
 
+  if (IsInnerWindow() && mDocument) {
+    mDoc->MarkAsOrphan();
+  }
+
   mDocument = nsnull;           // Forces Release
   mDoc = nsnull;
 
   NS_ASSERTION(!mArguments, "mArguments wasn't cleaned up properly!");
 
   CleanUp(true);
 
 #ifdef DEBUG
@@ -1306,16 +1310,18 @@ nsGlobalWindow::FreeInnerObjects(bool aC
     mNavigator = nsnull;
   }
 
   if (mDocument) {
     NS_ASSERTION(mDoc, "Why is mDoc null?");
 
     // Remember the document's principal.
     mDocumentPrincipal = mDoc->NodePrincipal();
+
+    mDoc->MarkAsOrphan();
   }
 
 #ifdef DEBUG
   if (mDocument)
     nsCycleCollector_DEBUG_shouldBeFreed(nsCOMPtr<nsISupports>(do_QueryInterface(mDocument)));
 #endif
 
   // Remove our reference to the document and the document principal.
@@ -2257,23 +2263,24 @@ nsGlobalWindow::SetNewDocument(nsIDocume
   // alive etc.
 
   if ((!reUseInnerWindow || aDocument != oldDoc) && !aState) {
     nsCOMPtr<nsIHTMLDocument> html_doc(do_QueryInterface(mDocument));
     nsWindowSH::InstallGlobalScopePolluter(cx, newInnerWindow->mJSObject,
                                            html_doc);
   }
 
-  if (aDocument) {
-    aDocument->SetScriptGlobalObject(newInnerWindow);
-  }
+  aDocument->SetScriptGlobalObject(newInnerWindow);
 
   if (!aState) {
     if (reUseInnerWindow) {
       if (newInnerWindow->mDoc != aDocument) {
+        newInnerWindow->mDoc->MarkAsOrphan();
+        aDocument->MarkAsNonOrphan();
+
         newInnerWindow->mDocument = do_QueryInterface(aDocument);
         newInnerWindow->mDoc = aDocument;
 
         // We're reusing the inner window for a new document. In this
         // case we don't clear the inner window's scope, but we must
         // make sure the cached document property gets updated.
 
         // XXXmarkh - tell other languages about this?
@@ -2382,16 +2389,19 @@ nsGlobalWindow::InnerSetNewDocument(nsID
     nsIURI *uri = aDocument->GetDocumentURI();
     nsCAutoString spec;
     if (uri)
       uri->GetSpec(spec);
     PR_LogPrint("DOMWINDOW %p SetNewDocument %s", this, spec.get());
   }
 #endif
 
+  MOZ_ASSERT(aDocument->IsOrphan(), "New document must be orphan!");
+  aDocument->MarkAsNonOrphan();
+
   mDocument = do_QueryInterface(aDocument);
   mDoc = aDocument;
   mLocalStorage = nsnull;
   mSessionStorage = nsnull;
 
 #ifdef DEBUG
   mLastOpenedURI = aDocument->GetDocumentURI();
 #endif
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -991,21 +991,16 @@ nsJSContext::JSOptionChangedCallback(con
   else
     newDefaultJSOptions &= ~JSOPTION_PCCOUNT;
 
   if (useMethodJITAlways)
     newDefaultJSOptions |= JSOPTION_METHODJIT_ALWAYS;
   else
     newDefaultJSOptions &= ~JSOPTION_METHODJIT_ALWAYS;
 
-  if (useHardening)
-    newDefaultJSOptions &= ~JSOPTION_SOFTEN;
-  else
-    newDefaultJSOptions |= JSOPTION_SOFTEN;
-
   if (useTypeInference)
     newDefaultJSOptions |= JSOPTION_TYPE_INFERENCE;
   else
     newDefaultJSOptions &= ~JSOPTION_TYPE_INFERENCE;
 
 #ifdef DEBUG
   // In debug builds, warnings are enabled in chrome context if
   // javascript.options.strict.debug is true
@@ -1030,16 +1025,19 @@ nsJSContext::JSOptionChangedCallback(con
   else
     newDefaultJSOptions &= ~JSOPTION_RELIMIT;
 
   ::JS_SetOptions(context->mContext, newDefaultJSOptions & JSRUNOPTION_MASK);
 
   // Save the new defaults for the next page load (InitContext).
   context->mDefaultJSOptions = newDefaultJSOptions;
 
+  JSRuntime *rt = JS_GetRuntime(context->mContext);
+  JS_SetJitHardening(rt, useHardening);
+
 #ifdef JS_GC_ZEAL
   PRInt32 zeal = Preferences::GetInt(js_zeal_option_str, -1);
   PRInt32 frequency = Preferences::GetInt(js_zeal_frequency_str, JS_DEFAULT_ZEAL_FREQ);
   bool compartment = Preferences::GetBool(js_zeal_compartment_str, false);
   if (zeal >= 0)
     ::JS_SetGCZeal(context->mContext, (PRUint8)zeal, frequency, compartment);
 #endif
 
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -335,61 +335,25 @@ NS_IMETHODIMP nsPluginDocReframeEvent::R
 }
 
 static bool UnloadPluginsASAP()
 {
   nsresult rv;
   nsCOMPtr<nsIPrefBranch> pref(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
   if (NS_SUCCEEDED(rv)) {
     bool unloadPluginsASAP = false;
-    rv = pref->GetBoolPref("plugins.unloadASAP", &unloadPluginsASAP);
+    rv = pref->GetBoolPref("dom.ipc.plugins.unloadASAP", &unloadPluginsASAP);
     if (NS_SUCCEEDED(rv)) {
       return unloadPluginsASAP;
     }
   }
 
   return false;
 }
 
-// helper struct for asynchronous handling of plugin unloading
-class nsPluginUnloadEvent : public nsRunnable {
-public:
-  nsPluginUnloadEvent(PRLibrary* aLibrary)
-    : mLibrary(aLibrary)
-  {}
- 
-  NS_DECL_NSIRUNNABLE
- 
-  PRLibrary* mLibrary;
-};
-
-NS_IMETHODIMP nsPluginUnloadEvent::Run()
-{
-  if (mLibrary) {
-    // put our unload call in a safety wrapper
-    NS_TRY_SAFE_CALL_VOID(PR_UnloadLibrary(mLibrary), nsnull);
-  } else {
-    NS_WARNING("missing library from nsPluginUnloadEvent");
-  }
-  return NS_OK;
-}
-
-// unload plugin asynchronously if possible, otherwise just unload now
-nsresult nsPluginHost::PostPluginUnloadEvent(PRLibrary* aLibrary)
-{
-  nsCOMPtr<nsIRunnable> ev = new nsPluginUnloadEvent(aLibrary);
-  if (ev && NS_SUCCEEDED(NS_DispatchToCurrentThread(ev)))
-    return NS_OK;
-
-  // failure case
-  NS_TRY_SAFE_CALL_VOID(PR_UnloadLibrary(aLibrary), nsnull);
-
-  return NS_ERROR_FAILURE;
-}
-
 nsPluginHost::nsPluginHost()
   // No need to initialize members to nsnull, false etc because this class
   // has a zeroing operator new.
 {
   // check to see if pref is set at startup to let plugins take over in
   // full page mode for certain image mime types that we handle internally
   mPrefService = do_GetService(NS_PREFSERVICE_CONTRACTID);
   if (mPrefService) {
@@ -422,18 +386,18 @@ nsPluginHost::nsPluginHost()
   PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_ALWAYS,("General Plugin Logging Active! (nsPluginHost::ctor)\n"));
   PR_LOG(nsPluginLogging::gNPPLog, PLUGIN_LOG_ALWAYS,("NPP Logging Active!\n"));
 
   PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("nsPluginHost::ctor\n"));
   PR_LogFlush();
 #endif
 
 #ifdef MAC_CARBON_PLUGINS
-  mVisiblePluginTimer = do_CreateInstance("@mozilla.org/timer;1");
-  mHiddenPluginTimer = do_CreateInstance("@mozilla.org/timer;1");
+  mVisiblePluginTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
+  mHiddenPluginTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
 #endif
 }
 
 nsPluginHost::~nsPluginHost()
 {
   PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("nsPluginHost::dtor\n"));
 
   Destroy();
@@ -527,17 +491,17 @@ nsresult nsPluginHost::ReloadPlugins(boo
       if (p == mPlugins)
         mPlugins = next;
       else
         prev->mNext = next;
 
       p->mNext = nsnull;
 
       // attempt to unload plugins whenever they are removed from the list
-      p->TryUnloadPlugin();
+      p->TryUnloadPlugin(false);
 
       p = next;
       continue;
     }
 
     prev = p;
     p = next;
   }
@@ -870,17 +834,17 @@ nsresult nsPluginHost::Destroy()
   mIsDestroyed = true;
 
   // we should call nsIPluginInstance::Stop and nsIPluginInstance::SetWindow
   // for those plugins who want it
   DestroyRunningInstances(nsnull, nsnull);
 
   nsPluginTag *pluginTag;
   for (pluginTag = mPlugins; pluginTag; pluginTag = pluginTag->mNext) {
-    pluginTag->TryUnloadPlugin();
+    pluginTag->TryUnloadPlugin(true);
   }
 
   NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mPlugins, mNext);
   NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mCachedPlugins, mNext);
   NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
 
   // Lets remove any of the temporary files that we created.
   if (sPluginTempDir) {
@@ -908,18 +872,43 @@ void nsPluginHost::OnPluginInstanceDestr
   bool hasInstance = false;
   for (PRUint32 i = 0; i < mInstances.Length(); i++) {
     if (TagForPlugin(mInstances[i]->GetPlugin()) == aPluginTag) {
       hasInstance = true;
       break;
     }
   }
 
-  if (!hasInstance && UnloadPluginsASAP()) {
-    aPluginTag->TryUnloadPlugin();
+  // We have some options for unloading plugins if they have no instances.
+  //
+  // Unloading plugins immediately can be bad - some plugins retain state
+  // between instances even when there are none. This is largely limited to
+  // going from one page to another, so state is retained without an instance
+  // for only a very short period of time. In order to allow this to work
+  // we don't unload plugins immediately by default. This is supported
+  // via a hidden user pref though.
+  //
+  // Another reason not to unload immediately is that loading is expensive,
+  // and it is better to leave popular plugins loaded.
+  //
+  // Our default behavior is to try to unload a plugin three minutes after
+  // its last instance is destroyed. This seems like a reasonable compromise
+  // that allows us to reclaim memory while allowing short state retention
+  // and avoid perf hits for loading popular plugins.
+  if (!hasInstance) {
+    if (UnloadPluginsASAP()) {
+      aPluginTag->TryUnloadPlugin(false);
+    } else {
+      if (aPluginTag->mUnloadTimer) {
+        aPluginTag->mUnloadTimer->Cancel();
+      } else {
+        aPluginTag->mUnloadTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
+      }
+      aPluginTag->mUnloadTimer->InitWithCallback(this, 1000 * 60 * 3, nsITimer::TYPE_ONE_SHOT);
+    }
   }
 }
 
 nsresult
 nsPluginHost::GetPluginTempDir(nsIFile **aDir)
 {
   if (!sPluginTempDir) {
     nsCOMPtr<nsIFile> tmpDir;
@@ -1343,16 +1332,22 @@ nsPluginHost::TrySetUpPluginInstance(con
   // except in some cases not Java, see bug 140931
   // our COM pointer will free the peer
   rv = instance->Initialize(aOwner, mimetype);
   if (NS_FAILED(rv)) {
     aOwner->SetInstance(nsnull);
     return rv;
   }
 
+  // Cancel the plugin unload timer since we are creating
+  // an instance for it.
+  if (pluginTag->mUnloadTimer) {
+    pluginTag->mUnloadTimer->Cancel();
+  }
+
   mInstances.AppendElement(instance.get());
 
 #ifdef PLUGIN_LOGGING
   nsCAutoString urlSpec2;
   if (aURL)
     aURL->GetSpec(urlSpec2);
 
   PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_BASIC,
@@ -2189,17 +2184,17 @@ nsresult nsPluginHost::ScanPluginsDirect
         pluginTag->mNext = mCachedPlugins;
         mCachedPlugins = pluginTag;
       }
 
       // Plugin unloading is tag-based. If we created a new tag and loaded
       // the library in the process then we want to attempt to unload it here.
       // Only do this if the pref is set for aggressive unloading.
       if (UnloadPluginsASAP()) {
-        pluginTag->TryUnloadPlugin();
+        pluginTag->TryUnloadPlugin(false);
       }
     }
 
     // set the flag that we want to add this plugin to the list for now
     // and see if it remains after we check several reasons not to do so
     bool bAddIt = true;
     
     if (HaveSamePlugin(pluginTag)) {
@@ -2616,17 +2611,17 @@ nsPluginHost::WritePluginInfo()
         (tag->mVersion.get()),
         PLUGIN_REGISTRY_FIELD_DELIMITER,
         PLUGIN_REGISTRY_END_OF_LINE_MARKER);
 
       // lastModifiedTimeStamp|canUnload|tag->mFlags
       PR_fprintf(fd, "%lld%c%d%c%lu%c%c\n",
         tag->mLastModifiedTime,
         PLUGIN_REGISTRY_FIELD_DELIMITER,
-        tag->mCanUnloadLibrary,
+        false, // did store whether or not to unload in-process plugins
         PLUGIN_REGISTRY_FIELD_DELIMITER,
         tag->Flags(),
         PLUGIN_REGISTRY_FIELD_DELIMITER,
         PLUGIN_REGISTRY_END_OF_LINE_MARKER);
 
       //description, name & mtypecount are on separate line
       PR_fprintf(fd, "%s%c%c\n%s%c%c\n%d\n",
         (tag->mDescription.get()),
@@ -2880,17 +2875,16 @@ nsPluginHost::ReadPluginInfo()
     }
 
     // lastModifiedTimeStamp|canUnload|tag.mFlag
     if (reader.ParseLine(values, 3) != 3)
       return rv;
 
     // If this is an old plugin registry mark this plugin tag to be refreshed
     PRInt64 lastmod = (vdiff == 0) ? nsCRT::atoll(values[0]) : -1;
-    bool canunload = atoi(values[1]);
     PRUint32 tagflag = atoi(values[2]);
     if (!reader.NextLine())
       return rv;
 
     const char *description = reader.LinePtr();
     if (!reader.NextLine())
       return rv;
 
@@ -2940,17 +2934,17 @@ nsPluginHost::ReadPluginInfo()
     nsRefPtr<nsPluginTag> tag = new nsPluginTag(name,
       description,
       filename,
       fullpath,
       version,
       (const char* const*)mimetypes,
       (const char* const*)mimedescriptions,
       (const char* const*)extensions,
-      mimetypecount, lastmod, canunload, true);
+      mimetypecount, lastmod, true);
     if (heapalloced)
       delete [] heapalloced;
 
     if (!tag)
       continue;
 
     // Mark plugin as loaded from cache
     tag->Mark(tagflag | NS_PLUGIN_FLAG_FROMCACHE);
@@ -3865,16 +3859,28 @@ NS_IMETHODIMP nsPluginHost::Notify(nsITi
   } else if (timer == mHiddenPluginTimer) {
     nsTObserverArray<nsIPluginInstanceOwner*>::ForwardIterator iter(mHiddenTimerTargets);
     while (iter.HasMore()) {
       iter.GetNext()->SendIdleEvent();
     }
     return NS_OK;
   }
 #endif
+
+  nsRefPtr<nsPluginTag> pluginTag = mPlugins;
+  while (pluginTag) {
+    if (pluginTag->mUnloadTimer == timer) {
+      if (!IsRunningPlugin(pluginTag)) {
+        pluginTag->TryUnloadPlugin(false);
+      }
+      return NS_OK;
+    }
+    pluginTag = pluginTag->mNext;
+  }
+
   return NS_ERROR_FAILURE;
 }
 
 #ifdef XP_WIN
 // Re-enable any top level browser windows that were disabled by modal dialogs
 // displayed by the crashed plugin.
 static void
 CheckForDisabledWindows()
--- a/dom/plugins/base/nsPluginTags.cpp
+++ b/dom/plugins/base/nsPluginTags.cpp
@@ -75,38 +75,32 @@ inline char* new_str(const char* str)
 nsPluginTag::nsPluginTag(nsPluginTag* aPluginTag)
 : mPluginHost(nsnull),
 mName(aPluginTag->mName),
 mDescription(aPluginTag->mDescription),
 mMimeTypes(aPluginTag->mMimeTypes),
 mMimeDescriptions(aPluginTag->mMimeDescriptions),
 mExtensions(aPluginTag->mExtensions),
 mLibrary(nsnull),
-mCanUnloadLibrary(true),
 mIsJavaPlugin(aPluginTag->mIsJavaPlugin),
 mIsNPRuntimeEnabledJavaPlugin(aPluginTag->mIsNPRuntimeEnabledJavaPlugin),
 mIsFlashPlugin(aPluginTag->mIsFlashPlugin),
 mFileName(aPluginTag->mFileName),
 mFullPath(aPluginTag->mFullPath),
 mVersion(aPluginTag->mVersion),
 mLastModifiedTime(0),
 mFlags(NS_PLUGIN_FLAG_ENABLED)
 {
 }
 
 nsPluginTag::nsPluginTag(nsPluginInfo* aPluginInfo)
 : mPluginHost(nsnull),
 mName(aPluginInfo->fName),
 mDescription(aPluginInfo->fDescription),
 mLibrary(nsnull),
-#ifdef XP_MACOSX
-mCanUnloadLibrary(false),
-#else
-mCanUnloadLibrary(true),
-#endif
 mIsJavaPlugin(false),
 mIsNPRuntimeEnabledJavaPlugin(false),
 mIsFlashPlugin(false),
 mFileName(aPluginInfo->fFileName),
 mFullPath(aPluginInfo->fFullPath),
 mVersion(aPluginInfo->fVersion),
 mLastModifiedTime(0),
 mFlags(NS_PLUGIN_FLAG_ENABLED)
@@ -185,23 +179,21 @@ nsPluginTag::nsPluginTag(const char* aNa
                          const char* aFileName,
                          const char* aFullPath,
                          const char* aVersion,
                          const char* const* aMimeTypes,
                          const char* const* aMimeDescriptions,
                          const char* const* aExtensions,
                          PRInt32 aVariants,
                          PRInt64 aLastModifiedTime,
-                         bool aCanUnload,
                          bool aArgsAreUTF8)
 : mPluginHost(nsnull),
 mName(aName),
 mDescription(aDescription),
 mLibrary(nsnull),
-mCanUnloadLibrary(aCanUnload),
 mIsJavaPlugin(false),
 mIsNPRuntimeEnabledJavaPlugin(false),
 mFileName(aFileName),
 mFullPath(aFullPath),
 mVersion(aVersion),
 mLastModifiedTime(aLastModifiedTime),
 mFlags(0) // Caller will read in our flags from cache
 {
@@ -508,27 +500,21 @@ bool nsPluginTag::Equals(nsPluginTag *aP
     if (!mMimeTypes[i].Equals(aPluginTag->mMimeTypes[i])) {
       return false;
     }
   }
 
   return true;
 }
 
-void nsPluginTag::TryUnloadPlugin()
+void nsPluginTag::TryUnloadPlugin(bool inShutdown)
 {
+  // We never want to send NPP_Shutdown to an in-process plugin unless
+  // this process is shutting down.
+  if (mLibrary && !inShutdown) {
+    return;
+  }
+
   if (mEntryPoint) {
     mEntryPoint->Shutdown();
     mEntryPoint = nsnull;
   }
-  
-  // before we unload check if we are allowed to, see bug #61388
-  if (mLibrary && mCanUnloadLibrary) {
-    // unload the plugin asynchronously by posting a PLEvent
-    nsPluginHost::PostPluginUnloadEvent(mLibrary);
-  }
-  
-  // we should zero it anyway, it is going to be unloaded by
-  // CleanUnsedLibraries before we need to call the library
-  // again so the calling code should not be fooled and reload
-  // the library fresh
-  mLibrary = nsnull;
 }
--- a/dom/plugins/base/nsPluginTags.h
+++ b/dom/plugins/base/nsPluginTags.h
@@ -42,16 +42,17 @@
 #include "nscore.h"
 #include "prtypes.h"
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsCOMArray.h"
 #include "nsIPluginTag.h"
 #include "nsNPAPIPluginInstance.h"
 #include "nsISupportsArray.h"
+#include "nsITimer.h"
 
 class nsPluginHost;
 struct PRLibrary;
 struct nsPluginInfo;
 
 // Remember that flags are written out to pluginreg.dat, be careful
 // changing their meaning.
 #define NS_PLUGIN_FLAG_ENABLED      0x0001    // is this plugin enabled?
@@ -80,22 +81,21 @@ public:
               const char* aFileName,
               const char* aFullPath,
               const char* aVersion,
               const char* const* aMimeTypes,
               const char* const* aMimeDescriptions,
               const char* const* aExtensions,
               PRInt32 aVariants,
               PRInt64 aLastModifiedTime = 0,
-              bool aCanUnload = true,
               bool aArgsAreUTF8 = false);
   virtual ~nsPluginTag();
   
   void SetHost(nsPluginHost * aHost);
-  void TryUnloadPlugin();
+  void TryUnloadPlugin(bool inShutdown);
   void Mark(PRUint32 mask);
   void UnMark(PRUint32 mask);
   bool HasFlag(PRUint32 flag);
   PRUint32 Flags();
   bool Equals(nsPluginTag* aPluginTag);
   bool IsEnabled();
   void RegisterWithCategoryManager(bool aOverrideInternalTypes,
                                    nsRegisterType aType = ePluginRegister);
@@ -104,23 +104,23 @@ public:
   nsPluginHost *mPluginHost;
   nsCString     mName; // UTF-8
   nsC