Merge from mozilla-central.
authorDavid Anderson <danderson@mozilla.com>
Wed, 15 Feb 2012 13:45:39 -0800
changeset 105833 98a0b13b8a0cec8430394da7d323021ff8ee7800
parent 105829 132462b85b08292526d3c3d87ecffe552ac81727 (current diff)
parent 86892 3a90a3dedf6c3339ae8ecc74e07719a1ee38d9cc (diff)
child 105834 6ea8ff60d96ec4fd4126a9d26cd132d3c0b2a312
push id23447
push userdanderson@mozilla.com
push dateTue, 11 Sep 2012 17:34:27 +0000
treeherderautoland@fdfaef738a00 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone13.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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) {