Merge m-c --> cedar
authorChris Jones <jones.chris.g@gmail.com>
Tue, 31 Aug 2010 00:35:03 -0500
changeset 54053 5061e3023aaef211904bb2e2bdf4263629620f58
parent 54052 a9c735c870b8db3c03f9908462f10e070d17b9db (current diff)
parent 51637 0886ad6e6aaa2c33e7ad319d1b80aa0f3f35c9e8 (diff)
child 54054 4a86675171a2fba61ad02ca805878bc819d6af97
push idunknown
push userunknown
push dateunknown
milestone2.0b5pre
Merge m-c --> cedar
browser/base/content/browser.xul
browser/themes/gnomestripe/browser/browser.css
build/mobile/sutagent/android/res/drawable/ateamlogo.png.rej
build/mobile/sutagent/android/res/drawable/ic_stat_first.png.rej
build/mobile/sutagent/android/res/drawable/ic_stat_neterror.png.rej
build/mobile/sutagent/android/res/drawable/ic_stat_second.png.rej
build/mobile/sutagent/android/res/drawable/ic_stat_warning.png.rej
content/html/content/src/nsIConstraintValidation.h
dom/interfaces/base/nsIDOMWindowUtils.idl
dom/ipc/PBrowser.ipdl
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
dom/ipc/TabParent.cpp
dom/ipc/TabParent.h
gfx/layers/Makefile.in
layout/base/nsDisplayList.cpp
layout/base/nsDisplayList.h
layout/base/nsDocumentViewer.cpp
layout/base/nsPresShell.cpp
layout/generic/nsFrameFrame.cpp
layout/style/test/hover_helper.html
widget/src/windows/nsWindow.cpp
widget/src/xpwidgets/Makefile.in
widget/src/xpwidgets/nsBaseWidget.cpp
--- a/.hgignore
+++ b/.hgignore
@@ -4,17 +4,17 @@
 ~$
 \.py(c|o)$
 (?i)(^|/)TAGS$
 (^|/)ID$
 (^|/)\.DS_Store$
 
 # User files that may appear at the root
 ^\.mozconfig
-^mozconfig
+^mozconfig$
 ^configure$
 ^config\.cache$
 ^config\.log$
 
 # Empty marker file that's generated when we check out NSS
 ^security/manager/\.nss\.checkout$
 
 # Build directories
--- a/browser/app/Makefile.in
+++ b/browser/app/Makefile.in
@@ -54,18 +54,16 @@ PREF_JS_EXPORTS = $(srcdir)/profile/fire
 
 # hardcode en-US for the moment
 AB_CD = en-US
 
 DEFINES += -DAB_CD=$(AB_CD)
 
 APP_VERSION = $(shell cat $(srcdir)/../config/version.txt)
 DEFINES += -DAPP_VERSION="$(APP_VERSION)"
-APP_UA_NAME = $(shell echo $(MOZ_APP_DISPLAYNAME) | sed -e's/[^A-Za-z]//g')
-DEFINES += -DAPP_UA_NAME="$(APP_UA_NAME)"
 
 DIST_FILES = application.ini
 
 GRE_MILESTONE = $(shell $(PYTHON) $(topsrcdir)/config/printconfigsetting.py $(LIBXUL_DIST)/bin/platform.ini Build Milestone)
 GRE_BUILDID = $(shell $(PYTHON) $(topsrcdir)/config/printconfigsetting.py $(LIBXUL_DIST)/bin/platform.ini Build BuildID)
 
 DEFINES += -DGRE_MILESTONE=$(GRE_MILESTONE) -DGRE_BUILDID=$(GRE_BUILDID)
 
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -186,17 +186,16 @@ pref("lightweightThemes.update.enabled",
 
 pref("keyword.enabled", true);
 // Override the default keyword.URL. Empty value means
 // "use the search service's default engine"
 pref("keyword.URL", "");
 
 pref("general.useragent.locale", "@AB_CD@");
 pref("general.skins.selectedSkin", "classic/1.0");
-pref("general.useragent.extra.firefox", "@APP_UA_NAME@/@APP_VERSION@");
 
 pref("general.smoothScroll", false);
 #ifdef UNIX_BUT_NOT_MAC
 pref("general.autoScroll", false);
 #else
 pref("general.autoScroll", true);
 #endif
 
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -500,16 +500,20 @@ const gPopupBlockerObserver = {
       item.parentNode.removeChild(item);
       item = next;
     }
 
     var foundUsablePopupURI = false;
     var pageReport = gBrowser.pageReport;
     if (pageReport) {
       for (var i = 0; i < pageReport.length; ++i) {
+        // popupWindowURI will be null if the file picker popup is blocked.
+        // xxxdz this should make the option say "Show file picker" and do it (Bug 590306) 
+        if (!pageReport[i].popupWindowURI)
+          continue;
         var popupURIspec = pageReport[i].popupWindowURI.spec;
 
         // Sometimes the popup URI that we get back from the pageReport
         // isn't useful (for instance, netscape.com's popup URI ends up
         // being "http://www.netscape.com", which isn't really the URI of
         // the popup they're trying to show).  This isn't going to be
         // useful to the user, so we won't create a menu item for it.
         if (popupURIspec == "" || popupURIspec == "about:blank" ||
@@ -1342,20 +1346,16 @@ function delayedStartup(isLoadingBlank, 
     let sidebarBox = document.getElementById("sidebar-box");
     sidebar.setAttribute("src", sidebarBox.getAttribute("src"));
   }
 
   UpdateUrlbarSearchSplitterState();
 
   PlacesStarButton.init();
 
-  // called when we go into full screen, even if it is
-  // initiated by a web page script
-  window.addEventListener("fullscreen", onFullScreen, true);
-
   if (isLoadingBlank && gURLBar && isElementVisible(gURLBar))
     gURLBar.focus();
   else
     gBrowser.selectedBrowser.focus();
 
   gNavToolbox.customizeDone = BrowserToolboxCustomizeDone;
   gNavToolbox.customizeChange = BrowserToolboxCustomizeChange;
 
@@ -1509,16 +1509,22 @@ function delayedStartup(isLoadingBlank, 
 
   gBrowser.mPanelContainer.addEventListener("InstallBrowserTheme", LightWeightThemeWebInstaller, false, true);
   gBrowser.mPanelContainer.addEventListener("PreviewBrowserTheme", LightWeightThemeWebInstaller, false, true);
   gBrowser.mPanelContainer.addEventListener("ResetBrowserThemePreview", LightWeightThemeWebInstaller, false, true);
 
   if (Win7Features)
     Win7Features.onOpenWindow();
 
+  // called when we go into full screen, even if it is
+  // initiated by a web page script
+  window.addEventListener("fullscreen", onFullScreen, true);
+  if (window.fullScreen)
+    onFullScreen();
+
 #ifdef MOZ_SERVICES_SYNC
   // initialize the sync UI
   gSyncUI.init();
 #endif
 
   TabView.init();
 
   Services.obs.notifyObservers(window, "browser-delayed-startup-finished", "");
@@ -2630,19 +2636,18 @@ function getMeOutOfHere() {
   content.location = url;
 }
 
 function BrowserFullScreen()
 {
   window.fullScreen = !window.fullScreen;
 }
 
-function onFullScreen()
-{
-  FullScreen.toggle();
+function onFullScreen(event) {
+  FullScreen.toggle(event);
 }
 
 function getWebNavigation()
 {
   try {
     return gBrowser.webNavigation;
   } catch (e) {
     return null;
@@ -2783,16 +2788,27 @@ function FillInHTMLTooltip(tipElement)
   var SVGTitleText = null;
 #ifdef MOZ_SVG
   var lookingForSVGTitle = true;
 #else
   var lookingForSVGTitle = false;
 #endif // MOZ_SVG
   var direction = tipElement.ownerDocument.dir;
 
+  // If the element is invalid per HTML5 Forms specifications,
+  // show the constraint validation error message instead of @tooltip.
+  if (tipElement instanceof HTMLInputElement ||
+      tipElement instanceof HTMLTextAreaElement ||
+      tipElement instanceof HTMLSelectElement ||
+      tipElement instanceof HTMLButtonElement) {
+    // If the element is barred from constraint validation or valid,
+    // the validation message will be the empty string.
+    titleText = tipElement.validationMessage;
+  }
+
   while (!titleText && !XLinkTitleText && !SVGTitleText && tipElement) {
     if (tipElement.nodeType == Node.ELEMENT_NODE) {
       titleText = tipElement.getAttribute("title");
       if ((tipElement instanceof HTMLAnchorElement && tipElement.href) ||
           (tipElement instanceof HTMLAreaElement && tipElement.href) ||
           (tipElement instanceof HTMLLinkElement && tipElement.href)
 #ifdef MOZ_SVG
           || (tipElement instanceof SVGAElement && tipElement.hasAttributeNS(XLinkNS, "href"))
@@ -3615,27 +3631,31 @@ function updateEditUIVisibility()
     goSetCommandEnabled("cmd_paste", true);
     goSetCommandEnabled("cmd_selectAll", true);
     goSetCommandEnabled("cmd_delete", true);
     goSetCommandEnabled("cmd_switchTextDirection", true);
   }
 #endif
 }
 
-var FullScreen =
-{
+var FullScreen = {
   _XULNS: "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
-  toggle: function()
-  {
+  toggle: function (event) {
+    var enterFS = window.fullScreen;
+
+    // We get the fullscreen event _before_ the window transitions into or out of FS mode.
+    if (event && event.type == "fullscreen")
+      enterFS = !enterFS;
+
     // show/hide all menubars, toolbars, and statusbars (except the full screen toolbar)
-    this.showXULChrome("toolbar", window.fullScreen);
-    this.showXULChrome("statusbar", window.fullScreen);
-    document.getElementById("View:FullScreen").setAttribute("checked", !window.fullScreen);
-
-    if (!window.fullScreen) {
+    this.showXULChrome("toolbar", !enterFS);
+    this.showXULChrome("statusbar", !enterFS);
+    document.getElementById("View:FullScreen").setAttribute("checked", enterFS);
+
+    if (enterFS) {
       // Add a tiny toolbar to receive mouseover and dragenter events, and provide affordance.
       // This will help simulate the "collapse" metaphor while also requiring less code and
       // events than raw listening of mouse coords.
       let fullScrToggler = document.getElementById("fullscr-toggler");
       if (!fullScrToggler) {
         fullScrToggler = document.createElement("hbox");
         fullScrToggler.id = "fullscr-toggler";
         fullScrToggler.collapsed = true;
@@ -6988,18 +7008,23 @@ var gIdentityHandler = {
   /**
    * Return the eTLD+1 version of the current hostname
    */
   getEffectiveHost : function() {
     // Cache the eTLDService if this is our first time through
     if (!this._eTLDService)
       this._eTLDService = Cc["@mozilla.org/network/effective-tld-service;1"]
                          .getService(Ci.nsIEffectiveTLDService);
+    if (!this._IDNService)
+      this._IDNService = Cc["@mozilla.org/network/idn-service;1"]
+                         .getService(Ci.nsIIDNService);
     try {
-      return this._eTLDService.getBaseDomainFromHost(this._lastLocation.hostname);
+      let baseDomain =
+        this._eTLDService.getBaseDomainFromHost(this._lastLocation.hostname);
+      return this._IDNService.convertToDisplayIDN(baseDomain, {});
     } catch (e) {
       // If something goes wrong (e.g. hostname is an IP address) just fail back
       // to the full domain.
       return this._lastLocation.hostname;
     }
   },
 
   /**
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -480,17 +480,16 @@
                             label="&openFileCmd.label;"
                             command="Browser:OpenFile"/>
                 </menupopup>
                </menu>
           </hbox>
           <menuitem id="appmenu_privateBrowsing"
                     class="menuitem-iconic"
                     label="&privateBrowsingCmd.start.label;"
-                    accesskey="&privateBrowsingCmd.start.accesskey;"
                     startlabel="&privateBrowsingCmd.start.label;"
                     stoplabel="&privateBrowsingCmd.stop.label;"
                     command="Tools:PrivateBrowsing"/>
           <menuseparator/>
           <hbox class="split-menuitem">
             <menuitem id="appmenu-edit-menuitem"
                       label="&editMenu.label;"
                       disabled="true"/>
@@ -555,17 +554,16 @@
                         type="checkbox"
                         command="Tools:Inspect"/>
               <menuseparator/>
               <menuitem id="appmenu_pageSource"
                         label="&viewPageSourceCmd.label;"
                         command="View:PageSource"/>
               <menuseparator/>
               <menuitem label="&goOfflineCmd.label;"
-                        accesskey="&goOfflineCmd.accesskey;"
                         type="checkbox"
                         oncommand="BrowserOffline.toggleOfflineStatus();"/>
             </menupopup>
           </menu>
           <menuseparator/>
           <menuitem id="appmenu_fullScreen"
                     label="&fullScreenCmd.label;"
                     type="checkbox"
@@ -1208,17 +1206,17 @@
                   flex="1" contenttooltip="aHTMLTooltip"
                   tabcontainer="tabbrowser-tabs"
                   contentcontextmenu="contentAreaContextMenu"
                   autocompletepopup="PopupAutoComplete"
                   onclick="return contentAreaClick(event, false);"/>
     </vbox>
   </hbox>
 
-  <vbox id="browser-bottombox">
+  <vbox id="browser-bottombox" layer="true">
     <statusbar class="chromeclass-status" id="status-bar"
 #ifdef WINCE
                hidden="true"
 #endif
                >
       <statusbarpanel id="statusbar-display" label="" flex="1"/>
       <statusbarpanel class="statusbarpanel-progress" collapsed="true" id="statusbar-progresspanel">
         <progressmeter class="progressmeter-statusbar" id="statusbar-icon" mode="normal" value="0"/>
--- a/browser/base/content/tabview/drag.js
+++ b/browser/base/content/tabview/drag.js
@@ -104,17 +104,17 @@ Drag.prototype = {
   //   stationaryCorner   - which corner is stationary? by default, the top left.
   //                        "topleft", "bottomleft", "topright", "bottomright"
   //   assumeConstantSize - (boolean) whether the bounds' dimensions are sacred or not.
   //   keepProportional   - (boolean) if assumeConstantSize is false, whether we should resize
   //                        proportionally or not
   //   checkItemStatus    - (boolean) make sure this is a valid item which should be snapped
   snapBounds: function Drag_snapBounds(bounds, stationaryCorner, assumeConstantSize, keepProportional, checkItemStatus) {
     if (!stationaryCorner)
-      stationaryCorner || 'topleft';
+      stationaryCorner = 'topleft';
     var update = false; // need to update
     var updateX = false;
     var updateY = false;
     var newRect;
     var snappedTrenches = {};
 
     // OH SNAP!
 
--- a/browser/base/content/tabview/groupitems.js
+++ b/browser/base/content/tabview/groupitems.js
@@ -718,29 +718,16 @@ window.GroupItem.prototype = Utils.exten
     var self = this;
     var toRemove = this._children.concat();
     toRemove.forEach(function(child) {
       self.remove(child, {dontArrange: true});
     });
   },
 
   // ----------
-  // Function: setNewTabButtonBounds
-  // Used for positioning the "new tab" button in the "new tabs" groupItem.
-  setNewTabButtonBounds: function(box, immediately) {
-    if (!immediately)
-      this.$ntb.animate(box.css(), {
-        duration: 320,
-        easing: "tabviewBounce"
-      });
-    else
-      this.$ntb.css(box.css());
-  },
-
-  // ----------
   // Function: hideExpandControl
   // Hide the control which expands a stacked groupItem into a quick-look view.
   hideExpandControl: function() {
     this.$expander.hide();
   },
 
   // ----------
   // Function: showExpandControl
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -137,16 +137,17 @@ endif
                  browser_bug555767.js \
                  browser_bug556061.js \
                  browser_bug561623.js \
                  browser_bug562649.js \
                  browser_bug563588.js \
                  browser_bug577121.js \
                  browser_bug580956.js \
                  browser_bug581242.js \
+                 browser_bug581947.js \
                  browser_bug585830.js \
                  browser_contextSearchTabPosition.js \
                  browser_ctrlTab.js \
                  browser_discovery.js \
                  browser_drag.js \
                  browser_duplicateIDs.js \
                  browser_gestureSupport.js \
                  browser_getshortcutoruri.js \
--- a/browser/base/content/test/browser_bug420160.js
+++ b/browser/base/content/test/browser_bug420160.js
@@ -13,20 +13,31 @@ function test() {
   ok(gIdentityHandler, "gIdentityHandler should exist");
 
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", listener, true);
   listener.testFunction = testNormalDomain;  
   content.location = "http://test1.example.org/";
 }
 
+// Greek IDN for 'example.test'.
+var idnDomain = "\u03C0\u03B1\u03C1\u03AC\u03B4\u03B5\u03B9\u03B3\u03BC\u03B1.\u03B4\u03BF\u03BA\u03B9\u03BC\u03AE";
+
 function testNormalDomain() {
   is(gIdentityHandler._lastLocation.host, 'test1.example.org', "Identity handler is getting the full location");
   is(gIdentityHandler.getEffectiveHost(), 'example.org', "getEffectiveHost should return example.org for test1.example.org");
 
+  listener.testFunction = testIDNDomain;
+  content.location = "http://sub1." + idnDomain + "/";
+}
+
+function testIDNDomain() {
+  is(gIdentityHandler._lastLocation.host, "sub1." + idnDomain, "Identity handler is getting the full location");
+  is(gIdentityHandler.getEffectiveHost(), idnDomain, "getEffectiveHost should return the IDN base domain in UTF-8");
+
   listener.testFunction = testNormalDomainWithPort;
   content.location = "http://sub1.test1.example.org:8000/";
 }
 
 function testNormalDomainWithPort() {
   is(gIdentityHandler._lastLocation.host, 'sub1.test1.example.org:8000', "Identity handler is getting port information");
   is(gIdentityHandler.getEffectiveHost(), 'example.org', "getEffectiveHost should return example.org for sub1.test1.example.org:8000");
 
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_bug581947.js
@@ -0,0 +1,85 @@
+function check(aElementName, aBarred, aType) {
+  let doc = gBrowser.contentDocument;
+  let tooltip = document.getElementById("aHTMLTooltip");
+  let content = doc.getElementById('content');
+
+  let e = doc.createElement(aElementName);
+  content.appendChild(e);
+
+  if (aType) {
+    e.type = aType;
+  }
+
+  ok(!FillInHTMLTooltip(e),
+     "No tooltip should be shown when the element is valid");
+
+  e.setCustomValidity('foo');
+  if (aBarred) {
+    ok(!FillInHTMLTooltip(e),
+       "No tooltip should be shown when the element is barred from constraint validation");
+  } else {
+    ok(FillInHTMLTooltip(e),
+       "A tooltip should be shown when the element isn't valid");
+  }
+
+  content.removeChild(e);
+}
+
+function todo_check(aElementName, aBarred) {
+  let doc = gBrowser.contentDocument;
+  let tooltip = document.getElementById("aHTMLTooltip");
+  let content = doc.getElementById('content');
+
+  let e = doc.createElement(aElementName);
+  content.appendChild(e);
+
+  let cought = false;
+  try {
+    e.setCustomValidity('foo');
+  } catch (e) {
+    cought = true;
+  }
+
+  todo(!cought, "setCustomValidity should exist for " + aElementName);
+
+  content.removeChild(e);
+}
+
+function test () {
+  waitForExplicitFinish();
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function () {
+    gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
+
+    let testData = [
+    /* element name, barred, type */
+      [ 'input',    false, null],
+      [ 'textarea', false, null],
+      [ 'button',   true,  'button'],
+      [ 'button',   false, 'submit' ],
+      [ 'select',   false, null],
+      [ 'output',   true,  null],
+      [ 'fieldset', true,  null],
+    ];
+
+    for each (let data in testData) {
+      check(data[0], data[1], data[2]);
+    }
+
+    let todo_testData = [
+      [ 'keygen', 'false' ],
+      [ 'object', 'false' ],
+    ];
+
+    for each(let data in todo_testData) {
+      todo_check(data[0], data[1]);
+    }
+
+    gBrowser.removeCurrentTab();
+    finish();
+  }, true);
+
+  content.location = 
+    "data:text/html,<!DOCTYPE html><html><body><div id='content'></div></body></html>";
+}
+
--- a/browser/branding/unofficial/configure.sh
+++ b/browser/branding/unofficial/configure.sh
@@ -1,1 +1,1 @@
-MOZ_APP_DISPLAYNAME="MozillaDeveloperPreview"
+MOZ_APP_DISPLAYNAME=MozillaDeveloperPreview
--- a/browser/components/places/content/bookmarkProperties.js
+++ b/browser/components/places/content/bookmarkProperties.js
@@ -541,17 +541,17 @@ var BookmarkPropertiesPanel = {
    *        the ID of the textbox element whose contents we'll test
    *
    * @returns true if the textbox contains a valid URI string, false otherwise
    */
   _containsValidURI: function BPP__containsValidURI(aTextboxID) {
     try {
       var value = this._element(aTextboxID).value;
       if (value) {
-        var uri = PlacesUIUtils.createFixedURI(value);
+        PlacesUIUtils.createFixedURI(value);
         return true;
       }
     } catch (e) { }
     return false;
   },
 
   /**
    * [New Item Mode] Get the insertion point details for the new item, given
--- a/browser/components/places/content/organizer.css
+++ b/browser/components/places/content/organizer.css
@@ -1,7 +1,3 @@
-#contentTitle {
-  width: 0px;
-}
-
 #searchFilter {
   width: 23em;
 }
--- a/browser/components/places/content/places.js
+++ b/browser/components/places/content/places.js
@@ -386,18 +386,17 @@ var PlacesOrganizer = {
              createInstance(Ci.nsIFilePicker);
     fp.init(window, PlacesUIUtils.getString("SelectImport"),
             Ci.nsIFilePicker.modeOpen);
     fp.appendFilters(Ci.nsIFilePicker.filterHTML);
     if (fp.show() != Ci.nsIFilePicker.returnCancel) {
       if (fp.file) {
         var importer = Cc["@mozilla.org/browser/places/import-export-service;1"].
                        getService(Ci.nsIPlacesImportExportService);
-        var file = fp.file.QueryInterface(Ci.nsILocalFile);
-        importer.importHTMLFromFile(file, false);
+        importer.importHTMLFromFile(fp.file, false);
       }
     }
   },
 
   /**
    * Allows simple exporting of bookmarks.
    */
   exportBookmarks: function PO_exportBookmarks() {
--- a/browser/components/places/tests/Makefile.in
+++ b/browser/components/places/tests/Makefile.in
@@ -35,16 +35,17 @@
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK *****
 
 DEPTH		= ../../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
+relativesrcdir = browser/components/places/tests
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= test_browser_places
 
 XPCSHELL_TESTS = unit
 
 DIRS = browser chrome perf
--- a/browser/components/places/tests/unit/head_bookmarks.js
+++ b/browser/components/places/tests/unit/head_bookmarks.js
@@ -40,17 +40,17 @@
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 const Cr = Components.results;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/Services.jsm");
 
 // Import common head.
-let (commonFile = do_get_file("../../test_places/head_common.js", false)) {
+let (commonFile = do_get_file("../../../../../toolkit/components/places/tests/head_common.js", false)) {
   let uri = Services.io.newFileURI(commonFile);
   Services.scriptloader.loadSubScript(uri.spec, this);
 }
 
 // Put any other stuff relative to this test folder below.
 
 
 XPCOMUtils.defineLazyGetter(this, "PlacesUIUtils", function() {
--- a/browser/components/sessionstore/src/nsSessionStore.js
+++ b/browser/components/sessionstore/src/nsSessionStore.js
@@ -2073,29 +2073,43 @@ SessionStoreService.prototype = {
       browser.userTypedValue = activePageData ? activePageData.url || null : null;
       
       // keep the data around to prevent dataloss in case
       // a tab gets closed before it's been properly restored
       browser.__SS_data = tabData;
     }
     
     if (aTabs.length > 0) {
+      // Load hidden tabs last, by pushing them to the end of the list
+      let unhiddenTabs = aTabs.length;
+      for (let t = 0; t < unhiddenTabs; ) {
+        if (aTabs[t].hidden) {
+          aTabs = aTabs.concat(aTabs.splice(t, 1));
+          aTabData = aTabData.concat(aTabData.splice(t, 1));
+          if (aSelectTab > t)
+            --aSelectTab;
+          --unhiddenTabs;
+          continue;
+        }
+        ++t;
+      }
+
       // Determine if we can optimize & load visible tabs first
       let maxVisibleTabs = Math.ceil(tabbrowser.tabContainer.mTabstrip.scrollClientSize /
-                                     aTabs[aTabs.length - 1].clientWidth);
+                                     aTabs[unhiddenTabs - 1].clientWidth);
 
       // make sure we restore visible tabs first, if there are enough
-      if (maxVisibleTabs < aTabs.length && aSelectTab > 1) {
+      if (maxVisibleTabs < unhiddenTabs && aSelectTab > 1) {
         let firstVisibleTab = 0;
-        if (aTabs.length - maxVisibleTabs > aSelectTab) {
+        if (unhiddenTabs - maxVisibleTabs > aSelectTab) {
           // aSelectTab is leftmost since we scroll to it when possible
           firstVisibleTab = aSelectTab - 1;
         } else {
           // aSelectTab is rightmost or no more room to scroll right
-          firstVisibleTab = aTabs.length - maxVisibleTabs;
+          firstVisibleTab = unhiddenTabs - maxVisibleTabs;
         }
         aTabs = aTabs.splice(firstVisibleTab, maxVisibleTabs).concat(aTabs);
         aTabData = aTabData.splice(firstVisibleTab, maxVisibleTabs).concat(aTabData);
         aSelectTab -= firstVisibleTab;
       }
 
       // make sure to restore the selected tab first (if any)
       if (aSelectTab-- && aTabs[aSelectTab]) {
--- a/browser/components/sessionstore/test/browser/browser_480148.js
+++ b/browser/components/sessionstore/test/browser/browser_480148.js
@@ -47,20 +47,26 @@ function browserWindowsCount() {
 function test() {
   /** Test for Bug 484108 **/
   is(browserWindowsCount(), 1, "Only one browser window should be open initially");
 
   let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
   waitForExplicitFinish();
 
   // builds the tests state based on a few parameters
-  function buildTestState(num, selected) {
+  function buildTestState(num, selected, hidden) {
     let state = { windows: [ { "tabs": [], "selected": selected } ] };
-    while (num--)
+    while (num--) {
       state.windows[0].tabs.push({entries: [{url: "http://example.com/"}]});
+      let i = state.windows[0].tabs.length - 1;
+      if (hidden.length > 0 && i == hidden[0]) {
+        state.windows[0].tabs[i].hidden = true;
+        hidden.splice(0, 1);
+      }
+    }
     return state;
   }
 
   // builds an array of the indexs we expect to see in the order they get loaded
   function buildExpectedOrder(num, selected, shown) {
     // assume selected is 1-based index
     selected--;
     let expected = [selected];
@@ -75,27 +81,27 @@ function test() {
       if (expected.indexOf(i) == -1) {
         expected.push(i);
       }
     }
     return expected;
   }
 
   // the number of tests we're running
-  let numTests = 4;
+  let numTests = 6;
   let completedTests = 0;
 
   let tabMinWidth = parseInt(getComputedStyle(gBrowser.selectedTab, null).minWidth);
 
-  function runTest(testNum, totalTabs, selectedTab, shownTabs, order) {
+  function runTest(testNum, totalTabs, selectedTab, shownTabs, hiddenTabs, order) {
     let test = {
       QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMEventListener,
                                              Ci.nsISupportsWeakReference]),
 
-      state: buildTestState(totalTabs, selectedTab),
+      state: buildTestState(totalTabs, selectedTab, hiddenTabs),
       numTabsToShow: shownTabs,
       expectedOrder: order,
       actualOrder: [],
       windowWidth: null,
       callback: null,
       window: null,
 
       handleSSTabRestoring: function (aEvent) {
@@ -152,15 +158,17 @@ function test() {
         this.window.addEventListener("SSTabRestoring", this, false);
         this.window.addEventListener("load", this, false);
       }
     };
     test.run();
   }
 
   // actually create & run the tests
-  runTest(1, 13, 1, 6,  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
-  runTest(2, 13, 13, 6, [12, 7, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5, 6]);
-  runTest(3, 13, 4, 6,  [3, 4, 5, 6, 7, 8, 0, 1, 2, 9, 10, 11, 12]);
-  runTest(4, 13, 11, 6, [10, 7, 8, 9, 11, 12, 0, 1, 2, 3, 4, 5, 6]);
+  runTest(1, 13, 1,  6, [],         [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
+  runTest(2, 13, 13, 6, [],         [12, 7, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5, 6]);
+  runTest(3, 13, 4,  6, [],         [3, 4, 5, 6, 7, 8, 0, 1, 2, 9, 10, 11, 12]);
+  runTest(4, 13, 11, 6, [],         [10, 7, 8, 9, 11, 12, 0, 1, 2, 3, 4, 5, 6]);
+  runTest(5, 13, 13, 6, [0, 4, 9],  [12, 6, 7, 8, 10, 11, 1, 2, 3, 5, 0, 4, 9]);
+  runTest(6, 13, 4,  6, [1, 7, 12], [3, 4, 5, 6, 8, 9, 0, 2, 10, 11, 1, 7, 12]);
 
   // finish() is run by the last test to finish, so no cleanup down here
 }
--- a/browser/themes/gnomestripe/browser/browser.css
+++ b/browser/themes/gnomestripe/browser/browser.css
@@ -63,16 +63,21 @@
 }
 
 #navigator-toolbox[inFullscreen="true"],
 #navigator-toolbox[inFullscreen="true"] > #nav-bar {
   border-top: none;
   padding-top: 0;
 }
 
+#browser-bottombox:not([lwthemefooter="true"]) {
+  /* opaque for layers optimization */
+  background-color: -moz-Dialog;
+}
+
 #urlbar:-moz-lwtheme:not([focused="true"]),
 .searchbar-textbox:-moz-lwtheme:not([focused="true"]),
 .tabbrowser-tab:-moz-lwtheme:not([selected="true"]) {
   opacity: .85;
 }
 
 .tabbrowser-tab:-moz-lwtheme {
   text-shadow: none;
--- a/browser/themes/winstripe/browser/browser-aero.css
+++ b/browser/themes/winstripe/browser/browser-aero.css
@@ -16,17 +16,17 @@
 }
 
 @media all and (-moz-windows-compositor) {
   /* these should be hidden w/glass enabled. windows draws it's own buttons. */
   #titlebar-buttonbox {
     display: none;
   }
 
-  #main-window:not(:-moz-lwtheme) {
+  #main-window {
     -moz-appearance: -moz-win-borderless-glass;
     background: transparent;
   }
 
   #main-window:not(:-moz-lwtheme)[inFullscreen="true"] {
     -moz-appearance: none;
     background-color: #556;
   }
@@ -35,17 +35,17 @@
   #TabsToolbar[tabsontop="true"]:not(:-moz-lwtheme),
   #navigator-toolbox[tabsontop="false"] > #nav-bar:not(:-moz-lwtheme),
   #nav-bar + #customToolbars + #PersonalToolbar[collapsed="true"] + #TabsToolbar[tabsontop="false"]:last-child:not(:-moz-lwtheme) {
     background: transparent !important;
     color: black;
     text-shadow: 0 0 .7em white, 0 0 .7em white, 0 1px 0 rgba(255,255,255,.4);
   }
 
-  #navigator-toolbox[tabsontop="true"] > toolbar:not(#toolbar-menubar):not(#TabsToolbar) {
+  #main-window[sizemode="normal"] #navigator-toolbox[tabsontop="true"] > toolbar:not(#toolbar-menubar):not(#TabsToolbar) {
     border-left: 1px solid ThreeDShadow;
     border-right: 1px solid ThreeDShadow;
   }
 
   /* Make the window draggable by glassed toolbars (bug 555081) */
   #toolbar-menubar:not([autohide="true"]),
   #TabsToolbar[tabsontop="true"],
   #navigator-toolbox[tabsontop="false"] > #nav-bar,
--- a/browser/themes/winstripe/browser/browser.css
+++ b/browser/themes/winstripe/browser/browser.css
@@ -87,16 +87,22 @@
 #navigator-toolbox[tabsontop="true"]:not([customizing]) > #nav-bar[collapsed="true"] + #customToolbars + #PersonalToolbar {
   background-image: -moz-linear-gradient(@toolbarHighlight@, rgba(255,255,255,0));
 }
 
 #navigator-toolbox[tabsontop="false"] > #toolbar-menubar:not(:-moz-lwtheme) {
   background-image: -moz-linear-gradient(@toolbarHighlight@, @toolbarHighlight@);
 }
 
+#navigator-toolbox[tabsontop="true"] > #nav-bar:not(:-moz-lwtheme),
+#navigator-toolbox[tabsontop="true"]:not([customizing]) > #nav-bar[collapsed="true"] + toolbar:not(:-moz-lwtheme),
+#navigator-toolbox[tabsontop="true"]:not([customizing]) > #nav-bar[collapsed="true"] + #customToolbars + #PersonalToolbar:not(:-moz-lwtheme) {
+  border-top: 1px solid ThreeDShadow;
+}
+
 #personal-bookmarks {
   min-height: 24px;
 }
 
 #print-preview-toolbar:not(:-moz-lwtheme) {
   -moz-appearance: toolbox;
 }
 
@@ -1272,18 +1278,25 @@ richlistitem[type~="action"][actiontype=
 }
 
 #TabsToolbar:not(:-moz-lwtheme),
 #TabsToolbar[tabsontop="false"] {
   background-image: -moz-linear-gradient(transparent, transparent 50%,
                                          rgba(0,0,0,.05) 90%, rgba(0,0,0,.1));
 }
 
-#TabsToolbar[tabsontop="true"]:not(:-moz-lwtheme) {
-  -moz-box-shadow: 0 -1px ThreeDShadow inset;
+#tabbrowser-tabs[tabsontop="true"] > .tabbrowser-arrowscrollbox > scrollbox:not(:-moz-lwtheme) {
+  padding-bottom: 1px;
+  margin-bottom: -1px;
+  position: relative;
+}
+
+#tabbrowser-tabs[tabsontop="true"] > .tabbrowser-tab[selected="true"]:not(:-moz-lwtheme) {
+  margin-bottom: -1px;
+  padding-bottom: 1px;
 }
 
 .tabbrowser-tabs:-moz-system-metric(touch-enabled) {
   min-height: 7.3mozmm;
 }
 
 .tabbrowser-tabs:not([overflow="true"]) {
   -moz-margin-start: 3px;
new file mode 100644
index 0000000000000000000000000000000000000000..6f65bd6e971379d331efd2ff162f2b61938085f1
GIT binary patch
literal 1512
zc$@*~1sD2>P)<h;3K|Lk000e1NJLTq001@s000>X1^@s6dcWO100001b5ch_0Itp)
z=>Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!~g&e!~vBn4jTXf06}y`Sad^gZEa<4
zbO1wAML|?gQaT`KWG!lNWoICEF)lD5D0OpbZf77#N=G0{P(?=|b~Z98EFf`pVQgu1
zc_2L?I5i5)@W$Z)00l5fL_t(oN5xoKD0W>Ge#;jZ=6RmSl&J`1jG~APxiJ(X7fNKf
zKoaG`5Em#iyPyn7Q8JV<ajBG{xKToqxG=51XVrc?Ug!8;ec${2yLM-vea;@9z1Dix
zT3hAk=SQBNp5*B0NVc}N<mBYUZF_rrva_?}Hu~_6>gDA{7~|{f%TLXUej6JbvbMJV
zQZZg`j4t|A0F{xEK@$@bw6d~7Jv}|t-Q7(yGcz<jJxzUmeN<apOLKE`w79rPVPRq9
z?d?rIK0dObtgNhPe}A9$_V#FJXNPuoce$c(YHErPM;Aae=VV~1si{;`Q$r}x(a|(I
zI!c?Ho3y&R%I&JEDtdf;q`bU53JeTnP;$T^&4`Eyo=2&enwly~`)>kPVKN5?2P!Qs
z<-Mp>DjFLb<I_GqKBj?z0jj8|pqrZ;YHDg?)r7jqsUm;{2M4P$eSCa;X(uKol81+f
z&ZWk{U_%`p9lQyg==SzDU0z<&$;k<=udlOezP!A!YNn;7u}unKQpusu$jC?**!%lC
z_qVjP@Opn!I&yO{Fl-2??d<H#D(3F)PA)DkT;1H<_#K<UH~c3jF#!}3-PqV*5K@T=
zU{G-oi`a}33H>Q>1}4B^<6_fjgCy`So=Ucb@c>^`R73>@1=QHss0a)njE#+TZf9y>
z-<^;+Rq5$!k_YT`a&nSJMn-;1Ah1K>HvUvHO5?QxrsS`r{bpunbbWnIS65d|K$=CU
zBKj*UD_QMz1q$;j0+ZU2R8mUPgKeQ49v(8V)6-K$%AcQ~=U_mmz`iqHIS_JvuCA_B
zQc}V|U|%LCCNwlOqzLTc;epv#r@)jZ{~cRNzvJ`~nV^J(gs`tGEG$$6h7ULlkdm&z
zDXOBTe^QDMh3^8j!;Zjs*f4zEcLnSTaQ_^bbU|Tna;yk;3knKi43HY6hyS3C&(F__
z*oKFPbs9Hn&5D|;Dc<3@8yXrIKyh)gS_+8pmzS3nft{b9lZA!F|06MN$OBOX;z|@m
z?x1~hb92(y*Qc_wvQNN7BqE-cmX<nm)Sn^`ZHPpZ`e9Qj7Z(>y^KWl&bbo(OcXxMm
zdwVM@F#!x&1tJz**qK(q1aWApBck-Aq$J*aS63IOJlYfiti8RR8T6k66U-<_9wOaA
z<c|V?{{H?HA0JOKF)>_`yPch#@prit2FWK9Q>Dm8#HLtZ+Q$Eyajp1A9KKfMq2$w`
zhDdKvq%;qzN5!U*&!I>|Dk(_0fEepxJk%5wnt@D1WEXHJa23cr@Gl4qZIPXzPb*-W
zCyIdxdLttvdDE}2ucW7^_k{?scy4a)7l{F+02dk>${?_Y*c{Yzc6OGIj*d7)8yp<u
zIiR|@zWDX)7r)on*Hc150=EMK0{EGdlEMe^V_*OUnodto|71?h&COrZ5in$3U7Z@3
zlodrA*}8#&0fVZpuBP7JUbZ`I4oNQJN<%|Ko&$F-QBhHpnVHGo*4EZ|&8Md)zREy>
zPla#&F)&a%7LY5r7XgKftQ>@*O+iM0!wL@%r`Xt7j$GhIwzjrtX=zEFE#U4YJ3E{G
z<-)=OPmMzWsJ6B?eitdxkAdN2^Yioh)(KY#qT;>3zhBLAu#K#&EIK$iU_U4QS94rv
zXD8bU0Ewg)s*VH1%@&+J)DnTf`1m*n*l}@jOibnF<<#2RN;x?>oKYkvC$nmQ3rvYL
z08Z}iP?Nd=PU((OdhaNA2ZFVs9RLW@z&s!atRYktbHI+#k3HdC*z14S7)K2k6Hd?o
O0000<MNUMnLSTZ(cchyD
deleted file mode 100644
--- a/build/mobile/sutagent/android/res/drawable/ateamlogo.png.rej
+++ /dev/null
@@ -1,35 +0,0 @@
---- ateamlogo.png
-+++ ateamlogo.png
-GIT binary patch
-literal 1512
-zc$@*~1sD2>P)<h;3K|Lk000e1NJLTq001@s000>X1^@s6dcWO100001b5ch_0Itp)
-z=>Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!~g&e!~vBn4jTXf06}y`Sad^gZEa<4
-zbO1wAML|?gQaT`KWG!lNWoICEF)lD5D0OpbZf77#N=G0{P(?=|b~Z98EFf`pVQgu1
-zc_2L?I5i5)@W$Z)00l5fL_t(oN5xoKD0W>Ge#;jZ=6RmSl&J`1jG~APxiJ(X7fNKf
-zKoaG`5Em#iyPyn7Q8JV<ajBG{xKToqxG=51XVrc?Ug!8;ec${2yLM-vea;@9z1Dix
-zT3hAk=SQBNp5*B0NVc}N<mBYUZF_rrva_?}Hu~_6>gDA{7~|{f%TLXUej6JbvbMJV
-zQZZg`j4t|A0F{xEK@$@bw6d~7Jv}|t-Q7(yGcz<jJxzUmeN<apOLKE`w79rPVPRq9
-z?d?rIK0dObtgNhPe}A9$_V#FJXNPuoce$c(YHErPM;Aae=VV~1si{;`Q$r}x(a|(I
-zI!c?Ho3y&R%I&JEDtdf;q`bU53JeTnP;$T^&4`Eyo=2&enwly~`)>kPVKN5?2P!Qs
-z<-Mp>DjFLb<I_GqKBj?z0jj8|pqrZ;YHDg?)r7jqsUm;{2M4P$eSCa;X(uKol81+f
-z&ZWk{U_%`p9lQyg==SzDU0z<&$;k<=udlOezP!A!YNn;7u}unKQpusu$jC?**!%lC
-z_qVjP@Opn!I&yO{Fl-2??d<H#D(3F)PA)DkT;1H<_#K<UH~c3jF#!}3-PqV*5K@T=
-zU{G-oi`a}33H>Q>1}4B^<6_fjgCy`So=Ucb@c>^`R73>@1=QHss0a)njE#+TZf9y>
-z-<^;+Rq5$!k_YT`a&nSJMn-;1Ah1K>HvUvHO5?QxrsS`r{bpunbbWnIS65d|K$=CU
-zBKj*UD_QMz1q$;j0+ZU2R8mUPgKeQ49v(8V)6-K$%AcQ~=U_mmz`iqHIS_JvuCA_B
-zQc}V|U|%LCCNwlOqzLTc;epv#r@)jZ{~cRNzvJ`~nV^J(gs`tGEG$$6h7ULlkdm&z
-zDXOBTe^QDMh3^8j!;Zjs*f4zEcLnSTaQ_^bbU|Tna;yk;3knKi43HY6hyS3C&(F__
-z*oKFPbs9Hn&5D|;Dc<3@8yXrIKyh)gS_+8pmzS3nft{b9lZA!F|06MN$OBOX;z|@m
-z?x1~hb92(y*Qc_wvQNN7BqE-cmX<nm)Sn^`ZHPpZ`e9Qj7Z(>y^KWl&bbo(OcXxMm
-zdwVM@F#!x&1tJz**qK(q1aWApBck-Aq$J*aS63IOJlYfiti8RR8T6k66U-<_9wOaA
-z<c|V?{{H?HA0JOKF)>_`yPch#@prit2FWK9Q>Dm8#HLtZ+Q$Eyajp1A9KKfMq2$w`
-zhDdKvq%;qzN5!U*&!I>|Dk(_0fEepxJk%5wnt@D1WEXHJa23cr@Gl4qZIPXzPb*-W
-zCyIdxdLttvdDE}2ucW7^_k{?scy4a)7l{F+02dk>${?_Y*c{Yzc6OGIj*d7)8yp<u
-zIiR|@zWDX)7r)on*Hc150=EMK0{EGdlEMe^V_*OUnodto|71?h&COrZ5in$3U7Z@3
-zlodrA*}8#&0fVZpuBP7JUbZ`I4oNQJN<%|Ko&$F-QBhHpnVHGo*4EZ|&8Md)zREy>
-zPla#&F)&a%7LY5r7XgKftQ>@*O+iM0!wL@%r`Xt7j$GhIwzjrtX=zEFE#U4YJ3E{G
-z<-)=OPmMzWsJ6B?eitdxkAdN2^Yioh)(KY#qT;>3zhBLAu#K#&EIK$iU_U4QS94rv
-zXD8bU0Ewg)s*VH1%@&+J)DnTf`1m*n*l}@jOibnF<<#2RN;x?>oKYkvC$nmQ3rvYL
-z08Z}iP?Nd=PU((OdhaNA2ZFVs9RLW@z&s!atRYktbHI+#k3HdC*z14S7)K2k6Hd?o
-O0000<MNUMnLSTZ(cchyD
-
new file mode 100644
index 0000000000000000000000000000000000000000..95b0904338c35d1b27b98600bd7c2c1fbb342a6d
GIT binary patch
literal 1636
zc$@)b2AlbbP)<h;3K|Lk000e1NJLTq000>P000>X1^@s6#OZ}&00001b5ch_0Itp)
z=>Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!T<mc!T}X1N`L?W1^G!tK~zXfjaCV4
z6lEBFbL_p_ld>(ngx0oTfff-3#Tb${VjzO86r|A*qC|@U1&u%mLD7g2kP23!oYDe<
z22>ObiN*spM3AF&UDg(QShn4^+wOL^d(HU$f7#M<2w$?<%*;RE`QH0R@Or(f+wF!Z
zieMHo839Fs<nMzj2#Ca3_<hOS3q_}cs*xdt!~Bd&*HU*6BC#<L0zs%+4OB_u&*dHu
z^m;w^?%fNasi{e|*=&$y8DVmm`PO;_(^Ap;+<b_1cy#$f2%!k<TQ|UZ;1JBG&%!%?
zECT6i7+&)^{`%<{3Lh>)TUiMbj-P<8^(una;ka0{6LFMCSy>q?K;!`=tro^M7c`;-
zi{l??D5EipUxjnYOK4uZ5(#I{LbRBHq+#fqTLRH!f=ro@UGO4gI_sbEAe!D=2hnVX
z?eKo+{eEaP8W@d6^!JENG3b%Jrv{S81KHh;VPAg5Gd7*S2(hmZ)?L-qJ`sJH8SrOk
zLGt<G9hZ;h#fzY8Y2`kIKmY=r6{rHJYSi`hAgurTD~-6kW+fU|EQ6Uczp`REgkTU!
zhmYdi@x$=vWb=Nfeee-{*&`9iNQZYq0Xk+pPP3wUP&YgiC-C_UHld}ZMYY@Q{WCF}
zc!r>Wie3k?*T;v-?#KW53sNhq28P@_CJ)YM=OLUBPiTp7piugV=nas2dwC3t#e(wk
zatsMFBs^V&DS+XU1DS8GhxvRh6v{MeB2^elOhi}lG^%(Rwb{#oGT81IBurV1_+$so
z=4(h)k!!|ubUsl`mHPUjPSuTEy$<^N26zj`q2cW{2*<@jpsJ!44KgtJ!5J{DYunqA
zbm#~~LJ4!2_|13ddFTOn3da*5^5MHT8@BCR(fa%Xn9rVvb^jrL?pAioAQ2*wwfX=g
zB2;_HEMAGK(_-w*IZz0?LMRN0P{iiuoIeMB1c3Ns#F5KaI2muWn}RUlG3J_TZSZG}
zLc_Y%aBO^s!&>Wf;=ZSzK`1GatAR+tLc<cN0y8~#L(|!Hmq7@MF(?1v%w{OlbacWq
zp%C^@w?OW6^ET!`Id344<n^NQjb%`Y)EVz?M8s&gb0z$QBvMA#i<HTchlp2Kvz>yv
zt_7M)=!z%jq*b91e~oeFu4W=A5d>0Fxc)HQlkoJ%C4uzi<&q}tf=tzE+o+<Fa)c?6
z<<B$xOwXi=)WZINgYBl72oVK=%yfiFqvS3(cgR{H&|4yrtao%SY`eBWB+_cHI*~`(
z6@TIf4&CmllSv`&A_&i#h}BHOsB8Hbc?&B@uhRR8NXU_?BG3H=XnJiakwt;gW~UZ*
zz?VM;-ok=`*8OiK>>=y1hq&M+XS(JVj4z(WwTvMsNN7?B;(-y#P)L;-%CzlmXsLLC
z3n{DkM!T)7FuXG&ChysY<lWVLVFi+{2*<~9)EVp!kj6umHMq2SJ^Jp+<jOlZ$SoPq
zvQkJW*$=9M2uVxRD;22Qy^YH{gRhaa%v4*4NL&mps#4S*+(UrK;rvkPJ?7)Kl}H3X
zR<73B#)lfnNTXhNacS?Sg~ZU5c;qN;#8gt=1VW${GR;n<YJyfPbd61%jJ}*KA~PvC
z3Fu9guw4-Q+bJ%3%(21IfSg532)bV6(tp~Pk6|D&G*X)k4hQMg1*C4S!l>6*^TB5=
zUj<{`-^h6XLs(9o9>SR+5LWbBzNED6E(B9k_(wzRufL;>)-6MquCbYPtQYP_A3^TY
zmtn8kKq#!hs8wqbp!4Q)7ic!LIkQIc9TPQC7}P_NHd=^=>zX~2YZX(~?x|Cdvg0#O
zvaX_O<fxYK7H7#kr0lGQb>Bfa%gcB>+n=6|#BaZc*5%@=5jCN+vs10FuZKkF(bAVj
z?9V4TyWNjXrMA@54k<#?!6WeJX3^HubM!Ehc1$m(7H;D^zhin4h9CF>J(DNVH$g3e
zBU1Q%i@|`>(ozVOm6htIO`BjM3Fip8w*44aIE<-%48D9BML!OxdiI?0g?)bwoMG<}
i!q{2%V=JB3JN^TR5x6H(GzMA#0000<MNUMnLSTY%<QAm>
deleted file mode 100644
--- a/build/mobile/sutagent/android/res/drawable/ic_stat_first.png.rej
+++ /dev/null
@@ -1,37 +0,0 @@
---- ic_stat_first.png
-+++ ic_stat_first.png
-GIT binary patch
-literal 1636
-zc$@)b2AlbbP)<h;3K|Lk000e1NJLTq000>P000>X1^@s6#OZ}&00001b5ch_0Itp)
-z=>Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!T<mc!T}X1N`L?W1^G!tK~zXfjaCV4
-z6lEBFbL_p_ld>(ngx0oTfff-3#Tb${VjzO86r|A*qC|@U1&u%mLD7g2kP23!oYDe<
-z22>ObiN*spM3AF&UDg(QShn4^+wOL^d(HU$f7#M<2w$?<%*;RE`QH0R@Or(f+wF!Z
-zieMHo839Fs<nMzj2#Ca3_<hOS3q_}cs*xdt!~Bd&*HU*6BC#<L0zs%+4OB_u&*dHu
-z^m;w^?%fNasi{e|*=&$y8DVmm`PO;_(^Ap;+<b_1cy#$f2%!k<TQ|UZ;1JBG&%!%?
-zECT6i7+&)^{`%<{3Lh>)TUiMbj-P<8^(una;ka0{6LFMCSy>q?K;!`=tro^M7c`;-
-zi{l??D5EipUxjnYOK4uZ5(#I{LbRBHq+#fqTLRH!f=ro@UGO4gI_sbEAe!D=2hnVX
-z?eKo+{eEaP8W@d6^!JENG3b%Jrv{S81KHh;VPAg5Gd7*S2(hmZ)?L-qJ`sJH8SrOk
-zLGt<G9hZ;h#fzY8Y2`kIKmY=r6{rHJYSi`hAgurTD~-6kW+fU|EQ6Uczp`REgkTU!
-zhmYdi@x$=vWb=Nfeee-{*&`9iNQZYq0Xk+pPP3wUP&YgiC-C_UHld}ZMYY@Q{WCF}
-zc!r>Wie3k?*T;v-?#KW53sNhq28P@_CJ)YM=OLUBPiTp7piugV=nas2dwC3t#e(wk
-zatsMFBs^V&DS+XU1DS8GhxvRh6v{MeB2^elOhi}lG^%(Rwb{#oGT81IBurV1_+$so
-z=4(h)k!!|ubUsl`mHPUjPSuTEy$<^N26zj`q2cW{2*<@jpsJ!44KgtJ!5J{DYunqA
-zbm#~~LJ4!2_|13ddFTOn3da*5^5MHT8@BCR(fa%Xn9rVvb^jrL?pAioAQ2*wwfX=g
-zB2;_HEMAGK(_-w*IZz0?LMRN0P{iiuoIeMB1c3Ns#F5KaI2muWn}RUlG3J_TZSZG}
-zLc_Y%aBO^s!&>Wf;=ZSzK`1GatAR+tLc<cN0y8~#L(|!Hmq7@MF(?1v%w{OlbacWq
-zp%C^@w?OW6^ET!`Id344<n^NQjb%`Y)EVz?M8s&gb0z$QBvMA#i<HTchlp2Kvz>yv
-zt_7M)=!z%jq*b91e~oeFu4W=A5d>0Fxc)HQlkoJ%C4uzi<&q}tf=tzE+o+<Fa)c?6
-z<<B$xOwXi=)WZINgYBl72oVK=%yfiFqvS3(cgR{H&|4yrtao%SY`eBWB+_cHI*~`(
-z6@TIf4&CmllSv`&A_&i#h}BHOsB8Hbc?&B@uhRR8NXU_?BG3H=XnJiakwt;gW~UZ*
-zz?VM;-ok=`*8OiK>>=y1hq&M+XS(JVj4z(WwTvMsNN7?B;(-y#P)L;-%CzlmXsLLC
-z3n{DkM!T)7FuXG&ChysY<lWVLVFi+{2*<~9)EVp!kj6umHMq2SJ^Jp+<jOlZ$SoPq
-zvQkJW*$=9M2uVxRD;22Qy^YH{gRhaa%v4*4NL&mps#4S*+(UrK;rvkPJ?7)Kl}H3X
-zR<73B#)lfnNTXhNacS?Sg~ZU5c;qN;#8gt=1VW${GR;n<YJyfPbd61%jJ}*KA~PvC
-z3Fu9guw4-Q+bJ%3%(21IfSg532)bV6(tp~Pk6|D&G*X)k4hQMg1*C4S!l>6*^TB5=
-zUj<{`-^h6XLs(9o9>SR+5LWbBzNED6E(B9k_(wzRufL;>)-6MquCbYPtQYP_A3^TY
-zmtn8kKq#!hs8wqbp!4Q)7ic!LIkQIc9TPQC7}P_NHd=^=>zX~2YZX(~?x|Cdvg0#O
-zvaX_O<fxYK7H7#kr0lGQb>Bfa%gcB>+n=6|#BaZc*5%@=5jCN+vs10FuZKkF(bAVj
-z?9V4TyWNjXrMA@54k<#?!6WeJX3^HubM!Ehc1$m(7H;D^zhin4h9CF>J(DNVH$g3e
-zBU1Q%i@|`>(ozVOm6htIO`BjM3Fip8w*44aIE<-%48D9BML!OxdiI?0g?)bwoMG<}
-i!q{2%V=JB3JN^TR5x6H(GzMA#0000<MNUMnLSTY%<QAm>
-
new file mode 100644
index 0000000000000000000000000000000000000000..90c0bf85058c0a650533147f43b083c8f156f695
GIT binary patch
literal 777
zc$@(V1NQuhP)<h;3K|Lk000e1NJLTq000>P000>X1^@s6#OZ}&00001b5ch_0Itp)
z=>Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!T<mc!T}X1N`L?W0-i}kK~zXf-Bn9Z
z6HydCgVIisAh<HRAR&m4V2q+z!A4O77!wi}hG;Y-n$!&&{RbNO5xNqeVdut3yE00;
zp>>*Wkf5f<Xws=OeT1QPhUxL1t260iI)w!+_>#%Yo%@~d%)N8Ia|L98Ek@H8>vQ>?
zlSJ6aKQR^}P<%B9#(WsS;{nih@Qe<F_vQpNpC4$v<CLTxOgWAD+jlVqfHE{;ZtOZ{
zN5=S_#`K=%iRT8WH)+HZm~L)EwYLuoFJ7auv|LGD`GSRKFY)(*E|$=X#IFa(B=lvJ
zAT4%a{>EfQY$B1sWHO0AQYrhr+Bbk{p#_akamPH4NoWhQC;Cylbq5vjVdmn99qjk}
z$yXSSMk@p}T}M&vKJK1G*L5@*MR^xSm5FF<D@tsfxiE~HZy)9YSGd5V>1m$Cf@zwZ
zr#*d+84!3p>5!fUn@xdq%`hS!>_+kbnBIlGZK&_vk8jz}ws_Td;#1sAS)7%`E@43s
zp!3XWSSbpyyi0(&;3TxK6~JU_Ij;vEzL|$oOoGMY8Z68{hTT7Z0hCI1`DR;uF!5v-
zdRRb@Sc311QCh&id3#Char|BoacC4*uZ=tIt$OMl=GoSnjN=3zH*}Tjly50-E1^&b
zg25oP?rDW@QXG&B=YWn_1lEro0p<M%$es<<GFaSKN#Z)BN4ga&vA#^3y3dWazVx|n
z-K+fp#$i=Ac**7vpW?Ks2Q_guCH&`Oggd#&s>=<T!OPr)R(ThhVJVK{t5Me{<m5)6
z1TuIq#|*2U^f&fNxga@S1`)~0jX)iNIs;vsay$uBFOpfSP&yCu9#!4DXiPngR*)9k
zH{ZLguQy^6Du;SqyD-fY&kfsho@^*d<5S6<v2)J&ZCS}*W1f+TE7&gi00000NkvXX
Hu0mjfOOjpk
deleted file mode 100644
--- a/build/mobile/sutagent/android/res/drawable/ic_stat_neterror.png.rej
+++ /dev/null
@@ -1,21 +0,0 @@
---- ic_stat_neterror.png
-+++ ic_stat_neterror.png
-GIT binary patch
-literal 777
-zc$@(V1NQuhP)<h;3K|Lk000e1NJLTq000>P000>X1^@s6#OZ}&00001b5ch_0Itp)
-z=>Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!T<mc!T}X1N`L?W0-i}kK~zXf-Bn9Z
-z6HydCgVIisAh<HRAR&m4V2q+z!A4O77!wi}hG;Y-n$!&&{RbNO5xNqeVdut3yE00;
-zp>>*Wkf5f<Xws=OeT1QPhUxL1t260iI)w!+_>#%Yo%@~d%)N8Ia|L98Ek@H8>vQ>?
-zlSJ6aKQR^}P<%B9#(WsS;{nih@Qe<F_vQpNpC4$v<CLTxOgWAD+jlVqfHE{;ZtOZ{
-zN5=S_#`K=%iRT8WH)+HZm~L)EwYLuoFJ7auv|LGD`GSRKFY)(*E|$=X#IFa(B=lvJ
-zAT4%a{>EfQY$B1sWHO0AQYrhr+Bbk{p#_akamPH4NoWhQC;Cylbq5vjVdmn99qjk}
-z$yXSSMk@p}T}M&vKJK1G*L5@*MR^xSm5FF<D@tsfxiE~HZy)9YSGd5V>1m$Cf@zwZ
-zr#*d+84!3p>5!fUn@xdq%`hS!>_+kbnBIlGZK&_vk8jz}ws_Td;#1sAS)7%`E@43s
-zp!3XWSSbpyyi0(&;3TxK6~JU_Ij;vEzL|$oOoGMY8Z68{hTT7Z0hCI1`DR;uF!5v-
-zdRRb@Sc311QCh&id3#Char|BoacC4*uZ=tIt$OMl=GoSnjN=3zH*}Tjly50-E1^&b
-zg25oP?rDW@QXG&B=YWn_1lEro0p<M%$es<<GFaSKN#Z)BN4ga&vA#^3y3dWazVx|n
-z-K+fp#$i=Ac**7vpW?Ks2Q_guCH&`Oggd#&s>=<T!OPr)R(ThhVJVK{t5Me{<m5)6
-z1TuIq#|*2U^f&fNxga@S1`)~0jX)iNIs;vsay$uBFOpfSP&yCu9#!4DXiPngR*)9k
-zH{ZLguQy^6Du;SqyD-fY&kfsho@^*d<5S6<v2)J&ZCS}*W1f+TE7&gi00000NkvXX
-Hu0mjfOOjpk
-
new file mode 100644
index 0000000000000000000000000000000000000000..e3304e50fc53726a21e35c29cce796ecd054ac80
GIT binary patch
literal 1256
zc$@*~1Q+{>P)<h;3K|Lk000e1NJLTq000>P000>X1^@s6#OZ}&00001b5ch_0Itp)
z=>Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!T<mc!T}X1N`L?W1bs<FK~zXfwN`Cx
zQ&kv#Zg0D;?dV)L9h=J7m>Zj8-?PGSpkpI|qG1w|AI2Xf5|RahEF*llAcBb+jS?fE
z=n|)qm`F635KT}R;#hE7ConKEaWHlC<KA}tTyO7vJ@+oo)VA9&@gyf{?|skvocBG?
zbDql)8~C4x?%(<S+qNV0){*JQVnscKk|iLo4}snLAvP>WuwxIxA9O<~D%avX1tkc-
zdkkV-BRsZzh&2z#rHunod_Hip4EfS!Ey%mP8J4%-fc50Zh@SZdT!sa@`MF5n*$&QP
z0eBrIo@fIULfV>ESkIgSXSN_~urD=8sOumUL4@pbBGTOh!^$SadVc@~LI}Qe5S&g2
zeR&!9ygZQK4@1*(SdP32W$af#ZF)EY3WXAyA6H52>Ikf#e}Z{EA0c}7J6K-n0!>C(
zD~`hU%P{oywIE4C@W3JHt1G7f7Z$+y{1$M=G;rzZFtn~p0O96(@UekG1h%~hE++?w
zgke}}hpB5XIK6&$+NdBijo5FaBtG;AIiVvbXZ~lh|L*L2$-lF6wiJY!B7(2JPVT%y
za+z!t*wIPS)e(yJ^=nfaxj~-NN(Ro6xO64S9wB9_lpsnVK<;}Mkjs)Y1!T@nNTNs*
z1rHq7stk1OxpNSv33Bln6xjbV1Ii}1EuVxcdm@l5lc%VR+;`tk^7wdCpp=z(n_5Vy
zTc$0F_FrbZO(T~zmxLwt35nD=C(=n8bj%$j`r{?GNKZmT<MblQ;b6}|?#%fKK(T8h
z<kXo-s7Ty^njqm?(fFv*l8>XNzOoWoBiAAO#-ZFCgIL)x{hlT#wcni4n3T>*LHI?5
z;1?9IeaGLO`o*QNUB96L$)mR*R@=3t(QnUciZ`rXom?}iDB)f9D7o(|PWoCJ8fJR2
zXlLYIw?S*4dS=ql9h`}pS#q#MOanrBZBn36ch3~-7^_s~sai=jW)!2?*i0_{JQAwy
z6uodUPH@rQi{xRIx^oNYHfxr|82Fz+lyH~=o%=LasTB1TmXT<0(su1KST%vV@5&?T
z%C#A<mKiJgd!^*~nZy+>nmE<UG{F2KruIXmL?W}^U2(TCg~E(Aqs%J;;1`!b`I9w*
zUG4gcDF+N6I|07*0qB^Y@I?zjqF1B4&XxuF_dh`6x53xefaBsx7DwUe>965m|Fo7^
z9vX(X#?DMkz(m`#@bBn=JbVSQp9T>d7=ZMBF9KWJG12lkGqoUm&1)d_^&_xlE2MMh
zQ#&JPHi06OP;QNZFJ1&NaYes84O8bXa3&LECPvlkjT_q_wl>51#b>Y_eFwg^PigVe
zxnA%OmV>XVN)4j6i!WqOw`n63rwd$WCX5@NL}+gpGpY!T&uxZu{(?5mFSH|a`~<>>
zUxRtyZYZNSVcNAFlkb1X?075=B<@UP78pJC6%^KM<1=lD4LOj$c@x6#9f!WImVHQM
zu*C#roJsGo6$rDh8lG8j+Uiw^e0maT>(?R1HozAbfiEeU2AZ?|&hD|PBmM$!rt6n6
Sg)=Jv0000<MNUMnLSTXxt4GHG
deleted file mode 100644
--- a/build/mobile/sutagent/android/res/drawable/ic_stat_second.png.rej
+++ /dev/null
@@ -1,30 +0,0 @@
---- ic_stat_second.png
-+++ ic_stat_second.png
-GIT binary patch
-literal 1256
-zc$@*~1Q+{>P)<h;3K|Lk000e1NJLTq000>P000>X1^@s6#OZ}&00001b5ch_0Itp)
-z=>Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!T<mc!T}X1N`L?W1bs<FK~zXfwN`Cx
-zQ&kv#Zg0D;?dV)L9h=J7m>Zj8-?PGSpkpI|qG1w|AI2Xf5|RahEF*llAcBb+jS?fE
-z=n|)qm`F635KT}R;#hE7ConKEaWHlC<KA}tTyO7vJ@+oo)VA9&@gyf{?|skvocBG?
-zbDql)8~C4x?%(<S+qNV0){*JQVnscKk|iLo4}snLAvP>WuwxIxA9O<~D%avX1tkc-
-zdkkV-BRsZzh&2z#rHunod_Hip4EfS!Ey%mP8J4%-fc50Zh@SZdT!sa@`MF5n*$&QP
-z0eBrIo@fIULfV>ESkIgSXSN_~urD=8sOumUL4@pbBGTOh!^$SadVc@~LI}Qe5S&g2
-zeR&!9ygZQK4@1*(SdP32W$af#ZF)EY3WXAyA6H52>Ikf#e}Z{EA0c}7J6K-n0!>C(
-zD~`hU%P{oywIE4C@W3JHt1G7f7Z$+y{1$M=G;rzZFtn~p0O96(@UekG1h%~hE++?w
-zgke}}hpB5XIK6&$+NdBijo5FaBtG;AIiVvbXZ~lh|L*L2$-lF6wiJY!B7(2JPVT%y
-za+z!t*wIPS)e(yJ^=nfaxj~-NN(Ro6xO64S9wB9_lpsnVK<;}Mkjs)Y1!T@nNTNs*
-z1rHq7stk1OxpNSv33Bln6xjbV1Ii}1EuVxcdm@l5lc%VR+;`tk^7wdCpp=z(n_5Vy
-zTc$0F_FrbZO(T~zmxLwt35nD=C(=n8bj%$j`r{?GNKZmT<MblQ;b6}|?#%fKK(T8h
-z<kXo-s7Ty^njqm?(fFv*l8>XNzOoWoBiAAO#-ZFCgIL)x{hlT#wcni4n3T>*LHI?5
-z;1?9IeaGLO`o*QNUB96L$)mR*R@=3t(QnUciZ`rXom?}iDB)f9D7o(|PWoCJ8fJR2
-zXlLYIw?S*4dS=ql9h`}pS#q#MOanrBZBn36ch3~-7^_s~sai=jW)!2?*i0_{JQAwy
-z6uodUPH@rQi{xRIx^oNYHfxr|82Fz+lyH~=o%=LasTB1TmXT<0(su1KST%vV@5&?T
-z%C#A<mKiJgd!^*~nZy+>nmE<UG{F2KruIXmL?W}^U2(TCg~E(Aqs%J;;1`!b`I9w*
-zUG4gcDF+N6I|07*0qB^Y@I?zjqF1B4&XxuF_dh`6x53xefaBsx7DwUe>965m|Fo7^
-z9vX(X#?DMkz(m`#@bBn=JbVSQp9T>d7=ZMBF9KWJG12lkGqoUm&1)d_^&_xlE2MMh
-zQ#&JPHi06OP;QNZFJ1&NaYes84O8bXa3&LECPvlkjT_q_wl>51#b>Y_eFwg^PigVe
-zxnA%OmV>XVN)4j6i!WqOw`n63rwd$WCX5@NL}+gpGpY!T&uxZu{(?5mFSH|a`~<>>
-zUxRtyZYZNSVcNAFlkb1X?075=B<@UP78pJC6%^KM<1=lD4LOj$c@x6#9f!WImVHQM
-zu*C#roJsGo6$rDh8lG8j+Uiw^e0maT>(?R1HozAbfiEeU2AZ?|&hD|PBmM$!rt6n6
-Sg)=Jv0000<MNUMnLSTXxt4GHG
-
new file mode 100644
index 0000000000000000000000000000000000000000..be00f470ad6a596d0054a6f95b112b835d1c21e0
GIT binary patch
literal 651
zc$@)?0(AX}P)<h;3K|Lk000e1NJLTq000>P000>X1^@s6#OZ}&0000PbVXQnQ*UN;
zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!ElET{RCwCVR=bKKQ4p>6{Q_GQ6r)xU
z1VLsZACY5Xs^ACACkTGPbZpXJAd{II8g6Q~n;Yx~1_q)cg81yMbJ-S^R;HbvWeSS2
z@2$r@r*3sC>AFr|Ee`q*^8Jbwi^Wzpo7GFDQp-x5^wFNwYBfcYq@QlLTi);Y3tmbM
z27`sINRG#&=J9ysXf#Srr&Hc;x0<bVRjbtsBd!Mm0Rr-1Fi4E*73;PYVS3)ZUN1Qu
z4%+Q@Ktg!tb<LI{<#Ji&Q!757kDARU^?E&FQH0l@u>YyX7YYT1&F^qHOn$$g`u)B@
z!~LN~qe1KSnwHBYn*Tf;4*xua1T=vfC^uRZ5J7B>XGSA4h1x@O<@5Ra;c%$9TrS#d
zHbTz@&nSd_K1`kcE0&7nayiU`1_Yo6$)M+AtbuO$F`9QvML4*{!HPl4y&_N)fqy2G
zQ9o29l}bG^iZ|w3BrvElqsvgEKZxUa<vOR!q}dvH1`XGz$Bgi6McC`T^UK2K<tCDN
zJpRf~nC^>{8rto4g&(ZhKH?CcNF?f}a}tZiWXuGkkb&e;;UZAX%=2_QU0fhx3f}n2
z%ICM~THEb5jmKkQZ~1TzsDgYKNJ60y?)VSf5@X=ZWImsten%padv9z}3}{xX)i05e
z7c^r7O}(X^PDdnxz2}EK|MF=kB*|n_$9G~h8rk;AO{dc|o6Tr4nMkL`tE#HsqspoP
l3Fnk7eSW|Fzm>lN3;+?=xV(guKZO7Q002ovPDHLkV1kv~Eqnj~
deleted file mode 100644
--- a/build/mobile/sutagent/android/res/drawable/ic_stat_warning.png.rej
+++ /dev/null
@@ -1,18 +0,0 @@
---- ic_stat_warning.png
-+++ ic_stat_warning.png
-GIT binary patch
-literal 651
-zc$@)?0(AX}P)<h;3K|Lk000e1NJLTq000>P000>X1^@s6#OZ}&0000PbVXQnQ*UN;
-zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!ElET{RCwCVR=bKKQ4p>6{Q_GQ6r)xU
-z1VLsZACY5Xs^ACACkTGPbZpXJAd{II8g6Q~n;Yx~1_q)cg81yMbJ-S^R;HbvWeSS2
-z@2$r@r*3sC>AFr|Ee`q*^8Jbwi^Wzpo7GFDQp-x5^wFNwYBfcYq@QlLTi);Y3tmbM
-z27`sINRG#&=J9ysXf#Srr&Hc;x0<bVRjbtsBd!Mm0Rr-1Fi4E*73;PYVS3)ZUN1Qu
-z4%+Q@Ktg!tb<LI{<#Ji&Q!757kDARU^?E&FQH0l@u>YyX7YYT1&F^qHOn$$g`u)B@
-z!~LN~qe1KSnwHBYn*Tf;4*xua1T=vfC^uRZ5J7B>XGSA4h1x@O<@5Ra;c%$9TrS#d
-zHbTz@&nSd_K1`kcE0&7nayiU`1_Yo6$)M+AtbuO$F`9QvML4*{!HPl4y&_N)fqy2G
-zQ9o29l}bG^iZ|w3BrvElqsvgEKZxUa<vOR!q}dvH1`XGz$Bgi6McC`T^UK2K<tCDN
-zJpRf~nC^>{8rto4g&(ZhKH?CcNF?f}a}tZiWXuGkkb&e;;UZAX%=2_QU0fhx3f}n2
-z%ICM~THEb5jmKkQZ~1TzsDgYKNJ60y?)VSf5@X=ZWImsten%padv9z}3}{xX)i05e
-z7c^r7O}(X^PDdnxz2}EK|MF=kB*|n_$9G~h8rk;AO{dc|o6Tr4nMkL`tE#HsqspoP
-l3Fnk7eSW|Fzm>lN3;+?=xV(guKZO7Q002ovPDHLkV1kv~Eqnj~
-
--- a/chrome/test/Makefile.in
+++ b/chrome/test/Makefile.in
@@ -33,16 +33,17 @@
 # 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 *****
 
 DEPTH = ../..
 topsrcdir = @top_srcdir@
 srcdir = @srcdir@
+relativesrcdir = chrome/test
 VPATH = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE = test_chrome
 
 XPCSHELL_TESTS = unit \
                  $(NULL)
--- a/config/autoconf.mk.in
+++ b/config/autoconf.mk.in
@@ -45,18 +45,18 @@ target          = @target@
 ac_configure_args = @ac_configure_args@
 BUILD_MODULES	= @BUILD_MODULES@
 MOZILLA_VERSION = @MOZILLA_VERSION@
 FIREFOX_VERSION	= @FIREFOX_VERSION@
 
 MOZ_BUILD_APP = @MOZ_BUILD_APP@
 MOZ_APP_NAME	= @MOZ_APP_NAME@
 MOZ_APP_DISPLAYNAME = @MOZ_APP_DISPLAYNAME@
+MOZ_APP_UA_NAME = @MOZ_APP_UA_NAME@
 MOZ_APP_VERSION = @MOZ_APP_VERSION@
-MOZ_APP_UA_EXTRA = @MOZ_APP_UA_EXTRA@
 
 MOZ_PKG_SPECIAL = @MOZ_PKG_SPECIAL@
 
 prefix		= @prefix@
 exec_prefix	= @exec_prefix@
 bindir		= @bindir@
 includedir	= @includedir@/$(MOZ_APP_NAME)-$(MOZ_APP_VERSION)
 libdir		= @libdir@
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -142,82 +142,82 @@ endif
 
 ################################################################################
 # Testing frameworks support
 ################################################################################
 
 ifdef ENABLE_TESTS
 
 ifdef XPCSHELL_TESTS
-ifndef MODULE
-$(error Must define MODULE when defining XPCSHELL_TESTS.)
-endif
+#ifndef MODULE
+#$(error Must define MODULE when defining XPCSHELL_TESTS.)
+#endif
 
 testxpcobjdir = $(DEPTH)/_tests/xpcshell
 
 # Test file installation
 ifneq (,$(filter WINNT os2-emx,$(HOST_OS_ARCH)))
 # Windows and OS/2 nsinstall can't recursively copy directories, so use nsinstall.py
 TEST_INSTALLER = $(PYTHON) $(topsrcdir)/config/nsinstall.py
 else
 TEST_INSTALLER = $(INSTALL)
 endif
 
 define _INSTALL_TESTS
-$(TEST_INSTALLER) $(wildcard $(srcdir)/$(dir)/*) $(testxpcobjdir)/$(MODULE)/$(dir)
+$(TEST_INSTALLER) $(wildcard $(srcdir)/$(dir)/*) $(testxpcobjdir)/$(relativesrcdir)/$(dir)
 
 endef # do not remove the blank line!
 
 SOLO_FILE ?= $(error Specify a test filename in SOLO_FILE when using check-interactive or check-one)
 
 libs::
 	$(foreach dir,$(XPCSHELL_TESTS),$(_INSTALL_TESTS))
 	$(PYTHON) $(MOZILLA_DIR)/config/buildlist.py \
           $(testxpcobjdir)/all-test-dirs.list \
-          $(addprefix $(MODULE)/,$(XPCSHELL_TESTS))
+          $(addprefix $(relativesrcdir)/,$(XPCSHELL_TESTS))
 
 testxpcsrcdir = $(topsrcdir)/testing/xpcshell
 
 # Execute all tests in the $(XPCSHELL_TESTS) directories.
 # See also testsuite-targets.mk 'xpcshell-tests' target for global execution.
 xpcshell-tests:
 	$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
           -I$(topsrcdir)/build \
           $(testxpcsrcdir)/runxpcshelltests.py \
           --symbols-path=$(DIST)/crashreporter-symbols \
           $(EXTRA_TEST_ARGS) \
           $(DIST)/bin/xpcshell \
-          $(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(MODULE)/$(dir))
+          $(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(relativesrcdir)/$(dir))
 
 # Execute a single test, specified in $(SOLO_FILE), but don't automatically
 # start the test. Instead, present the xpcshell prompt so the user can
 # attach a debugger and then start the test.
 check-interactive:
 	$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
           -I$(topsrcdir)/build \
           $(testxpcsrcdir)/runxpcshelltests.py \
           --symbols-path=$(DIST)/crashreporter-symbols \
           --test-path=$(SOLO_FILE) \
           --profile-name=$(MOZ_APP_NAME) \
           --interactive \
           $(DIST)/bin/xpcshell \
-          $(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(MODULE)/$(dir))
+          $(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(relativesrcdir)/$(dir))
 
 # Execute a single test, specified in $(SOLO_FILE)
 check-one:
 	$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
           -I$(topsrcdir)/build \
           $(testxpcsrcdir)/runxpcshelltests.py \
           --symbols-path=$(DIST)/crashreporter-symbols \
           --test-path=$(SOLO_FILE) \
           --profile-name=$(MOZ_APP_NAME) \
           --verbose \
           $(EXTRA_TEST_ARGS) \
           $(DIST)/bin/xpcshell \
-          $(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(MODULE)/$(dir))
+          $(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(relativesrcdir)/$(dir))
 
 endif # XPCSHELL_TESTS
 
 ifdef CPP_UNIT_TESTS
 
 # Compile the tests to $(DIST)/bin.  Make lots of niceties available by default
 # through TestHarness.h, by modifying the list of includes and the libs against
 # which stuff links.
--- a/configure.in
+++ b/configure.in
@@ -2029,17 +2029,17 @@ case "$target" in
 	    MKSHLIB_UNFORCE_ALL=''
 	;;
     esac
     ;;
 
 *-darwin*) 
     MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -o $@'
     MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -o $@'
-    MOZ_OPTIMIZE_FLAGS="-O3"
+    MOZ_OPTIMIZE_FLAGS="-O3 -fomit-frame-pointer"
     _PEDANTIC=
     CFLAGS="$CFLAGS -fpascal-strings -fno-common"
     CXXFLAGS="$CXXFLAGS -fpascal-strings -fno-common"
     DLL_SUFFIX=".dylib"
     DSO_LDOPTS=''
     STRIP="$STRIP -x -S"
     _PLATFORM_DEFAULT_TOOLKIT='cairo-cocoa'
     TARGET_NSPR_MDCPUCFG='\"md/_darwin.cfg\"'
@@ -8762,18 +8762,21 @@ AC_SUBST(MOZ_OS2_USE_DECLSPEC)
 
 AC_SUBST(MOZ_POST_DSO_LIB_COMMAND)
 AC_SUBST(MOZ_POST_PROGRAM_COMMAND)
 AC_SUBST(MOZ_TIMELINE)
 AC_SUBST(OGLES_SDK_DIR)
 
 AC_SUBST(MOZ_APP_NAME)
 AC_SUBST(MOZ_APP_DISPLAYNAME)
+AC_DEFINE_UNQUOTED(MOZ_APP_UA_NAME, "$MOZ_APP_UA_NAME")
+AC_SUBST(MOZ_APP_UA_NAME)
+AC_DEFINE_UNQUOTED(MOZ_APP_VERSION, "$MOZ_APP_VERSION")
 AC_SUBST(MOZ_APP_VERSION)
-AC_SUBST(MOZ_APP_UA_EXTRA)
+AC_DEFINE_UNQUOTED(FIREFOX_VERSION, "$FIREFOX_VERSION")
 AC_SUBST(FIREFOX_VERSION)
 
 AC_SUBST(MOZ_PKG_SPECIAL)
 
 AC_SUBST(MOZILLA_OFFICIAL)
 
 dnl win32 options
 AC_SUBST(MOZ_MAPINFO)
--- a/content/base/public/nsIImageLoadingContent.idl
+++ b/content/base/public/nsIImageLoadingContent.idl
@@ -36,16 +36,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "imgIDecoderObserver.idl"
 
 interface imgIRequest;
 interface nsIChannel;
 interface nsIStreamListener;
 interface nsIURI;
+interface nsIDocument;
 
 /**
  * This interface represents a content node that loads images.  The interface
  * exists to allow getting information on the images that the content node
  * loads and to allow registration of observers for the image loads.
  *
  * Implementors of this interface should handle all the mechanics of actually
  * loading an image -- getting the URI, checking with content policies and
@@ -59,17 +60,17 @@ interface nsIURI;
  * observers to check which request they are getting notifications for.
  *
  * Observers added in mid-load will not get any notifications they
  * missed.  We should NOT freeze this interface without considering
  * this issue.  (It could be that the image status on imgIRequest is
  * sufficient, when combined with the imageBlockingStatus information.)
  */
 
-[scriptable, uuid(e036857e-3417-4812-a5f2-89668a616781)]
+[scriptable, uuid(7974082c-3267-4ab1-96fa-bd47d7e3a093)]
 interface nsIImageLoadingContent : imgIDecoderObserver
 {
   /**
    * Request types.  Image loading content nodes attempt to do atomic
    * image changes when the image url is changed.  This means that
    * when the url changes the new image load will start, but the old
    * image will remain the "current" request until the new image is
    * fully loaded.  At that point, the old "current" request will be
@@ -169,10 +170,14 @@ interface nsIImageLoadingContent : imgID
   void forceReload();
 
   /**
    * Enables/disables image state forcing. When |aForce| is PR_TRUE, we force
    * nsImageLoadingContent::ImageState() to return |aState|. Call again with |aForce|
    * as PR_FALSE to revert ImageState() to its original behaviour.
    */
   void forceImageState(in boolean aForce, in long aState);
-  
+
+  /**
+    * We need to be notified when our document changes.
+    */
+  [noscript, notxpcom] void NotifyOwnerDocumentChanged(in nsIDocument aOldDoc);
 };
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -6713,24 +6713,21 @@ nsDocument::RetrieveRelevantHeaders(nsIC
 
         if (NS_SUCCEEDED(rv)) {
           PRInt64 intermediateValue;
           LL_I2L(intermediateValue, PR_USEC_PER_MSEC);
           LL_MUL(modDate, msecs, intermediateValue);
         }
       }
     } else {
-      nsCOMPtr<nsIMultiPartChannel> partChannel = do_QueryInterface(aChannel);
-      if (partChannel) {
-        nsCAutoString contentDisp;
-        rv = partChannel->GetContentDisposition(contentDisp);
-        if (NS_SUCCEEDED(rv) && !contentDisp.IsEmpty()) {
-          SetHeaderData(nsGkAtoms::headerContentDisposition,
-                        NS_ConvertASCIItoUTF16(contentDisp));
-        }
+      nsCAutoString contentDisp;
+      rv = aChannel->GetContentDisposition(contentDisp);
+      if (NS_SUCCEEDED(rv) && !contentDisp.IsEmpty()) {
+        SetHeaderData(nsGkAtoms::headerContentDisposition,
+                      NS_ConvertASCIItoUTF16(contentDisp));
       }
     }
   }
 
   if (LL_IS_ZERO(modDate)) {
     // We got nothing from our attempt to ask nsIFileChannel and
     // nsIHttpChannel for the last modified time. Return the current
     // time.
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -612,17 +612,17 @@ nsFrameScriptExecutor::LoadFrameScriptIn
   if (!channel) {
     return;
   }
 
   nsCOMPtr<nsIInputStream> input;
   channel->Open(getter_AddRefs(input));
   nsString dataString;
   if (input) {
-    const PRUint32 bufferSize = 1024;
+    const PRUint32 bufferSize = 8192;
     char buffer[bufferSize];
     nsCString data;
     PRUint32 avail = 0;
     input->Available(&avail);
     PRUint32 read = 0;
     if (avail) {
       while (NS_SUCCEEDED(input->Read(buffer, bufferSize, &read)) && read) {
         data.Append(buffer, read);
--- a/content/base/src/nsImageLoadingContent.cpp
+++ b/content/base/src/nsImageLoadingContent.cpp
@@ -550,16 +550,34 @@ NS_IMETHODIMP nsImageLoadingContent::For
 
   return LoadImage(currentURI, PR_TRUE, PR_TRUE, nsnull, nsIRequest::VALIDATE_ALWAYS);
 }
 
 /*
  * Non-interface methods
  */
 
+void
+nsImageLoadingContent::NotifyOwnerDocumentChanged(nsIDocument *aOldDoc)
+{
+  // If we had a document before, unregister ourselves with it.
+  if (aOldDoc) {
+    if (mCurrentRequest)
+      aOldDoc->RemoveImage(mCurrentRequest);
+    if (mPendingRequest)
+      aOldDoc->RemoveImage(mPendingRequest);
+  }
+
+  // Re-track the images
+  if (mCurrentRequest)
+    TrackImage(mCurrentRequest);
+  if (mPendingRequest)
+    TrackImage(mPendingRequest);
+}
+
 nsresult
 nsImageLoadingContent::LoadImage(const nsAString& aNewURI,
                                  PRBool aForce,
                                  PRBool aNotify)
 {
   // First, get a document (needed for security checks and the like)
   nsIDocument* doc = GetOurDocument();
   if (!doc) {
--- a/content/base/src/nsNodeUtils.cpp
+++ b/content/base/src/nsNodeUtils.cpp
@@ -55,16 +55,17 @@
 #ifdef MOZ_XUL
 #include "nsXULElement.h"
 #endif
 #include "nsBindingManager.h"
 #include "nsGenericHTMLElement.h"
 #ifdef MOZ_MEDIA
 #include "nsHTMLMediaElement.h"
 #endif // MOZ_MEDIA
+#include "nsImageLoadingContent.h"
 
 using namespace mozilla::dom;
 
 // This macro expects the ownerDocument of content_ to be in scope as
 // |nsIDocument* doc|
 #define IMPL_MUTATION_NOTIFICATION(func_, content_, params_)      \
   PR_BEGIN_MACRO                                                  \
   nsINode* node = content_;                                       \
@@ -525,16 +526,23 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNod
       nsCOMPtr<nsIDOMHTMLMediaElement> domMediaElem(do_QueryInterface(aNode));
       if (domMediaElem) {
         nsHTMLMediaElement* mediaElem = static_cast<nsHTMLMediaElement*>(aNode);
         mediaElem->NotifyOwnerDocumentActivityChanged();
       }
     }
 #endif
 
+    // nsImageLoadingContent needs to know when its document changes
+    if (oldDoc != newDoc) {
+      nsCOMPtr<nsIImageLoadingContent> imageContent(do_QueryInterface(aNode));
+      if (imageContent)
+        imageContent->NotifyOwnerDocumentChanged(oldDoc);
+    }
+
     if (elem) {
       elem->RecompileScriptEventListeners();
     }
 
     if (aCx) {
       nsIXPConnect *xpc = nsContentUtils::XPConnect();
       if (xpc) {
         nsCOMPtr<nsIXPConnectJSObjectHolder> oldWrapper;
--- a/content/base/src/nsWebSocket.cpp
+++ b/content/base/src/nsWebSocket.cpp
@@ -2402,16 +2402,17 @@ NOT_IMPLEMENTED_IF_FUNC_1(GetOwner, nsIS
 NOT_IMPLEMENTED_IF_FUNC_1(SetOwner, nsISupports *owner)
 NOT_IMPLEMENTED_IF_FUNC_1(SetNotificationCallbacks,
                           nsIInterfaceRequestor *callbacks)
 NOT_IMPLEMENTED_IF_FUNC_1(GetSecurityInfo, nsISupports **securityInfo)
 NOT_IMPLEMENTED_IF_FUNC_1(GetContentType, nsACString &value)
 NOT_IMPLEMENTED_IF_FUNC_1(SetContentType, const nsACString &value)
 NOT_IMPLEMENTED_IF_FUNC_1(GetContentCharset, nsACString &value)
 NOT_IMPLEMENTED_IF_FUNC_1(SetContentCharset, const nsACString &value)
+NOT_IMPLEMENTED_IF_FUNC_1(GetContentDisposition, nsACString &value)
 NOT_IMPLEMENTED_IF_FUNC_1(GetContentLength, PRInt64 *value)
 NOT_IMPLEMENTED_IF_FUNC_1(SetContentLength, PRInt64 value)
 NOT_IMPLEMENTED_IF_FUNC_1(Open, nsIInputStream **_retval)
 NOT_IMPLEMENTED_IF_FUNC_2(AsyncOpen, nsIStreamListener *listener,
                           nsISupports *context)
 
 //-----------------------------------------------------------------------------
 // nsWebSocketEstablishedConnection::nsIHttpAuthenticableChannel
--- a/content/canvas/test/webgl/conformance/00_testFIXME_list.txt
+++ b/content/canvas/test/webgl/conformance/00_testFIXME_list.txt
@@ -67,12 +67,12 @@ tex-sub-image-2d.html
 texparameter-test.html
 texture-active-bind-2.html
 texture-active-bind.html
 texture-complete.html
 texture-formats-test.html
 texture-npot.html
 texture-transparent-pixels-initialized.html
 triangle.html
-uniform-location.html
+#uniform-location.html
 uniform-samplers-test.html
 uninitialized-test.html
 viewport-unchanged-upon-resize.html
--- a/content/canvas/test/webgl/failing_tests.txt
+++ b/content/canvas/test/webgl/failing_tests.txt
@@ -34,17 +34,16 @@ conformance/tex-image-and-sub-image-2d-w
 conformance/tex-image-and-sub-image-2d-with-video.html
 conformance/tex-image-with-format-and-type.html
 conformance/tex-image-with-invalid-data.html
 conformance/tex-input-validation.html
 conformance/texparameter-test.html
 conformance/texture-active-bind-2.html
 conformance/texture-formats-test.html
 conformance/texture-npot.html
-conformance/uniform-location.html
 conformance/uniform-samplers-test.html
 conformance/viewport-unchanged-upon-resize.html
 more/conformance/constants.html
 more/conformance/getContext.html
 more/conformance/quickCheckAPI.html
 more/conformance/quickCheckAPIBadArgs.html
 more/conformance/webGLArrays.html
 more/functions/bufferDataBadArgs.html
--- a/content/events/test/Makefile.in
+++ b/content/events/test/Makefile.in
@@ -85,16 +85,17 @@ include $(topsrcdir)/config/rules.mk
 		test_bug508479.html \
 		test_bug508906.html \
 		test_bug517851.html \
 		test_bug534833.html \
 		test_bug545268.html \
 		test_bug547996-1.html \
 		test_bug547996-2.xhtml \
 		test_bug556493.html \
+		test_bug586961.xul \
 		$(NULL)
 
 #bug 585630
 ifneq (mobile,$(MOZ_BUILD_APP))
 _TEST_FILES += \
 		test_dragstart.html \
 		$(NULL)
 endif
new file mode 100644
--- /dev/null
+++ b/content/events/test/test_bug586961.xul
@@ -0,0 +1,48 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="/tests/SimpleTest/test.css" type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=586961
+-->
+<window title="Mozilla Bug 586961"
+  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+  <title>Test for Bug 586961</title>
+  <script type="application/javascript" src="/MochiKit/packed.js" />
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"/>
+  <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"/>
+<body  xmlns="http://www.w3.org/1999/xhtml">
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=586961">Mozilla Bug 586961</a>
+
+  <p id="display"></p>
+<div id="content" style="display: none">
+</div>
+</body>
+
+<box onclick="clicked(event)">
+  <label id="controllabel" control="controlbutton" accesskey="k" value="Click here" />
+  <button id="controlbutton" label="Button" />
+</box>
+
+<script class="testbody" type="application/javascript;version=1.7"><![CDATA[
+
+/** Test for Bug 586961 **/
+
+function clicked(event) {
+  is(event.target.id, "controlbutton", "Accesskey was directed to controlled element.");
+  SimpleTest.finish();
+}
+
+function test() {
+  var accessKeyDetails = (navigator.platform.indexOf("Mac") >= 0) ?
+                         { ctrlKey : true } : 
+                         { altKey : true, shiftKey: true };
+  synthesizeKey("k", accessKeyDetails);
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(test, window);
+
+]]></script>
+
+</window>
--- a/content/html/content/public/Makefile.in
+++ b/content/html/content/public/Makefile.in
@@ -47,16 +47,17 @@ XPIDL_MODULE = content_html
 
 XPIDLSRCS = \
 		nsISelectElement.idl \
 		nsIFormSubmitObserver.idl \
 		nsIPhonetic.idl \
 		$(NULL)
 
 EXPORTS = \
+		nsIConstraintValidation.h \
 		nsIFormControl.h \
 		nsIForm.h \
 		nsIFormProcessor.h \
 		nsILink.h \
 		nsIRadioVisitor.h \
 		nsIRadioGroupContainer.h \
 		nsITextControlElement.h \
 		nsIFileControlElement.h \
rename from content/html/content/src/nsIConstraintValidation.h
rename to content/html/content/public/nsIConstraintValidation.h
--- a/content/html/content/src/nsIConstraintValidation.h
+++ b/content/html/content/public/nsIConstraintValidation.h
@@ -66,16 +66,18 @@ public:
   friend class nsDOMValidityState;
 
   virtual ~nsIConstraintValidation();
 
   PRBool IsValid() const { return mValidityBitField == 0; }
 
   PRBool IsCandidateForConstraintValidation() const;
 
+  NS_IMETHOD GetValidationMessage(nsAString& aValidationMessage);
+
 protected:
 
   enum ValidityStateType
   {
     VALIDITY_STATE_VALUE_MISSING    = 0x01, // 0b00000001
     VALIDITY_STATE_TYPE_MISMATCH    = 0x02, // 0b00000010
     VALIDITY_STATE_PATTERN_MISMATCH = 0x04, // 0b00000100
     VALIDITY_STATE_TOO_LONG         = 0x08, // 0b00001000
@@ -84,17 +86,16 @@ protected:
     VALIDITY_STATE_STEP_MISMATCH    = 0x40, // 0b01000000
     VALIDITY_STATE_CUSTOM_ERROR     = 0x80  // 0b10000000
   };
 
   // You can't instantiate an object from that class.
   nsIConstraintValidation();
 
   nsresult GetValidity(nsIDOMValidityState** aValidity);
-  nsresult GetValidationMessage(nsAString& aValidationMessage);
   nsresult CheckValidity(PRBool* aValidity);
   void     SetCustomValidity(const nsAString& aError);
 
   bool GetValidityState(ValidityStateType mState) const {
          return mValidityBitField & mState;
        }
 
   void   SetValidityState(ValidityStateType mState, PRBool mValue) {
--- a/content/html/content/src/nsHTMLInputElement.cpp
+++ b/content/html/content/src/nsHTMLInputElement.cpp
@@ -88,24 +88,34 @@
 
 #include "nsIDOMMutationEvent.h"
 #include "nsIDOMEventTarget.h"
 #include "nsMutationEvent.h"
 #include "nsIEventListenerManager.h"
 
 #include "nsRuleData.h"
 
-// input type=radio
-#include "nsIRadioGroupContainer.h"
+// input type=radio
+#include "nsIRadioGroupContainer.h"
 
 // input type=file
 #include "nsIFile.h"
 #include "nsILocalFile.h"
 #include "nsNetUtil.h"
 #include "nsDOMFile.h"
+#include "nsFileControlFrame.h"
+#include "nsTextControlFrame.h"
+#include "nsIFilePicker.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsIPrivateBrowsingService.h"
+#include "nsIContentURIGrouper.h"
+#include "nsIContentPrefService.h"
+#include "nsIObserverService.h"
+#include "nsIPopupWindowManager.h"
+#include "nsGlobalWindow.h"
 
 // input type=image
 #include "nsImageLoadingContent.h"
 #include "nsIDOMWindowInternal.h"
 
 #include "mozAutoDocUpdate.h"
 #include "nsHTMLFormElement.h"
 #include "nsContentCreatorFunctions.h"
@@ -134,16 +144,17 @@ static NS_DEFINE_CID(kLookAndFeelCID, NS
 #define NS_ORIGINAL_INDETERMINATE_VALUE (1 << 12)
 #define NS_CONTROL_TYPE(bits)  ((bits) & ~( \
   NS_OUTER_ACTIVATE_EVENT | NS_ORIGINAL_CHECKED_VALUE | NS_NO_CONTENT_DISPATCH | \
   NS_ORIGINAL_INDETERMINATE_VALUE))
 
 // whether textfields should be selected once focused:
 //  -1: no, 1: yes, 0: uninitialized
 static PRInt32 gSelectTextFieldOnFocus;
+UploadLastDir* nsHTMLInputElement::gUploadLastDir;
 
 static const nsAttrValue::EnumTable kInputTypeTable[] = {
   { "button", NS_FORM_INPUT_BUTTON },
   { "checkbox", NS_FORM_INPUT_CHECKBOX },
   { "email", NS_FORM_INPUT_EMAIL },
   { "file", NS_FORM_INPUT_FILE },
   { "hidden", NS_FORM_INPUT_HIDDEN },
   { "reset", NS_FORM_INPUT_RESET },
@@ -215,16 +226,374 @@ class nsHTMLInputElementState : public n
     nsTArray<nsString> mFilenames;
     PRPackedBool mChecked;
     PRPackedBool mCheckedSet;
 };
 
 NS_IMPL_ISUPPORTS1(nsHTMLInputElementState, nsHTMLInputElementState)
 NS_DEFINE_STATIC_IID_ACCESSOR(nsHTMLInputElementState, NS_INPUT_ELEMENT_STATE_IID)
 
+class AsyncClickHandler : public nsRunnable {
+public:
+  AsyncClickHandler(nsHTMLInputElement* aInput)
+   : mInput(aInput) {
+    
+    nsIDocument* doc = aInput->GetOwnerDoc();
+    if (doc) {
+      nsPIDOMWindow* win = doc->GetWindow();
+      if (win)
+        mPopupControlState = win->GetPopupControlState();
+    }
+  };
+
+  NS_IMETHOD Run();
+
+protected:
+  nsRefPtr<nsHTMLInputElement> mInput;
+  PopupControlState mPopupControlState;
+};
+
+NS_IMETHODIMP
+AsyncClickHandler::Run()
+{
+  nsresult rv;
+
+  // Get parent nsIDOMWindowInternal object.
+  nsCOMPtr<nsIDocument> doc = mInput->GetOwnerDoc();
+  if (!doc)
+    return NS_ERROR_FAILURE;
+
+  nsPIDOMWindow* win = doc->GetWindow();
+  if (!win) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // Check if page is allowed to open the popup
+  if (mPopupControlState != openAllowed) {
+    nsCOMPtr<nsIPopupWindowManager> pm =
+      do_GetService(NS_POPUPWINDOWMANAGER_CONTRACTID);
+ 
+    if (!pm) {
+      return NS_OK;
+    }
+
+    PRUint32 permission;
+    pm->TestPermission(doc->GetDocumentURI(), &permission);
+    if (permission == nsIPopupWindowManager::DENY_POPUP) {
+      nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(doc);
+      nsGlobalWindow::FirePopupBlockedEvent(domDoc, win, nsnull, EmptyString(), EmptyString());
+      return NS_OK;
+    }
+  }
+
+  // Get Loc title
+  nsXPIDLString title;
+  nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
+                                     "FileUpload", title);
+
+  nsCOMPtr<nsIFilePicker> filePicker = do_CreateInstance("@mozilla.org/filepicker;1");
+  if (!filePicker)
+    return NS_ERROR_FAILURE;
+
+  nsFileControlFrame* frame = static_cast<nsFileControlFrame*>(mInput->GetPrimaryFrame());
+  nsTextControlFrame* textFrame = nsnull;
+  if (frame)
+    textFrame = static_cast<nsTextControlFrame*>(frame->GetTextFrame());
+
+  PRBool multi;
+  rv = mInput->GetMultiple(&multi);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = filePicker->Init(win, title, multi ?
+                        (PRInt16)nsIFilePicker::modeOpenMultiple :
+                        (PRInt16)nsIFilePicker::modeOpen);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // We want to get the file filter from the accept attribute and we add the
+  // |filterAll| filter to be sure the user has a valid fallback.
+  PRUint32 filter = 0;
+  if (frame)
+    filter = frame->GetFileFilterFromAccept();
+  filePicker->AppendFilters(filter | nsIFilePicker::filterAll);
+
+  // If the accept attribute asks for a filter, it has to be the default one.
+  if (filter) {
+    // We have two filters: |filterAll| and another one. |filterAll| is
+    // always the first one (index=0) so we can assume the one we want to be
+    // the default is at index 1.
+    filePicker->SetFilterIndex(1);
+  }
+
+  // Set default directry and filename
+  nsAutoString defaultName;
+
+  nsCOMArray<nsIFile> oldFiles;
+  mInput->GetFileArray(oldFiles);
+
+  if (oldFiles.Count()) {
+    // set directory
+    nsCOMPtr<nsIFile> parentFile;
+    oldFiles[0]->GetParent(getter_AddRefs(parentFile));
+    if (parentFile) {
+      nsCOMPtr<nsILocalFile> parentLocalFile = do_QueryInterface(parentFile, &rv);
+      if (parentLocalFile) {
+        filePicker->SetDisplayDirectory(parentLocalFile);
+      }
+    }
+
+    // Unfortunately nsIFilePicker doesn't allow multiple files to be
+    // default-selected, so only select something by default if exactly
+    // one file was selected before.
+    if (oldFiles.Count() == 1) {
+      nsAutoString leafName;
+      oldFiles[0]->GetLeafName(leafName);
+      if (!leafName.IsEmpty()) {
+        filePicker->SetDefaultString(leafName);
+      }
+    }
+  } else {
+    // Attempt to retrieve the last used directory from the content pref service
+    nsCOMPtr<nsILocalFile> localFile;
+    nsHTMLInputElement::gUploadLastDir->FetchLastUsedDirectory(doc->GetDocumentURI(),
+                                                               getter_AddRefs(localFile));
+    if (!localFile) {
+      // Default to "desktop" directory for each platform
+      nsCOMPtr<nsIFile> homeDir;
+      NS_GetSpecialDirectory(NS_OS_DESKTOP_DIR, getter_AddRefs(homeDir));
+      localFile = do_QueryInterface(homeDir);
+    }
+    filePicker->SetDisplayDirectory(localFile);
+  }
+
+  // Tell our textframe to remember the currently focused value
+  if (textFrame)
+    textFrame->InitFocusedValue();
+
+  // Open dialog
+  PRInt16 mode;
+  rv = filePicker->Show(&mode);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (mode == nsIFilePicker::returnCancel)
+    return NS_OK;
+  
+  // Collect new selected filenames
+  nsTArray<nsString> newFileNames;
+  if (multi) {
+    nsCOMPtr<nsISimpleEnumerator> iter;
+    rv = filePicker->GetFiles(getter_AddRefs(iter));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsISupports> tmp;
+    PRBool prefSaved = PR_FALSE;
+    while (NS_SUCCEEDED(iter->GetNext(getter_AddRefs(tmp)))) {
+      nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(tmp);
+      if (localFile) {
+        nsString unicodePath;
+        rv = localFile->GetPath(unicodePath);
+        if (!unicodePath.IsEmpty()) {
+          newFileNames.AppendElement(unicodePath);
+        }
+        if (!prefSaved) {
+          // Store the last used directory using the content pref service
+          rv = nsHTMLInputElement::gUploadLastDir->StoreLastUsedDirectory(doc->GetDocumentURI(), 
+                                                                          localFile);
+          NS_ENSURE_SUCCESS(rv, rv);
+          prefSaved = PR_TRUE;
+        }
+      }
+    }
+  }
+  else {
+    nsCOMPtr<nsILocalFile> localFile;
+    rv = filePicker->GetFile(getter_AddRefs(localFile));
+    if (localFile) {
+      nsString unicodePath;
+      rv = localFile->GetPath(unicodePath);
+      if (!unicodePath.IsEmpty()) {
+        newFileNames.AppendElement(unicodePath);
+      }
+      // Store the last used directory using the content pref service
+      rv = nsHTMLInputElement::gUploadLastDir->StoreLastUsedDirectory(doc->GetDocumentURI(),
+                                                                      localFile);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+  }
+
+  // Set new selected files
+  if (!newFileNames.IsEmpty()) {
+    // Tell mTextFrame that this update of the value is a user initiated
+    // change. Otherwise it'll think that the value is being set by a script
+    // and not fire onchange when it should.
+    PRBool oldState;
+    if (textFrame) {
+      oldState = textFrame->GetFireChangeEventState();
+      textFrame->SetFireChangeEventState(PR_TRUE);
+    }
+
+    mInput->SetFileNames(newFileNames);
+    if (textFrame) {
+      textFrame->SetFireChangeEventState(oldState);
+      // May need to fire an onchange here
+      textFrame->CheckFireOnChange();
+    }
+  }
+
+  return NS_OK;
+}
+
+#define CPS_PREF_NAME NS_LITERAL_STRING("browser.upload.lastDir")
+
+NS_IMPL_ISUPPORTS2(UploadLastDir, nsIObserver, nsISupportsWeakReference)
+
+void
+nsHTMLInputElement::InitUploadLastDir() {
+  gUploadLastDir = new UploadLastDir();
+  NS_ADDREF(gUploadLastDir);
+
+  nsCOMPtr<nsIObserverService> observerService =
+    mozilla::services::GetObserverService();
+  if (observerService && gUploadLastDir) {
+    observerService->AddObserver(gUploadLastDir, NS_PRIVATE_BROWSING_SWITCH_TOPIC, PR_TRUE);
+    observerService->AddObserver(gUploadLastDir, "browser:purge-session-history", PR_TRUE);
+  }
+}
+
+void 
+nsHTMLInputElement::DestroyUploadLastDir() {
+  NS_IF_RELEASE(gUploadLastDir);
+}
+
+UploadLastDir::UploadLastDir():
+  mInPrivateBrowsing(PR_FALSE)
+{
+  nsCOMPtr<nsIPrivateBrowsingService> pbService =
+    do_GetService(NS_PRIVATE_BROWSING_SERVICE_CONTRACTID);
+  if (pbService) {
+    pbService->GetPrivateBrowsingEnabled(&mInPrivateBrowsing);
+  }
+
+  mUploadLastDirStore.Init();
+}
+
+nsresult
+UploadLastDir::FetchLastUsedDirectory(nsIURI* aURI, nsILocalFile** aFile)
+{
+  NS_PRECONDITION(aURI, "aURI is null");
+  NS_PRECONDITION(aFile, "aFile is null");
+  // Retrieve the data from memory if it's present during private browsing mode,
+  // otherwise fall through to check the CPS
+  if (mInPrivateBrowsing) {
+    nsCOMPtr<nsIContentURIGrouper> hostnameGrouperService =
+      do_GetService(NS_HOSTNAME_GROUPER_SERVICE_CONTRACTID);
+    if (!hostnameGrouperService)
+      return NS_ERROR_NOT_AVAILABLE;
+    nsString group;
+    hostnameGrouperService->Group(aURI, group);
+
+    if (mUploadLastDirStore.Get(group, aFile)) {
+      return NS_OK;
+    }
+  }
+
+  // Attempt to get the CPS, if it's not present we'll just return
+  nsCOMPtr<nsIContentPrefService> contentPrefService =
+    do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID);
+  if (!contentPrefService)
+    return NS_ERROR_NOT_AVAILABLE;
+  nsCOMPtr<nsIWritableVariant> uri = do_CreateInstance(NS_VARIANT_CONTRACTID);
+  if (!uri)
+    return NS_ERROR_OUT_OF_MEMORY;
+  uri->SetAsISupports(aURI);
+
+  // Get the last used directory, if it is stored
+  PRBool hasPref;
+  if (NS_SUCCEEDED(contentPrefService->HasPref(uri, CPS_PREF_NAME, &hasPref)) && hasPref) {
+    nsCOMPtr<nsIVariant> pref;
+    contentPrefService->GetPref(uri, CPS_PREF_NAME, nsnull, getter_AddRefs(pref));
+    nsString prefStr;
+    pref->GetAsAString(prefStr);
+
+    nsCOMPtr<nsILocalFile> localFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
+    if (!localFile)
+      return NS_ERROR_OUT_OF_MEMORY;
+    localFile->InitWithPath(prefStr);
+
+    *aFile = localFile;
+    NS_ADDREF(*aFile);
+  }
+  return NS_OK;
+}
+
+nsresult
+UploadLastDir::StoreLastUsedDirectory(nsIURI* aURI, nsILocalFile* aFile)
+{
+  NS_PRECONDITION(aURI, "aURI is null");
+  NS_PRECONDITION(aFile, "aFile is null");
+  nsCOMPtr<nsIFile> parentFile;
+  aFile->GetParent(getter_AddRefs(parentFile));
+  nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(parentFile);
+
+  // Store the data in memory instead of the CPS during private browsing mode
+  if (mInPrivateBrowsing) {
+    nsCOMPtr<nsIContentURIGrouper> hostnameGrouperService =
+      do_GetService(NS_HOSTNAME_GROUPER_SERVICE_CONTRACTID);
+    if (!hostnameGrouperService)
+      return NS_ERROR_NOT_AVAILABLE;
+    nsString group;
+    hostnameGrouperService->Group(aURI, group);
+
+    return mUploadLastDirStore.Put(group, localFile);
+  }
+
+  // Attempt to get the CPS, if it's not present we'll just return
+  nsCOMPtr<nsIContentPrefService> contentPrefService =
+    do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID);
+  if (!contentPrefService)
+    return NS_ERROR_NOT_AVAILABLE;
+  nsCOMPtr<nsIWritableVariant> uri = do_CreateInstance(NS_VARIANT_CONTRACTID);
+  if (!uri)
+    return NS_ERROR_OUT_OF_MEMORY;
+  uri->SetAsISupports(aURI);
+ 
+  // Find the parent of aFile, and store it
+  nsString unicodePath;
+  parentFile->GetPath(unicodePath);
+  if (unicodePath.IsEmpty()) // nothing to do
+    return NS_OK;
+  nsCOMPtr<nsIWritableVariant> prefValue = do_CreateInstance(NS_VARIANT_CONTRACTID);
+  if (!prefValue)
+    return NS_ERROR_OUT_OF_MEMORY;
+  prefValue->SetAsAString(unicodePath);
+  return contentPrefService->SetPref(uri, CPS_PREF_NAME, prefValue);
+}
+
+NS_IMETHODIMP
+UploadLastDir::Observe(nsISupports *aSubject, char const *aTopic, PRUnichar const *aData)
+{
+  if (strcmp(aTopic, NS_PRIVATE_BROWSING_SWITCH_TOPIC) == 0) {
+    if (NS_LITERAL_STRING(NS_PRIVATE_BROWSING_ENTER).Equals(aData)) {
+      mInPrivateBrowsing = PR_TRUE;
+    } else if (NS_LITERAL_STRING(NS_PRIVATE_BROWSING_LEAVE).Equals(aData)) {
+      mInPrivateBrowsing = PR_FALSE;
+      if (mUploadLastDirStore.IsInitialized()) {
+        mUploadLastDirStore.Clear();
+      }
+    }
+  } else if (strcmp(aTopic, "browser:purge-session-history") == 0) {
+    if (mUploadLastDirStore.IsInitialized()) {
+      mUploadLastDirStore.Clear();
+    }
+    nsCOMPtr<nsIContentPrefService> contentPrefService =
+      do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID);
+    if (contentPrefService)
+      contentPrefService->RemovePrefsByName(CPS_PREF_NAME);
+  }
+  return NS_OK;
+}
+
 #ifdef ACCESSIBILITY
 //Helper method
 static nsresult FireEventForAccessibility(nsIDOMHTMLInputElement* aTarget,
                                           nsPresContext* aPresContext,
                                           const nsAString& aEventType);
 #endif
 
 //
@@ -239,16 +608,19 @@ nsHTMLInputElement::nsHTMLInputElement(a
     mType(kInputDefaultType->value),
     mBitField(0)
 {
   SET_BOOLBIT(mBitField, BF_PARSER_CREATING, aFromParser);
   SET_BOOLBIT(mBitField, BF_INHIBIT_RESTORATION,
       aFromParser & NS_FROM_PARSER_FRAGMENT);
   mInputData.mState = new nsTextEditorState(this);
   NS_ADDREF(mInputData.mState);
+  
+  if (!gUploadLastDir)
+    nsHTMLInputElement::InitUploadLastDir();
 }
 
 nsHTMLInputElement::~nsHTMLInputElement()
 {
   DestroyImageLoadingContent();
   FreeData();
 }
 
@@ -1409,17 +1781,18 @@ nsHTMLInputElement::Click()
 
   // see what type of input we are.  Only click button, checkbox, radio,
   // reset, submit, & image
   if (mType == NS_FORM_INPUT_BUTTON   ||
       mType == NS_FORM_INPUT_CHECKBOX ||
       mType == NS_FORM_INPUT_RADIO    ||
       mType == NS_FORM_INPUT_RESET    ||
       mType == NS_FORM_INPUT_SUBMIT   ||
-      mType == NS_FORM_INPUT_IMAGE) {
+      mType == NS_FORM_INPUT_IMAGE    ||
+      mType == NS_FORM_INPUT_FILE) {
 
     // Strong in case the event kills it
     nsCOMPtr<nsIDocument> doc = GetCurrentDoc();
     if (!doc) {
       return rv;
     }
 
     nsCOMPtr<nsIPresShell> shell = doc->GetShell();
@@ -1441,27 +1814,36 @@ nsHTMLInputElement::Click()
       // called from chrome JS. Mark this event trusted if Click()
       // is called from chrome code.
       nsMouseEvent event(nsContentUtils::IsCallerChrome(),
                          NS_MOUSE_CLICK, nsnull, nsMouseEvent::eReal);
       event.inputSource = nsIDOMNSMouseEvent::MOZ_SOURCE_UNKNOWN;
       nsEventStatus status = nsEventStatus_eIgnore;
 
       SET_BOOLBIT(mBitField, BF_HANDLING_CLICK, PR_TRUE);
-
+      if (mType == NS_FORM_INPUT_FILE){
+        FireAsyncClickHandler();
+      }
       nsEventDispatcher::Dispatch(static_cast<nsIContent*>(this), context,
                                   &event, nsnull, &status);
 
       SET_BOOLBIT(mBitField, BF_HANDLING_CLICK, PR_FALSE);
     }
   }
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsHTMLInputElement::FireAsyncClickHandler()
+{
+  nsCOMPtr<nsIRunnable> event = new AsyncClickHandler(this);
+  return NS_DispatchToMainThread(event);
+}
+
 PRBool
 nsHTMLInputElement::NeedToInitializeEditorForEvent(nsEventChainPreVisitor& aVisitor) const
 {
   // We only need to initialize the editor for single line input controls because they
   // are lazily initialized.  We don't need to initialize the control for
   // certain types of events, because we know that those events are safe to be
   // handled without the editor being initialized.  These events include:
   // mousein/move/out, and DOM mutation events.
--- a/content/html/content/src/nsHTMLInputElement.h
+++ b/content/html/content/src/nsHTMLInputElement.h
@@ -70,16 +70,49 @@
 #define GET_BOOLBIT(bitfield, field) (((bitfield) & (0x01 << (field))) \
                                         ? PR_TRUE : PR_FALSE)
 #define SET_BOOLBIT(bitfield, field, b) ((b) \
                                         ? ((bitfield) |=  (0x01 << (field))) \
                                         : ((bitfield) &= ~(0x01 << (field))))
 
 class nsDOMFileList;
 
+class UploadLastDir : public nsIObserver, public nsSupportsWeakReference {
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+
+  UploadLastDir();
+
+  /**
+   * Fetch the last used directory for this location from the content
+   * pref service, if it is available.
+   *
+   * @param aURI URI of the current page
+   * @param aFile path to the last used directory
+   */
+  nsresult FetchLastUsedDirectory(nsIURI* aURI, nsILocalFile** aFile);
+
+  /**
+   * Store the last used directory for this location using the
+   * content pref service, if it is available
+   * @param aURI URI of the current page
+   * @param aFile file chosen by the user - the path to the parent of this
+   *        file will be stored
+   */
+  nsresult StoreLastUsedDirectory(nsIURI* aURI, nsILocalFile* aFile);
+private:
+  // Directories are stored here during private browsing mode
+  nsInterfaceHashtable<nsStringHashKey, nsILocalFile> mUploadLastDirStore;
+  PRBool mInPrivateBrowsing;
+};
+
+class nsIRadioGroupContainer;
+class nsIRadioVisitor;
+
 class nsHTMLInputElement : public nsGenericHTMLFormElement,
                            public nsImageLoadingContent,
                            public nsIDOMHTMLInputElement,
                            public nsITextControlElement,
                            public nsIPhonetic,
                            public nsIDOMNSEditableElement,
                            public nsIFileControlElement,
                            public nsIConstraintValidation
@@ -194,24 +227,32 @@ public:
    * button in the group.
    *
    * @return the selected button (or null).
    */
   already_AddRefed<nsIDOMHTMLInputElement> GetSelectedRadioButton();
 
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
 
+  NS_IMETHOD FireAsyncClickHandler();
+
   virtual void UpdateEditableState()
   {
     return UpdateEditableFormControlState();
   }
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_NO_UNLINK(nsHTMLInputElement,
                                                      nsGenericHTMLFormElement)
 
+  static UploadLastDir* gUploadLastDir;
+  // create and destroy the static UploadLastDir object for remembering
+  // which directory was last used on a site-by-site basis
+  static void InitUploadLastDir();
+  static void DestroyUploadLastDir();
+
   void MaybeLoadImage();
 
   virtual nsXPCClassInfo* GetClassInfo();
 
   // nsIConstraintValidation
   PRBool   IsTooLong();
   PRBool   IsValueMissing();
   PRBool   HasTypeMismatch();
--- a/content/html/content/src/nsIConstraintValidation.cpp
+++ b/content/html/content/src/nsIConstraintValidation.cpp
@@ -63,17 +63,17 @@ nsIConstraintValidation::GetValidity(nsI
     mValidity = new nsDOMValidityState(this);
   }
 
   NS_ADDREF(*aValidity = mValidity);
 
   return NS_OK;
 }
 
-nsresult
+NS_IMETHODIMP
 nsIConstraintValidation::GetValidationMessage(nsAString& aValidationMessage)
 {
   aValidationMessage.Truncate();
 
   if (IsCandidateForConstraintValidation() && !IsValid()) {
     if (GetValidityState(VALIDITY_STATE_CUSTOM_ERROR)) {
       aValidationMessage.Assign(mCustomValidity);
     } else if (GetValidityState(VALIDITY_STATE_TOO_LONG)) {
--- a/content/html/content/test/test_bug500885.html
+++ b/content/html/content/test/test_bug500885.html
@@ -47,25 +47,25 @@ MockFilePicker.prototype = {
   get files() {
     throw Cr.NS_ERROR_NOT_IMPLEMENTED;
   },
   show: function MFP_show() {
     return Ci.nsIFilePicker.returnOK;
   }
 };
 
-var mockFilePickerRegisterer =
+var mockFilePickerRegisterer = 
   new MockObjectRegisterer("@mozilla.org/filepicker;1",MockFilePicker);
 
-
 function test() {
   netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
   var wu = window.QueryInterface(Ci.nsIInterfaceRequestor)
                   .getInterface(Ci.nsIDOMWindowUtils);
                   
+
   mockFilePickerRegisterer.register();
   try {
     var domActivateEvents;
     var fileInput = document.getElementById("file");
     var rect = fileInput.getBoundingClientRect();
 
     fileInput.addEventListener ("DOMActivate", function () {
       domActivateEvents++;
@@ -77,18 +77,24 @@ function test() {
     is(domActivateEvents, 1, "click on text field should only fire 1 DOMActivate event");
 
     domActivateEvents = 0;
     wu.sendMouseEvent("mousedown", rect.right - 5, rect.top + 5, 0, 1, 0);
     wu.sendMouseEvent("mouseup", rect.right - 5, rect.top + 5, 0, 1, 0);
     is(domActivateEvents, 1, "click on button should only fire 1 DOMActivate event");
 
   } finally {
-    mockFilePickerRegisterer.unregister();
+    SimpleTest.executeSoon(unregister);
+    
   }
+}
+
+function unregister()
+{
+  mockFilePickerRegisterer.unregister();
   SimpleTest.finish();
 }
 
 window.onload = function() {
   SimpleTest.waitForExplicitFinish();
   setTimeout(test, 0);
 };
 
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -201,16 +201,29 @@ MyPrefChangedCallback(const char*aPrefNa
   }
 
   return 0;
 }
 
 // ==================================================================
 // =
 // ==================================================================
+static void
+ReportUseOfDeprecatedMethod(nsHTMLDocument* aDoc, const char* aWarning)
+{
+  nsContentUtils::ReportToConsole(nsContentUtils::eDOM_PROPERTIES,
+                                  aWarning,
+                                  nsnull, 0,
+                                  static_cast<nsIDocument*>(aDoc)->
+                                    GetDocumentURI(),
+                                  EmptyString(), 0, 0,
+                                  nsIScriptError::warningFlag,
+                                  "DOM Events");
+}
+
 nsresult
 NS_NewHTMLDocument(nsIDocument** aInstancePtrResult)
 {
   nsHTMLDocument* doc = new nsHTMLDocument();
   NS_ENSURE_TRUE(doc, NS_ERROR_OUT_OF_MEMORY);
 
   NS_ADDREF(doc);
   nsresult rv = doc->Init();
@@ -2351,26 +2364,32 @@ nsHTMLDocument::GetBodySize(PRInt32* aWi
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHTMLDocument::GetWidth(PRInt32* aWidth)
 {
   NS_ENSURE_ARG_POINTER(aWidth);
-
+  if (!mWarnedWidthHeight) {
+    ReportUseOfDeprecatedMethod(this, "UseOfDocumentWidthWarning");
+    mWarnedWidthHeight = true;
+  }
   PRInt32 height;
   return GetBodySize(aWidth, &height);
 }
 
 NS_IMETHODIMP
 nsHTMLDocument::GetHeight(PRInt32* aHeight)
 {
   NS_ENSURE_ARG_POINTER(aHeight);
-
+  if (!mWarnedWidthHeight) {
+    ReportUseOfDeprecatedMethod(this, "UseOfDocumentHeightWarning");
+    mWarnedWidthHeight = true;
+  }
   PRInt32 width;
   return GetBodySize(&width, aHeight);
 }
 
 NS_IMETHODIMP
 nsHTMLDocument::GetAlinkColor(nsAString& aAlinkColor)
 {
   aAlinkColor.Truncate();
@@ -2535,29 +2554,16 @@ nsHTMLDocument::GetSelection(nsAString& 
 
   rv = selection->ToString(getter_Copies(str));
 
   aReturn.Assign(str);
 
   return rv;
 }
 
-static void
-ReportUseOfDeprecatedMethod(nsHTMLDocument* aDoc, const char* aWarning)
-{
-  nsContentUtils::ReportToConsole(nsContentUtils::eDOM_PROPERTIES,
-                                  aWarning,
-                                  nsnull, 0,
-                                  static_cast<nsIDocument*>(aDoc)->
-                                    GetDocumentURI(),
-                                  EmptyString(), 0, 0,
-                                  nsIScriptError::warningFlag,
-                                  "DOM Events");
-}
-
 NS_IMETHODIMP
 nsHTMLDocument::CaptureEvents(PRInt32 aEventFlags)
 {
   ReportUseOfDeprecatedMethod(this, "UseOfCaptureEventsWarning");
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/content/html/document/src/nsHTMLDocument.h
+++ b/content/html/document/src/nsHTMLDocument.h
@@ -356,16 +356,18 @@ protected:
   PRUint32 mLoadFlags;
 
   PRPackedBool mIsFrameset;
 
   PRPackedBool mTooDeepWriteRecursion;
 
   PRPackedBool mDisableDocWrite;
 
+  PRPackedBool mWarnedWidthHeight;
+
   nsCOMPtr<nsIWyciwygChannel> mWyciwygChannel;
 
   /* Midas implementation */
   nsresult   GetMidasCommandManager(nsICommandManager** aCommandManager);
 
   nsCOMPtr<nsICommandManager> mMidasCommandManager;
 
   nsresult TurnEditingOff();
--- a/content/html/document/src/nsWyciwygChannel.cpp
+++ b/content/html/document/src/nsWyciwygChannel.cpp
@@ -260,16 +260,23 @@ nsWyciwygChannel::GetContentCharset(nsAC
 
 NS_IMETHODIMP
 nsWyciwygChannel::SetContentCharset(const nsACString &aContentCharset)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
+nsWyciwygChannel::GetContentDisposition(nsACString &aContentDisposition)
+{
+  aContentDisposition.Truncate();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsWyciwygChannel::GetContentLength(PRInt64 *aContentLength)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsWyciwygChannel::SetContentLength(PRInt64 aContentLength)
 {
--- a/content/media/test/Makefile.in
+++ b/content/media/test/Makefile.in
@@ -118,16 +118,18 @@ include $(topsrcdir)/config/rules.mk
 		test_new_audio.html \
 		test_paused.html \
 		test_paused_after_ended.html \
 		test_play_events.html \
 		test_play_events_2.html \
 		test_play_twice.html \
 		test_playback.html \
 		test_playback_errors.html \
+		test_preload_actions.html \
+		test_preload_attribute.html \
 		test_progress.html \
 		test_reactivate.html \
 		test_readyState.html \
 		test_replay_metadata.html \
 		test_seek.html \
 		test_seek2.html \
 		test_seek_out_of_range.html \
 		test_source.html \
--- a/content/media/test/file_access_controls.html
+++ b/content/media/test/file_access_controls.html
@@ -127,16 +127,20 @@ function nextTest() {
       // We're done, exit the test.
       window.close();
       return;
     }
   }
 
   if (gVideo && gVideo.parentNode)
     gVideo.parentNode.removeChild(gVideo);
+
+  gVideo = null;
+  netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+  Components.utils.forceGC();  
   
   gVideo = createVideo();
   gVideo.expectedResult = gTests[gTestNum].result;
   gVideo.testDescription = gTests[gTestNum].description;
   gVideo.src = gTests[gTestNum].url;
   //dump("Starting test " + gTestNum + " at " + gVideo.src + " expecting:" + gVideo.expectedResult + "\n");
   if (!gTestedRemoved) {
     document.body.appendChild(gVideo); 
@@ -149,15 +153,16 @@ function nextTest() {
 
 function done() {
   netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
   // Undo change to access control check pref.
   var prefService = Components.classes["@mozilla.org/preferences-service;1"]
                                .getService(Components.interfaces.nsIPrefService);
   var branch = prefService.getBranch("media.");
   branch.setBoolPref("enforce_same_site_origin", gOldPref);
+  mediaTestCleanup();
   opener.done();
 }
 
 </script>
 </body>
 </html>
 
--- a/content/media/test/manifest.js
+++ b/content/media/test/manifest.js
@@ -246,30 +246,132 @@ function checkMetadata(msg, e, test) {
     is(e.videoHeight, test.height, msg + " video height");
   }
   if (test.duration) {
     ok(Math.abs(e.duration - test.duration) < 0.1,
        msg + " duration (" + e.duration + ") should be around " + test.duration);
   }
 }
 
-// Returns true if all members of array 'v' have their _finished field set to true.
-function AllFinished(v) {
-  if (v.length == 0) {
-    return false;
-  }
-  for (var i=0; i<v.length; ++i) {
-    if (!v[i]._finished) {
-      return false;
-    }
-  }
-  return true;
-}
-
 // Returns the first test from candidates array which we can play with the
 // installed video backends.
 function getPlayableVideo(candidates) {
   var v = document.createElement("video");
   var resources = candidates.filter(function(x){return /^video/.test(x.type) && v.canPlayType(x.type);});
   if (resources.length > 0)
     return resources[0];
   return null;
 }
+
+// Number of tests to run in parallel. Warning: Each media element requires
+// at least 3 threads (4 on Linux), and on Linux each thread uses 10MB of
+// virtual address space. Beware!
+var PARALLEL_TESTS = 2;
+
+// Manages a run of media tests. Runs them in chunks in order to limit
+// the number of media elements/threads running in parallel. This limits peak
+// memory use, particularly on Linux x86 where thread stacks use 10MB of
+// virtual address space.
+// Usage:
+//   1. Create a new MediaTestManager object.
+//   2. Create a test startTest function. This takes a test object and a token,
+//      and performs anything necessary to start the test. The test object is an
+//      element in one of the g*Tests above. Your startTest function must call 
+//      MediaTestManager.start(token) if it starts a test. The test object is
+//      guaranteed to be playable by our supported decoders; you don't need to
+//      check canPlayType.
+//   3. When your tests finishes, call MediaTestManager.finished(), passing
+//      the token back to the manager. The manager may either start the next run
+//      or end the mochitest if all the tests are done.
+function MediaTestManager() {
+
+  // Sets up a MediaTestManager to runs through the 'tests' array, which needs
+  // to be one of, or have the same fields as, the g*Test arrays of tests. Uses
+  // the user supplied 'startTest' function to initialize the test. This 
+  // function must accept two arguments, the test entry from the 'tests' array,
+  // and a token. Call MediaTestManager.started(token) if you start the test,
+  // and MediaTestManager.finished(token) when the test finishes. You don't have
+  // to start every test, but if you call started() you *must* call finish()
+  // else you'll timeout. 
+  this.runTests = function(tests, startTest) {
+    this.testNum = 0;
+    this.tests = tests;
+    this.startTest = startTest;
+    this.tokens = [];
+    // Always wait for explicit finish.
+    SimpleTest.waitForExplicitFinish();
+    this.nextTest();
+  }
+  
+  // Registers that the test corresponding to 'token' has been started.
+  // Don't call more than once per token.
+  this.started = function(token) {
+    this.tokens.push(token);
+  }
+  
+  // Registers that the test corresponding to 'token' has finished. Call when
+  // you've finished your test. If all tests are complete this will finish the
+  // run, otherwise it may start up the next run. It's ok to call multiple times
+  // per token.
+  this.finished = function(token) {
+    var i = this.tokens.indexOf(token);
+    if (i != -1) {
+      // Remove the element from the list of running tests.
+      this.tokens.splice(i, 1);
+    }
+    if (this.tokens.length == 0) {
+      this.nextTest();
+    }
+  }
+  
+  // Starts the next batch of tests, or finishes if they're all done.
+  // Don't call this directly, call finished(token) when you're done.
+  this.nextTest = function() {
+    // Force a GC after every completed testcase. This ensures that any decoders
+    // with live threads waiting for the GC are killed promptly, to free up the
+    // thread stacks' address space.
+    netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+    Components.utils.forceGC();
+    if (this.testNum == this.tests.length) {
+      if (this.onFinished) {
+        this.onFinished();
+      }
+      mediaTestCleanup();
+      SimpleTest.finish();
+      return;
+    }
+    while (this.testNum < this.tests.length && this.tokens.length < PARALLEL_TESTS) {
+      var test = this.tests[this.testNum];
+      var token = (test.name ? (test.name + "-"): "") + this.testNum;
+      this.testNum++;
+
+      // Ensure we can play the resource type.
+      if (test.type && !document.createElement('video').canPlayType(test.type))
+        continue;
+      
+      // Do the init. This should start the test.
+      this.startTest(test, token);
+      
+    }
+    if (this.tokens.length == 0) {
+      // No tests were added, we must have tried everything, exit.
+      SimpleTest.finish();
+    }
+  }
+}
+
+// Ensures we've got no active video or audio elements in the document, and
+// forces a GC to release the address space reserved by the decoders' threads'
+// stacks.
+function mediaTestCleanup() {
+    var V = document.getElementsByTagName("video");
+    for (i=0; i<V.length; i++) {
+      V[i].parentNode.removeChild(V[i]);
+      V[i] = null;
+    }
+    var A = document.getElementsByTagName("audio");
+    for (i=0; i<A.length; i++) {
+      A[i].parentNode.removeChild(A[i]);
+      A[i] = null;
+    }
+    netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+    Components.utils.forceGC();
+}
--- a/content/media/test/seek6.js
+++ b/content/media/test/seek6.js
@@ -1,33 +1,42 @@
 function test_seek6(v, seekTime, is, ok, finish) {
 
 // Test for bug identified by Chris Pearce in comment 40 on
 // bug 449159.
 var seekCount = 0;
 var completed = false;
 var interval;
+var sum = 0;
+
+function poll() {
+  sum += v.currentTime;
+}
 
 function startTest() {
   if (completed)
     return false;
-  interval = setInterval(function() { v.currentTime=Math.random()*v.duration; }, 10);
+  interval = setInterval(poll, 10);
+  v.currentTime = Math.random() * v.duration;
   return false;
 }
 
 function seekEnded() {
   if (completed)
     return false;
 
   seekCount++;
   ok(true, "Seek " + seekCount);
   if (seekCount == 3) {
     clearInterval(interval);
     completed = true;
+    dump("Seek test 6 time sum:" + sum + "\n");
     finish();
+  } else {
+    v.currentTime = Math.random() * v.duration;
   }
   return false;
 }
 
 v.addEventListener("loadedmetadata", startTest, false);
 v.addEventListener("seeked", seekEnded, false);
 
 }
--- a/content/media/test/test_access_control.html
+++ b/content/media/test/test_access_control.html
@@ -3,32 +3,34 @@
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=451958
 -->
 <head>
   <title>Test for Bug 451958</title>
   <script type="application/javascript" src="/MochiKit/packed.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=451958">Mozilla Bug 451958</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 451958 **/
 
 function run() {
   window.open("http://example.org:80/tests/content/media/test/file_access_controls.html", "", "width=500,height=500");
 }
 
 function done() {
+  mediaTestCleanup();
   SimpleTest.finish();
 }
 
 addLoadEvent(run);
 SimpleTest.waitForExplicitFinish();
   
 </script>
 </pre>
--- a/content/media/test/test_audio1.html
+++ b/content/media/test/test_audio1.html
@@ -5,21 +5,29 @@
   <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
-for (var i = 0; i < gAudioTests.length; ++i) {
-  var test = gAudioTests[i];
+
+var manager = new MediaTestManager;
+
+function startTest(test, token) {
   var a1 = new Audio();
   if (!a1.canPlayType(test.type))
-    continue;
-
+    return;
+  manager.started(token);
+  
   is(a1.getAttribute("preload"), "auto", "preload:auto automatically set");
   is(a1.src, "", "Src set?");
+  a1 = null;
+  manager.finished(token);
 }
+
+manager.runTests(gAudioTests, startTest);
+
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_audio2.html
+++ b/content/media/test/test_audio2.html
@@ -5,22 +5,29 @@
   <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
+
 var tmpAudio = new Audio();
-for (var i = 0; i < gAudioTests.length; ++i) {
-  var test = gAudioTests[i];
+
+var manager = new MediaTestManager;
+
+function startTest(test, token) {
   if (!tmpAudio.canPlayType(test.type))
     continue;
-
+  manager.started(token);
   var a1 = new Audio(test.name);
   is(a1.getAttribute("preload"), "auto", "Preload automatically set to auto");
   is(a1.src.match("/[^/]+$"), "/" + test.name, "src OK");
+  manager.finished(token);
 }
+
+manager.runTests(gAudioTests, startTest);
+
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_autoplay.html
+++ b/content/media/test/test_autoplay.html
@@ -1,15 +1,16 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <title>Media test: autoplay attribute</title>
   <script type="text/javascript" src="/MochiKit/packed.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <video id='v1' onerror="event.stopPropagation();"></video><audio id='a1' onerror="event.stopPropagation();"></audio>
 <video id='v2' onerror="event.stopPropagation();" autoplay></video><audio id='a2' onerror="event.stopPropagation();"autoplay></audio>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 var v1 = document.getElementById('v1');
 var a1 = document.getElementById('a1');
@@ -21,12 +22,15 @@ ok(v2.autoplay, "v2.autoplay should be t
 ok(a2.autoplay, "v2.autoplay should be true");
 
 v1.autoplay = true;
 a1.autoplay = true;
 ok(v1.autoplay, "video.autoplay not true");
 ok(a1.autoplay, "audio.autoplay not true");
 is(v1.getAttribute("autoplay"), "", "video autoplay attribute not set");
 is(a1.getAttribute("autoplay"), "", "video autoplay attribute not set");
+
+mediaTestCleanup();
+
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_buffered.html
+++ b/content/media/test/test_buffered.html
@@ -18,17 +18,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 // Test for Bug 462957; HTMLMediaElement.buffered.
 
-var videos = [];
+var manager = new MediaTestManager;
 
 function ended(e) {
   var v = e.target;
   
   // The whole media should be buffered...
   var b = v.buffered;
   is(b.length, 1, v._name + ": Should be buffered in one range");
   is(b.start(0), 0, v._name + ": First range start should be media start");
@@ -62,47 +62,43 @@ function ended(e) {
   caught = false;
   try {
     b.end(b.length);
   } catch (e) {
     caught = e.code == DOMException.INDEX_SIZE_ERR;
   }
   is(caught, true, "Should throw INDEX_SIZE_ERR on over end bounds range");
   
-  v._finished = true;
-  if (AllFinished(videos)) {
-    SimpleTest.finish();
-  }
+  v.parentNode.removeChild(v);
+  manager.finished(v.token);
+
   return false;
 }
 
 // Only support for buffered is available for Ogg and WAV.
 // Eventually we'll support WebM as well.
 function supportsBuffered(type) {
   var s = type.toLowerCase();
   return /ogg$/.test(s) || /wav$/.test(s)
 }
 
-for (var i=0; i<gSeekTests.length; ++i) {
+function startTest(test, token) {
   var v = document.createElement('video');
-  var test = gSeekTests[i];
-  if (!v.canPlayType(test.type) || !supportsBuffered(test.type))
-    continue;
+  if (!supportsBuffered(test.type))
+    return;
+  v.token = token;
+  manager.started(token);
+
   v.src = test.name;
   v._name = test.name;
   v._test = test;
   v._finished = false;
   v.autoplay = true;
   v.addEventListener("ended", ended, false);
   document.body.appendChild(v);
-  videos.push(v);
 }
 
-if (videos.length == 0) {
-  todo(false, "No types supported");
-} else {
-  SimpleTest.waitForExplicitFinish();
-}
+manager.runTests(gSeekTests, startTest);
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_bug448534.html
+++ b/content/media/test/test_bug448534.html
@@ -11,17 +11,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=448535">Mozilla Bug 448534</a>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
-var videos = [];
+var manager = new MediaTestManager;
 
 function loaded(event) {
   var v = event.target;
   if (v._finished)
     return false;
   v.play();
   return false;
 }
@@ -37,39 +37,32 @@ function started(event) {
 }
 
 function stopped(event) {
   var v = event.target;
   if (v._finished)
     return false;
   v._finished = true;
   ok(v.paused, "Video should be paused after removing from the Document");
-  if (AllFinished(videos))
-    SimpleTest.finish();
+  manager.finished(v.token);
   return false;
 }
 
 
-for (var i=0; i<gSmallTests.length; ++i) {
+function startTest(test, token) {
   var v = document.createElement('video');
-  var test = gSmallTests[i];
-  if (!v.canPlayType(test.type))
-    continue;
+  v.token = token;
+  manager.started(token);
   v.src = test.name;
   v._played = false;
   v._finished = false;
   v.addEventListener("loadedmetadata", loaded, false);
   v.addEventListener("play", started, false);
   v.addEventListener("pause", stopped, false);
   document.body.appendChild(v);
-  videos.push(v);
 }
 
-if (videos.length == 0) {
-  todo(false, "No types supported");
-} else {
-  SimpleTest.waitForExplicitFinish();
-}
+manager.runTests(gSmallTests, startTest);
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_bug465498.html
+++ b/content/media/test/test_bug465498.html
@@ -7,17 +7,17 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=465498">Mozilla Bug 465498</a>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
-var videos = [];
+var manager = new MediaTestManager;
 
 function startTest(e) {
   e.target.play();
 }
 
 function playbackEnded(e) {
   var v = e.target;
   if (v._finished)
@@ -34,45 +34,40 @@ function playbackEnded(e) {
 function seekEnded(e) {
   var v = e.target;
   if (v._finished)
     return;
   ok(v.currentTime == 0, "Checking currentTime after seek: " +
      v.currentTime  + " for " + v._name);
   ok(!v.ended, "Checking ended is false for " + v._name);
   v._finished = true;
-  if (AllFinished(videos))
-    SimpleTest.finish();
+  v.parentNode.removeChild(v);
+  manager.finished(v.token);
 }
 
-for (var i=0; i<gSmallTests.length; ++i) {
-  var test = gSmallTests[i];
+function initTest(test, token) {
   var type = /^video/.test(test.type) ? "video" : "audio";
   var v = document.createElement(type);
-  var test = gSmallTests[i];
   if (!v.canPlayType(test.type))
-    continue;
+    return;
+  v.token = token;
+  manager.started(token);
   v._name = test.name;
   
   var s = document.createElement("source");
   s.type = test.type;
   s.src = test.name;
   v.appendChild(s);
 
   v._seeked = false;
   v._finished = false;
   v.addEventListener("loadedmetadata", startTest, false);
   v.addEventListener("ended", playbackEnded, false);
   v.addEventListener("seeked", seekEnded, false);
   document.body.appendChild(v);
-  videos.push(v);
 }
 
-if (videos.length == 0) {
-  todo(false, "No types supported");
-} else {
-  SimpleTest.waitForExplicitFinish();
-}
+manager.runTests(gSmallTests, initTest);
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_bug493187.html
+++ b/content/media/test/test_bug493187.html
@@ -12,63 +12,57 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script type="text/javascript" src="use_large_cache.js"></script>
   <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=493187">Mozilla Bug 493187</a>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
-var videos = [];
+var manager = new MediaTestManager;
 
 function start(e) {
   e.target.currentTime = e.target.duration / 4;
 }
 
 function startSeeking(e) {
   e.target._seeked = true;
 }
 
 function canPlayThrough(e) {
   var v = e.target;
   if (v._seeked && !v._finished) {
     ok(true, "Got canplaythrough after seek for " + v._name);
     v._finished = true;
-    if (AllFinished(videos))
-      SimpleTest.finish();
+    v.parentNode.removeChild(v);
+    manager.finished(v.token);
   }
 }
 
-for (var i=0; i<gSeekTests.length; ++i) {
-  var test = gSeekTests[i];
-
+function startTest(test, token) {
   // TODO: Bug 568402, there's a bug in the WAV backend where we sometimes
   // don't send canplaythrough events after seeking. Once that is fixed,
   // we should remove this guard below so that we run this test for audio.
   var isVideo = /^video/.test(test.type) ? true : false;
   if (!isVideo)
-    continue;
+    return;
 
   var v = document.createElement('video');
-  if (!v.canPlayType(test.type))
-    continue;
+  v.token = token;
+  manager.started(token);
+
   v.src = test.name;
   v._name = test.name;
   v._seeked = false;
   v._finished = false;
   v.preload = "auto";
   v.addEventListener("loadedmetadata", start, false);
   v.addEventListener("canplaythrough", canPlayThrough, false);
   v.addEventListener("seeking", startSeeking, false);
   document.body.appendChild(v);
-  videos.push(v);
 }
 
-if (videos.length == 0) {
-  todo(false, "No types supported");
-} else {
-  SimpleTest.waitForExplicitFinish();
-}
+manager.runTests(gSeekTests, startTest);
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_bug495145.html
+++ b/content/media/test/test_bug495145.html
@@ -12,33 +12,33 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script type="text/javascript" src="manifest.js"></script>
 
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=495145">Mozilla Bug 495145</a>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
-var videos = [];
+var manager = new MediaTestManager;
 
 function start(e) {
   e.target.play();
 }
 
 function ended1(e) {
   var v = e.target;
   if (v._finished)
     return;
 
   ++v._endCount;
   if (v._endCount == 2) {
     ok(true, "Playing after pause while ended works for " + v._name);
     v._finished = true;
-    if (AllFinished(videos))
-      SimpleTest.finish();
+    v.parentNode.removeChild(v);
+    manager.finished(v.token);
     return;
   }
 
   v.pause();
   v.play();
 }
 
 function ended2(e) {
@@ -52,44 +52,39 @@ function ended2(e) {
 
 function seeked2(e) {
   var v = e.target;
   if (v._finished)
     return;
 
   ok(v.paused, "Paused after seek after pause while ended for " + v._name);
   v._finished = true;
-  if (AllFinished(videos))
-    SimpleTest.finish();
+  v.parentNode.removeChild(v);
+  manager.finished(v.token);
 }
 
-function createVideo(test, x) {
+function createVideo(test, x, token) {
   var v = document.createElement('video');
-  if (!v.canPlayType(test.type))
-    return null;
+  v.token = token;
+  manager.started(token);
   v.src = test.name;
   v._name = test.name + "#" + x;
   v._endCount = 0;
   v._finished = false;
   v.addEventListener("loadedmetadata", start, false);
   v.addEventListener("ended", x == 1 ? ended1 : ended2, false);
   if (x == 2)
     v.addEventListener("seeked", seeked2, false);
   document.body.appendChild(v);
-  videos.push(v);
 }
 
-for (var i=0; i<gSmallTests.length; ++i) {
-  var test = gSmallTests[i];
-  createVideo(test, 1);
-  createVideo(test, 2);
+function startTest(test, token) {
+  createVideo(test, 1, token + "a");
+  createVideo(test, 2, token + "b");
 }
 
-if (videos.length == 0) {
-  todo(false, "No types supported");
-} else {
-  SimpleTest.waitForExplicitFinish();
-}
+manager.runTests(gSmallTests, startTest);
+
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_can_play_type.html
+++ b/content/media/test/test_can_play_type.html
@@ -3,16 +3,17 @@
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=469247
 -->
 <head>
   <title>Test for Bug 469247</title>
   <script type="application/javascript" src="/MochiKit/packed.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=469247">Mozill
 a Bug 469247</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 
@@ -27,12 +28,14 @@ function check(type, expected) {
   is(v.canPlayType(type), expected, type);
 }
 
 // Invalid types
 check("foo/bar", "");
 check("", "");
 check("!!!", "");
 
+mediaTestCleanup();
+
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_can_play_type_no_ogg.html
+++ b/content/media/test/test_can_play_type_no_ogg.html
@@ -3,27 +3,29 @@
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=469247
 -->
 <head>
   <title>Test for Bug 469247: Ogg backend disabled</title>
   <script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=469247">Mozill
 a Bug 469247</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 
 <video id="v" onerror="event.stopPropagation();"></video>
 
 <pre id="test">
 <script src="can_play_type_ogg.js"></script>
 
 check_ogg(document.getElementById('v'), false);
 
+mediaTestCleanup();
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_can_play_type_no_wave.html
+++ b/content/media/test/test_can_play_type_no_wave.html
@@ -3,27 +3,30 @@
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=469247
 -->
 <head>
   <title>Test for Bug 469247: WAVE backend disabled</title>
   <script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=469247">Mozill
 a Bug 469247</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 
 <video id="v" onerror="event.stopPropagation();"></video>
 
 <pre id="test">
 <script src="can_play_type_wave.js"></script>
 
 check_wave(document.getElementById('v'), false);
 
+mediaTestCleanup();
+
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_can_play_type_no_webm.html
+++ b/content/media/test/test_can_play_type_no_webm.html
@@ -3,27 +3,30 @@
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=566245
 -->
 <head>
   <title>Test for Bug 566245: WebM backend disabled</title>
   <script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=566245">Mozill
 a Bug 566245</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 
 <video id="v" onerror="event.stopPropagation();"></video>
 
 <pre id="test">
 <script src="can_play_type_webm.js"></script>
 
 check_webm(document.getElementById('v'), false);
 
+mediaTestCleanup();
+
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_can_play_type_ogg.html
+++ b/content/media/test/test_can_play_type_ogg.html
@@ -3,26 +3,29 @@
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=469247
 -->
 <head>
   <title>Test for Bug 469247: Ogg backend</title>
   <script type="application/javascript" src="/MochiKit/packed.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=469247">Mozill
 a Bug 469247</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 
 <video id="v" onerror="event.stopPropagation();"></video>
 
 <pre id="test">
 <script src="can_play_type_ogg.js"></script>
 <script>
 check_ogg(document.getElementById('v'), true);
+
+mediaTestCleanup();
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_can_play_type_wave.html
+++ b/content/media/test/test_can_play_type_wave.html
@@ -3,26 +3,29 @@
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=469247
 -->
 <head>
   <title>Test for Bug 469247: WAVE backend</title>
   <script type="application/javascript" src="/MochiKit/packed.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=469247">Mozill
 a Bug 469247</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 
 <video id="v" onerror="event.stopPropagation();"></video>
 
 <pre id="test">
 <script src="can_play_type_wave.js"></script>
 <script>
 check_wave(document.getElementById('v'), true);
+
+mediaTestCleanup();
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_can_play_type_webm.html
+++ b/content/media/test/test_can_play_type_webm.html
@@ -3,26 +3,28 @@
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=566245
 -->
 <head>
   <title>Test for Bug 566245: WebM backend</title>
   <script type="application/javascript" src="/MochiKit/packed.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=566245">Mozill
 a Bug 566245</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 
 <video id="v" onerror="event.stopPropagation();"></video>
 
 <pre id="test">
 <script src="can_play_type_webm.js"></script>
 <script>
 check_webm(document.getElementById('v'), true);
+mediaTestCleanup();
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_closing_connections.html
+++ b/content/media/test/test_closing_connections.html
@@ -16,16 +16,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <p id="display"></p>
 <div id="content" style="display: none">
   
 </div>
 
 <script type="application/javascript">
 window.onload = function() {
   ok(true, "loaded metadata for all videos");
+  mediaTestCleanup();
   SimpleTest.finish();
 }
 
 /*   With normal per-domain connection limits and a naive implementation, we
      won't ever be able to load all these videos because the first 15 (or whatever)
      will each take up one HTTP connection (which will be suspended) and then
      the others will be blocked by the per-domain HTTP connection limit. We
      pass this test by closing the connection for non-buffered videos after
--- a/content/media/test/test_contentDuration1.html
+++ b/content/media/test/test_contentDuration1.html
@@ -1,17 +1,17 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <title>Media test: seek test 1</title>
   <script type="text/javascript" src="/MochiKit/packed.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
-<body>
+<body onunload="mediaTestCleanup();">
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 function on_metadataloaded() {
   var v = document.getElementById('v');
   var d = Math.round(v.duration*1000);
   ok(d == 233, "Checking duration: " + d);
   SimpleTest.finish();
--- a/content/media/test/test_contentDuration2.html
+++ b/content/media/test/test_contentDuration2.html
@@ -1,17 +1,17 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <title>Media test: seek test 1</title>
   <script type="text/javascript" src="/MochiKit/packed.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
-<body>
+<body onunload="mediaTestCleanup();">
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 function on_metadataloaded() {
   var v = document.getElementById('v');
   var d = Math.round(v.duration*1000);
   ok(d == 233, "Checking duration: " + d);
   SimpleTest.finish();
--- a/content/media/test/test_contentDuration3.html
+++ b/content/media/test/test_contentDuration3.html
@@ -1,17 +1,17 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <title>Media test: seek test 1</title>
   <script type="text/javascript" src="/MochiKit/packed.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
-<body>
+<body onunload="mediaTestCleanup();">
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 function on_metadataloaded() {
   var v = document.getElementById('v');
   var d = Math.round(v.duration*1000);
   ok(d.toString() == "NaN", "Checking duration: " + d);
   SimpleTest.finish();
--- a/content/media/test/test_contentDuration4.html
+++ b/content/media/test/test_contentDuration4.html
@@ -1,17 +1,17 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <title>Media test: seek test 1</title>
   <script type="text/javascript" src="/MochiKit/packed.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
-<body>
+<body onunload="mediaTestCleanup();">
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 function on_metadataloaded() {
   var v = document.getElementById('v');
   var d = Math.round(v.duration*1000);
   ok(d.toString() == "NaN", "Checking duration: " + d);
   SimpleTest.finish();
--- a/content/media/test/test_contentDuration5.html
+++ b/content/media/test/test_contentDuration5.html
@@ -1,17 +1,17 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <title>Media test: seek test 1</title>
   <script type="text/javascript" src="/MochiKit/packed.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
-<body>
+<body onunload="mediaTestCleanup();">
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 function on_metadataloaded() {
   var v = document.getElementById('v');
   var d = Math.round(v.duration*1000);
   ok(d.toString() == "NaN", "Checking duration: " + d);
   SimpleTest.finish();
--- a/content/media/test/test_contentDuration6.html
+++ b/content/media/test/test_contentDuration6.html
@@ -1,17 +1,17 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <title>Media test: seek test 1</title>
   <script type="text/javascript" src="/MochiKit/packed.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
-<body>
+<body onunload="mediaTestCleanup();">
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 function on_metadataloaded() {
   var v = document.getElementById('v');
   var d = Math.round(v.duration*1000);
   ok(d.toString() == "NaN", "Checking duration: " + d);
   SimpleTest.finish();
--- a/content/media/test/test_contentDuration7.html
+++ b/content/media/test/test_contentDuration7.html
@@ -1,17 +1,18 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <title>Media test: X-Content-Duration</title>
   <script type="text/javascript" src="/MochiKit/packed.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="text/javascript" src="manifest.js"></script>
 </head>
-<body>
+<body onunload="mediaTestCleanup();">
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 function on_metadataloaded() {
   var v = document.getElementById('v');
   var d = Math.round(v.duration*1000);
   ok(d == 233, "Checking duration: " + d);
   SimpleTest.finish();
--- a/content/media/test/test_decode_error.html
+++ b/content/media/test/test_decode_error.html
@@ -6,60 +6,50 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="use_large_cache.js"></script>
   <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
-var testsWaiting = 0;
+var manager = new MediaTestManager;
 
-gDecodeErrorTests.forEach(function (test, index) {
-  var v1 = document.createElement("video");
-  if (!v1.canPlayType(test.type)) {
-    return;
-  }
-
-  v1.addEventListener("error", function (event) {
+function startTest(test, token) {
+  var v = document.createElement("video");
+  manager.started(token);
+  v.addEventListener("error", function (event) {
     event.stopPropagation();
     var el = event.currentTarget;
     is(event.type, "error", "Expected event of type 'error'");
     ok(el.error, "Element 'error' attr expected to have a value");
     ok(el.error instanceof MediaError, "Element 'error' attr expected to be MediaError");
     is(el.error.code, MediaError.MEDIA_ERR_DECODE, "Expected a decode error");
     is(el.networkState, HTMLMediaElement.NETWORK_EMPTY, "networkState should be EMPTY");
     el._sawError = true;
   }, false);
 
-  v1.addEventListener("emptied", function (event) {
+  v.addEventListener("emptied", function (event) {
     var el = event.currentTarget;
     is(el.networkState, HTMLMediaElement.NETWORK_EMPTY, "networkState should be EMPTY");
     ok(el._sawError, "Expected error event");
-    testsWaiting -= 1;
-    if (testsWaiting == 0) {
-      SimpleTest.finish();
-    }
+    manager.finished(token);
   }, false);
 
-  v1.addEventListener("loadedmetadata", function () {
+  v.addEventListener("loadedmetadata", function () {
     ok(false, "Unexpected loadedmetadata event");
   }, false);
 
-  v1.autoplay = true;
-  v1.addEventListener("ended", function () {
+  v.autoplay = true;
+  v.addEventListener("ended", function () {
     ok(false, "Unexpected ended event");
   }, false);
 
-  v1.src = test.name;
-  testsWaiting += 1;
-  v1.load();
-});
+  v.src = test.name;
+  v.load();
+}
 
-if (testsWaiting > 0) {
-  SimpleTest.waitForExplicitFinish();
-} else {
-  todo(false, "No types supported");
-}
+manager.runTests(gDecodeErrorTests, startTest);
+
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_delay_load.html
+++ b/content/media/test/test_delay_load.html
@@ -35,16 +35,18 @@ https://bugzilla.mozilla.org/show_bug.cg
          v._name + ":" + v.id + " is not ready before onload fired (" + v.readyState + ")");
     }
 
     netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
     for (i=0; i<testWindows.length; ++i) {
       testWindows[i].close();
     }
 
+    mediaTestCleanup();    
+
     SimpleTest.finish();
   }
   
   addLoadEvent(loaded);
   
 
   </script>
 </head>
@@ -59,67 +61,55 @@ https://bugzilla.mozilla.org/show_bug.cg
 <div id="log" style="font-size: small;"></div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 479711 **/
 
 function createVideo(name, type, id) {
   var v = document.createElement("video");
-  if (!v.canPlayType(type)) {
-    return null;
-  }
   v.src = name;
   v._name = name;
   v.id = id;
   register(v);
   return v;
 }
 
-for (var i=0; i<gSmallTests.length; ++i) {
-  var test = gSmallTests[i];
+var test = getPlayableVideo(gSmallTests);
+
+// Straightforward add, causing a load.
+var v = createVideo(test.name, test.type, "1");
+document.body.appendChild(v);
 
-  // Straightforward add, causing a load.
-  var v1 = createVideo(test.name, test.type, "1");
-  if (v1 == null) {
-    continue;
-  }
-  document.body.appendChild(v1);
-
-  // Load, add, then remove.
-  var v1 = createVideo(test.name, test.type, "1");
-  if (!v1) {
-    continue;
-  }
-  v1.load();
-  document.body.appendChild(v1);
-  v1.parentNode.removeChild(v1);
+// Load, add, then remove.
+v = createVideo(test.name, test.type, "1");
+v.load();
+document.body.appendChild(v);
+v.parentNode.removeChild(v);
 
-  // Load and add.
-  var v2 = createVideo(test.name, test.type, "2");
-  v2.load();
-  document.body.appendChild(v2);
+// Load and add.
+v = createVideo(test.name, test.type, "2");
+v.load();
+document.body.appendChild(v);
 
-  // Load outside of doc.
-  var v3 = createVideo(test.name, test.type, "3");
-  v3.load();
+// Load outside of doc.
+v = createVideo(test.name, test.type, "3");
+v.load();
 
-  // Load and move to another document.
-  netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
-  var v4 = createVideo(test.name, test.type, "4");
-  v4.load(); // load started while in this document, this doc's load will block until
-              // the video's finished loading (in the other document).
-  // Opening a new window to do this is a bit annoying, but if we use an iframe here,
-  // delaying of the iframe's load event might interfere with the firing of our load event
-  // in some confusing way. So it's simpler just to open another window.
-  var w = window.open("", "testWindow"+i, "width=400,height=400");
-  w.document.body.appendChild(v4);
-  testWindows.push(w);
-
-}
+// Load and move to another document.
+netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+v = createVideo(test.name, test.type, "4");
+v.load(); // load started while in this document, this doc's load will block until
+            // the video's finished loading (in the other document).
+// Opening a new window to do this is a bit annoying, but if we use an iframe here,
+// delaying of the iframe's load event might interfere with the firing of our load event
+// in some confusing way. So it's simpler just to open another window.
+var w = window.open("", "testWindow"+i, "width=400,height=400");
+w.document.body.appendChild(v);
+testWindows.push(w);
 
 if (gRegisteredElements.length > 0) {
   SimpleTest.waitForExplicitFinish();
 } else {
   todo(false, "No types supported");
 }
 
 </script>
--- a/content/media/test/test_info_leak.html
+++ b/content/media/test/test_info_leak.html
@@ -37,16 +37,17 @@ var gFinished = false;
 
 function log(msg) {
   //dump(msg + "\n");
   var l = document.getElementById('log');
   l.innerHTML += msg + "<br>";
 }
 
 function nextTest() {
+  mediaTestCleanup();
   if (gTestNum == gInfoLeakTests.length) {
     gFinished = true;
     SimpleTest.finish();
     return;
   }
   if (gMedia && gMedia.parentNode) {
     gMedia.parentNode.removeChild(gMedia);
     gMedia = null;
--- a/content/media/test/test_load_candidates.html
+++ b/content/media/test/test_load_candidates.html
@@ -16,45 +16,44 @@ https://bugzilla.mozilla.org/show_bug.cg
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 465458 **/
 
-var videos = [];
+var manager = new MediaTestManager;
 
 function finish(evt) {
-  is(evt.target._error, false, "Shouldn't have thrown an error - " + evt.target._name);
-  evt.target._finished = true;
-  if (AllFinished(videos)) {
-    SimpleTest.finish();
-  }
+  var v = evt.target;
+  is(v._error, false, "Shouldn't have thrown an error - " + v._name);
+  v._finished = true;
+  v.parentNode.removeChild(v);
+  manager.finished(v.token);
 }
 
 function errorHandler(evt) {
   evt.stopPropagation();
   evt.target._error = true;
   finish(evt);
 }
 
 var extenstion = {
   "audio/wav" : "wav",
   "audio/x-wav": "wav",
   "video/ogg" : "ogv",
   "audio/ogg" : "oga",
   "video/webm" : "webm"
 };
 
-for (var i=0; i<gSmallTests.length; ++i) {
+function startTest(test, token) {
   var v = document.createElement('video');
-  var test = gSmallTests[i];
-  if (!v.canPlayType(test.type))
-    continue;
+  v.token = token;
+  manager.started(token);
   v._error = false;
   v._finished = false;
   v._name = test.name;
   
   var s1 = document.createElement("source");
   s1.type = test.type;
   s1.src = "404." + extenstion[test.type];
   v.appendChild(s1);
@@ -63,23 +62,16 @@ for (var i=0; i<gSmallTests.length; ++i)
   s2.type = test.type;
   s2.src = test.name;
   v.appendChild(s2);
   
   v.addEventListener("error", errorHandler, false);
   v.addEventListener("loadeddata", finish, false);
   
   document.body.appendChild(v);
-  
-  videos.push(v);
 }
 
-
-if (videos.length == 0) {
-  todo(false, "No types supported");
-} else {
-  SimpleTest.waitForExplicitFinish();
-}
+manager.runTests(gSmallTests, startTest);
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_media_selection.html
+++ b/content/media/test/test_media_selection.html
@@ -5,46 +5,47 @@
   <script type="text/javascript" src="/MochiKit/packed.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="application/javascript" src="manifest.js"></script>
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
-SimpleTest.waitForExplicitFinish();
+var manager = new MediaTestManager;
 
 function maketest(attach_media, name, type, check_metadata) {
-  return function (testNum) {
+  return function (token) {
     var e = document.createElement('video');
+    manager.started(token);
     var errorRun = false;
     if (check_metadata) {
       e.addEventListener('loadedmetadata', function () {
           ok(e.readyState >= HTMLMediaElement.HAVE_METADATA,
-             'test ' +  testNum + ' readyState ' + e.readyState + ' expected >= ' + HTMLMediaElement.HAVE_METADATA);
-          is(e.currentSrc.substring(e.currentSrc.length - name.length), name, 'test ' + testNum);
+             'test ' +  token + ' readyState ' + e.readyState + ' expected >= ' + HTMLMediaElement.HAVE_METADATA);
+          is(e.currentSrc.substring(e.currentSrc.length - name.length), name, 'test ' + token);
           // The load can go idle due to cache size limits
           ok(e.networkState >= HTMLMediaElement.NETWORK_IDLE,
-              'test ' + testNum + ' networkState = ' + e.networkState + ' expected >= ' + HTMLMediaElement.NETWORK_IDLE);
+              'test ' + token + ' networkState = ' + e.networkState + ' expected >= ' + HTMLMediaElement.NETWORK_IDLE);
           check_metadata(e);
           e.parentNode.removeChild(e);
-          runNextTest();
+          manager.finished(token);
         }, false);
       e.addEventListener('error', function(e) { e.stopPropagation();}, false);      
     } else {
       e.addEventListener('error', function(event) {
         event.stopPropagation();
         is(errorRun, false, "error handler should run once only!");
         errorRun = true;
         is(e.readyState, HTMLMediaElement.HAVE_NOTHING,
-           'test ' + testNum + ' readyState should be HAVE_NOTHING when load fails.');
+           'test ' + token + ' readyState should be HAVE_NOTHING when load fails.');
         e.parentNode.removeChild(e);
-        runNextTest();
+        manager.finished(token);
       }, false);
-    }    
+    }
     attach_media(e, name, type);
   }
 }
 
 function set_src(element, name, type) {
   element.src = name;
   document.body.appendChild(element);
 }
@@ -123,24 +124,18 @@ for (var i = 0; i < gSmallTests.length; 
                 // should not start loading, type excludes it from media candiate list
                 maketest(add_source, src, 'bogus/type', null),
 
                 // element doesn't notice source children attached later, needs bug 462455 fixed
                 maketest(late_add_sources_last, src, type, check),
                 maketest(late_add_sources_first, src, type, check));
 }
 
-function runNextTest() {
-  if (nextTest >= subtests.length) {
-    SimpleTest.finish();
-    return;
-  }
-  setTimeout(function () {
-    subtests[nextTest](nextTest);
-    nextTest += 1;
-  }, 0);
+function startTest(test, token) {
+  test(token);
 }
 
-addLoadEvent(runNextTest);
+manager.runTests(subtests, startTest);
+
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_mozLoadFrom.html
+++ b/content/media/test/test_mozLoadFrom.html
@@ -7,67 +7,60 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
-var testsWaiting = 0;
+var manager = new MediaTestManager;
 
 function cloneLoaded(event) {
   ok(true, "Clone loaded OK");
   var e = event.target;
 
   if (e._expectedDuration) {
     ok(Math.abs(e.duration - e._expectedDuration) < 0.1,
        "Clone " + e.currentSrc + " duration: " + e.duration + " expected: " + e._expectedDuration);
   }
 
-  --testsWaiting;
-  if (testsWaiting == 0) {
-    SimpleTest.finish();
-  }
+  manager.finished(e.token);
 }
 
 function tryClone(event) {
   var e = event.target;
   var clone = e.cloneNode(false);
-
+  clone.token = e.token;
+  
   if (e._expectedDuration) {
     ok(Math.abs(e.duration - e._expectedDuration) < 0.1,
        e.currentSrc + " duration: " + e.duration + " expected: " + e._expectedDuration);
     clone._expectedDuration = e._expectedDuration;
   }
 
   clone.mozLoadFrom(e);
   is(clone.currentSrc, e.currentSrc, "Clone source OK");
 
   clone.addEventListener("loadeddata", cloneLoaded, false);
 }
 
-// Find something we can play
-for (var i = 0; i < gCloneTests.length; ++i) {
-  var test = gCloneTests[i];
+function initTest(test, token) {
   var elemType = /^audio/.test(test.type) ? "audio" : "video";
   var e = document.createElement(elemType);
   if (e.canPlayType(test.type)) {
     e.src = test.name;
     if (test.duration) {
       e._expectedDuration = test.duration;
     }
     e.addEventListener("loadeddata", tryClone, false);
     e.load();
-    ++testsWaiting;
+    e.token = token;
+    manager.started(token);
   }
 }
 
-if (testsWaiting == 0) {
-  todo(false, "Can't play anything");
-} else {
-  SimpleTest.waitForExplicitFinish();
-}
+manager.runTests(gCloneTests, initTest);
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_networkState.html
+++ b/content/media/test/test_networkState.html
@@ -1,17 +1,18 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <title>Media test: networkState</title>
   <script type="text/javascript" src="/MochiKit/packed.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="text/javascript" src="manifest.js"></script>
 </head>
-<body>
+<body onunload="mediaTestCleanup();">
 <video id='v1' onerror="event.stopPropagation();"></video><audio id='a1' onerror="event.stopPropagation();"></audio>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 "use strict";
 var v1 = document.getElementById('v1');
 var a1 = document.getElementById('a1');
 var passed = "truthy";
 
--- a/content/media/test/test_new_audio.html
+++ b/content/media/test/test_new_audio.html
@@ -16,46 +16,35 @@ https://bugzilla.mozilla.org/show_bug.cg
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 528566 **/
 
-var testsStarted = 0;
-var testsComplete = 0;
+var manager = new MediaTestManager;
 
 var player = new Audio();
 
-var audios = [];
-for (var i=0; i<gAudioTests.length; i++) {
-  var test = gAudioTests[i];
+function startTest(test, token) {
   if (!player.canPlayType(test.type))
-    continue;
+    return;
+  manager.started(token);
   var a = new Audio(test.name);
-  ++testsStarted;
   a.setAttribute("autoplay", false);
   a.addEventListener("canplaythrough",
                      function(e){ e.target.play(); },
                       false);
   a.addEventListener("ended",
                      function(e){
                         ok(true, "We should get to the end. Oh look we did.");
-                        testsComplete++;
-                        if (testsStarted == testsComplete) {
-                          SimpleTest.finish();
-                        }
+                        manager.finished(token);
                      },
                      false);
-  audios.push(a);
 }
 
-if (testsStarted != 0) {
-  SimpleTest.waitForExplicitFinish();
-} else {
-  todo(false, "No types supported");
-}
+manager.runTests(gAudioTests, startTest);
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_paused.html
+++ b/content/media/test/test_paused.html
@@ -1,20 +1,22 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <title>Media test: paused</title>
   <script type="text/javascript" src="/MochiKit/packed.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <video id='v1' onerror="event.stopPropagation();"></video><audio id='a1' onerror="event.stopPropagation();"></audio>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 var v1 = document.getElementById('v1');
 var a1 = document.getElementById('a1');
 ok(v1.paused, "v1.paused must initially be true");
 ok(a1.paused, "a1.paused must initially be true");
+mediaTestCleanup();
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_paused_after_ended.html
+++ b/content/media/test/test_paused_after_ended.html
@@ -6,45 +6,38 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
-var videos = [];
+var manager = new MediaTestManager;
 
 function ended(evt) {
   var v = evt.target;
   is(v.paused, false, v._name + " must not be paused after end");
-  if (AllFinished(videos)) {
-    SimpleTest.finish();
-  }
+  manager.finished(v.token);
 }
 
-for (var i=0; i<gPausedAfterEndedTests.length; ++i) {
+function startTest(test, token) {
   var v = document.createElement('video');
-  var test = gPausedAfterEndedTests[i];
-  if (!v.canPlayType(test.type))
-    continue;
+  v.token = v;
+  manager.started(v);
   v.src = test.name;
   v._name = test.name;
   v._finished = false;
   v.load();
   is(v.paused, true,  v._name + " must be paused at start");
 
   v.play();
   is(v.paused, false, v._name + " must not be paused after play");
 
   v.addEventListener("ended", ended, false);
 }
 
-if (videos.length == 0) {
-  todo(false, "No types supported");
-} else {
-  SimpleTest.waitForExplicitFinish();
-}
+manager.runTests(gPausedAfterEndedTests, startTest);
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_play_events.html
+++ b/content/media/test/test_play_events.html
@@ -6,17 +6,18 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <pre id="test">
 
 <script>
-var videos = [];
+
+var manager = new MediaTestManager;
 
 var tokens = {
   0:                ["play"],
   "play":           ["canplay"],
   "canplay":        ["playing"],
   "playing":        ["canplay", "canplaythrough"],
   "canplaythrough": ["canplay", "canplaythrough"]
 };
@@ -25,46 +26,36 @@ function gotPlayEvent(event) {
   var v = event.target;
   ok(tokens[v._state].indexOf(event.type) >= 0,
      "Check expected event got " + event.type + " at " + v._state + " for " + v.src);
   v._state = event.type;
 }
 
 function ended(event) {
   var v = event.target;
-  v._finished = true;
   is(v._state, "canplaythrough", "Last event should be canplaythrough for " + v.src);
-  if (AllFinished(videos)) {
-    SimpleTest.finish();
-  }
+  v.parentNode.removeChild(v);
+  manager.finished(v.token);
 }
 
-for (var i=0; i<gSmallTests.length; ++i) {
+function initTest(test, token) {
   var v = document.createElement('video');
-  var test = gSmallTests[i];
-  if (!v.canPlayType(test.type))
-    continue;
+  v.token = token;
+  manager.started(token);
   v._state = 0;
-  v._finished = false;
-
+ 
   ["play", "canplay", "playing", "canplaythrough"].forEach(function (e) {
     v.addEventListener(e, gotPlayEvent, false);
   });
 
   v.addEventListener("ended", ended, false);
 
   v.src = test.name;
   document.body.appendChild(v); // Causes load.
   v.play();
-  videos.push(v);
 }
 
-if (videos.length == 0) {
-  todo(false, "No types supported");
-} else {
-  SimpleTest.waitForExplicitFinish();
-}
-
+manager.runTests(gSmallTests, initTest);
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_play_events_2.html
+++ b/content/media/test/test_play_events_2.html
@@ -6,17 +6,17 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <pre id="test">
 
 <script>
-var videos = [];
+var manager = new MediaTestManager;
 
 var tokens = {
   0:                ["play"],
   "play":           ["canplay"],
   "canplay":        ["playing"],
   "playing":        ["canplay", "canplaythrough"],
   "canplaythrough": ["canplay", "canplaythrough"]
 };
@@ -27,44 +27,36 @@ function gotPlayEvent(event) {
      "Check expected event got " + event.type + " at " + v._state + " for " + v.src);
   v._state = event.type;
 }
 
 function ended(event) {
   var v = event.target;
   v._finished = true;
   is(v._state, "canplaythrough", "Last event should be canplaythrough for " + v.src);
-  if (AllFinished(videos)) {
-    SimpleTest.finish();
-  }
+  v.parentNode.removeChild(v);
+  manager.finished(v.token);
 }
 
-for (var i=0; i<gSmallTests.length; ++i) {
+function startTest(test, token) {
   var v = document.createElement('video');
-  var test = gSmallTests[i];
-  if (!v.canPlayType(test.type))
-    continue;
+  v.token = token;
+  manager.started(token);
   v._state = 0;
   v._finished = false;
 
   ["play", "canplay", "playing", "canplaythrough"].forEach(function (e) {
     v["on" + e] = gotPlayEvent;
   });
 
   v.onended = ended;
 
   v.src = test.name;
   document.body.appendChild(v); // Causes load.
   v.play();
-  videos.push(v);
 }
 
-if (videos.length == 0) {
-  todo(false, "No types supported");
-} else {
-  SimpleTest.waitForExplicitFinish();
-}
-
+manager.runTests(gSmallTests, startTest);
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_play_twice.html
+++ b/content/media/test/test_play_twice.html
@@ -5,98 +5,75 @@
   <script type="text/javascript" src="/MochiKit/packed.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
-var PARALLEL_TESTS = 2;
 
-var testIndex = 0;
-var videos = [];
-
-var testsWaiting = 0;
+var manager = new MediaTestManager;
 
-function startTests() {
-  for (var i = 0; i < videos.length; ++i) {
-    document.body.removeChild(videos[i]);
-  }
-  videos = [];
-  while (videos.length < PARALLEL_TESTS && testIndex < gReplayTests.length) {
-    var v = document.createElement('video');
-    var test = gReplayTests[testIndex];
-    ++testIndex;
-    if (!v.canPlayType(test.type))
-      continue;
-
-    v.src = test.name;
-    v.name = test.name;
+function startTest(test, token) {
+  var v = document.createElement('video');
+  v.token = token;
+  manager.started(token);
+  v.src = test.name;
+  v.name = test.name;
+  v.playingCount = 0;
+  var check = function(test, v) { return function() {
+    checkMetadata(test.name, v, test);
+  }}(test, v);
+  var noLoad = function(test, v) { return function() {
+    ok(false, test.name + " should not fire 'load' event");
+  }}(test, v);
+  var checkEnded = function(test, v) { return function() {
+    if (test.duration) {
+      ok(Math.abs(v.currentTime - test.duration) < 0.1,
+         test.name + " current time at end: " + v.currentTime);
+    }
+    is(v.readyState, v.HAVE_CURRENT_DATA, test.name + " checking readyState");
+    ok(v.readyState != v.NETWORK_LOADED, test.name + " shouldn't report NETWORK_LOADED");
+    ok(v.ended, test.name + " checking playback has ended");
+    ok(v.playingCount > 0, "Expect at least one playing event");
     v.playingCount = 0;
-    var check = function(test, v) { return function() {
-      checkMetadata(test.name, v, test);
-    }}(test, v);
-    var noLoad = function(test, v) { return function() {
-      ok(false, test.name + " should not fire 'load' event");
-    }}(test, v);
-    var checkEnded = function(test, v) { return function() {
-      if (test.duration) {
-        ok(Math.abs(v.currentTime - test.duration) < 0.1,
-           test.name + " current time at end: " + v.currentTime);
-      }
-      is(v.readyState, v.HAVE_CURRENT_DATA, test.name + " checking readyState");
-      ok(v.readyState != v.NETWORK_LOADED, test.name + " shouldn't report NETWORK_LOADED");
-      ok(v.ended, test.name + " checking playback has ended");
-      ok(v.playingCount > 0, "Expect at least one playing event");
-      v.playingCount = 0;
-      if (v._playedOnce) {
-        --testsWaiting;
-        if (testsWaiting == 0) {
-          setTimeout(startTests, 0);
-        }
-      } else {
-        v._playedOnce = true;
-        v.play();
-      }
-    }}(test, v);
-    var checkSuspended = function(test, v) { return function() {
-      if (v.seenSuspend)
-        return;
+    if (v._playedOnce && v.seenSuspend) {
+      v.parentNode.removeChild(v);
+      manager.finished(v.token);
+    } else {
+      v._playedOnce = true;
+      v.play();
+    }
+  }}(test, v);
+  var checkSuspended = function(test, v) { return function() {
+    if (v.seenSuspend)
+      return;
 
-      v.seenSuspend = true;
-      ok(true, test.name + " got suspend");
-      --testsWaiting;
-      if (testsWaiting == 0) {
-        setTimeout(startTests, 0);
-      }
-    }}(test, v);
-    var checkPlaying = function(test, v) { return function() {
-      is(test.name, v.name, "Should be testing file we think we're testing...");
-      v.playingCount++;
-    }}(test, v);
-    v.addEventListener("load", noLoad, false);
-    v.addEventListener("loadedmetadata", check, false);
-    v.addEventListener("playing", checkPlaying, false);
+    v.seenSuspend = true;
+    ok(true, test.name + " got suspend");
+    if (v._playedOnce && v.seenSuspend) {
+      v.parentNode.removeChild(v);
+      manager.finished(v.token);
+    }
+  }}(test, v);
+  var checkPlaying = function(test, v) { return function() {
+    is(test.name, v.name, "Should be testing file we think we're testing...");
+    v.playingCount++;
+  }}(test, v);
+  v.addEventListener("load", noLoad, false);
+  v.addEventListener("loadedmetadata", check, false);
+  v.addEventListener("playing", checkPlaying, false);
 
-    // We should get "ended" and "suspend" events for every resource
-    v.addEventListener("ended", checkEnded, false);
-    v.addEventListener("suspend", checkSuspended, false);
-    testsWaiting += 2;
-
-    document.body.appendChild(v);
-    v.play();
-    videos.push(v);
-  }
-  if (videos.length == 0) {
-    // No new tests were spawned, perhaps the remaining tests on the list are
-    // not supported, or we just reached the end of the list.
-    SimpleTest.finish();
-  }
+  // We should get "ended" and "suspend" events for every resource
+  v.addEventListener("ended", checkEnded, false);
+  v.addEventListener("suspend", checkSuspended, false);
+  
+  document.body.appendChild(v);
+  v.play();
 }
 
-SimpleTest.waitForExplicitFinish();
+manager.runTests(gReplayTests, startTest);
 
-addLoadEvent(startTests);
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_playback.html
+++ b/content/media/test/test_playback.html
@@ -5,98 +5,80 @@
   <script type="text/javascript" src="/MochiKit/packed.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
-var PARALLEL_TESTS = 2;
 
-var testIndex = 0;
-var videos = [];
-
-var testsWaiting = 0;
+var manager = new MediaTestManager;
 
-function startTests() {
-  for (var i = 0; i < videos.length; ++i) {
-    document.body.removeChild(videos[i]);
-  }
-  videos = [];
-  while (videos.length < PARALLEL_TESTS && testIndex < gPlayTests.length) {
-    var v = document.createElement('video');
-    var test = gPlayTests[testIndex];
-    ++testIndex;
-    if (!v.canPlayType(test.type))
-      continue;
+function startTest(test, token) {
+  var v = document.createElement('video');
+  v.token = token;
+  manager.started(token);
 
-    v.src = test.name;
-    v.name = test.name;
-    var check = function(test, v) { return function() {
-      is(test.name, v.name, "Name should match test.name #1");
-      checkMetadata(test.name, v, test);
-    }}(test, v);
-    var noLoad = function(test, v) { return function() {
-      ok(false, test.name + " should not fire 'load' event");
-    }}(test, v);
-    var checkEnded = function(test, v) { return function() {
-      if (test.duration) {
-        ok(Math.abs(v.currentTime - test.duration) < 0.1,
-           test.name + " current time at end: " + v.currentTime + " should be: " + test.duration);
-      }
-      is(test.name, v.name, "Name should match test.name #2");
-      is(v.readyState, v.HAVE_CURRENT_DATA, test.name + " checking readyState");
-      ok(v.readyState != v.NETWORK_LOADED, test.name + " shouldn't report NETWORK_LOADED");
-      ok(v.ended, test.name + " checking playback has ended");
-      --testsWaiting;
-      if (testsWaiting == 0) {
-        setTimeout(startTests, 0);
-      }
-    }}(test, v);
-    var checkSuspended = function(test, v) { return function() {
-      is(test.name, v.name, "Name should match test.name #3");
-      if (v.seenSuspend)
-        return;
+  v.src = test.name;
+  v.name = test.name;
+  var check = function(test, v) { return function() {
+    is(test.name, v.name, "Name should match test.name #1");
+    checkMetadata(test.name, v, test);
+  }}(test, v);
+  var noLoad = function(test, v) { return function() {
+    ok(false, test.name + " should not fire 'load' event");
+  }}(test, v);
+  var checkEnded = function(test, v) { return function() {
+    if (test.duration) {
+      ok(Math.abs(v.currentTime - test.duration) < 0.1,
+         test.name + " current time at end: " + v.currentTime + " should be: " + test.duration);
+    }
+    is(test.name, v.name, "Name should match test.name #2");
+    is(v.readyState, v.HAVE_CURRENT_DATA, test.name + " checking readyState");
+    ok(v.readyState != v.NETWORK_LOADED, test.name + " shouldn't report NETWORK_LOADED");
+    ok(v.ended, test.name + " checking playback has ended");
+    if (v.ended && v.seenSuspend) {
+      if (v.parentNode)
+        v.parentNode.removeChild(v);
+      manager.finished(v.token);
+    }
+  }}(test, v);
+  var checkSuspended = function(test, v) { return function() {
+    is(test.name, v.name, "Name should match test.name #3");
+    if (v.seenSuspend)
+      return;
 
-      v.seenSuspend = true;
-      ok(true, test.name + " got suspend");
-      --testsWaiting;
-      if (testsWaiting == 0) {
-        setTimeout(startTests, 0);
-      }
-    }}(test, v);
-    v.prevTime = 0;
-    var timeUpdate = function(test, v) { return function() {
-      is(test.name, v.name, "Name should match test.name #4");
-      checkMetadata(test.name, v, test);
-      ok(v.prevTime <= v.currentTime,
-         test.name + " time should run forwards: p=" +
-         v.prevTime + " c=" + v.currentTime);
-      v.prevTime = v.currentTime;
-    }}(test, v);
-    v.addEventListener("load", noLoad, false);
-    v.addEventListener("loadedmetadata", check, false);
-    v.addEventListener("timeupdate", timeUpdate, false);
+    v.seenSuspend = true;
+    ok(true, test.name + " got suspend");
+    if (v.ended && v.seenSuspend) {
+      if (v.parentNode)
+        v.parentNode.removeChild(v);
+      manager.finished(v.token);
+    }
+  }}(test, v);
+  v.prevTime = 0;
+  var timeUpdate = function(test, v) { return function() {
+    is(test.name, v.name, "Name should match test.name #4");
+    checkMetadata(test.name, v, test);
+    ok(v.prevTime <= v.currentTime,
+       test.name + " time should run forwards: p=" +
+       v.prevTime + " c=" + v.currentTime);
+    v.prevTime = v.currentTime;
+  }}(test, v);
+  v.addEventListener("load", noLoad, false);
+  v.addEventListener("loadedmetadata", check, false);
+  v.addEventListener("timeupdate", timeUpdate, false);
 
-    // We should get "ended" and "suspend" events for every resource
-    v.addEventListener("ended", checkEnded, false);
-    v.addEventListener("suspend", checkSuspended, false);
-    testsWaiting += 2;
+  // We should get "ended" and "suspend" events for every resource
+  v.addEventListener("ended", checkEnded, false);
+  v.addEventListener("suspend", checkSuspended, false);
 
-    document.body.appendChild(v);
-    v.play();
-    videos.push(v);
-  }
-  if (videos.length == 0) {
-    // No new tests were spawned, perhaps the remaining tests on the list are
-    // not supported, or we just reached the end of the list.
-    SimpleTest.finish();
-  }
+  document.body.appendChild(v);
+  v.play();
 }
 
-SimpleTest.waitForExplicitFinish();
+manager.runTests(gPlayTests, startTest);
 
-addLoadEvent(startTests);
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_playback_errors.html
+++ b/content/media/test/test_playback_errors.html
@@ -5,70 +5,46 @@
   <script type="text/javascript" src="/MochiKit/packed.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
-var PARALLEL_TESTS = 2;
 
-var testIndex = 0;
-var videos = [];
-
-var testsWaiting = 0;
-
-function startTests() {
-  for (var i = 0; i < videos.length; ++i) {
-    document.body.removeChild(videos[i]);
-  }
-  videos = [];
-  while (videos.length < PARALLEL_TESTS && testIndex < gErrorTests.length) {
-    var v = document.createElement('video');
-    var test = gErrorTests[testIndex];
-    ++testIndex;
-    if (!v.canPlayType(test.type))
-      continue;
+var manager = new MediaTestManager;
 
-    v.src = test.name;
-    v._errorCount = 0;
-    v._ignore = false;
-    function endedTest(v) {
-      if (v._ignore)
-        return;
-      v._ignore = true;
-      --testsWaiting;
-      if (testsWaiting == 0) {
-        setTimeout(startTests, 0);
-      }
-    }
-    var checkError = function(test, v) { return function(evt) {
-      evt.stopPropagation();
-      v._errorCount++;
-      is(v._errorCount, 1, test.name + " only one error fired");
-      endedTest(v);
-    }}(test, v);
-    var checkEnded = function(test, v) { return function() {
-      ok(false, test.name + " successfully played");
-      endedTest(v);
-    }}(test, v);
-    v.addEventListener("error", checkError, false);
-    v.addEventListener("ended", checkEnded, false);
-    ++testsWaiting;
-    document.body.appendChild(v);
-    v.play();
-    videos.push(v);
+function startTest(test, token) {
+  var v = document.createElement('video');
+  manager.started(token);
+  v._errorCount = 0;
+  v._ignore = false;
+  function endedTest(v) {
+    if (v._ignore)
+      return;
+    v._ignore = true;
+    v.parentNode.removeChild(v);
+    manager.finished(token);
   }
-  if (videos.length == 0) {
-    // No new tests were spawned, perhaps the remaining tests on the list are
-    // not supported, or we just reached the end of the list.
-    SimpleTest.finish();
-  }
+  var checkError = function(test, v) { return function(evt) {
+    evt.stopPropagation();
+    v._errorCount++;
+    is(v._errorCount, 1, test.name + " only one error fired");
+    endedTest(v);
+  }}(test, v);
+  var checkEnded = function(test, v) { return function() {
+    ok(false, test.name + " successfully played");
+    endedTest(v);
+  }}(test, v);
+  v.addEventListener("error", checkError, false);
+  v.addEventListener("ended", checkEnded, false);
+  v.src = test.name;
+  document.body.appendChild(v);
+  v.play();
 }
 
-SimpleTest.waitForExplicitFinish();
+manager.runTests(gErrorTests, startTest);
 
-addLoadEvent(startTests);
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_preload_actions.html
+++ b/content/media/test/test_preload_actions.html
@@ -19,41 +19,40 @@ https://bugzilla.mozilla.org/show_bug.cg
 </div>
 <!-- <button onClick="SimpleTest.finish();">Finish</button> -->
 <div>Tests complete: <span id="log" style="font-size: small;"></span></div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 548523 **/
 
-var videos = [];
+var manager = new MediaTestManager;
+
+manager.onFinished = function() {
+  is(gotLoadEvent, true, "Should not have delayed the load event indefinitely");
+};
 
 var test = getPlayableVideo(gSeekTests);
+var gTest = test;
 var bogusSrc = "bogus.duh";
 var bogusType = "video/bogus";
 var gotLoadEvent = false;
 var finished = false;
 
 addLoadEvent(function() {gotLoadEvent=true;});
 
 function log(m) {
   var l = document.getElementById("log");
   l.innerHTML += m;
 }
 
-function maybeFinish(n) {
+function maybeFinish(v, n) {
   log(n + ",");
-  setTimeout(
-  function() {
-    if (!finished && AllFinished(videos)) {
-      finished = true;
-      is(gotLoadEvent, true, "Should not have delayed the load event indefinitely");
-      SimpleTest.finish();
-    }
-  }, 0);
+  v.parentNode.removeChild(v);
+  manager.finished(v.token);
 }
 
 function filename(uri) {
   return uri.substr(uri.lastIndexOf("/")+1);
 }
 
 // Every test must have a setup(v) function, and must set _finished field on target v to
 // true when test is complete.
@@ -66,17 +65,17 @@ var tests = [
     suspend:
     function(e) {
       var v = e.target;
       is(v._gotLoadStart, true, "(1) Must get loadstart.");
       is(v._gotLoadedMetaData, false, "(1) Must not get loadedmetadata.");
       is(v.readyState, v.HAVE_NOTHING, "(1) ReadyState must be HAVE_NOTHING");
       is(v.networkState, v.NETWORK_IDLE, "(1) NetworkState must be NETWORK_IDLE");
       v._finished = true;
-      maybeFinish(1);
+      maybeFinish(v, 1);
     },
     
     setup:
     function(v) {
       v._gotLoadStart = false;
       v._gotLoadedMetaData = false;
       v._finished = false;
       v.preload = "none";
@@ -93,17 +92,17 @@ var tests = [
     loadeddata:
     function(e) {
       var v = e.target;
       is(v._gotLoadStart, true, "(2) Must get loadstart.");
       is(v._gotLoadedMetaData, true, "(2) Must get loadedmetadata.");
       ok(v.readyState >= v.HAVE_CURRENT_DATA, "(2) ReadyState must be >= HAVE_CURRENT_DATA");
       is(v.networkState, v.NETWORK_IDLE, "(2) NetworkState must be NETWORK_IDLE");
       v._finished = true;
-      maybeFinish(2);
+      maybeFinish(v, 2);
     },
     
     setup:
     function(v) {
       v._gotLoadStart = false;
       v._gotLoadedMetaData = false;
       v._finished = false;
       v.preload = "metadata";
@@ -118,17 +117,17 @@ var tests = [
   {
     // 3. Add preload:auto to document. Should receive canplaythrough eventually.
     canplaythrough:
     function(e) {
       var v = e.target;
       is(v._gotLoadStart, true, "(3) Must get loadstart.");
       is(v._gotLoadedMetaData, true, "(3) Must get loadedmetadata.");
       v._finished = true;
-      maybeFinish(3);
+      maybeFinish(v, 3);
     },
     
     setup:
     function(v) {
       v._gotLoadStart = false;
       v._gotLoadedMetaData = false;
       v._finished = false;
       v.preload = "auto";
@@ -153,17 +152,17 @@ var tests = [
       is(v.networkState, v.NETWORK_IDLE, "(4) NetworkState must be NETWORK_IDLE");
       v.play(); // Should load and play through.
     },
     
     ended:
     function(e) {
       ok(true, "(4) Got playback ended");
       e.target._finished = true;
-      maybeFinish(4);
+      maybeFinish(e.target, 4);
     },
       
     setup:
     function(v) {
       v._gotLoadStart = false;
       v._gotLoadedMetaData = false;
       v._gotSuspend = false;
       v._finished = false;
@@ -182,17 +181,17 @@ var tests = [
     suspend:
     function(e) {
       var v = e.target;
       is(v._gotLoadStart, true, "(5) Must get loadstart.");
       is(v._gotLoadedMetaData, false, "(5) Must not get loadedmetadata.");
       is(v.readyState, v.HAVE_NOTHING, "(5) ReadyState must be HAVE_NOTHING");
       is(v.networkState, v.NETWORK_IDLE, "(5) NetworkState must be NETWORK_IDLE");
       v._finished = true;
-      maybeFinish(5);
+      maybeFinish(v, 5);
     },
       
     setup:
     function(v) {
       v._gotLoadStart = false;
       v._gotLoadedMetaData = false;
       v._finished = false;
       v.preload = "none";
@@ -209,17 +208,17 @@ var tests = [
     suspend:
     function(e) {
       var v = e.target;
       is(v._gotLoadStart, true, "(6) Must get loadstart.");
       is(v._gotLoadedMetaData, false, "(6) Must not get loadedmetadata.");
       is(v.readyState, v.HAVE_NOTHING, "(6) ReadyState must be HAVE_NOTHING");
       is(v.networkState, v.NETWORK_IDLE, "(6) NetworkState must be NETWORK_IDLE");
       v._finished = true;
-      maybeFinish(6);
+      maybeFinish(v, 6);
     },
       
     setup:
     function(v) {
       v._gotLoadStart = false;
       v._gotLoadedMetaData = false;
       v._finished = false;
       v.preload = "none";
@@ -247,18 +246,19 @@ var tests = [
       is(v.readyState, v.HAVE_NOTHING, "(7) ReadyState must be HAVE_NOTHING");
       is(v.networkState, v.NETWORK_IDLE, "(7) NetworkState must be NETWORK_IDLE");
       v.play(); // Should load and play through.
     },
 
     ended:
     function(e) {
       ok(true, "(7) Got playback ended");
-      e.target._finished = true;
-      maybeFinish(7);
+      var v = e.target;
+      v._finished = true;
+      maybeFinish(v, 7);
     },
       
     setup:
     function(v) {
       v._gotLoadStart = false;
       v._gotLoadedMetaData = false;
       v._finished = false;
       v.preload = "none";
@@ -281,17 +281,17 @@ var tests = [
     // 8. Change preload value from none to metadata should cause metadata to be loaded.
     loadeddata:
     function(e) {
       var v = e.target;
       is(v._gotLoadedMetaData, true, "(8) Must get loadedmetadata.");
       ok(v.readyState >= v.HAVE_CURRENT_DATA, "(8) ReadyState must be >= HAVE_CURRENT_DATA on suspend.");
       is(v.networkState, v.NETWORK_IDLE, "(8) NetworkState must be NETWORK_IDLE when load is halted");
       v._finished = true;
-      maybeFinish(8);
+      maybeFinish(v, 8);
     },
     
     setup:
     function(v) {
       v._gotLoadedMetaData = false;
       v._finished = false;
       v.preload = "none";
       v.addEventListener("loadstart", function(e){v.preload = "metadata";}, false);
@@ -308,17 +308,17 @@ var tests = [
     canplaythrough:
     function(e) {
       var v = e.target;
       if (v._finished)
         return;
       is(v._gotLoadStart, true, "(9) Must get loadstart.");
       is(v._gotLoadedMetaData, true, "(9) Must get loadedmetadata.");
       v._finished = true;
-      maybeFinish(9);
+      maybeFinish(v, 9);
     },
     
     setup:
     function(v) {
       v._gotLoadStart = false;
       v._gotLoadedMetaData = false;
       v._finished = false;
       v.preload = "metadata";
@@ -332,17 +332,17 @@ var tests = [
   },*/
   {
     // 10. Change preload value from none to auto should cause entire media to be loaded.
     canplaythrough:
     function(e) {
       var v = e.target;
       is(v._gotLoadedMetaData, true, "(10) Must get loadedmetadata.");
       v._finished = true;
-      maybeFinish(10);
+      maybeFinish(v, 10);
     },
     
     setup:
     function(v) {
       v._gotLoadedMetaData = false;
       v._finished = false;
       v.preload = "none";
       v.addEventListener("loadstart", function(e){v.preload = "auto";}, false);
@@ -356,17 +356,17 @@ var tests = [
     // 11. Change preload value from none to metadata should cause metadata to load.
     loadeddata:
     function(e) {
       var v = e.target;
       is(v._gotLoadedMetaData, true, "(11) Must get loadedmetadata.");
       ok(v.readyState >= v.HAVE_CURRENT_DATA, "(11) ReadyState must be >= HAVE_CURRENT_DATA.");
       is(v.networkState, v.NETWORK_IDLE, "(11) NetworkState must be NETWORK_IDLE.");
       v._finished = true;
-      maybeFinish(11);
+      maybeFinish(v, 11);
     },
 
     setup:
     function(v) {
       v._gotLoadedMetaData = false;
       v._finished = false;
       v.preload = "none";
       v.addEventListener("loadstart", function(e){v.preload = "metadata";}, false);
@@ -380,17 +380,17 @@ var tests = [
     // 12. Change preload value from auto to metadata after load started,
     // should still do full load, should not halt after metadata only.
     canplaythrough:
     function(e) {
       var v = e.target;
       is(v._gotLoadedMetaData, true, "(12) Must get loadedmetadata.");
       is(v._gotLoadStart, true, "(12) Must get loadstart.");
       v._finished = true;
-      maybeFinish(12);
+      maybeFinish(v, 12);
     },
 
     setup:
     function(v) {
       v._gotLoadStart = false;
       v._gotLoadedMetaData = false;
       v._finished = false;
       v.preload = "auto";
@@ -409,17 +409,17 @@ var tests = [
     loadeddata:
     function(e) {
       var v = e.target;
       is(v._gotLoadStart, true, "(13) Must get loadstart.");
       is(v._gotLoadedMetaData, true, "(13) Must get loadedmetadata.");
       ok(v.readyState >= v.HAVE_CURRENT_DATA, "(13) ReadyState must be >= HAVE_CURRENT_DATA.");
       is(v.networkState, v.NETWORK_IDLE, "(13) NetworkState must be NETWORK_IDLE.");
       v._finished = true;
-      maybeFinish(13);
+      maybeFinish(v, 13);
     },
 
     setup:
     function(v) {
       v._gotLoadStart = false;
       v._gotLoadedMetaData = false;
       v._finished = false;
       v.preload = "metadata";
@@ -441,18 +441,19 @@ var tests = [
       ok(v.readyState >= v.HAVE_CURRENT_DATA, "(14) ReadyState must be >= HAVE_CURRENT_DATA");
       is(v.networkState, v.NETWORK_IDLE, "(14) NetworkState must be NETWORK_IDLE");
       v.play();
     },
     
     ended:
     function(e) {
       ok(true, "(14) Got playback ended");
-      e.target._finished = true;
-      maybeFinish(14);
+      var v = e.target;
+      v._finished = true;
+      maybeFinish(v, 14);
     },
 
     setup:
     function(v) {
       v._gotLoadStart = false;
       v._gotLoadedMetaData = false;
       v._finished = false;
       v.preload = "metadata";
@@ -465,18 +466,19 @@ var tests = [
                                      // metadata due to preload:metadata.
     },
   },
   {
     // 15. Autoplay should override preload:none.
     ended:
     function(e) {
       ok(true, "(15) Got playback ended.");
-      e.target._finished = true;
-      maybeFinish(15);
+      var v = e.target;
+      v._finished = true;
+      maybeFinish(v, 15);
     },
     
     setup:
     function(v) {
       v._gotLoadStart = false;
       v._gotLoadedMetaData = false;
       v._finished = false;
       v.preload = "none";
@@ -488,18 +490,19 @@ var tests = [
       document.body.appendChild(v); // Causes implicit load.
     },
   },
   {
     // 16. Autoplay should override preload:metadata.
     ended:
     function(e) {
       ok(true, "(16) Got playback ended.");
-      e.target._finished = true;
-      maybeFinish(16);
+      var v = e.target;
+      v._finished = true;
+      maybeFinish(v, 16);
     },
     
     setup:
     function(v) {
       v._finished = false;
       v.preload = "metadata";
       v.autoplay = true;
       v.addEventListener("ended", this.ended, false);
@@ -507,44 +510,38 @@ var tests = [
       document.body.appendChild(v); // Causes implicit load.
     },
   },
   {
     // 17. On a preload:none video, adding autoplay should disable preload none, i.e. don't break autoplay!
     ended:
     function(e) {
       ok(true, "(17) Got playback ended.");
-      e.target._finished = true;
-      maybeFinish(17);
+      var v = e.target;
+      v._finished = true;
+      maybeFinish(v, 17);
     },
     
     setup:
     function(v) {
       v.addEventListener("ended", this.ended, false);
       v._finished = false;
       v.preload = "none";
       document.body.appendChild(v); // Causes implicit load, which will be halted due to preload:none.
       v.autoplay = true;
       v.src = test.name;
     },    
   },
 ];
 
-if (test) {
-  for (var i=0; i<tests.length; i++) {
-    var t = tests[i];
-    var v = document.createElement("video");
-    videos.push(v);
-    t.setup(v);
-  }
+function startTest(test, token) {
+  var v = document.createElement("video");
+  v.token = token;
+  test.setup(v);
+  manager.started(token);
 }
 
-if (test && videos.length == 0) {
-  todo(false, "No types or tests supported");
-} else {
-  SimpleTest.waitForExplicitFinish();
-}
-
+manager.runTests(tests, startTest);
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_progress.html
+++ b/content/media/test/test_progress.html
@@ -7,56 +7,50 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <pre id="test">
 <script src="use_large_cache.js"></script>
 <script class="testbody" type="text/javascript">
 
-var videos = [];
+var manager = new MediaTestManager;
 
 function do_progress(e) {
   var v = e.target;
   ok(!v._finished, "Check no progress events after completed for " + v._name);
   ok(e.lengthComputable, "Check progress lengthComputable for " + v._name);
   ok(e.loaded >= v._last_progress_total, "Check progress increasing: " + e.loaded + " for " + v._name);
   v._last_progress_total = e.loaded;
   ok(e.loaded <= e.total, "Check progress in bounds: " + e.loaded + " for " + v._name);
   is(e.total, v._size, "Check progress total for " + v._name);
 }
 
 function do_ended(e) {
   var v = e.target;
   ok(!v._finished, "Only one ended event for " + v._name);
   v._finished = true;
-  if (AllFinished(videos))
-    SimpleTest.finish();
+  v.parentNode.removeChild(v);
+  manager.finished(v.token);
 }
 
-for (var i=0; i<gProgressTests.length; ++i) {
-  var test = gProgressTests[i];
+function startTest(test, token) {
   var type = /^video/.test(test.type) ? "video" : "audio";
   var v = document.createElement(type);
-  if (!v.canPlayType(test.type))
-    continue;
+  v.token = token;
+  manager.started(token);
   v.src = test.name;
   v.autoplay = true;
   v._name = test.name;
   v._size = test.size;
   v._finished = false;
   v._last_progress_total = 0;
   v.addEventListener("ended", do_ended, false);
   v.addEventListener("progress", do_progress, false);
   document.body.appendChild(v);
-  videos.push(v);
 }
 
-if (videos.length == 0) {
-  todo(false, "No types supported");
-} else {
-  SimpleTest.waitForExplicitFinish();
-}
+manager.runTests(gProgressTests, startTest);
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_replay_metadata.html
+++ b/content/media/test/test_replay_metadata.html
@@ -18,17 +18,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 // Test for Bug 467972. Tests that when we play to end, seek to 0, and play again, that we don't
 // send/receive multiple loadeddata and loadedmetadata events.
 
-var videos = [];
+var manager = new MediaTestManager;
 
 function seekStarted(evt) {
   var v = evt.target;
   v._gotSeekStarted = true;
 }
 
 function seekEnded(evt) {
   var v = evt.target;
@@ -70,28 +70,26 @@ function playbackEnded(evt) {
   } else {
     ok(v._gotSeekEnded, "Should have received seekended for " + v._name);
     ok(v._gotSeekStarted, "Should have received seekstarted for " + v._name);
     is(v._loadedDataCount, 1, "Should have 1 onloadeddata event for " + v._name);
     is(v._loadedMetaDataCount, 1, "Should have 1 onloadedmetadata event for " + v._name);
     is(v._endCount, 2, "Should have received two ended events for " + v._name);
     ok(v._playingCount > 0, "Should have at least one playing event for " + v._name);
     v._finished = true;
-    if (AllFinished(videos)) {
-      SimpleTest.finish();
-    }
+    v.parentNode.removeChild(v);
+    manager.finished(v.token);
   }  
   return false;
 }
 
-for (var i=0; i<gSmallTests.length; ++i) {
+function startTest(test, token) {
   var v = document.createElement('video');
-  var test = gSmallTests[i];
-  if (!v.canPlayType(test.type))
-    continue;
+  v.token = token;
+  manager.started(token);
   v.src = test.name;
   v._name = test.name;
   v._playedOnce = false;
   v._gotSeekEnded = false;
   v._gotSeekStarted = false;
   v._loadedDataCount = 0;
   v._loadedMetaDataCount = 0;
   v._playingCount = 0;
@@ -100,21 +98,16 @@ for (var i=0; i<gSmallTests.length; ++i)
   v._finished = false;
   v.addEventListener("loadedmetadata", loadedMetaData, false);
   v.addEventListener("ended", playbackEnded, false);
   v.addEventListener("playing", playing, false);
   v.addEventListener("loadeddata", loadedData, false);
   v.addEventListener("seeking", seekStarted, false);
   v.addEventListener("seeked", seekEnded, false);
   document.body.appendChild(v);
-  videos.push(v);
 }
 
-if (videos.length == 0) {
-  todo(false, "No types supported");
-} else {
-  SimpleTest.waitForExplicitFinish();
-}
+manager.runTests(gSmallTests, startTest);
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_seek.html
+++ b/content/media/test/test_seek.html
@@ -16,61 +16,64 @@
   <script type="text/javascript" src="seek8.js"></script>
   <script type="text/javascript" src="seek9.js"></script>
   <script type="text/javascript" src="seek10.js"></script>
   <script type="text/javascript" src="seek11.js"></script>
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
-var testNum = 0;
-var testsWaiting = 0;
-var videos = [];
+
+var manager = new MediaTestManager;
 
-var tmpVid = document.createElement("video");
-function doNextFile() {
-  if (testNum >= gSeekTests.length) {
-    SimpleTest.finish();
-    return;
-  }
+const NUM_SEEK_TESTS = 11
 
-  var test = gSeekTests[testNum];
-  ++testNum;
-
-  if (!tmpVid.canPlayType(test.type)) {
-    doNextFile();
-    return;
-  }
+function createTestArray() {
+  var tests = [];
+  var tmpVid = document.createElement("video");
 
-  for (var i = 1; i <= 11; ++i) {
-    var v = document.createElement('video');
-    v.src = test.name;
-    document.body.appendChild(v);
-    var name = test.name + " seek test " + i;
-    var localIs = function(name) { return function(a, b, msg) {
-      is(a, b, name + ": " + msg);
-    }}(name);
-    var localOk = function(name) { return function(a, msg) {
-      ok(a, name + ": " + msg);
-    }}(name);
-    var localFinish = function() {
-      --testsWaiting;
-      if (testsWaiting == 0) {
-        for (var j = 0; j < videos.length; ++j) {
-          document.body.removeChild(videos[j]);
-        }
-        videos = [];
-        doNextFile();
-      }
-    };
-    dump("Seek test: " + i + "\n");
-    window['test_seek' + i](v, test.duration/2, localIs, localOk, localFinish);
-    ++testsWaiting;
+  for (var testNum=0; testNum<gSeekTests.length; testNum++) {
+    var test = gSeekTests[testNum];
+    if (!tmpVid.canPlayType(test.type)) {
+      continue;
+    }
+
+    for (var i = 1; i <= NUM_SEEK_TESTS; ++i) {
+      var t = new Object;
+      t.name = test.name;
+      t.type = test.type;
+      t.duration = test.duration;
+      t.number = i;
+      tests.push(t);
+    }
   }
+  return tests;
 }
 
-doNextFile();
+function startTest(test, token) {
+  var v = document.createElement('video');
+  manager.started(token);
+  v.src = test.name;
+  v.token = token;
+  document.body.appendChild(v);
+  var name = test.name + " seek test " + test.number;
+  var localIs = function(name) { return function(a, b, msg) {
+    is(a, b, name + ": " + msg);
+  }}(name);
+  var localOk = function(name) { return function(a, msg) {
+    ok(a, name + ": " + msg);
+  }}(name);
+  var localFinish = function(v, manager) { return function() {
+    if (v.parentNode) {
+      v.parentNode.removeChild(v);
+    }
+    manager.finished(v.token);
+  }}(v, manager);
+  dump("Seek test: " + test.number + "\n");
+  window['test_seek' + test.number](v, test.duration/2, localIs, localOk, localFinish);
+}
 
-SimpleTest.waitForExplicitFinish();
+manager.runTests(createTestArray(), startTest);
+
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_seek_out_of_range.html
+++ b/content/media/test/test_seek_out_of_range.html
@@ -6,17 +6,17 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
-var videos = [];
+var manager = new MediaTestManager;
 
 // Test if the ended event works correctly.
 
 function startTest(e) {
   var v = e.target;
   checkMetadata(v._name, v, v._test);
   is(v._loadedMetadata, false, "Should only receive one loadedmetadata event for " + v._name);
   v._loadedMetadata = true;
@@ -28,57 +28,51 @@ function playbackEnded(e) {
   // We should have dispatched an ended event when we seeked to the end of
   // media, but we want the ended event which dispatches once playback has
   // completed after the seek to the beginning.
   if (!v._played)
     return;
   ok(v.ended, "Checking ended set after seeking to EOF and playing for " + v._name);
   ok(!v._finished, "Should only hit the end once for " + v._name);
   v._finished = true;
-  if (AllFinished(videos))
-    SimpleTest.finish();
+  v.parentNode.removeChild(v);
+  manager.finished(v.token);
 }
 
 function endSeek(e) {
   var v = e.target;
   if (v._seeked)
     return;
   v._seeked = true;
   ok(Math.abs(v.duration - v.currentTime) < 0.1,
      "Should be at end of media for " + v._name + " t=" + v.currentTime + " d=" + v.duration);
   v.play();
 }
 
 function playing(e) {
   e.target._played = true;
 }
 
-for (var i=0; i<gSmallTests.length; ++i) {
-  var test = gSmallTests[i];
+function initTest(test, token) {
   var type = /^video/.test(test.type) ? "video" : "audio";
   var v = document.createElement(type);
-  if (!v.canPlayType(test.type))
-    continue;
+  v.token = token;
+  manager.started(token);
   v.src = test.name;
   v._name = test.name;
   v._finished = false;
   v._test = test;
   v._loadedMetadata = false;
   v._seeked = false;
   v._played = false;
   v.addEventListener("loadedmetadata", startTest, false);
   v.addEventListener("playing", playing, false);
   v.addEventListener("seeked", endSeek, false);
   v.addEventListener("ended", playbackEnded, false);
   document.body.appendChild(v);
-  videos.push(v);
 }
 
-if (videos.length == 0) {
-  todo(false, "No types supported");
-} else {
-  SimpleTest.waitForExplicitFinish();
-}
+manager.runTests(gSmallTests, initTest);
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_timeupdate_small_files.html
+++ b/content/media/test/test_timeupdate_small_files.html
@@ -11,49 +11,43 @@ https://bugzilla.mozilla.org/show_bug.cg
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=495319">Mozilla Bug 495319</a>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
-var videos = [];
+var manager = new MediaTestManager;
 
 function timeupdate(e) {
   e.target._timeupdateCount++;
 }
 
 function ended(e) {
   var v = e.target;
   ok(v._timeupdateCount > 0, v._name + " - should see at least one timeupdate: " + v.currentTime);
-  e.target._finished = true;
-  if (AllFinished(videos))
-    SimpleTest.finish();
+  v._finished = true;
+  v.parentNode.removeChild(v);
+  manager.finished(v.token);
 }
 
-for (var i=0; i<gSmallTests.length; ++i) {
-  var test = gSmallTests[i];
+function startTest(test, token) {
   var type = /^video/.test(test.type) ? "video" : "audio";
   var v = document.createElement(type);
-  if (!v.canPlayType(test.type))
-    continue;
+  v.token = token;
+  manager.started(token);
   v.src = test.name;
   v._name = test.name;
   v._timeupdateCount = 0;
   v._finished = false;
   v.autoplay = true;
   v.addEventListener("ended", ended, false);
   v.addEventListener("timeupdate", timeupdate, false);
   document.body.appendChild(v);
-  videos.push(v);
 }
 
-if (videos.length == 0) {
-  todo(false, "No types supported");
-} else {
-  SimpleTest.waitForExplicitFinish();
-}
+manager.runTests(gSmallTests, startTest);
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/test/test_video_to_canvas.html
+++ b/content/media/test/test_video_to_canvas.html
@@ -10,17 +10,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="use_large_cache.js"></script>
   <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <script class="testbody" type="text/javascript">
 
-var videos = [];
+var manager = new MediaTestManager;
 
 function loaded(e) {
   var v = e.target;
   ok(v.readyState >= v.HAVE_CURRENT_DATA,
      "readyState must be >= HAVE_CURRENT_DATA for " + v._name);
 
   var canvas = document.createElement("canvas");
   canvas.width = v.videoWidth;
@@ -30,44 +30,38 @@ function loaded(e) {
   try {
     ctx.drawImage(v, 0, 0);
     ok(true, "Shouldn't throw exception while drawing to canvas from video for " + v._name);
   } catch (ex) {
     ok(false, "Shouldn't throw exception while drawing to canvas from video for " + v._name);
   }
 
   v._finished = true;
-  if (AllFinished(videos))
-    SimpleTest.finish();
+  v.parentNode.removeChild(v);
+  manager.finished(v.token);
   return false;
 }
 
-for (var i=0; i<gSmallTests.length; ++i) {
-  var test = gSmallTests[i];
+function startTest(test, token) {
   var isVideo = /^video/.test(test.type) ? true : false;
   if (!isVideo)
-    continue;
+    return;
   
   var v = document.createElement('video');
-  if (!v.canPlayType(test.type))
-    continue;
+  v.token = token;
+  manager.started(token);
   v.src = test.name;
   v._name = test.name;
   v._finished = false;
   v.autoplay = true;
   v.style.display = "none";
   v.addEventListener("ended", loaded, false);
   document.body.appendChild(v);
-  videos.push(v);
 }
 
-if (videos.length == 0) {
-  todo(false, "No types supported");
-} else {
-  SimpleTest.waitForExplicitFinish();
-}
+manager.runTests(gSmallTests, startTest);
 
 
 </script>
 </pre>
 
 </body>
 </html>
--- a/content/test/Makefile.in
+++ b/content/test/Makefile.in
@@ -35,16 +35,17 @@
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK *****
 
 DEPTH		= ../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
+relativesrcdir = content/test
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= test_content
 
 XPCSHELL_TESTS  = unit
 
 include $(topsrcdir)/config/rules.mk
--- a/content/xtf/test/Makefile.in
+++ b/content/xtf/test/Makefile.in
@@ -44,16 +44,17 @@
 # for detailed instructions.
 #
 
 # Note: DEPTH should be set to the relative path to mozilla/
 DEPTH		= ../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
+relativesrcdir = content/xtf/test
 
 include $(DEPTH)/config/autoconf.mk
 
 # Note: set the test module's name to test_<yourmodule>
 MODULE		= test_xtf
 
 XPCSHELL_TESTS  = unit
 
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -667,27 +667,27 @@ nsXULElement::PerformAccesskey(PRBool aK
 
     const nsStyleVisibility* vis = frame->GetStyleVisibility();
 
     if (vis->mVisible == NS_STYLE_VISIBILITY_COLLAPSE ||
         vis->mVisible == NS_STYLE_VISIBILITY_HIDDEN ||
         !frame->AreAncestorViewsVisible())
         return;
 
-    nsCOMPtr<nsIDOMXULElement> elm(do_QueryInterface(content));
+    nsXULElement* elm = FromContent(content);
     if (elm) {
         // Define behavior for each type of XUL element.
         nsIAtom *tag = content->Tag();
         if (tag != nsGkAtoms::toolbarbutton) {
           nsIFocusManager* fm = nsFocusManager::GetFocusManager();
           if (fm) {
             nsCOMPtr<nsIDOMElement> element;
             // for radio buttons, focus the radiogroup instead
             if (tag == nsGkAtoms::radio) {
-              nsCOMPtr<nsIDOMXULSelectControlItemElement> controlItem(do_QueryInterface(elm));
+              nsCOMPtr<nsIDOMXULSelectControlItemElement> controlItem(do_QueryInterface(content));
               if (controlItem) {
                 PRBool disabled;
                 controlItem->GetDisabled(&disabled);
                 if (!disabled) {
                   nsCOMPtr<nsIDOMXULSelectControlElement> selectControl;
                   controlItem->GetControl(getter_AddRefs(selectControl));
                   element = do_QueryInterface(selectControl);
                 }
@@ -696,17 +696,17 @@ nsXULElement::PerformAccesskey(PRBool aK
             else {
               element = do_QueryInterface(content);
             }
             if (element)
               fm->SetFocus(element, nsIFocusManager::FLAG_BYKEY);
           }
         }
         if (aKeyCausesActivation && tag != nsGkAtoms::textbox && tag != nsGkAtoms::menulist) {
-            ClickWithInputSource(nsIDOMNSMouseEvent::MOZ_SOURCE_KEYBOARD);
+          elm->ClickWithInputSource(nsIDOMNSMouseEvent::MOZ_SOURCE_KEYBOARD);
         }
     }
     else {
         content->PerformAccesskey(aKeyCausesActivation, aIsTrustedEvent);
     }
 }
 
 
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -8094,21 +8094,25 @@ nsDocShell::InternalLoad(nsIURI * aURI,
     if (aLoadType == LOAD_NORMAL ||
         aLoadType == LOAD_STOP_CONTENT ||
         LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_REPLACE_HISTORY) ||
         aLoadType == LOAD_HISTORY ||
         aLoadType == LOAD_LINK) {
 
         PRBool wasAnchor = PR_FALSE;
         PRBool doHashchange = PR_FALSE;
+
+        // Get the position of the scrollers.
         nscoord cx = 0, cy = 0;
+        GetCurScrollPos(ScrollOrientation_X, &cx);
+        GetCurScrollPos(ScrollOrientation_Y, &cy);
 
         if (allowScroll) {
-            NS_ENSURE_SUCCESS(ScrollIfAnchor(aURI, &wasAnchor, aLoadType, &cx,
-                                             &cy, &doHashchange),
+            NS_ENSURE_SUCCESS(ScrollIfAnchor(aURI, &wasAnchor, aLoadType,
+                                             &doHashchange),
                               NS_ERROR_FAILURE);
         }
 
         // If this is a history load, aSHEntry will have document identifier X
         // if it was created as a result of a History.pushState() from a
         // SHEntry with doc ident X, or if it was created by changing the hash
         // of the URI corresponding to a SHEntry with doc ident X.
         PRBool sameDocIdent = PR_FALSE;
@@ -8877,18 +8881,17 @@ nsresult nsDocShell::DoChannelLoad(nsICh
                              this);
     NS_ENSURE_SUCCESS(rv, rv);
 
     return NS_OK;
 }
 
 nsresult
 nsDocShell::ScrollIfAnchor(nsIURI * aURI, PRBool * aWasAnchor,
-                           PRUint32 aLoadType, nscoord *cx, nscoord *cy,
-                           PRBool * aDoHashchange)
+                           PRUint32 aLoadType, PRBool * aDoHashchange)
 {
     NS_ASSERTION(aURI, "null uri arg");
     NS_ASSERTION(aWasAnchor, "null anchor arg");
     NS_PRECONDITION(aDoHashchange, "null hashchange arg");
 
     if (!aURI || !aWasAnchor) {
         return NS_ERROR_FAILURE;
     }
@@ -9002,22 +9005,16 @@ nsDocShell::ScrollIfAnchor(nsIURI * aURI
     *aWasAnchor = PR_TRUE;
 
     // We should fire a hashchange event once we're done here if the two hashes
     // are different.
     *aDoHashchange = !Substring(currentRefStart, currentRefEnd).Equals(sNewRef);
 
     // Both the new and current URIs refer to the same page. We can now
     // browse to the hash stored in the new URI.
-    //
-    // But first let's capture positions of scroller(s) that can
-    // (and usually will) be modified by GoToAnchor() call.
-
-    GetCurScrollPos(ScrollOrientation_X, cx);
-    GetCurScrollPos(ScrollOrientation_Y, cy);
 
     if (!sNewRef.IsEmpty()) {
         // anchor is there, but if it's a load from history,
         // we don't have any anchor jumping to do
         PRBool scroll = aLoadType != LOAD_HISTORY &&
                         aLoadType != LOAD_RELOAD_NORMAL;
 
         char *str = ToNewCString(sNewRef);
@@ -9411,17 +9408,18 @@ nsDocShell::AddState(nsIVariant *aData, 
     // 2. If the third argument is present,
     //     a. Resolve the url, relative to the first script's base URL
     //     b. If (a) fails, raise a SECURITY_ERR
     //     c. Compare the resulting absolute URL to the document's address.  If
     //        any part of the URLs difer other than the <path>, <query>, and
     //        <fragment> components, raise a SECURITY_ERR and abort.
     // 3. If !aReplace:
     //     Remove from the session history all entries after the current entry,
-    //     as we would after a regular navigation.
+    //     as we would after a regular navigation, and save the current
+    //     entry's scroll position (bug 590573).
     // 4. As apropriate, either add a state object entry to the session history
     //    after the current entry with the following properties, or modify the
     //    current session history entry to set
     //      a. cloned data as the state object,
     //      b. if the third argument was present, the absolute URL found in
     //         step 2
     //    Also clear the new history entry's POST data (see bug 580069).
     // 5. If aReplace is false (i.e. we're doing a pushState instead of a
@@ -9550,25 +9548,31 @@ nsDocShell::AddState(nsIVariant *aData, 
         GetRootSessionHistory(getter_AddRefs(sessionHistory));
     }
     NS_ENSURE_TRUE(sessionHistory, NS_ERROR_FAILURE);
 
     nsCOMPtr<nsISHistoryInternal> shInternal =
         do_QueryInterface(sessionHistory, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    // Step 3: Create a new entry in the session history; this will erase
+    // Step 3: Create a new entry in the session history. This will erase
     // all SHEntries after the new entry and make this entry the current
     // one.  This operation may modify mOSHE, which we need later, so we
     // keep a reference here.
     NS_ENSURE_TRUE(mOSHE, NS_ERROR_FAILURE);
     nsCOMPtr<nsISHEntry> oldOSHE = mOSHE;
 
     nsCOMPtr<nsISHEntry> newSHEntry;
     if (!aReplace) {
+        // Save the current scroll position (bug 590573).
+        nscoord cx = 0, cy = 0;
+        GetCurScrollPos(ScrollOrientation_X, &cx);
+        GetCurScrollPos(ScrollOrientation_Y, &cy);
+        mOSHE->SetScrollPosition(cx, cy);
+
         rv = AddToSessionHistory(newURI, nsnull, nsnull,
                                  getter_AddRefs(newSHEntry));
         NS_ENSURE_SUCCESS(rv, rv);
 
         NS_ENSURE_TRUE(newSHEntry, NS_ERROR_FAILURE);
 
         // Set the new SHEntry's document identifier, if we can.
         PRUint64 ourDocIdent;
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -321,18 +321,17 @@ protected:
                                PRBool aForceAllowCookies);
     NS_IMETHOD AddHeadersToChannel(nsIInputStream * aHeadersData, 
                                   nsIChannel * aChannel);
     virtual nsresult DoChannelLoad(nsIChannel * aChannel,
                                    nsIURILoader * aURILoader,
                                    PRBool aBypassClassifier);
 
     nsresult ScrollIfAnchor(nsIURI * aURI, PRBool * aWasAnchor,
-                            PRUint32 aLoadType, nscoord *cx, nscoord *cy,
-                            PRBool * aDoHashchange);
+                            PRUint32 aLoadType, PRBool * aDoHashchange);
 
     // Tries to stringify a given variant by converting it to JSON.  This only
     // works if the variant is backed by a JSVal.
     nsresult StringifyJSValVariant(nsIVariant *aData, nsAString &aResult);
 
     // Returns PR_TRUE if would have called FireOnLocationChange,
     // but did not because aFireOnLocationChange was false on entry.
     // In this case it is the caller's responsibility to ensure
--- a/docshell/test/Makefile.in
+++ b/docshell/test/Makefile.in
@@ -85,16 +85,19 @@ include $(topsrcdir)/config/rules.mk
 		test_bug529119-1.html \
 		test_bug529119-2.html \
 		bug529119-window.html \
 		test_bug540462.html \
 		file_bug540462.html \
 		test_bug580069.html \
 		file_bug580069_1.html \
 		file_bug580069_2.sjs \
+		test_bug590573.html \
+		file_bug590573_1.html \
+		file_bug590573_2.html \
 		$(NULL)
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)
 _TEST_FILES += \
 		test_bug511449.html \
 		file_bug511449.html \
 		$(NULL)
 endif
new file mode 100644
--- /dev/null
+++ b/docshell/test/file_bug590573_1.html
@@ -0,0 +1,7 @@
+<html>
+<body onpopstate='parent.iframe1Popstate();'>
+
+<div style='height:1000px' id='div1'>This is a very tall div.</div>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/docshell/test/file_bug590573_2.html
@@ -0,0 +1,7 @@
+<html>
+<body onpopstate='parent.iframe2Popstate()'>
+
+<div style='height:1000px' id='div2'>The second page also has a big div.</div>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/docshell/test/test_bug590573.html
@@ -0,0 +1,151 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=590573
+-->
+<head>
+  <title>Test for Bug 590573</title>
+  <script type="application/javascript" src="/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=590573">Mozilla Bug 590573</a>
+
+<iframe id='iframe' width='100px' height='100px' src='file_bug590573_1.html'></iframe>
+
+<div>iframe's scrollY=<span id='iframeScrollLabel'></span></div>
+
+<script>
+SimpleTest.waitForExplicitFinish();
+
+setTimeout(updateIframeScroll, 100);
+function updateIframeScroll()
+{
+  document.getElementById('iframeScrollLabel').innerHTML =
+    document.getElementById('iframe').contentWindow.scrollY;
+  setTimeout(updateIframeScroll, 100);
+}
+
+// Listen to the first callback, since this indicates that the page loaded.
+var iframe1PopstateCallbackEnabled = true;
+function iframe1Popstate()
+{
+  if (iframe1PopstateCallbackEnabled) {
+    iframe1PopstateCallbackEnabled = false;
+    dump('Got iframe1 popstate.\n');
+    iframeLoad();
+  }
+  else {
+    dump('Ignoring iframe1 popstate.\n');
+  }
+}
+
+var iframe2PopstateCallbackEnabled = false;
+function iframe2Popstate()
+{
+  if (iframe2PopstateCallbackEnabled) {
+    iframe2PopstateCallbackEnabled = false;
+    dump('Got iframe2 popstate.\n');
+    iframeLoad();
+  }
+  else {
+    dump('Ignoring iframe2 popstate.\n');
+  }
+}
+
+var loads = 0;
+function iframeLoad()
+{
+  var iframe = document.getElementById('iframe');
+  var iframeCw = iframe.contentWindow;
+
+  loads++;
+  dump('iframeLoad(loads=' + loads + ')\n');
+
+  if (loads == 1) {
+    is(iframeCw.scrollY, 0, "test 1");
+    iframeCw.scroll(0, 100);
+
+    iframeCw.history.pushState('', '', '?pushed');
+    is(iframeCw.scrollY, 100, "test 2");
+    iframeCw.scroll(0, 200); // set state-2's position to 200
+
+    iframeCw.history.back();
+    is(iframeCw.scrollY, 100, "test 3");
+    iframeCw.scroll(0, 150); // set original page's position to 150
+
+    iframeCw.history.forward();
+    is(iframeCw.scrollY, 200, "test 4");
+
+    iframeCw.history.back();
+    is(iframeCw.scrollY, 150, "test 5");
+
+    iframeCw.history.forward();
+    is(iframeCw.scrollY, 200, "test 6");
+
+    // At this point, the history looks like:
+    //   PATH                         POSITION
+    //   file_bug590573_1.html        150       <-- oldest
+    //   file_bug590573_1.html?pushed 200       <-- newest, current
+
+    // Now test that the scroll position is persisted when we have real
+    // navigations involved.  First, we need to spin the event loop so that the
+    // navigation doesn't replace our current history entry.
+
+    setTimeout(iframeLoad, 0);
+  }
+  else if (loads == 2) {
+    iframe2PopstateCallbackEnabled = true;
+    iframeCw.location = 'file_bug590573_2.html';
+  }
+  else if (loads == 3) {
+    ok(iframeCw.location.href.match('file_bug590573_2.html$'),
+       "Location was " + iframeCw.location +
+       " but should end with file_bug590573_2.html");
+
+    is(iframeCw.scrollY, 0, "test 7");
+    iframeCw.scroll(0, 300);
+
+    // We need to spin the event loop again before we go back, otherwise the
+    // scroll positions don't get updated properly.
+    setTimeout(iframeLoad, 0);
+  }
+  else if (loads == 4) {
+    iframe1PopstateCallbackEnabled = true;
+    iframeCw.history.back();
+  }
+  else if (loads == 5) {
+    // Spin the event loop again so that we get the right scroll positions.
+    setTimeout(iframeLoad, 0);
+  }
+  else if (loads == 6) {
+    is(iframeCw.location.search, "?pushed");
+    ok(iframeCw.document.getElementById('div1'), 'Iframe should have div1.');
+
+    is(iframeCw.scrollY, 200, "test 8");
+    iframeCw.history.back();
+    is(iframeCw.scrollY, 150, "test 9");
+    iframeCw.history.forward();
+    is(iframeCw.scrollY, 200, "test 10");
+
+    // Spin one last time...
+    setTimeout(iframeLoad, 0);
+  }
+  else if (loads == 7) {
+    iframe2PopstateCallbackEnabled = true;
+    iframeCw.history.forward();
+  }
+  else if (loads == 8) {
+    is(iframeCw.scrollY, 300, "test 11");
+    SimpleTest.finish();
+  }
+  else {
+    ok(false, "Got extra load!");
+  }
+}
+</script>
+
+</body>
+</html>
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -5002,21 +5002,22 @@ PRBool IsPopupBlocked(nsIDOMDocument* aD
   if (doc) {
     PRUint32 permission = nsIPopupWindowManager::ALLOW_POPUP;
     pm->TestPermission(doc->GetDocumentURI(), &permission);
     blocked = (permission == nsIPopupWindowManager::DENY_POPUP);
   }
   return blocked;
 }
 
-static
-void FirePopupBlockedEvent(nsIDOMDocument* aDoc,
-                           nsIDOMWindow *aRequestingWindow, nsIURI *aPopupURI,
-                           const nsAString &aPopupWindowName,
-                           const nsAString &aPopupWindowFeatures)
+/* static */
+void 
+nsGlobalWindow::FirePopupBlockedEvent(nsIDOMDocument* aDoc,
+                                      nsIDOMWindow *aRequestingWindow, nsIURI *aPopupURI,
+                                      const nsAString &aPopupWindowName,
+                                      const nsAString &aPopupWindowFeatures)
 {
   if (aDoc) {
     // Fire a "DOMPopupBlocked" event so that the UI can hear about
     // blocked popups.
     nsCOMPtr<nsIDOMDocumentEvent> docEvent(do_QueryInterface(aDoc));
     nsCOMPtr<nsIDOMEvent> event;
     docEvent->CreateEvent(NS_LITERAL_STRING("PopupBlockedEvents"),
                           getter_AddRefs(event));
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -476,16 +476,21 @@ public:
   void MaybeForgiveSpamCount();
   PRBool IsClosedOrClosing() {
     return (mIsClosed ||
             mInClose ||
             mHavePendingClose ||
             mCleanedUp);
   }
 
+  static void FirePopupBlockedEvent(nsIDOMDocument* aDoc,
+                                    nsIDOMWindow *aRequestingWindow, nsIURI *aPopupURI,
+                                    const nsAString &aPopupWindowName,
+                                    const nsAString &aPopupWindowFeatures);
+
 protected:
   // Object Management
   virtual ~nsGlobalWindow();
   void CleanUp(PRBool aIgnoreModalDialog);
   void ClearControllers();
   nsresult FinalClose();
 
   void FreeInnerObjects(PRBool aClearScope);
--- a/dom/indexedDB/IDBCursor.cpp
+++ b/dom/indexedDB/IDBCursor.cpp
@@ -32,25 +32,22 @@
  * 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 ***** */
 
-// XXX remove once we can get jsvals out of XPIDL
-#include "jscntxt.h"
-#include "jsapi.h"
-
 #include "IDBCursor.h"
 
 #include "nsIIDBDatabaseException.h"
 #include "nsIVariant.h"
 
+#include "jscntxt.h"
 #include "mozilla/storage.h"
 #include "nsComponentManagerUtils.h"
 #include "nsContentUtils.h"
 #include "nsDOMClassInfo.h"
 #include "nsJSON.h"
 #include "nsJSUtils.h"
 #include "nsThreadUtils.h"
 
@@ -479,17 +476,18 @@ IDBCursor::Update(const jsval &aValue,
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIJSON> json(new nsJSON());
 
   nsString jsonValue;
   rv = json->EncodeFromJSVal(clone.jsval_addr(), aCx, jsonValue);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsRefPtr<IDBRequest> request = GenerateWriteRequest();
+  nsRefPtr<IDBRequest> request =
+    GenerateWriteRequest(mTransaction->ScriptContext(), mTransaction->Owner());
   NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
 
   nsRefPtr<UpdateHelper> helper =
     new UpdateHelper(mTransaction, request, mObjectStore->Id(), jsonValue, key,
                      mObjectStore->IsAutoIncrement(), indexUpdateInfo);
   rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -513,17 +511,18 @@ IDBCursor::Remove(nsIIDBRequest** _retva
 
   if (!mObjectStore->IsWriteAllowed()) {
     return NS_ERROR_OBJECT_IS_IMMUTABLE;
   }
 
   const Key& key = mData[mDataIndex].key;
   NS_ASSERTION(!key.IsUnset() && !key.IsNull(), "Bad key!");
 
-  nsRefPtr<IDBRequest> request = GenerateWriteRequest();
+  nsRefPtr<IDBRequest> request =
+    GenerateWriteRequest(mTransaction->ScriptContext(), mTransaction->Owner());
   NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
 
   nsRefPtr<RemoveHelper> helper =
     new RemoveHelper(mTransaction, request, mObjectStore->Id(), key,
                      mObjectStore->IsAutoIncrement());
   nsresult rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, rv);
 
--- a/dom/indexedDB/IDBDatabase.cpp
+++ b/dom/indexedDB/IDBDatabase.cpp
@@ -359,16 +359,17 @@ IDBDatabase::GetObjectStoreNames(nsIDOMD
   list.forget(aObjectStores);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 IDBDatabase::CreateObjectStore(const nsAString& aName,
                                const nsAString& aKeyPath,
                                PRBool aAutoIncrement,
+                               JSContext* aCx,
                                nsIIDBRequest** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (aName.IsEmpty()) {
     return NS_ERROR_INVALID_ARG;
   }
 
@@ -383,42 +384,45 @@ IDBDatabase::CreateObjectStore(const nsA
     NS_ERROR("This should never fail!");
     return NS_ERROR_UNEXPECTED;
   }
 
   if (info->ContainsStoreName(aName)) {
     return NS_ERROR_ALREADY_INITIALIZED;
   }
 
-  nsRefPtr<IDBRequest> request = GenerateWriteRequest();
-  NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
-
   nsTArray<nsString> objectStores;
   nsString* name = objectStores.AppendElement(aName);
   if (!name) {
     NS_ERROR("Out of memory?");
     return nsIIDBDatabaseException::UNKNOWN_ERR;
   }
 
   nsRefPtr<IDBTransaction> transaction =
-    IDBTransaction::Create(this, objectStores, nsIIDBTransaction::READ_WRITE,
+    IDBTransaction::Create(aCx, this, objectStores,
+                           nsIIDBTransaction::READ_WRITE,
                            kDefaultDatabaseTimeoutSeconds);
 
+  nsRefPtr<IDBRequest> request =
+    GenerateWriteRequest(transaction->ScriptContext(), transaction->Owner());
+  NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
+
   nsRefPtr<CreateObjectStoreHelper> helper =
     new CreateObjectStoreHelper(transaction, request, aName, keyPath,
                                 !!aAutoIncrement);
   nsresult rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, rv);
 
   request.forget(_retval);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 IDBDatabase::RemoveObjectStore(const nsAString& aName,
+                               JSContext* aCx,
                                nsIIDBRequest** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (aName.IsEmpty()) {
     return NS_ERROR_INVALID_ARG;
   }
 
@@ -434,66 +438,72 @@ IDBDatabase::RemoveObjectStore(const nsA
 
   nsTArray<nsString> storesToOpen;
   if (!storesToOpen.AppendElement(aName)) {
     NS_ERROR("Out of memory!");
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   nsRefPtr<IDBTransaction> transaction =
-    IDBTransaction::Create(this, storesToOpen, nsIIDBTransaction::READ_WRITE,
+    IDBTransaction::Create(aCx, this, storesToOpen,
+                           nsIIDBTransaction::READ_WRITE,
                            kDefaultDatabaseTimeoutSeconds);
   NS_ENSURE_TRUE(transaction, NS_ERROR_FAILURE);
 
-
-  nsRefPtr<IDBRequest> request = GenerateWriteRequest();
+  nsRefPtr<IDBRequest> request =
+    GenerateWriteRequest(transaction->ScriptContext(), transaction->Owner());
+  NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
 
   nsRefPtr<RemoveObjectStoreHelper> helper =
     new RemoveObjectStoreHelper(transaction, request, aName);
   nsresult rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, rv);
 
   request.forget(_retval);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 IDBDatabase::SetVersion(const nsAString& aVersion,
+                        JSContext* aCx,
                         nsIIDBRequest** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   DatabaseInfo* info;
   if (!DatabaseInfo::Get(mDatabaseId, &info)) {
     NS_ERROR("This should never fail!");
     return NS_ERROR_UNEXPECTED;
   }
 
-  nsRefPtr<IDBRequest> request = GenerateWriteRequest();
-
   // Lock the whole database
   nsTArray<nsString> storesToOpen;
   nsRefPtr<IDBTransaction> transaction =
-    IDBTransaction::Create(this, storesToOpen, IDBTransaction::FULL_LOCK,
+    IDBTransaction::Create(aCx, this, storesToOpen, IDBTransaction::FULL_LOCK,
                            kDefaultDatabaseTimeoutSeconds);
   NS_ENSURE_TRUE(transaction, NS_ERROR_FAILURE);
 
+  nsRefPtr<IDBRequest> request =
+    GenerateWriteRequest(transaction->ScriptContext(), transaction->Owner());
+  NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
+
   nsRefPtr<SetVersionHelper> helper =
     new SetVersionHelper(transaction, request, aVersion);
   nsresult rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, rv);
 
   request.forget(_retval);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 IDBDatabase::Transaction(nsIVariant* aStoreNames,
                          PRUint16 aMode,
                          PRUint32 aTimeout,
+                         JSContext* aCx,
                          PRUint8 aOptionalArgCount,
                          nsIIDBTransaction** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (aOptionalArgCount) {
     if (aMode != nsIIDBTransaction::READ_WRITE &&
         aMode != nsIIDBTransaction::READ_ONLY &&
@@ -608,27 +618,28 @@ IDBDatabase::Transaction(nsIVariant* aSt
       }
     } break;
 
     default:
       return NS_ERROR_ILLEGAL_VALUE;
   }
 
   nsRefPtr<IDBTransaction> transaction =
-    IDBTransaction::Create(this, storesToOpen, aMode,
+    IDBTransaction::Create(aCx, this, storesToOpen, aMode,
                            kDefaultDatabaseTimeoutSeconds);
   NS_ENSURE_TRUE(transaction, NS_ERROR_FAILURE);
 
   transaction.forget(_retval);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 IDBDatabase::ObjectStore(const nsAString& aName,
                          PRUint16 aMode,
+                         JSContext* aCx,
                          PRUint8 aOptionalArgCount,
                          nsIIDBObjectStore** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (aName.IsEmpty()) {
     return NS_ERROR_INVALID_ARG;
   }
@@ -656,17 +667,17 @@ IDBDatabase::ObjectStore(const nsAString
 
   nsTArray<nsString> storesToOpen;
   if (!storesToOpen.AppendElement(aName)) {
     NS_ERROR("Out of memory?");
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   nsRefPtr<IDBTransaction> transaction =
-    IDBTransaction::Create(this, storesToOpen, aMode,
+    IDBTransaction::Create(aCx, this, storesToOpen, aMode,
                            kDefaultDatabaseTimeoutSeconds);
   NS_ENSURE_TRUE(transaction, NS_ERROR_FAILURE);
 
   nsresult rv = transaction->ObjectStore(aName, _retval);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
--- a/dom/indexedDB/IDBFactory.cpp
+++ b/dom/indexedDB/IDBFactory.cpp
@@ -36,35 +36,37 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "IDBFactory.h"
 
 #include "nsIIDBDatabaseException.h"
 #include "nsILocalFile.h"
+#include "nsIScriptContext.h"
 
 #include "mozilla/storage.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsComponentManagerUtils.h"
 #include "nsContentUtils.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsDOMClassInfo.h"
 #include "nsHashKeys.h"
+#include "nsPIDOMWindow.h"
 #include "nsServiceManagerUtils.h"
 #include "nsThreadUtils.h"
 #include "nsXPCOMCID.h"
 
 #include "AsyncConnectionHelper.h"
 #include "DatabaseInfo.h"
 #include "IDBDatabase.h"
 #include "IDBKeyRange.h"
 #include "LazyIdleThread.h"
 
-#define DB_SCHEMA_VERSION 2
+#define DB_SCHEMA_VERSION 3
 
 USING_INDEXEDDB_NAMESPACE
 
 namespace {
 
 const PRUint32 kDefaultThreadTimeoutMS = 30000;
 
 struct ObjectStoreInfoMap
@@ -158,20 +160,19 @@ CreateTables(mozIStorageConnection* aDBC
     "CREATE UNIQUE INDEX key_index "
     "ON object_data (key_value, object_store_id);"
   ));
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Table `ai_object_data`
   rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     "CREATE TABLE ai_object_data ("
-      "id INTEGER, "
+      "id INTEGER PRIMARY KEY AUTOINCREMENT, "
       "object_store_id INTEGER NOT NULL, "
       "data TEXT NOT NULL, "
-      "PRIMARY KEY (id), "
       "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
         "CASCADE"
     ");"
   ));
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     "CREATE UNIQUE INDEX ai_key_index "
@@ -513,16 +514,17 @@ NS_INTERFACE_MAP_BEGIN(IDBFactory)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(IDBFactory)
 NS_INTERFACE_MAP_END
 
 DOMCI_DATA(IDBFactory, IDBFactory)
 
 NS_IMETHODIMP
 IDBFactory::Open(const nsAString& aName,
                  const nsAString& aDescription,
+                 JSContext* aCx,
                  nsIIDBRequest** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (aName.IsEmpty()) {
     return NS_ERROR_INVALID_ARG;
   }
 
@@ -535,17 +537,31 @@ IDBFactory::Open(const nsAString& aName,
   if (nsContentUtils::IsSystemPrincipal(principal)) {
     origin.AssignLiteral("chrome");
   }
   else {
     rv = nsContentUtils::GetASCIIOrigin(principal, origin);
     NS_ENSURE_SUCCESS(rv, nsnull);
   }
 
-  nsRefPtr<IDBRequest> request = GenerateRequest();
+  nsIScriptContext* context = GetScriptContextFromJSContext(aCx);
+  NS_ENSURE_STATE(context);
+
+  nsCOMPtr<nsPIDOMWindow> innerWindow;
+
+  nsCOMPtr<nsPIDOMWindow> window =
+    do_QueryInterface(context->GetGlobalObject());
+  if (window) {
+    innerWindow = window->GetCurrentInnerWindow();
+  }
+  NS_ENSURE_STATE(innerWindow);
+
+  nsRefPtr<IDBRequest> request = GenerateRequest(context, innerWindow);
+  NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
+
   nsRefPtr<LazyIdleThread> thread(new LazyIdleThread(kDefaultThreadTimeoutMS,
                                                      nsnull));
 
   nsRefPtr<OpenDatabaseHelper> runnable =
     new OpenDatabaseHelper(request, aName, aDescription, origin, thread);
 
   rv = runnable->Dispatch(thread);
   NS_ENSURE_SUCCESS(rv, rv);
--- a/dom/indexedDB/IDBIndex.cpp
+++ b/dom/indexedDB/IDBIndex.cpp
@@ -314,22 +314,24 @@ IDBIndex::Get(nsIVariant* aKey,
   Key key;
   nsresult rv = IDBObjectStore::GetKeyFromVariant(aKey, key);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (key.IsUnset() || key.IsNull()) {
     return NS_ERROR_INVALID_ARG;
   }
 
-  nsRefPtr<IDBRequest> request = GenerateRequest();
+  IDBTransaction* transaction = mObjectStore->Transaction();
+
+  nsRefPtr<IDBRequest> request =
+    GenerateRequest(transaction->ScriptContext(), transaction->Owner());
   NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
 
   nsRefPtr<GetHelper> helper =
-    new GetHelper(mObjectStore->Transaction(), request, key, mId, mUnique,
-                  mAutoIncrement);
+    new GetHelper(transaction, request, key, mId, mUnique, mAutoIncrement);
   rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, rv);
 
   request.forget(_retval);
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -341,21 +343,24 @@ IDBIndex::GetObject(nsIVariant* aKey,
   Key key;
   nsresult rv = IDBObjectStore::GetKeyFromVariant(aKey, key);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (key.IsUnset() || key.IsNull()) {
     return NS_ERROR_INVALID_ARG;
   }
 
-  nsRefPtr<IDBRequest> request = GenerateRequest();
+  IDBTransaction* transaction = mObjectStore->Transaction();
+
+  nsRefPtr<IDBRequest> request =
+    GenerateRequest(transaction->ScriptContext(), transaction->Owner());
   NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
 
   nsRefPtr<GetObjectHelper> helper =
-    new GetObjectHelper(mObjectStore->Transaction(), request, key, mId, mUnique,
+    new GetObjectHelper(transaction, request, key, mId, mUnique,
                         mAutoIncrement);
   rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, rv);
 
   request.forget(_retval);
   return NS_OK;
 }
 
@@ -374,22 +379,25 @@ IDBIndex::GetAll(nsIVariant* aKey,
   if (key.IsNull()) {
     key = Key::UNSETKEY;
   }
 
   if (aOptionalArgCount < 2) {
     aLimit = PR_UINT32_MAX;
   }
 
-  nsRefPtr<IDBRequest> request = GenerateRequest();
+  IDBTransaction* transaction = mObjectStore->Transaction();
+
+  nsRefPtr<IDBRequest> request =
+    GenerateRequest(transaction->ScriptContext(), transaction->Owner());
   NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
 
   nsRefPtr<GetAllHelper> helper =
-    new GetAllHelper(mObjectStore->Transaction(), request, key, mId, mUnique,
-                     mAutoIncrement, aLimit);
+    new GetAllHelper(transaction, request, key, mId, mUnique, mAutoIncrement,
+                     aLimit);
   rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, rv);
 
   request.forget(_retval);
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -407,22 +415,25 @@ IDBIndex::GetAllObjects(nsIVariant* aKey
   if (key.IsNull()) {
     key = Key::UNSETKEY;
   }
 
   if (aOptionalArgCount < 2) {
     aLimit = PR_UINT32_MAX;
   }
 
-  nsRefPtr<IDBRequest> request = GenerateRequest();
+  IDBTransaction* transaction = mObjectStore->Transaction();
+
+  nsRefPtr<IDBRequest> request =
+    GenerateRequest(transaction->ScriptContext(), transaction->Owner());
   NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
 
   nsRefPtr<GetAllObjectsHelper> helper =
-    new GetAllObjectsHelper(mObjectStore->Transaction(), request, key, mId,
-                            mUnique, mAutoIncrement, aLimit);
+    new GetAllObjectsHelper(transaction, request, key, mId, mUnique,
+                            mAutoIncrement, aLimit);
   rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, rv);
 
   request.forget(_retval);
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -472,23 +483,26 @@ IDBIndex::OpenCursor(nsIIDBKeyRange* aKe
     aDirection = nsIIDBCursor::NEXT;
   }
 
   if (aPreload) {
     NS_NOTYETIMPLEMENTED("Implement me!");
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
-  nsRefPtr<IDBRequest> request = GenerateRequest();
+  IDBTransaction* transaction = mObjectStore->Transaction();
+
+  nsRefPtr<IDBRequest> request =
+    GenerateRequest(transaction->ScriptContext(), transaction->Owner());
   NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
 
   nsRefPtr<OpenCursorHelper> helper =
-    new OpenCursorHelper(mObjectStore->Transaction(), request, this, mId,
-                         mUnique, mAutoIncrement, leftKey, rightKey,
-                         keyRangeFlags, aDirection, aPreload);
+    new OpenCursorHelper(transaction, request, this, mId, mUnique,
+                         mAutoIncrement, leftKey, rightKey, keyRangeFlags,
+                         aDirection, aPreload);
 
   rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, rv);
 
   request.forget(_retval);
   return NS_OK;
 }
 
@@ -539,23 +553,26 @@ IDBIndex::OpenObjectCursor(nsIIDBKeyRang
     aDirection = nsIIDBCursor::NEXT;
   }
 
   if (aPreload) {
     NS_NOTYETIMPLEMENTED("Implement me!");
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
-  nsRefPtr<IDBRequest> request = GenerateRequest();
+  IDBTransaction* transaction = mObjectStore->Transaction();
+
+  nsRefPtr<IDBRequest> request =
+    GenerateRequest(transaction->ScriptContext(), transaction->Owner());
   NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
 
   nsRefPtr<OpenObjectCursorHelper> helper =
-    new OpenObjectCursorHelper(mObjectStore->Transaction(), request, this, mId,
-                               mUnique, mAutoIncrement, leftKey, rightKey,
-                               keyRangeFlags, aDirection, aPreload);
+    new OpenObjectCursorHelper(transaction, request, this, mId, mUnique,
+                               mAutoIncrement, leftKey, rightKey, keyRangeFlags,
+                               aDirection, aPreload);
 
   rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, rv);
 
   request.forget(_retval);
   return NS_OK;
 }
 
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -71,22 +71,21 @@ class AddHelper : public AsyncConnection
 public:
   AddHelper(IDBTransaction* aTransaction,
             IDBRequest* aRequest,
             PRInt64 aObjectStoreID,
             const nsAString& aKeyPath,
             const nsAString& aValue,
             const Key& aKey,
             bool aAutoIncrement,
-            bool aCreate,
             bool aOverwrite,
             nsTArray<IndexUpdateInfo>& aIndexUpdateInfo)
   : AsyncConnectionHelper(aTransaction, aRequest), mOSID(aObjectStoreID),
     mKeyPath(aKeyPath), mValue(aValue), mKey(aKey),
-    mAutoIncrement(aAutoIncrement), mCreate(aCreate), mOverwrite(aOverwrite)
+    mAutoIncrement(aAutoIncrement), mOverwrite(aOverwrite)
   {
     mIndexUpdateInfo.SwapElements(aIndexUpdateInfo);
   }
 
   PRUint16 DoDatabaseWork(mozIStorageConnection* aConnection);
   PRUint16 GetSuccessResult(nsIWritableVariant* aResult);
 
   nsresult ModifyValueForNewKey();
@@ -96,17 +95,16 @@ public:
 private:
   // In-params.
   const PRInt64 mOSID;
   const nsString mKeyPath;
   // These may change in the autoincrement case.
   nsString mValue;
   Key mKey;
   const bool mAutoIncrement;
-  const bool mCreate;
   const bool mOverwrite;
   nsTArray<IndexUpdateInfo> mIndexUpdateInfo;
 };
 
 class GetHelper : public AsyncConnectionHelper
 {
 public:
   GetHelper(IDBTransaction* aTransaction,
@@ -143,16 +141,35 @@ public:
   : GetHelper(aTransaction, aRequest, aObjectStoreID, aKey, aAutoIncrement)
   { }
 
   PRUint16 DoDatabaseWork(mozIStorageConnection* aConnection);
   PRUint16 OnSuccess(nsIDOMEventTarget* aTarget);
   PRUint16 GetSuccessResult(nsIWritableVariant* aResult);
 };
 
+class ClearHelper : public AsyncConnectionHelper
+{
+public:
+  ClearHelper(IDBTransaction* aTransaction,
+              IDBRequest* aRequest,
+              PRInt64 aObjectStoreID,
+              bool aAutoIncrement)
+  : AsyncConnectionHelper(aTransaction, aRequest), mOSID(aObjectStoreID),
+    mAutoIncrement(aAutoIncrement)
+  { }
+
+  PRUint16 DoDatabaseWork(mozIStorageConnection* aConnection);
+
+protected:
+  // In-params.
+  const PRInt64 mOSID;
+  const bool mAutoIncrement;
+};
+
 class OpenCursorHelper : public AsyncConnectionHelper
 {
 public:
   OpenCursorHelper(IDBTransaction* aTransaction,
                    IDBRequest* aRequest,
                    IDBObjectStore* aObjectStore,
                    const Key& aLeftKey,
                    const Key& aRightKey,
@@ -834,17 +851,18 @@ IDBObjectStore::Get(nsIVariant* aKey,
   Key key;
   nsresult rv = GetKeyFromVariant(aKey, key);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (key.IsUnset()) {
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
-  nsRefPtr<IDBRequest> request = GenerateRequest();
+  nsRefPtr<IDBRequest> request =
+    GenerateRequest(mTransaction->ScriptContext(), mTransaction->Owner());
   NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
 
   nsRefPtr<GetHelper> helper =
     new GetHelper(mTransaction, request, mId, key, !!mAutoIncrement);
   rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, rv);
 
   request.forget(_retval);
@@ -884,17 +902,18 @@ IDBObjectStore::GetAll(nsIIDBKeyRange* a
 
     rv = aKeyRange->GetRight(getter_AddRefs(variant));
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = GetKeyFromVariant(variant, rightKey);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  nsRefPtr<IDBRequest> request = GenerateRequest();
+  nsRefPtr<IDBRequest> request =
+    GenerateRequest(mTransaction->ScriptContext(), mTransaction->Owner());
   NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
 
   nsRefPtr<GetAllHelper> helper =
     new GetAllHelper(mTransaction, request, mId, leftKey, rightKey,
                      keyRangeFlags, aLimit, mAutoIncrement);
 
   rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, rv);
@@ -915,50 +934,60 @@ IDBObjectStore::Add(const jsval &aValue,
   if (!mTransaction->TransactionIsOpen()) {
     return NS_ERROR_UNEXPECTED;
   }
 
   if (mMode != nsIIDBTransaction::READ_WRITE) {
     return NS_ERROR_OBJECT_IS_IMMUTABLE;
   }
 
-  jsval keyval = (aOptionalArgCount >= 1) ? aKey : JSVAL_VOID;
+  jsval keyval;
+  if (aOptionalArgCount >= 1) {
+    keyval = aKey;
+    if (mAutoIncrement && JSVAL_IS_NULL(keyval)) {
+      return NS_ERROR_ILLEGAL_VALUE;
+    }
+  }
+  else {
+    keyval = JSVAL_VOID;
+  }
 
   nsString jsonValue;
   Key key;
   nsTArray<IndexUpdateInfo> updateInfo;
 
   nsresult rv = GetAddInfo(aCx, aValue, keyval, jsonValue, key, updateInfo);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   if (key.IsUnset() && !mAutoIncrement) {
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
-  nsRefPtr<IDBRequest> request = GenerateWriteRequest();
+  nsRefPtr<IDBRequest> request =
+    GenerateWriteRequest(mTransaction->ScriptContext(), mTransaction->Owner());
   NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
 
   nsRefPtr<AddHelper> helper =
     new AddHelper(mTransaction, request, mId, mKeyPath, jsonValue, key,
-                  !!mAutoIncrement, true, false, updateInfo);
+                  !!mAutoIncrement, false, updateInfo);
   rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, rv);
 
   request.forget(_retval);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-IDBObjectStore::Modify(const jsval &aValue,
-                       const jsval &aKey,
-                       JSContext* aCx,
-                       PRUint8 aOptionalArgCount,
-                       nsIIDBRequest** _retval)
+IDBObjectStore::Put(const jsval &aValue,
+                    const jsval &aKey,
+                    JSContext* aCx,
+                    PRUint8 aOptionalArgCount,
+                    nsIIDBRequest** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (!mTransaction->TransactionIsOpen()) {
     return NS_ERROR_UNEXPECTED;
   }
 
   if (mMode != nsIIDBTransaction::READ_WRITE) {
@@ -975,67 +1004,23 @@ IDBObjectStore::Modify(const jsval &aVal
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   if (key.IsUnset() || key.IsNull()) {
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
-  nsRefPtr<IDBRequest> request = GenerateWriteRequest();
+  nsRefPtr<IDBRequest> request =
+    GenerateWriteRequest(mTransaction->ScriptContext(), mTransaction->Owner());
   NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
 
   nsRefPtr<AddHelper> helper =
     new AddHelper(mTransaction, request, mId, mKeyPath, jsonValue, key,
-                  !!mAutoIncrement, false, true, updateInfo);
-  rv = helper->DispatchToTransactionPool();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  request.forget(_retval);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-IDBObjectStore::AddOrModify(const jsval &aValue,
-                            const jsval &aKey,
-                            JSContext* aCx,
-                            PRUint8 aOptionalArgCount,
-                            nsIIDBRequest** _retval)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  if (!mTransaction->TransactionIsOpen()) {
-    return NS_ERROR_UNEXPECTED;
-  }
-
-  if (mMode != nsIIDBTransaction::READ_WRITE) {
-    return NS_ERROR_OBJECT_IS_IMMUTABLE;
-  }
-
-  jsval keyval = (aOptionalArgCount >= 1) ? aKey : JSVAL_VOID;
-
-  nsString jsonValue;
-  Key key;
-  nsTArray<IndexUpdateInfo> updateInfo;
-
-  nsresult rv = GetAddInfo(aCx, aValue, keyval, jsonValue, key, updateInfo);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  if (key.IsUnset() || key.IsNull()) {
-    return NS_ERROR_ILLEGAL_VALUE;
-  }
-
-  nsRefPtr<IDBRequest> request = GenerateWriteRequest();
-  NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
-
-  nsRefPtr<AddHelper> helper =
-    new AddHelper(mTransaction, request, mId, mKeyPath, jsonValue, key,
-                  !!mAutoIncrement, true, true, updateInfo);
+                  !!mAutoIncrement, true, updateInfo);
   rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, rv);
 
   request.forget(_retval);
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -1057,29 +1042,56 @@ IDBObjectStore::Remove(nsIVariant* aKey,
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   if (key.IsUnset() || key.IsNull()) {
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
-  nsRefPtr<IDBRequest> request = GenerateWriteRequest();
+  nsRefPtr<IDBRequest> request =
+    GenerateWriteRequest(mTransaction->ScriptContext(), mTransaction->Owner());
   NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
 
   nsRefPtr<RemoveHelper> helper =
     new RemoveHelper(mTransaction, request, mId, key, !!mAutoIncrement);
   rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, rv);
 
   request.forget(_retval);
   return NS_OK;
 }
 
 NS_IMETHODIMP
+IDBObjectStore::Clear(nsIIDBRequest** _retval)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  if (!mTransaction->TransactionIsOpen()) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  if (mMode != nsIIDBTransaction::READ_WRITE) {
+    return NS_ERROR_OBJECT_IS_IMMUTABLE;
+  }
+
+  nsRefPtr<IDBRequest> request =
+    GenerateWriteRequest(mTransaction->ScriptContext(), mTransaction->Owner());
+  NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
+
+  nsRefPtr<ClearHelper> helper =
+    new ClearHelper(mTransaction, request, mId, !!mAutoIncrement);
+  nsresult rv = helper->DispatchToTransactionPool();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  request.forget(_retval);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 IDBObjectStore::OpenCursor(nsIIDBKeyRange* aKeyRange,
                            PRUint16 aDirection,
                            PRBool aPreload,
                            PRUint8 aOptionalArgCount,
                            nsIIDBRequest** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
@@ -1122,17 +1134,18 @@ IDBObjectStore::OpenCursor(nsIIDBKeyRang
     aDirection = nsIIDBCursor::NEXT;
   }
 
   if (aPreload) {
     NS_NOTYETIMPLEMENTED("Implement me!");
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
-  nsRefPtr<IDBRequest> request = GenerateRequest();
+  nsRefPtr<IDBRequest> request =
+    GenerateRequest(mTransaction->ScriptContext(), mTransaction->Owner());
   NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
 
   nsRefPtr<OpenCursorHelper> helper =
     new OpenCursorHelper(mTransaction, request, this, leftKey, rightKey,
                          keyRangeFlags, aDirection, aPreload);
 
   rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, rv);
@@ -1173,17 +1186,18 @@ IDBObjectStore::CreateIndex(const nsAStr
     NS_NOTYETIMPLEMENTED("Implement me!");
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
   if (!mTransaction->TransactionIsOpen()) {
     return NS_ERROR_UNEXPECTED;
   }
 
-  nsRefPtr<IDBRequest> request = GenerateWriteRequest();
+  nsRefPtr<IDBRequest> request =
+    GenerateWriteRequest(mTransaction->ScriptContext(), mTransaction->Owner());
   NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
 
   nsRefPtr<CreateIndexHelper> helper =
     new CreateIndexHelper(mTransaction, request, aName, aKeyPath, !!aUnique,
                           mAutoIncrement, this);
   nsresult rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -1252,17 +1266,19 @@ IDBObjectStore::RemoveIndex(const nsAStr
       break;
     }
   }
 
   if (!found) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
-  nsRefPtr<IDBRequest> request = GenerateWriteRequest();
+  nsRefPtr<IDBRequest> request =
+    GenerateWriteRequest(mTransaction->ScriptContext(), mTransaction->Owner());
+  NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
 
   nsRefPtr<RemoveIndexHelper> helper =
     new RemoveIndexHelper(mTransaction, request, aName, this);
   nsresult rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, rv);
 
   request.forget(_retval);
   return NS_OK;
@@ -1336,17 +1352,17 @@ AddHelper::DoDatabaseWork(mozIStorageCon
     NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
 
     if (hasResult) {
       return nsIIDBDatabaseException::CONSTRAINT_ERR;
     }
   }
 
   // Now we add it to the database (or update, depending on our variables).
-  stmt = mTransaction->AddStatement(mCreate, mayOverwrite, mAutoIncrement);
+  stmt = mTransaction->AddStatement(true, mayOverwrite, mAutoIncrement);
   NS_ENSURE_TRUE(stmt, nsIIDBDatabaseException::UNKNOWN_ERR);
 
   mozStorageStatementScoper scoper(stmt);
 
   NS_NAMED_LITERAL_CSTRING(keyValue, "key_value");
 
   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), mOSID);
   NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
@@ -1366,17 +1382,17 @@ AddHelper::DoDatabaseWork(mozIStorageCon
     NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
   }
 
   rv = stmt->BindStringByName(NS_LITERAL_CSTRING("data"), mValue);
   NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
 
   rv = stmt->Execute();
   if (NS_FAILED(rv)) {
-    if (mCreate && mayOverwrite && rv == NS_ERROR_STORAGE_CONSTRAINT) {
+    if (mayOverwrite && rv == NS_ERROR_STORAGE_CONSTRAINT) {
       scoper.Abandon();
 
       stmt = mTransaction->AddStatement(false, true, mAutoIncrement);
       NS_ENSURE_TRUE(stmt, nsIIDBDatabaseException::UNKNOWN_ERR);
 
       mozStorageStatementScoper scoper2(stmt);
 
       rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), mOSID);
@@ -1404,17 +1420,17 @@ AddHelper::DoDatabaseWork(mozIStorageCon
     }
 
     if (NS_FAILED(rv)) {
       return nsIIDBDatabaseException::CONSTRAINT_ERR;
     }
   }
 
   // If we are supposed to generate a key, get the new id.
-  if (mAutoIncrement && mCreate && !mOverwrite) {
+  if (mAutoIncrement && !mOverwrite) {
 #ifdef DEBUG
     PRInt64 oldKey = unsetKey ? 0 : mKey.IntValue();
 #endif
 
     rv = aConnection->GetLastInsertRowID(mKey.ToIntPtr());
     NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
 
 #ifdef DEBUG
@@ -1641,16 +1657,46 @@ RemoveHelper::GetSuccessResult(nsIWritab
   }
   else {
     NS_NOTREACHED("Unknown key type!");
   }
   return OK;
 }
 
 PRUint16
+ClearHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
+{
+  NS_PRECONDITION(aConnection, "Passed a null connection!");
+
+  nsCString table;
+  if (mAutoIncrement) {
+    table.AssignLiteral("ai_object_data");
+  }
+  else {
+    table.AssignLiteral("object_data");
+  }
+
+  nsCString query = NS_LITERAL_CSTRING("DELETE FROM ") + table +
+                    NS_LITERAL_CSTRING(" WHERE object_store_id = :osid");
+
+  nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
+  NS_ENSURE_TRUE(stmt, nsIIDBDatabaseException::UNKNOWN_ERR);
+
+  mozStorageStatementScoper scoper(stmt);
+
+  nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), mOSID);
+  NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
+
+  rv = stmt->Execute();
+  NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
+
+  return OK;
+}
+
+PRUint16
 OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
 {
   nsCString table;
   nsCString keyColumn;
 
   if (mObjectStore->IsAutoIncrement()) {
     table.AssignLiteral("ai_object_data");
     keyColumn.AssignLiteral("id");
--- a/dom/indexedDB/IDBRequest.cpp
+++ b/dom/indexedDB/IDBRequest.cpp
@@ -40,31 +40,48 @@
 
 #include "IDBRequest.h"
 
 #include "nsIScriptContext.h"
 #include "nsIVariant.h"
 
 #include "nsComponentManagerUtils.h"
 #include "nsDOMClassInfo.h"
+#include "nsDOMJSUtils.h"
+#include "nsPIDOMWindow.h"
 #include "nsStringGlue.h"
 #include "nsThreadUtils.h"
 
 #include "IDBEvents.h"
 
 USING_INDEXEDDB_NAMESPACE
 
-IDBRequest::IDBRequest(Generator* aGenerator,
-                       bool aWriteRequest)
-: mGenerator(aGenerator),
-  mReadyState(nsIIDBRequest::INITIAL),
-  mAborted(false),
-  mWriteRequest(aWriteRequest)
+already_AddRefed<IDBRequest>
+IDBRequest::Generator::GenerateRequestInternal(nsIScriptContext* aScriptContext,
+                                               nsPIDOMWindow* aOwner,
+                                               PRBool aWriteRequest)
 {
-  NS_ASSERTION(aGenerator, "Null generator!");
+  if (!aScriptContext || !aOwner) {
+    NS_ERROR("Null context and owner!");
+    return nsnull;
+  }
+
+  nsRefPtr<IDBRequest> request(new IDBRequest());
+
+  request->mGenerator = this;
+  request->mWriteRequest = aWriteRequest;
+  request->mScriptContext = aScriptContext;
+  request->mOwner = aOwner;
+
+  if (!mLiveRequests.AppendElement(request)) {
+    NS_ERROR("Append failed!");
+    return nsnull;
+  }
+
+  return request.forget();
 }
 
 IDBRequest::~IDBRequest()
 {
   mGenerator->NoteDyingRequest(this);
 
   if (mListenerManager) {
     mListenerManager->Disconnect();
@@ -79,17 +96,17 @@ IDBRequest::Abort()
   if (mAborted || mReadyState != nsIIDBRequest::LOADING) {
     return NS_OK;
   }
 
   if (mWriteRequest) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
-  mAborted = true;
+  mAborted = PR_TRUE;
   mReadyState = nsIIDBRequest::DONE;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 IDBRequest::GetReadyState(PRUint16* aReadyState)
 {
   *aReadyState = mReadyState;
--- a/dom/indexedDB/IDBRequest.h
+++ b/dom/indexedDB/IDBRequest.h
@@ -44,16 +44,19 @@
 #include "mozilla/dom/indexedDB/IndexedDatabase.h"
 
 #include "nsIIDBRequest.h"
 #include "nsIVariant.h"
 
 #include "nsDOMEventTargetHelper.h"
 #include "nsCycleCollectionParticipant.h"
 
+class nsIScriptContext;
+class nsPIDOMWindow;
+
 BEGIN_INDEXEDDB_NAMESPACE
 
 class AsyncConnectionHelper;
 class IDBFactory;
 class IDBDatabase;
 
 class IDBRequest : public nsDOMEventTargetHelper,
                    public nsIIDBRequest
@@ -67,38 +70,39 @@ public:
       friend class IDBRequest;
 
       Generator() { }
 
       virtual ~Generator() {
         NS_ASSERTION(mLiveRequests.IsEmpty(), "Huh?!");
       }
 
-      IDBRequest* GenerateRequest() {
-        IDBRequest* request = new IDBRequest(this, false);
-        if (!mLiveRequests.AppendElement(request)) {
-          NS_ERROR("Append failed!");
-        }
-        return request;
+      already_AddRefed<IDBRequest>
+      GenerateRequest(nsIScriptContext* aScriptContext,
+                      nsPIDOMWindow* aOwner) {
+        return GenerateRequestInternal(aScriptContext, aOwner, PR_FALSE);
       }
 
-      IDBRequest* GenerateWriteRequest() {
-        IDBRequest* request = new IDBRequest(this, true);
-        if (!mLiveRequests.AppendElement(request)) {
-          NS_ERROR("Append failed!");
-        }
-        return request;
+      already_AddRefed<IDBRequest>
+      GenerateWriteRequest(nsIScriptContext* aScriptContext,
+                           nsPIDOMWindow* aOwner) {
+        return GenerateRequestInternal(aScriptContext, aOwner, PR_TRUE);
       }
 
       void NoteDyingRequest(IDBRequest* aRequest) {
         NS_ASSERTION(mLiveRequests.Contains(aRequest), "Unknown request!");
         mLiveRequests.RemoveElement(aRequest);
       }
 
     private:
+      already_AddRefed<IDBRequest>
+      GenerateRequestInternal(nsIScriptContext* aScriptContext,
+                              nsPIDOMWindow* aOwner,
+                              PRBool aWriteRequest);
+
       // XXXbent Assuming infallible nsTArray here, make sure it lands!
       nsAutoTArray<IDBRequest*, 1> mLiveRequests;
   };
 
   friend class IDBRequestGenerator;
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIIDBREQUEST
@@ -108,27 +112,31 @@ public:
   already_AddRefed<nsISupports> GetGenerator()
   {
     nsCOMPtr<nsISupports> generator(mGenerator);
     return generator.forget();
   }
 
 private:
   // Only called by IDBRequestGenerator::Generate().
-  IDBRequest(Generator* aGenerator,
-             bool aWriteRequest);
+  IDBRequest()
+  : mReadyState(nsIIDBRequest::INITIAL),
+    mAborted(PR_FALSE),
+    mWriteRequest(PR_FALSE)
+  { }
 
   nsRefPtr<Generator> mGenerator;
 
 protected:
   // Called by Release().
   ~IDBRequest();
 
-  PRUint16 mReadyState;
-  PRBool mAborted;
-  PRBool mWriteRequest;
   nsRefPtr<nsDOMEventListenerWrapper> mOnSuccessListener;
   nsRefPtr<nsDOMEventListenerWrapper> mOnErrorListener;
+
+  PRUint16 mReadyState;
+  PRPackedBool mAborted;
+  PRPackedBool mWriteRequest;
 };
 
 END_INDEXEDDB_NAMESPACE
 
 #endif // mozilla_dom_indexeddb_idbrequest_h__
--- a/dom/indexedDB/IDBTransaction.cpp
+++ b/dom/indexedDB/IDBTransaction.cpp
@@ -34,18 +34,21 @@
  * 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 "IDBTransaction.h"
 
+#include "nsIScriptContext.h"
+
 #include "mozilla/storage.h"
 #include "nsDOMClassInfo.h"
+#include "nsPIDOMWindow.h"
 #include "nsProxyRelease.h"
 #include "nsThreadUtils.h"
 
 #include "IDBEvents.h"
 #include "IDBCursor.h"
 #include "IDBObjectStore.h"
 #include "IDBFactory.h"
 #include "DatabaseInfo.h"
@@ -67,17 +70,18 @@ DoomCachedStatements(const nsACString& a
   helper->AddDoomedObject(aStatement);
   return PL_DHASH_REMOVE;
 }
 
 } // anonymous namespace
 
 // static
 already_AddRefed<IDBTransaction>
-IDBTransaction::Create(IDBDatabase* aDatabase,
+IDBTransaction::Create(JSContext* aCx,
+                       IDBDatabase* aDatabase,
                        nsTArray<nsString>& aObjectStoreNames,
                        PRUint16 aMode,
                        PRUint32 aTimeout)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   nsRefPtr<IDBTransaction> transaction = new IDBTransaction();
 
@@ -90,16 +94,31 @@ IDBTransaction::Create(IDBDatabase* aDat
     return nsnull;
   }
 
   if (!transaction->mCachedStatements.Init()) {
     NS_ERROR("Failed to initialize hash!");
     return nsnull;
   }
 
+  nsIScriptContext* context = GetScriptContextFromJSContext(aCx);
+  if (context) {
+    transaction->mScriptContext = context;
+    nsCOMPtr<nsPIDOMWindow> window =
+      do_QueryInterface(context->GetGlobalObject());
+    if (window) {
+      transaction->mOwner = window->GetCurrentInnerWindow();
+    }
+  }
+
+  if (!transaction->mOwner) {
+    NS_ERROR("Couldn't get script context and owner!");
+    return nsnull;
+  }
+
   return transaction.forget();
 }
 
 IDBTransaction::IDBTransaction()
 : mReadyState(nsIIDBTransaction::INITIAL),
   mMode(nsIIDBTransaction::READ_ONLY),
   mTimeout(0),
   mPendingRequests(0),
--- a/dom/indexedDB/IDBTransaction.h
+++ b/dom/indexedDB/IDBTransaction.h
@@ -48,17 +48,19 @@
 
 #include "nsDOMEventTargetHelper.h"
 #include "nsCycleCollectionParticipant.h"
 
 #include "nsAutoPtr.h"
 #include "nsHashKeys.h"
 #include "nsInterfaceHashtable.h"
 
+class nsIScriptContext;
 class nsIThread;
+class nsPIDOMWindow;
 
 BEGIN_INDEXEDDB_NAMESPACE
 
 class AsyncConnectionHelper;
 class CommitHelper;
 struct ObjectStoreInfo;
 class TransactionThreadPool;
 
@@ -73,17 +75,18 @@ class IDBTransaction : public nsDOMEvent
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIIDBTRANSACTION
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBTransaction,
                                            nsDOMEventTargetHelper)
 
   static already_AddRefed<IDBTransaction>
-  Create(IDBDatabase* aDatabase,
+  Create(JSContext* aCx,
+         IDBDatabase* aDatabase,
          nsTArray<nsString>& aObjectStoreNames,
          PRUint16 aMode,
          PRUint32 aTimeout);
 
   void OnNewRequest();
   void OnRequestFinished();
 
   bool StartSavepoint();
@@ -139,16 +142,26 @@ public:
   bool IsWriteAllowed() const
   {
     return mMode == nsIIDBTransaction::READ_WRITE;
   }
 #endif
 
   enum { FULL_LOCK = nsIIDBTransaction::SNAPSHOT_READ + 1 };
 
+  nsIScriptContext* ScriptContext()
+  {
+    return mScriptContext;
+  }
+
+  nsPIDOMWindow* Owner()
+  {
+    return mOwner;
+  }
+
 private:
   IDBTransaction();
   ~IDBTransaction();
 
   // Only meant to be called on mStorageThread!
   nsresult GetOrCreateConnection(mozIStorageConnection** aConnection);
 
   nsresult CommitOrRollback();
--- a/dom/indexedDB/IndexedDatabase.h
+++ b/dom/indexedDB/IndexedDatabase.h
@@ -38,16 +38,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef mozilla_dom_indexeddb_indexeddatabase_h__
 #define mozilla_dom_indexeddb_indexeddatabase_h__
 
 #include "nsIProgrammingLanguage.h"
 
+#include "jsapi.h"
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsDebug.h"
 #include "nsStringGlue.h"
 #include "nsTArray.h"
 
 #define BEGIN_INDEXEDDB_NAMESPACE \
   namespace mozilla { namespace dom { namespace indexedDB {
--- a/dom/indexedDB/nsIIDBDatabase.idl
+++ b/dom/indexedDB/nsIIDBDatabase.idl
@@ -40,46 +40,53 @@
 #include "nsISupports.idl"
 
 interface nsIVariant;
 interface nsIIDBObjectStore;
 interface nsIIDBRequest;
 interface nsIIDBTransaction;
 interface nsIDOMDOMStringList;
 
+%{C++
+#include "jsapi.h"
+%}
+
 /**
  * IDBDatabase interface.  See
  * http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#idl-def-IDBDatabase
  * for more information.
  */
 [scriptable, uuid(e258ad44-3306-427f-ac17-c528060c661a)]
 interface nsIIDBDatabase : nsISupports
 {
   readonly attribute DOMString name;
 
   readonly attribute DOMString description;
 
   readonly attribute DOMString version;
 
   readonly attribute nsIDOMDOMStringList objectStoreNames;
 
+  [implicit_jscontext]
   nsIIDBRequest
   createObjectStore(in AString name,
                     in AString keyPath,
                     [optional /* false */] in boolean autoIncrement);
 
+  [implicit_jscontext]
   nsIIDBRequest
   removeObjectStore(in AString name);
 
+  [implicit_jscontext]
   nsIIDBRequest
   setVersion(in AString version);
 
-  [optional_argc]
+  [optional_argc, implicit_jscontext]
   nsIIDBTransaction
   transaction(in nsIVariant storeNames, // js array of strings
               [optional /* READ_ONLY */] in unsigned short mode,
               [optional /* 5000ms */] in unsigned long timeout);
 
-  [optional_argc]
+  [optional_argc, implicit_jscontext]
   nsIIDBObjectStore
   objectStore(in AString name,
               [optional /* READ_ONLY */] in unsigned short mode);
 };
--- a/dom/indexedDB/nsIIDBFactory.idl
+++ b/dom/indexedDB/nsIIDBFactory.idl
@@ -39,24 +39,29 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 
 interface nsIIDBKeyRange;
 interface nsIIDBRequest;
 interface nsIVariant;
 
+%{C++
+#include "jsapi.h"
+%}
+
 /**
  * Interface that defines the indexedDB property on a window.  See
  * http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#idl-def-IDBFactory
  * for more information.
  */
 [scriptable, uuid(a1e1dbd1-53a7-490a-ab6f-aa55809dd867)]
 interface nsIIDBFactory : nsISupports
 {
+  [implicit_jscontext]
   nsIIDBRequest
   open(in AString name,
        in AString description);
 
   nsIIDBKeyRange
   makeSingleKeyRange(in nsIVariant value);
 
   nsIIDBKeyRange
--- a/dom/indexedDB/nsIIDBObjectStore.idl
+++ b/dom/indexedDB/nsIIDBObjectStore.idl
@@ -77,29 +77,27 @@ interface nsIIDBObjectStore : nsISupport
   [implicit_jscontext, optional_argc]
   nsIIDBRequest
   add(in jsval value,
       [optional /* undefined */] in jsval key);
 
   // Success fires IDBTransactionEvent, result == key
   [implicit_jscontext, optional_argc]
   nsIIDBRequest
-  modify(in jsval value,
-         [optional /* undefined */] in jsval key);
-
-  // Success fires IDBTransactionEvent, result == key
-  [implicit_jscontext, optional_argc]
-  nsIIDBRequest
-  addOrModify(in jsval value,
-              [optional /* undefined */] in jsval key);
+  put(in jsval value,
+      [optional /* undefined */] in jsval key);
 
   // Success fires IDBTransactionEvent, result == null
   nsIIDBRequest
   remove(in nsIVariant key);
 
+  // Success fires IDBTransactionEvent, result == null
+  nsIIDBRequest
+  clear();
+
   // Success fires IDBTransactionEvent, result == IDBCursor or
   // IDBCursorPreloadedRequest if preload == true. result == null if no match.
   [optional_argc]
   nsIIDBRequest
   openCursor([optional /* null */] in nsIIDBKeyRange range,
              [optional /* NEXT */] in unsigned short direction,
              [optional /* false */] in boolean preload);
 
--- a/dom/indexedDB/test/Makefile.in
+++ b/dom/indexedDB/test/Makefile.in
@@ -43,16 +43,17 @@ relativesrcdir = dom/indexedDB/test
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES = \
   helpers.js \
   test_add_twice_failure.html \
   test_bad_keypath.html \
+  test_clear.html \
   test_create_index.html \
   test_create_objectStore.html \
   test_cursors.html \
   test_cursor_update_updates_indexes.html \
   test_event_source.html \
   test_getAll.html \
   test_global_data.html \
   test_index_getAll.html \
--- a/dom/indexedDB/test/helpers.js
+++ b/dom/indexedDB/test/helpers.js
@@ -18,16 +18,23 @@ function finishTest()
   });
 }
 
 function grabEventAndContinueHandler(event)
 {
   testGenerator.send(event);
 }
 
+function continueToNextStep()
+{
+  SimpleTest.executeSoon(function() {
+    testGenerator.next();
+  });
+}
+
 function errorHandler(event)
 {
   ok(false, "indexedDB error (" + event.code + "): " + event.message);
   finishTest();
 }
 
 function unexpectedSuccessHandler()
 {
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/test_clear.html
@@ -0,0 +1,113 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>Indexed Database Property Test</title>
+
+  <script type="text/javascript" src="/MochiKit/packed.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+  <script type="text/javascript;version=1.7">
+    function testSteps()
+    {
+      const READ_WRITE = Components.interfaces.nsIIDBTransaction.READ_WRITE;
+
+      const name = window.location.pathname;
+      const description = "My Test Database";
+      const entryCount = 1000;
+
+      let request = moz_indexedDB.open(name, description);
+      request.onerror = errorHandler;
+      request.onsuccess = grabEventAndContinueHandler;
+      let event = yield;
+
+      let db = event.result;
+
+      request = db.createObjectStore("foo", "", true);
+      request.onerror = errorHandler;
+      request.onsuccess = grabEventAndContinueHandler;
+      let event = yield;
+
+      event.transaction.oncomplete = continueToNextStep;
+
+      let objectStore = event.result;
+
+      let firstKey;
+      for (let i = 0; i < entryCount; i++) {
+        request = objectStore.add({});
+        request.onerror = errorHandler;
+        if (!i) {
+          request.onsuccess = function(event) {
+            firstKey = event.result;
+          };
+        }
+      }
+      yield;
+
+      isnot(firstKey, undefined, "got first key");
+
+      let seenEntryCount = 0;
+
+      request = db.objectStore("foo").openCursor();
+      request.onerror = errorHandler;
+      request.onsuccess = function(event) {
+        let cursor = event.result;
+        if (cursor) {
+          seenEntryCount++;
+          cursor.continue();
+        }
+        else {
+          continueToNextStep();
+        }
+      }
+      yield;
+
+      is(seenEntryCount, entryCount, "Correct entry count");
+
+      try {
+        db.objectStore("foo").clear();
+        ok(false, "clear should throw on READ_ONLY transactions");
+      }
+      catch (e) {
+        ok(true, "clear should throw on READ_ONLY transactions");
+      }
+
+      request = db.objectStore("foo", READ_WRITE).clear();
+      request.onerror = errorHandler;
+      request.onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      ok(event.result === null, "Correct event.result");
+
+      request = db.objectStore("foo").openCursor();
+      request.onerror = errorHandler;
+      request.onsuccess = function(event) {
+        let cursor = event.result;
+        if (cursor) {
+          ok(false, "Shouldn't have any entries");
+        }
+        continueToNextStep();
+      }
+      yield;
+
+      request = db.objectStore("foo", READ_WRITE).add({});
+      request.onerror = errorHandler;
+      request.onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      isnot(event.result, firstKey, "Got a different key");
+
+      finishTest();
+      yield;
+    }
+  </script>
+  <script type="text/javascript;version=1.7" src="helpers.js"></script>
+
+</head>
+
+<body onload="runTest();"></body>
+
+</html>
--- a/dom/indexedDB/test/test_cursors.html
+++ b/dom/indexedDB/test/test_cursors.html
@@ -277,17 +277,17 @@
 
       request = objectStore.get(sortedKeys[4]);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       event = yield;
 
       is(event.result, "bar", "Update succeeded");
 
-      request = objectStore.modify("foo", sortedKeys[4]);
+      request = objectStore.put("foo", sortedKeys[4]);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       event = yield;
 
       keyIndex = 0;
 
       let gotRemoveEvent = false;
       let retval = false;
--- a/dom/indexedDB/test/test_key_requirements.html
+++ b/dom/indexedDB/test/test_key_requirements.html
@@ -32,55 +32,55 @@
 
       request = objectStore.add({});
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       event = yield;
 
       let key1 = event.result;
 
-      request = objectStore.modify({}, key1);
+      request = objectStore.put({}, key1);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       event = yield;
 
-      is(event.result, key1, "modify gave the same key back");
+      is(event.result, key1, "put gave the same key back");
 
       let key2 = 10;
 
-      request = objectStore.addOrModify({}, key2);
+      request = objectStore.put({}, key2);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       event = yield;
 
-      is(event.result, key2, "modify gave the same key back");
+      is(event.result, key2, "put gave the same key back");
 
       key2 = 100;
 
       request = objectStore.add({}, key2);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       event = yield;
 
-      is(event.result, key2, "modify gave the same key back");
+      is(event.result, key2, "put gave the same key back");
 
       try {
-        objectStore.addOrModify({});
-        ok(false, "addOrModify with no key should throw!");
+        objectStore.put({});
+        ok(false, "put with no key should throw!");
       }
       catch (e) {
-        ok(true, "addOrModify with no key threw");
+        ok(true, "put with no key threw");
       }
 
       try {
-        objectStore.modify({});
-        ok(false, "modify with no key should throw!");
+        objectStore.put({});
+        ok(false, "put with no key should throw!");
       }
       catch (e) {
-        ok(true, "modify with no key threw");
+        ok(true, "put with no key threw");
       }
 
       try {
         objectStore.remove();
         ok(false, "remove with no key should throw!");
       }
       catch (e) {
         ok(true, "remove with no key threw");
@@ -97,29 +97,29 @@
         objectStore.add({});
         ok(false, "add with no key should throw!");
       }
       catch (e) {
         ok(true, "add with no key threw");
       }
 
       try {
-        objectStore.addOrModify({});
-        ok(false, "addOrModify with no key should throw!");
+        objectStore.put({});
+        ok(false, "put with no key should throw!");
       }
       catch (e) {
-        ok(true, "addOrModify with no key threw");
+        ok(true, "put with no key threw");
       }
 
       try {
-        objectStore.modify({});
-        ok(false, "modify with no key should throw!");
+        objectStore.put({});
+        ok(false, "put with no key should throw!");
       }
       catch (e) {
-        ok(true, "modify with no key threw");
+        ok(true, "put with no key threw");
       }
 
       try {
         objectStore.remove();
         ok(false, "remove with no key should throw!");
       }
       catch (e) {
         ok(true, "remove with no key threw");
@@ -136,29 +136,29 @@
         objectStore.add({});
         ok(false, "add with no key should throw!");
       }
       catch (e) {
         ok(true, "add with no key threw");
       }
 
       try {
-        objectStore.addOrModify({});
-        ok(false, "addOrModify with no key should throw!");
+        objectStore.put({});
+        ok(false, "put with no key should throw!");
       }
       catch (e) {
-        ok(true, "addOrModify with no key threw");
+        ok(true, "put with no key threw");
       }
 
       try {
-        objectStore.modify({});
-        ok(false, "modify with no key should throw!");
+        objectStore.put({});
+        ok(false, "put with no key should throw!");
       }
       catch (e) {
-        ok(true, "modify with no key threw");
+        ok(true, "put with no key threw");
       }
 
       try {
         objectStore.remove();
         ok(false, "remove with no key should throw!");
       }
       catch (e) {
         ok(true, "remove with no key threw");
@@ -168,56 +168,56 @@
 
       request = objectStore.add({id:key1});
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       event = yield;
 
       is(event.result, key1, "add gave back the same key");
 
-      request = objectStore.modify({id:10});
+      request = objectStore.put({id:10});
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       event = yield;
 
-      is(event.result, key1, "modify gave back the same key");
+      is(event.result, key1, "put gave back the same key");
 
-      request = objectStore.addOrModify({id:10});
+      request = objectStore.put({id:10});
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       event = yield;
 
-      is(event.result, key1, "addOrModify gave back the same key");
+      is(event.result, key1, "put gave back the same key");
 
       request = objectStore.add({id:10});
       request.onerror = grabEventAndContinueHandler;
       request.onsuccess = unexpectedSuccessHandler;
       yield;
 
       request = objectStore.add({}, null);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       event = yield;
 
       is(typeof(event.result), "string", "Good generated key");
 
       try {
-        objectStore.modify({}, null);
-        ok(false, "modify with null key should throw!");
+        objectStore.put({}, null);
+        ok(false, "put with null key should throw!");
       }
       catch (e) {
-        ok(true, "modify with null key threw");
+        ok(true, "put with null key threw");
       }
 
       try {
-        objectStore.addOrModify({}, null);
-        ok(false, "addOrModify with null key should throw!");
+        objectStore.put({}, null);
+        ok(false, "put with null key should throw!");
       }
       catch (e) {
-        ok(true, "addOrModify with null key threw");
+        ok(true, "put with null key threw");
       }
 
       try {
         objectStore.remove({}, null);
         ok(false, "remove with null key should throw!");
       }
       catch (e) {
         ok(true, "remove with null key threw");
@@ -232,46 +232,46 @@
 
       request = objectStore.add({});
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       event = yield;
 
       key1 = event.result;
 
-      request = objectStore.modify({id:key1});
+      request = objectStore.put({id:key1});
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       event = yield;
 
-      is(event.result, key1, "modify gave the same key back");
+      is(event.result, key1, "put gave the same key back");
 
       key2 = 10;
 
-      request = objectStore.addOrModify({id:key2});
+      request = objectStore.put({id:key2});
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       event = yield;
 
-      is(event.result, key2, "modify gave the same key back");
+      is(event.result, key2, "put gave the same key back");
 
       try {
-        objectStore.addOrModify({});
-        ok(false, "addOrModify with no key should throw!");
+        objectStore.put({});
+        ok(false, "put with no key should throw!");
       }
       catch (e) {
-        ok(true, "addOrModify with no key threw");
+        ok(true, "put with no key threw");
       }
 
       try {
-        objectStore.modify({});
-        ok(false, "modify with no key should throw!");
+        objectStore.put({});
+        ok(false, "put with no key should throw!");
       }
       catch (e) {
-        ok(true, "modify with no key threw");
+        ok(true, "put with no key threw");
       }
 
       try {
         objectStore.remove();
         ok(false, "remove with no key should throw!");
       }
       catch (e) {
         ok(true, "remove with no key threw");
--- a/dom/indexedDB/test/test_readonly_transactions.html
+++ b/dom/indexedDB/test/test_readonly_transactions.html
@@ -55,43 +55,43 @@
         is(event.transaction.mode, READ_WRITE, "Correct mode");
         key2 = event.result;
         testGenerator.next();
       }
       yield;
 
       request = db.transaction([osName], READ_WRITE)
                   .objectStore(osName)
-                  .addOrModify({}, key1);
+                  .put({}, key1);
       request.onerror = errorHandler;
       request.onsuccess = function(event) {
         is(event.transaction.mode, READ_WRITE, "Correct mode");
         testGenerator.next();
       }
       yield;
 
-      request = db.objectStore(osName, READ_WRITE).addOrModify({}, key2);
+      request = db.objectStore(osName, READ_WRITE).put({}, key2);
       request.onerror = errorHandler;
       request.onsuccess = function(event) {
         is(event.transaction.mode, READ_WRITE, "Correct mode");
         testGenerator.next();
       }
       yield;
 
       request = db.transaction([osName], READ_WRITE)
                   .objectStore(osName)
-                  .modify({}, key1);
+                  .put({}, key1);
       request.onerror = errorHandler;
       request.onsuccess = function(event) {
         is(event.transaction.mode, READ_WRITE, "Correct mode");
         testGenerator.next();
       }
       yield;
 
-      request = db.objectStore(osName, READ_WRITE).modify({}, key1);
+      request = db.objectStore(osName, READ_WRITE).put({}, key1);
       request.onerror = errorHandler;
       request.onsuccess = function(event) {
         is(event.transaction.mode, READ_WRITE, "Correct mode");
         testGenerator.next();
       }
       yield;
 
       request = db.transaction([osName], READ_WRITE)
@@ -124,41 +124,41 @@
         request = db.objectStore(osName).add({});
         ok(false, "Adding to a readonly transaction should fail!");
       }
       catch (e) {
         ok(true, "Adding to a readonly transaction failed");
       }
 
       try {
-        request = db.transaction([osName]).objectStore(osName).addOrModify({});
+        request = db.transaction([osName]).objectStore(osName).put({});
         ok(false, "Adding or modifying a readonly transaction should fail!");
       }
       catch (e) {
         ok(true, "Adding or modifying a readonly transaction failed");
       }
 
       try {
-        request = db.objectStore(osName).addOrModify({});
+        request = db.objectStore(osName).put({});
         ok(false, "Adding or modifying a readonly transaction should fail!");
       }
       catch (e) {
         ok(true, "Adding or modifying a readonly transaction failed");
       }
 
       try {
-        request = db.transaction([osName]).objectStore(osName).modify({}, key1);
+        request = db.transaction([osName]).objectStore(osName).put({}, key1);
         ok(false, "Modifying a readonly transaction should fail!");
       }
       catch (e) {
         ok(true, "Modifying a readonly transaction failed");
       }
 
       try {
-        request = db.objectStore(osName).modify({}, key1);
+        request = db.objectStore(osName).put({}, key1);
         ok(false, "Modifying a readonly transaction should fail!");
       }
       catch (e) {
         ok(true, "Modifying a readonly transaction failed");
       }
 
       try {
         request = db.transaction([osName]).objectStore(osName).remove(key1);
--- a/dom/indexedDB/test/test_transaction_abort.html
+++ b/dom/indexedDB/test/test_transaction_abort.html
@@ -86,29 +86,29 @@
         objectStore.add({});
         ok(false, "Should have thrown");
       }
       catch (e) {
         ok(true, "Add threw");
       }
 
       try {
-        objectStore.modify({}, 1);
+        objectStore.put({}, 1);
         ok(false, "Should have thrown");
       }
       catch (e) {
-        ok(true, "Modify threw");
+        ok(true, "Put threw");
       }
 
       try {
-        objectStore.addOrModify({}, 1);
+        objectStore.put({}, 1);
         ok(false, "Should have thrown");
       }
       catch (e) {
-        ok(true, "AddOrModify threw");
+        ok(true, "Put threw");
       }
 
       try {
         objectStore.remove(1);
         ok(false, "Should have thrown");
       }
       catch (e) {
         ok(true, "Remove threw");
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -48,17 +48,17 @@
 interface nsIDOMNode;
 interface nsIDOMNodeList;
 interface nsIDOMElement;
 interface nsIDOMHTMLCanvasElement;
 interface nsIDOMEvent;
 interface nsITransferable;
 interface nsIQueryContentEventResult;
 
-[scriptable, uuid(1e042706-0343-4cba-a549-6a83eefb1835)]
+[scriptable, uuid(59e6f1be-ba0e-4102-9fe5-d5af8777742a)]
 interface nsIDOMWindowUtils : nsISupports {
 
   /**
    * Image animation mode of the window. When this attribute's value
    * is changed, the implementation should set all images in the window
    * to the given value. That is, when set to kDontAnimMode, all images
    * will stop animating. The attribute's value must be one of the
    * animationMode values from imgIContainer.
@@ -95,18 +95,17 @@ interface nsIDOMWindowUtils : nsISupport
   /**
    * Force an immediate redraw of this window.  The parameter specifies
    * the number of times to redraw, and the return value is the length,
    * in milliseconds, that the redraws took.  If aCount is not specified
    * or is 0, it is taken to be 1.
    */
   unsigned long redraw([optional] in unsigned long aCount);
 
-  /** Synthesize a mouse event for a window. The event types supported
-   *  are: 
+  /** Synthesize a mouse event. The event types supported are:
    *    mousedown, mouseup, mousemove, mouseover, mouseout, contextmenu
    *
    * Events are sent in coordinates offset by aX and aY from the window.
    *
    * Note that additional events may be fired as a result of this call. For
    * instance, typically a click event will be fired as a result of a
    * mousedown and mouseup in sequence.
    *
@@ -114,16 +113,20 @@ interface nsIDOMWindowUtils : nsISupport
    * only fired when the window is entered or exited. For inter-element
    * mouseover and mouseout events, a movemove event fired on the new element
    * should be sufficient to generate the correct over and out events as well.
    *
    * Cannot be accessed from unprivileged context (not content-accessible)
    * Will throw a DOM security error if called without UniversalXPConnect
    * privileges.
    *
+   * The event is dispatched via the toplevel window, so it could go to any
+   * window under the toplevel window, in some cases it could never reach this
+   * window at all.
+   *
    * @param aType event type
    * @param aX x offset in CSS pixels
    * @param aY y offset in CSS pixels
    * @param aButton button to synthesize
    * @param aClickCount number of clicks that have been performed
    * @param aModifiers modifiers pressed, using constants defined in nsIDOMNSEvent
    * @param aIgnoreRootScrollFrame whether the event should ignore viewport bounds
    *                           during dispatch
@@ -131,16 +134,27 @@ interface nsIDOMWindowUtils : nsISupport
   void sendMouseEvent(in AString aType,
                       in float aX,
                       in float aY,
                       in long aButton,
                       in long aClickCount,
                       in long aModifiers,
                       [optional] in boolean aIgnoreRootScrollFrame);
 
+  /** The same as sendMouseEvent but ensures that the event is dispatched to
+   *  this DOM window or one of its children.
+   */
+  void sendMouseEventToWindow(in AString aType,
+                              in float aX,
+                              in float aY,
+                              in long aButton,
+                              in long aClickCount,
+                              in long aModifiers,
+                              [optional] in boolean aIgnoreRootScrollFrame);
+
   /** Synthesize a mouse scroll event for a window. The event types supported
    *  are: 
    *    DOMMouseScroll
    *    MozMousePixelScroll
    *
    * Events are sent in coordinates offset by aX and aY from the window.
    *
    * Cannot be accessed from unprivileged context (not content-accessible)
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -123,17 +123,19 @@ parent:
     /**
      * Create a layout frame (encapsulating a remote layer tree) for
      * the page that is currently loaded in the <browser>.
      */
     async PRenderFrame();
 
     __delete__();
 
-    PExternalHelperApp(URI uri, nsCString aMimeContentType, bool aForceSave, PRInt64 aContentLength);
+    PExternalHelperApp(URI uri, nsCString aMimeContentType,
+                       nsCString aContentDisposition, bool aForceSave,
+                       PRInt64 aContentLength);
 
 child:
     /**
      * Notify the remote browser that it has been Show()n on this
      * side, with the given |visibleRect|.  This message is expected
      * to trigger creation of the remote browser's "widget".
      *
      * |Show()| and |Move()| take IntSizes rather than Rects because
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -1284,16 +1284,17 @@ TabChildGlobal::GetPrincipal()
   if (!mTabChild)
     return nsnull;
   return mTabChild->GetPrincipal();
 }
 
 PExternalHelperAppChild*
 TabChild::AllocPExternalHelperApp(const IPC::URI& uri,
                                   const nsCString& aMimeContentType,
+                                  const nsCString& aContentDisposition,
                                   const bool& aForceSave,
                                   const PRInt64& aContentLength)
 {
   ExternalHelperAppChild *child = new ExternalHelperAppChild();
   child->AddRef();
   return child;
 }
 
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -225,16 +225,17 @@ public:
                                                      const nsCString&,
                                                      const nsCString&,
                                                      const nsTArray<int>&,
                                                      const nsTArray<nsString>&);
     virtual bool DeallocPContentDialog(PContentDialogChild* aDialog);
     virtual PExternalHelperAppChild *AllocPExternalHelperApp(
             const IPC::URI& uri,
             const nsCString& aMimeContentType,
+            const nsCString& aContentDisposition,
             const bool& aForceSave,
             const PRInt64& aContentLength);
     virtual bool DeallocPExternalHelperApp(PExternalHelperAppChild *aService);
     static void ParamsToArrays(nsIDialogParamBlock* aParams,
                                nsTArray<int>& aIntParams,
                                nsTArray<nsString>& aStringParams);
     static void ArraysToParams(const nsTArray<int>& aIntParams,
                                const nsTArray<nsString>& aStringParams,
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -827,22 +827,23 @@ TabParent::GetFrameLoader() const
 {
   nsCOMPtr<nsIFrameLoaderOwner> frameLoaderOwner = do_QueryInterface(mFrameElement);
   return frameLoaderOwner ? frameLoaderOwner->GetFrameLoader() : nsnull;
 }
 
 PExternalHelperAppParent*
 TabParent::AllocPExternalHelperApp(const IPC::URI& uri,
                                    const nsCString& aMimeContentType,
+                                   const nsCString& aContentDisposition,
                                    const bool& aForceSave,
                                    const PRInt64& aContentLength)
 {
   ExternalHelperAppParent *parent = new ExternalHelperAppParent(uri, aContentLength);
   parent->AddRef();
-  parent->Init(this, aMimeContentType, aForceSave);
+  parent->Init(this, aMimeContentType, aContentDisposition, aForceSave);
   return parent;
 }
 
 bool
 TabParent::DeallocPExternalHelperApp(PExternalHelperAppParent* aService)
 {
   ExternalHelperAppParent *parent = static_cast<ExternalHelperAppParent *>(aService);
   parent->Release();
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -145,16 +145,17 @@ public:
     {
       delete aDialog;
       return true;
     }
 
     virtual PExternalHelperAppParent* AllocPExternalHelperApp(
             const IPC::URI& uri,
             const nsCString& aMimeContentType,
+            const nsCString& aContentDisposition,
             const bool& aForceSave,
             const PRInt64& aContentLength);
     virtual bool DeallocPExternalHelperApp(PExternalHelperAppParent* aService);
 
     void LoadURL(nsIURI* aURI);
     // XXX/cjones: it's not clear what we gain by hiding these
     // message-sending functions under a layer of indirection and
     // eating the return values
--- a/dom/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -67,9 +67,10 @@ ElementSuffersFromBeingTooLong=The text 
 TextElementSuffersFromBeingMissing=This field is mandatory, you have to fill it.
 CheckboxElementSuffersFromBeingMissing=This checkbox is mandatory, you have to check it.
 RadioElementSuffersFromBeingMissing=You have to select one of these options.
 FileElementSuffersFromBeingMissing=You have to select a file.
 ElementSuffersFromInvalidEmail=The entered email address is not valid.
 ElementSuffersFromInvalidURL=The entered URL is not valid.
 ElementSuffersFromPatternMismatch=The field value is not in the requested format.
 ElementSuffersFromPatternMismatchWithTitle=The field value is not in the requested format. %S
-
+UseOfDocumentWidthWarning=Non-standard document.width was used. Use standard document.body.clientWidth instead.
+UseOfDocumentHeightWarning=Non-standard document.height was used. Use standard document.body.clientHeight instead.
--- a/dom/plugins/PluginModuleParent.cpp
+++ b/dom/plugins/PluginModuleParent.cpp
@@ -228,17 +228,23 @@ PluginModuleParent::CleanupFromTimeout()
 }
 
 bool
 PluginModuleParent::ShouldContinueFromReplyTimeout()
 {
 #ifdef MOZ_CRASHREPORTER
     nsCOMPtr<nsILocalFile> pluginDump;
     nsCOMPtr<nsILocalFile> browserDump;
-    if (CrashReporter::CreatePairedMinidumps(OtherProcess(),
+    CrashReporter::ProcessHandle child;
+#ifdef XP_MACOSX
+    child = mSubprocess->GetChildTask();
+#else
+    child = OtherProcess();
+#endif
+    if (CrashReporter::CreatePairedMinidumps(child,
                                              mPluginThread,
                                              &mHangID,
                                              getter_AddRefs(pluginDump),
                                              getter_AddRefs(browserDump)) &&
         CrashReporter::GetIDFromMinidump(pluginDump, mPluginDumpID) &&
         CrashReporter::GetIDFromMinidump(browserDump, mBrowserDumpID)) {
 
         PLUGIN_LOG_DEBUG(
--- a/dom/plugins/PluginProcessParent.h
+++ b/dom/plugins/PluginProcessParent.h
@@ -49,17 +49,17 @@
 #include "chrome/common/child_process_host.h"
 
 #include "mozilla/ipc/GeckoChildProcessHost.h"
 
 namespace mozilla {
 namespace plugins {
 //-----------------------------------------------------------------------------
 
-class PluginProcessParent : mozilla::ipc::GeckoChildProcessHost
+class PluginProcessParent : public mozilla::ipc::GeckoChildProcessHost
 {
 public:
     PluginProcessParent(const std::string& aPluginFilePath);
     ~PluginProcessParent();
 
     /**
      * Synchronously launch the plugin process. If the process fails to launch
      * after timeoutMs, this method will return false.
--- a/dom/src/jsurl/nsJSProtocolHandler.cpp
+++ b/dom/src/jsurl/nsJSProtocolHandler.cpp
@@ -1007,16 +1007,22 @@ nsJSChannel::GetContentCharset(nsACStrin
 
 NS_IMETHODIMP
 nsJSChannel::SetContentCharset(const nsACString &aContentCharset)
 {
     return mStreamChannel->SetContentCharset(aContentCharset);
 }
 
 NS_IMETHODIMP
+nsJSChannel::GetContentDisposition(nsACString &aContentDisposition)
+{
+    return mStreamChannel->GetContentDisposition(aContentDisposition);
+}
+
+NS_IMETHODIMP
 nsJSChannel::GetContentLength(PRInt64 *aContentLength)
 {
     return mStreamChannel->GetContentLength(aContentLength);
 }
 
 NS_IMETHODIMP
 nsJSChannel::SetContentLength(PRInt64 aContentLength)
 {
--- a/dom/tests/Makefile.in
+++ b/dom/tests/Makefile.in
@@ -34,16 +34,17 @@
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK *****
 
 DEPTH		= ../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
+relativesrcdir = dom/tests
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE = test_dom
 
 DIRS		+= mochitest \
 		$(NULL)
 XPCSHELL_TESTS = unit
--- a/embedding/Makefile.in
+++ b/embedding/Makefile.in
@@ -35,16 +35,17 @@
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK *****
 
 DEPTH		= ..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
+relativesrcdir = embedding
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE       = embed
 
 DIRS = base components browser
 
 ifdef ENABLE_TESTS
--- a/embedding/android/GeckoAppShell.java
+++ b/embedding/android/GeckoAppShell.java
@@ -418,9 +418,25 @@ class GeckoAppShell
         intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
         try {
             GeckoApp.surfaceView.getContext().startActivity(intent);
             return true;
         } catch(ActivityNotFoundException e) {
             return false;
         }
     }
+
+    static String getClipboardText() {
+        Context context = GeckoApp.surfaceView.getContext();
+        ClipboardManager cm = (ClipboardManager)
+            context.getSystemService(Context.CLIPBOARD_SERVICE);
+        if (!cm.hasText())
+            return null;
+        return cm.getText().toString();
+    }
+
+    static void setClipboardText(String text) {
+        Context context = GeckoApp.surfaceView.getContext();
+        ClipboardManager cm = (ClipboardManager)
+            context.getSystemService(Context.CLIPBOARD_SERVICE);
+        cm.setText(text);
+    }
 }
--- a/embedding/browser/webBrowser/nsDocShellTreeOwner.cpp
+++ b/embedding/browser/webBrowser/nsDocShellTreeOwner.cpp
@@ -104,16 +104,17 @@
 #include "nsContextMenuInfo.h"
 #include "nsPresContext.h"
 #include "nsIViewManager.h"
 #include "nsIView.h"
 #include "nsPIDOMEventTarget.h"
 #include "nsIEventListenerManager.h"
 #include "nsIDOMEventGroup.h"
 #include "nsIDOMDragEvent.h"
+#include "nsIConstraintValidation.h"
 
 //
 // GetEventReceiver
 //
 // A helper routine that navigates the tricky path from a |nsWebBrowser| to
 // a |nsPIDOMEventTarget| via the window root and chrome event handler.
 //
 static nsresult
@@ -1098,16 +1099,25 @@ DefaultTooltipTextProvider::GetNodeText(
   NS_ENSURE_ARG_POINTER(aNode);
   NS_ENSURE_ARG_POINTER(aText);
     
   nsString outText;
 
   PRBool lookingForSVGTitle = PR_TRUE;
   PRBool found = PR_FALSE;
   nsCOMPtr<nsIDOMNode> current ( aNode );
+
+  // If the element implement the constraint validation API,
+  // show the validation message, if any, instead of the title.
+  nsCOMPtr<nsIConstraintValidation> cvElement = do_QueryInterface(current);
+  if (cvElement) {
+    cvElement->GetValidationMessage(outText);
+    found = !outText.IsEmpty();
+  }
+
   while ( !found && current ) {
     nsCOMPtr<nsIDOMElement> currElement ( do_QueryInterface(current) );
     if ( currElement ) {
       nsCOMPtr<nsIContent> content(do_QueryInterface(currElement));
       if (content) {
         nsIAtom *tagAtom = content->Tag();
         if (tagAtom != mTag_dialog &&
             tagAtom != mTag_dialogheader &&
--- a/gfx/cairo/cairo/src/cairo-d2d-surface.cpp
+++ b/gfx/cairo/cairo/src/cairo-d2d-surface.cpp
@@ -2420,25 +2420,25 @@ static cairo_status_t
      * -ever- want this to happen.
      */
     softDesc.MipLevels = 1;
     softDesc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE | D3D10_CPU_ACCESS_READ;
     softDesc.Usage = D3D10_USAGE_STAGING;
     softDesc.BindFlags = 0;
     hr = d2dsurf->device->mD3D10Device->CreateTexture2D(&softDesc, NULL, &softTexture);
     if (FAILED(hr)) {
-	return CAIRO_STATUS_NO_MEMORY;
+	return _cairo_error(CAIRO_STATUS_NO_MEMORY);
     }
 
     d2dsurf->device->mD3D10Device->CopyResource(softTexture, d2dsurf->surface);
 
     D3D10_MAPPED_TEXTURE2D data;
     hr = softTexture->Map(0, D3D10_MAP_READ_WRITE, 0, &data);
     if (FAILED(hr)) {
-	return (cairo_status_t)CAIRO_INT_STATUS_UNSUPPORTED;
+	return _cairo_error(CAIRO_STATUS_NO_DEVICE);
     }
     *image_out = 
 	(cairo_image_surface_t*)_cairo_image_surface_create_for_data_with_content((unsigned char*)data.pData,
 										  d2dsurf->base.content,
 										  size.width,
 										  size.height,
 										  data.RowPitch);
     *image_extra = softTexture.forget();
@@ -2493,24 +2493,24 @@ static cairo_status_t
     image_rect->x = image_rect->y = 0;
 
     softDesc.MipLevels = 1;
     softDesc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE | D3D10_CPU_ACCESS_READ;
     softDesc.Usage = D3D10_USAGE_STAGING;
     softDesc.BindFlags = 0;
     hr = d2dsurf->device->mD3D10Device->CreateTexture2D(&softDesc, NULL, &softTexture);
     if (FAILED(hr)) {
-	return CAIRO_STATUS_NO_MEMORY;
+	return _cairo_error(CAIRO_STATUS_NO_MEMORY);
     }
     d2dsurf->device->mD3D10Device->CopyResource(softTexture, d2dsurf->surface);
 
     D3D10_MAPPED_TEXTURE2D data;
     hr = softTexture->Map(0, D3D10_MAP_READ_WRITE, 0, &data);
     if (FAILED(hr)) {
-	return (cairo_status_t)CAIRO_INT_STATUS_UNSUPPORTED;
+	return _cairo_error(CAIRO_STATUS_NO_DEVICE);
     }
     *image_out = 
 	(cairo_image_surface_t*)_cairo_image_surface_create_for_data_with_content((unsigned char*)data.pData,
 										  d2dsurf->base.content,
 										  size.width,
 										  size.height,
 										  data.RowPitch);
     *image_extra = softTexture.forget();
--- a/gfx/cairo/cairo/src/cairo-dwrite-font.cpp
+++ b/gfx/cairo/cairo/src/cairo-dwrite-font.cpp
@@ -130,16 +130,18 @@ struct _cairo_dwrite_font_face {
 };
 typedef struct _cairo_dwrite_font_face cairo_dwrite_font_face_t;
 
 /* cairo_scaled_font_t implementation */
 struct _cairo_dwrite_scaled_font {
     cairo_scaled_font_t base;
     cairo_matrix_t mat;
     cairo_matrix_t mat_inverse;
+    cairo_antialias_t antialias_mode;
+    DWRITE_MEASURING_MODE measuring_mode;
 };
 typedef struct _cairo_dwrite_scaled_font cairo_dwrite_scaled_font_t;
 
 /* Functions cairo_font_face_backend_t */
 static cairo_status_t
 _cairo_dwrite_font_face_create_for_toy (cairo_toy_font_face_t   *toy_face,
 					cairo_font_face_t      **font_face);
 static void
@@ -328,16 +330,64 @@ static void
 {
     cairo_dwrite_font_face_t *dwrite_font_face = static_cast<cairo_dwrite_font_face_t*>(font_face);
     if (dwrite_font_face->dwriteface)
 	dwrite_font_face->dwriteface->Release();
     if (dwrite_font_face->font)
 	dwrite_font_face->font->Release();
 }
 
+
+static inline unsigned short
+read_short(const char *buf)
+{
+    return be16_to_cpu(*(unsigned short*)buf);
+}
+
+#define GASP_TAG 0x70736167
+#define GASP_DOGRAY 0x2
+
+static cairo_bool_t
+do_grayscale(IDWriteFontFace *dwface, unsigned int ppem)
+{
+    void *tableContext;
+    char *tableData;
+    UINT32 tableSize;
+    BOOL exists;
+    dwface->TryGetFontTable(GASP_TAG, (const void**)&tableData, &tableSize, &tableContext, &exists);
+
+    if (exists) {
+	if (tableSize < 4) {
+	    dwface->ReleaseFontTable(tableContext);
+	    return true;
+	}
+	struct gaspRange {
+	    unsigned short maxPPEM; // Stored big-endian
+	    unsigned short behavior; // Stored big-endian
+	};
+	unsigned short numRanges = read_short(tableData + 2);
+	if (tableSize < (UINT)4 + numRanges * 4) {
+	    dwface->ReleaseFontTable(tableContext);
+	    return true;
+	}
+	gaspRange *ranges = (gaspRange *)(tableData + 4);
+	for (int i = 0; i < numRanges; i++) {
+	    if (be16_to_cpu(ranges[i].maxPPEM) > ppem) {
+		if (!(be16_to_cpu(ranges[i].behavior) & GASP_DOGRAY)) {
+		    dwface->ReleaseFontTable(tableContext);
+		    return false;
+		}
+		break;
+	    }
+	}
+	dwface->ReleaseFontTable(tableContext);
+    }
+    return true;
+}
+
 static cairo_status_t
 _cairo_dwrite_font_face_scaled_font_create (void			*abstract_face,
 					    const cairo_matrix_t	*font_matrix,
 					    const cairo_matrix_t	*ctm,
 					    const cairo_font_options_t  *options,
 					    cairo_scaled_font_t **font)
 {
     cairo_dwrite_font_face_t *font_face = static_cast<cairo_dwrite_font_face_t*>(abstract_face);
@@ -358,16 +408,51 @@ static cairo_status_t
     extents.max_x_advance = 14.0;
     extents.max_y_advance = 0.0;
 
     dwriteFont->mat = dwriteFont->base.ctm;
     cairo_matrix_multiply(&dwriteFont->mat, &dwriteFont->mat, font_matrix);
     dwriteFont->mat_inverse = dwriteFont->mat;
     cairo_matrix_invert (&dwriteFont->mat_inverse);
 
+    cairo_antialias_t default_quality = CAIRO_ANTIALIAS_SUBPIXEL;
+
+    dwriteFont->measuring_mode = DWRITE_MEASURING_MODE_NATURAL;
+
+    // The following code detects the system quality at scaled_font creation time,
+    // this means that if cleartype settings are changed but the scaled_fonts
+    // are re-used, they might not adhere to the new system setting until re-
+    // creation.
+    switch (_cairo_win32_get_system_text_quality()) {
+	case CLEARTYPE_QUALITY:
+	    default_quality = CAIRO_ANTIALIAS_SUBPIXEL;
+	    break;
+	case ANTIALIASED_QUALITY:
+	    default_quality = CAIRO_ANTIALIAS_GRAY;
+	    dwriteFont->measuring_mode = DWRITE_MEASURING_MODE_GDI_CLASSIC;
+	    break;
+	case DEFAULT_QUALITY:
+	    // _get_system_quality() seems to think aliased is default!
+	    default_quality = CAIRO_ANTIALIAS_NONE;
+	    dwriteFont->measuring_mode = DWRITE_MEASURING_MODE_GDI_CLASSIC;
+	    break;
+    }
+
+    if (default_quality == CAIRO_ANTIALIAS_GRAY) {
+	if (!do_grayscale(font_face->dwriteface, (unsigned int)_cairo_round(font_matrix->yy))) {
+	    default_quality = CAIRO_ANTIALIAS_NONE;
+	}
+    }
+
+    if (options->antialias == CAIRO_ANTIALIAS_DEFAULT) {
+	dwriteFont->antialias_mode = default_quality;
+    } else {
+	dwriteFont->antialias_mode = options->antialias;
+    }
+
     return _cairo_scaled_font_set_metrics (*font, &extents);
 }
 
 /* Implementation cairo_dwrite_scaled_font_backend_t */
 void
 _cairo_dwrite_scaled_font_fini(void *scaled_font)
 {
 }
@@ -1232,16 +1317,17 @@ cairo_int_status_t
     delete [] indices;
     delete [] offsets;
     delete [] advances;
 
     return CAIRO_INT_STATUS_SUCCESS;
 }
 
 #if CAIRO_HAS_D2D_SURFACE
+
 /* Surface helper function */
 //XXX: this function should probably be in cairo-d2d-surface.cpp
 cairo_int_status_t
 _cairo_dwrite_show_glyphs_on_d2d_surface(void			*surface,
 					cairo_operator_t	 op,
 					const cairo_pattern_t	*source,
 					cairo_glyph_t		*glyphs,
 					int			 num_glyphs,
@@ -1282,35 +1368,32 @@ cairo_int_status_t
 	_cairo_d2d_begin_draw_state(dst);
 	status = (cairo_int_status_t)_cairo_d2d_set_clip (dst, clip);
 
 	if (unlikely(status))
 	    return status;
     }
 #endif
 
-    D2D1_TEXT_ANTIALIAS_MODE highest_quality = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE;
+    D2D1_TEXT_ANTIALIAS_MODE cleartype_quality = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE;
 
     // If we're rendering to a temporary surface we cannot do sub-pixel AA.
     if (dst->base.content != CAIRO_CONTENT_COLOR || dst->rt.get() != target_rt.get()) {
-	highest_quality = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE;
+	cleartype_quality = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE;
     }
 
-    switch (scaled_font->options.antialias) {
-	case CAIRO_ANTIALIAS_DEFAULT:
-	    target_rt->SetTextAntialiasMode(highest_quality);
-	    break;
+    switch (dwritesf->antialias_mode) {
 	case CAIRO_ANTIALIAS_NONE:
 	    target_rt->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_ALIASED);
 	    break;
 	case CAIRO_ANTIALIAS_GRAY:
 	    target_rt->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE);
 	    break;
 	case CAIRO_ANTIALIAS_SUBPIXEL:
-	    target_rt->SetTextAntialiasMode(highest_quality);
+	    target_rt->SetTextAntialiasMode(cleartype_quality);
 	    break;
     }
 
     /* It is vital that dx values for dxy_buf are calculated from the delta of
      * _logical_ x coordinates (not user x coordinates) or else the sum of all
      * previous dx values may start to diverge from the current glyph's x
      * coordinate due to accumulated rounding error. As a result strings could
      * be painted shorter or longer than expected. */
@@ -1409,17 +1492,17 @@ cairo_int_status_t
 	// The brush matrix needs to be multiplied with the inverted matrix
 	// as well, to move the brush into the space of the glyphs. Before
 	// the render target transformation.
 	brush->GetTransform(&mat_brush);
 	mat_brush = mat_brush * mat_inverse;
 	brush->SetTransform(&mat_brush);
     }
     
-    target_rt->DrawGlyphRun(D2D1::Point2F(0, 0), &run, brush);
+    target_rt->DrawGlyphRun(D2D1::Point2F(0, 0), &run, brush, dwritesf->measuring_mode);
     
     if (transform) {
 	target_rt->SetTransform(D2D1::Matrix3x2F::Identity());
     }
 
     delete [] indices;
     delete [] offsets;
     delete [] advances;
--- a/gfx/cairo/cairo/src/cairo-win32-font.c
+++ b/gfx/cairo/cairo/src/cairo-win32-font.c
@@ -234,32 +234,32 @@ static cairo_bool_t
 	return FALSE;
     }
 
     return (version_info.dwMajorVersion > 5 ||
 	    (version_info.dwMajorVersion == 5 &&
 	     version_info.dwMinorVersion >= 1));	/* XP or newer */
 }
 
-static BYTE
-_get_system_quality (void)
+BYTE
+_cairo_win32_get_system_text_quality (void)
 {
     BOOL font_smoothing;
     UINT smoothing_type;
 
     if (!SystemParametersInfo (SPI_GETFONTSMOOTHING, 0, &font_smoothing, 0)) {
-	_cairo_win32_print_gdi_error ("_get_system_quality");
+	_cairo_win32_print_gdi_error ("_cairo_win32_get_system_text_quality");
 	return DEFAULT_QUALITY;
     }
 
     if (font_smoothing) {
 	if (_have_cleartype_quality ()) {
 	    if (!SystemParametersInfo (SPI_GETFONTSMOOTHINGTYPE,
 				       0, &smoothing_type, 0)) {
-		_cairo_win32_print_gdi_error ("_get_system_quality");
+		_cairo_win32_print_gdi_error ("_cairo_win32_get_system_text_quality");
 		return DEFAULT_QUALITY;
 	    }
 
 	    if (smoothing_type == FE_FONTSMOOTHINGCLEARTYPE)
 		return CLEARTYPE_QUALITY;
 	}
 
 	return ANTIALIASED_QUALITY;
@@ -302,17 +302,17 @@ static cairo_status_t
      * order in the Win32 font API, so we ignore those parts of
      * cairo_font_options_t. We use the 'antialias' field to set
      * the 'quality'.
      *
      * XXX: The other option we could pay attention to, but don't
      *      here is the hint_metrics options.
      */
     if (options->antialias == CAIRO_ANTIALIAS_DEFAULT)
-	f->quality = _get_system_quality ();
+	f->quality = _cairo_win32_get_system_text_quality ();
     else {
 	switch (options->antialias) {
 	case CAIRO_ANTIALIAS_NONE:
 	    f->quality = NONANTIALIASED_QUALITY;
 	    break;
 	case CAIRO_ANTIALIAS_GRAY:
 	    f->quality = ANTIALIASED_QUALITY;
 	    break;
--- a/gfx/cairo/cairo/src/cairo-win32-private.h
+++ b/gfx/cairo/cairo/src/cairo-win32-private.h
@@ -208,16 +208,19 @@ void
 _cairo_win32_debug_dump_hrgn (HRGN rgn, char *header);
 
 cairo_bool_t
 _cairo_win32_scaled_font_is_type1 (cairo_scaled_font_t *scaled_font);
 
 cairo_bool_t
 _cairo_win32_scaled_font_is_bitmap (cairo_scaled_font_t *scaled_font);
 
+BYTE
+_cairo_win32_get_system_text_quality (void);
+
 #ifdef WINCE
 
 // These are the required stubs for windows mobile
 #define ETO_GLYPH_INDEX 0
 #define ETO_PDY 0
 #define HALFTONE COLORONCOLOR
 #define GM_ADVANCED 2
 #define MWT_IDENTITY 1
--- a/gfx/layers/Makefile.in
+++ b/gfx/layers/Makefile.in
@@ -92,16 +92,17 @@ EXPORTS += \
 CPPSRCS += \
         LayerManagerD3D9.cpp \
         ThebesLayerD3D9.cpp \
         ContainerLayerD3D9.cpp \
         ImageLayerD3D9.cpp \
         ColorLayerD3D9.cpp \
         CanvasLayerD3D9.cpp \
         DeviceManagerD3D9.cpp \
+        Nv3DVUtils.cpp \
         $(NULL)
 endif
 endif
 
 ifdef MOZ_IPC #{
 EXPORTS_NAMESPACES = mozilla/layers
 EXPORTS_mozilla/layers =\
         ShadowLayers.h \
--- a/gfx/layers/d3d9/DeviceManagerD3D9.cpp
+++ b/gfx/layers/d3d9/DeviceManagerD3D9.cpp
@@ -36,16 +36,18 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "DeviceManagerD3D9.h"
 #include "LayerManagerD3D9Shaders.h"
 #include "ThebesLayerD3D9.h"
 #include "nsIServiceManager.h"
 #include "nsIConsoleService.h"
 #include "nsPrintfCString.h"
+#include "nsIPrefService.h" 
+#include "Nv3DVUtils.h"
 
 namespace mozilla {
 namespace layers {
 
 const LPCWSTR kClassName       = L"D3D9WindowClass";
 
 #define USE_D3D9EX
 
@@ -208,16 +210,29 @@ DeviceManagerD3D9::Init()
                               CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL,
                               NULL, GetModuleHandle(NULL), NULL);
 
   if (!mFocusWnd) {
     NS_WARNING("Failed to create DeviceManagerD3D9 Window.");
     return false;
   }
 
+  /* Create an Nv3DVUtils instance */ 
+  if (!mNv3DVUtils) { 
+    mNv3DVUtils = new Nv3DVUtils(); 
+    if (!mNv3DVUtils) { 
+      NS_WARNING("Could not create a new instance of Nv3DVUtils.\n"); 
+    } 
+  } 
+
+  /* Initialize the Nv3DVUtils object */ 
+  if (mNv3DVUtils) { 
+    mNv3DVUtils->Initialize(); 
+  } 
+
   HMODULE d3d9 = LoadLibraryW(L"d3d9.dll");
   Direct3DCreate9Func d3d9Create = (Direct3DCreate9Func)
     GetProcAddress(d3d9, "Direct3DCreate9");
   Direct3DCreate9ExFunc d3d9CreateEx = (Direct3DCreate9ExFunc)
     GetProcAddress(d3d9, "Direct3DCreate9Ex");
   
 #ifdef USE_D3D9EX
   if (d3d9CreateEx) {
@@ -293,16 +308,27 @@ DeviceManagerD3D9::Init()
       return false;
     }
   }
 
   if (!VerifyCaps()) {
     return false;
   }
 
+  /* 
+   * Do some post device creation setup 
+   */ 
+  if (mNv3DVUtils) { 
+    IUnknown* devUnknown = NULL; 
+    if (mDevice) { 
+      mDevice->QueryInterface(IID_IUnknown, (void **)&devUnknown); 
+    } 
+    mNv3DVUtils->SetDeviceInfo(devUnknown); 
+  } 
+
   hr = mDevice->CreateVertexShader((DWORD*)LayerQuadVS,
                                    getter_AddRefs(mLayerVS));
 
   if (FAILED(hr)) {
     return false;
   }
 
   hr = mDevice->CreatePixelShader((DWORD*)RGBShaderPS,
@@ -397,18 +423,18 @@ DeviceManagerD3D9::SetupRenderState()
   mDevice->SetStreamSource(0, mVB, 0, sizeof(vertex));
   mDevice->SetVertexDeclaration(mVD);
   mDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
   mDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
   mDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
   mDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
   mDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
   mDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE);
-  mDevice->SetRenderState(D3DRS_SRCBLENDALPHA, D3DBLEND_ONE);
-  mDevice->SetRenderState(D3DRS_DESTBLENDALPHA, D3DBLEND_INVSRCALPHA);
+  mDevice->SetRenderState(D3DRS_SRCBLENDALPHA, D3DBLEND_ONE);
+  mDevice->SetRenderState(D3DRS_DESTBLENDALPHA, D3DBLEND_INVSRCALPHA);
   mDevice->SetRenderState(D3DRS_BLENDOPALPHA, D3DBLENDOP_ADD);
   mDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
   mDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
   mDevice->SetSamplerState(1, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
   mDevice->SetSamplerState(1, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
   mDevice->SetSamplerState(2, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
   mDevice->SetSamplerState(2, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
 }
--- a/gfx/layers/d3d9/DeviceManagerD3D9.h
+++ b/gfx/layers/d3d9/DeviceManagerD3D9.h
@@ -44,16 +44,17 @@
 #include "d3d9.h"
 #include "nsTArray.h"
 
 namespace mozilla {
 namespace layers {
 
 class DeviceManagerD3D9;
 class ThebesLayerD3D9;
+class Nv3DVUtils;
 
 /**
  * SwapChain class, this class manages the swap chain belonging to a
  * LayerManagerD3D9.
  */
 class THEBES_API SwapChainD3D9
 {
   NS_INLINE_DECL_REFCOUNTING(SwapChainD3D9)
@@ -129,16 +130,21 @@ public:
   enum ShaderMode {
     RGBLAYER,
     YCBCRLAYER,
     SOLIDCOLORLAYER
   };
 
   void SetShaderMode(ShaderMode aMode);
 
+  /** 
+   * Return pointer to the Nv3DVUtils instance 
+   */ 
+  Nv3DVUtils *GetNv3DVUtils()  { return mNv3DVUtils; } 
+
   /**
    * We keep a list of all thebes layers since we need their D3DPOOL_DEFAULT
    * surfaces to be released when we want to reset the device.
    */
   nsTArray<ThebesLayerD3D9*> mThebesLayers;
 private:
   friend class SwapChainD3D9;
 
@@ -187,16 +193,19 @@ private:
   /* Our focus window - this is really a dummy window we can associate our
    * device with.
    */
   HWND mFocusWnd;
 
   /* If this device supports dynamic textures */
   bool mHasDynamicTextures;
 
+  /* Nv3DVUtils instance */ 
+  nsAutoPtr<Nv3DVUtils> mNv3DVUtils; 
+
   /**
    * Verifies all required device capabilities are present.
    */
   bool VerifyCaps();
 };
 
 } /* namespace layers */
 } /* namespace mozilla */
--- a/gfx/layers/d3d9/ImageLayerD3D9.cpp
+++ b/gfx/layers/d3d9/ImageLayerD3D9.cpp
@@ -33,16 +33,20 @@
  * 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 "ImageLayerD3D9.h"
 #include "gfxImageSurface.h"
 #include "yuv_convert.h"
+#include "nsIServiceManager.h" 
+#include "nsIConsoleService.h" 
+#include "nsPrintfCString.h" 
+#include "Nv3DVUtils.h"
 
 namespace mozilla {
 namespace layers {
 
 using mozilla::MutexAutoLock;
 
 ImageContainerD3D9::ImageContainerD3D9(LayerManagerD3D9 *aManager)
   : ImageContainer(aManager)
@@ -182,16 +186,28 @@ ImageLayerD3D9::RenderLayer()
      * We always upload a 4 component float, but the shader will
      * only use the the first component since it's declared as a 'float'.
      */
     opacity[0] = GetOpacity();
     device()->SetPixelShaderConstantF(0, opacity, 1);
 
     mD3DManager->SetShaderMode(DeviceManagerD3D9::YCBCRLAYER);
 
+    /* 
+     * Send 3d control data and metadata 
+     */ 
+    if (mD3DManager->Is3DEnabled() && mD3DManager->GetNv3DVUtils()) { 
+      mD3DManager->GetNv3DVUtils()->SendNv3DVControl(STEREO_MODE_RIGHT_LEFT, true, FIREFOX_3DV_APP_HANDLE); 
+
+      nsRefPtr<IDirect3DSurface9> renderTarget; 
+      device()->GetRenderTarget(0, getter_AddRefs(renderTarget)); 
+      mD3DManager->GetNv3DVUtils()->SendNv3DVMetaData((unsigned int)yuvImage->mSize.width, 
+        (unsigned int)yuvImage->mSize.height, (HANDLE)(yuvImage->mYTexture), (HANDLE)(renderTarget)); 
+    } 
+
     device()->SetTexture(0, yuvImage->mYTexture);
     device()->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
     device()->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
     device()->SetTexture(1, yuvImage->mCbTexture);
     device()->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
     device()->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
     device()->SetTexture(2, yuvImage->mCrTexture);
     device()->SetSamplerState(2, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
--- a/gfx/layers/d3d9/LayerManagerD3D9.cpp
+++ b/gfx/layers/d3d9/LayerManagerD3D9.cpp
@@ -37,23 +37,26 @@
 
 #include "LayerManagerD3D9.h"
 
 #include "ThebesLayerD3D9.h"
 #include "ContainerLayerD3D9.h"
 #include "ImageLayerD3D9.h"
 #include "ColorLayerD3D9.h"
 #include "CanvasLayerD3D9.h"
+#include "nsIServiceManager.h"
+#include "nsIPrefService.h"
 
 namespace mozilla {
 namespace layers {
 
 DeviceManagerD3D9 *LayerManagerD3D9::mDeviceManager = nsnull;
 
 LayerManagerD3D9::LayerManagerD3D9(nsIWidget *aWidget)
+  : mIs3DEnabled(PR_FALSE)
 {
     mWidget = aWidget;
     mCurrentCallbackInfo.Callback = NULL;
     mCurrentCallbackInfo.CallbackData = NULL;
 }
 
 LayerManagerD3D9::~LayerManagerD3D9()
 {
@@ -65,16 +68,20 @@ LayerManagerD3D9::~LayerManagerD3D9()
   if (mDeviceManager) {
     mDeviceManager->Release();
   }
 }
 
 PRBool
 LayerManagerD3D9::Initialize()
 {
+  /* Check the user preference for whether 3d video is enabled or not */ 
+  nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); 
+  prefs->GetBoolPref("gfx.3d_video.enabled", &mIs3DEnabled); 
+
   if (!mDeviceManager) {
     mDeviceManager = new DeviceManagerD3D9;
 
     if (!mDeviceManager->Init()) {
       mDeviceManager = nsnull;
       return PR_FALSE;
     }
   }
--- a/gfx/layers/d3d9/LayerManagerD3D9.h
+++ b/gfx/layers/d3d9/LayerManagerD3D9.h
@@ -125,16 +125,26 @@ public:
   void SetClippingEnabled(PRBool aEnabled);
 
   void SetShaderMode(DeviceManagerD3D9::ShaderMode aMode)
     { mDeviceManager->SetShaderMode(aMode); }
 
   IDirect3DDevice9 *device() const { return mDeviceManager->device(); }
   DeviceManagerD3D9 *deviceManager() const { return mDeviceManager; }
 
+  /** 
+   * Return pointer to the Nv3DVUtils instance. Re-direct to mDeviceManager.
+   */ 
+  Nv3DVUtils *GetNv3DVUtils()  { return mDeviceManager ? mDeviceManager->GetNv3DVUtils() : NULL; } 
+
+  /** 
+   * Indicate whether 3D is enabled or not 
+   */ 
+  PRBool Is3DEnabled() { return mIs3DEnabled; } 
+
   static void OnDeviceManagerDestroy(DeviceManagerD3D9 *aDeviceManager) {
     if(aDeviceManager == mDeviceManager)
       mDeviceManager = nsnull;
   }
 
 private:
   /* Device manager instance */
   static DeviceManagerD3D9 *mDeviceManager;
@@ -151,16 +161,19 @@ private:
   nsRefPtr<gfxContext> mTarget;
 
   /* Current root layer. */
   LayerD3D9 *mRootLayer;
 
   /* Callback info for current transaction */
   CallbackInfo mCurrentCallbackInfo;
 
+  /* Flag that indicates whether 3D is enabled or not*/ 
+  PRBool mIs3DEnabled; 
+
   /*
    * Region we're clipping our current drawing to.
    */
   nsIntRegion mClippingRegion;
 
   /*
    * Render the current layer tree to the active target.
    */
new file mode 100644
--- /dev/null
+++ b/gfx/layers/d3d9/Nv3DVUtils.cpp
@@ -0,0 +1,237 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Corporation code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Atul Apte <aapte135@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * 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 "nsIServiceManager.h"
+#include "nsIConsoleService.h"
+#include "nsPrintfCString.h"
+#include <initguid.h>
+#include "Nv3DVUtils.h"
+
+DEFINE_GUID(CLSID_NV3DVStreaming, 
+0xf7747266, 0x777d, 0x4f61, 0xa1, 0x75, 0xdd, 0x5a, 0xdf, 0x1e, 0x37, 0xdf);
+
+DEFINE_GUID(IID_INV3DVStreaming, 
+0xf98f9bb2, 0xb914, 0x4d44, 0x98, 0xfa, 0x6e, 0x37, 0x85, 0x16, 0x98, 0x55);
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * Constructor and Destructor
+ */
+Nv3DVUtils::Nv3DVUtils()
+  : m3DVStreaming (NULL)
+{
+}
+
+Nv3DVUtils::~Nv3DVUtils()
+{
+  UnInitialize();
+}
+
+/**
+ * Initializes the Nv3DVUtils object.
+ */
+void
+Nv3DVUtils::Initialize()
+{
+  nsCOMPtr<nsIConsoleService>
+  consoleCustom(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
+
+  /*
+   * Detect if 3D Streaming object is already loaded. Do nothing in that case.
+   */
+  if (m3DVStreaming) {
+    NS_WARNING("Nv3DVStreaming COM object already instantiated.\n");
+    return;
+  }
+
+  /*
+   * Create the COM object. If we fail at any stage, just return
+   */
+  HRESULT hr = CoCreateInstance(CLSID_NV3DVStreaming, NULL, CLSCTX_INPROC_SERVER, IID_INV3DVStreaming, (void**)(getter_AddRefs(m3DVStreaming)));
+  if (FAILED(hr) || !m3DVStreaming) {
+    if (consoleCustom) {
+      nsString msg;
+      msg += NS_LITERAL_STRING("CoCreateInstance() FAILED.\n");
+      consoleCustom->LogStringMessage(msg.get());
+    }
+    return;
+  }
+
+  if (consoleCustom) {
+    nsString msg;
+    msg += NS_LITERAL_STRING("Nv3DVStreaming COM object instantiated successfully.\n");
+    consoleCustom->LogStringMessage(msg.get());
+  }
+
+  /*
+   * Initialize the object. Note that m3DVStreaming cannot be NULL at this point.
+   */
+  bool bRetVal = m3DVStreaming->Nv3DVInitialize();
+
+  if (!bRetVal) {
+    if (consoleCustom) {
+      nsString msg;
+      msg += NS_LITERAL_STRING("Nv3DVInitialize() FAILED!\n");
+      consoleCustom->LogStringMessage(msg.get());
+    }
+    return;
+  }
+
+  /*
+   * Nv3DVUtils::Initialize() was successful
+   */
+  if (consoleCustom) {
+    nsString msg;
+    msg += NS_LITERAL_STRING("Nv3DVUtils::Initialize SUCCEEDED!\n");
+    consoleCustom->LogStringMessage(msg.get());
+  }
+}
+
+/**
+ * Release resources used by the COM Object, and then release 
+ * the COM Object (nsRefPtr gets released by setting to NULL) 
+ *
+ */
+void
+Nv3DVUtils::UnInitialize()
+{
+  if (m3DVStreaming) {
+    m3DVStreaming->Nv3DVRelease();
+  }
+}
+
+/**
+ * Sets the device info, along with any other initialization that is needed after device creation
+ * Pass the D3D9 device pointer is an IUnknown input argument.
+ */
+void 
+Nv3DVUtils::SetDeviceInfo(IUnknown *devUnknown)
+{
+  nsCOMPtr<nsIConsoleService>
+  consoleCustom(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
+
+  if (!devUnknown) {
+    NS_WARNING("D3D Device Pointer (IUnknown) is NULL.\n");
+    return;
+  }
+
+  if (m3DVStreaming) {
+    bool rv = false;
+    rv = m3DVStreaming->Nv3DVSetDevice(devUnknown);
+    if (!rv) {
+      if (consoleCustom) {
+        nsString msg;
+        msg += NS_LITERAL_STRING("Nv3DVSetDevice() FAILED!\n");
+        consoleCustom->LogStringMessage(msg.get());
+      }
+      return;
+    }
+
+    rv = m3DVStreaming->Nv3DVControl(STEREO_MODE_RIGHT_LEFT, true, FIREFOX_3DV_APP_HANDLE);
+    if (!rv) {
+      if (consoleCustom) {
+        nsString msg;
+        msg += NS_LITERAL_STRING("Nv3DVControl() FAILED!\n");
+        consoleCustom->LogStringMessage(msg.get());
+      }
+      return;
+    }
+
+    if (consoleCustom) {
+      nsString msg;
+      msg += NS_LITERAL_STRING("Nv3DVSetDevice() and Nv3DVControl() both SUCCEEDED!\n");
+      consoleCustom->LogStringMessage(msg.get());
+    }
+
+  } // m3DVStreaming
+
+  return;
+}
+
+/*
+ * Send Stereo Control Information. Used mainly to re-route 
+ * calls from ImageLayerD3D9 to the 3DV COM object
+ */
+void 
+Nv3DVUtils::SendNv3DVControl(Stereo_Mode eStereoMode, bool bEnableStereo, DWORD dw3DVAppHandle)
+{
+  nsCOMPtr<nsIConsoleService>
+    consoleCustom(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
+
+  if (m3DVStreaming) {
+    bool rv = m3DVStreaming->Nv3DVControl(eStereoMode, bEnableStereo, dw3DVAppHandle);
+    if (consoleCustom) {
+      nsString msg;
+      if (rv) {
+        msg += NS_LITERAL_STRING("Nv3DVControl() SUCCEEDED!\n");
+      } else {
+        msg += NS_LITERAL_STRING("Nv3DVControl()FAILED!\n");
+      }
+      consoleCustom->LogStringMessage(msg.get());
+    }
+  } // m3DVStreaming
+
+}
+
+/*
+ * Send Stereo Metadata. Used mainly to re-route calls 
+ * from ImageLayerD3D9 to the 3DV COM object
+ */
+void 
+Nv3DVUtils::SendNv3DVMetaData(unsigned int dwWidth, unsigned int dwHeight, HANDLE hSrcLuma, HANDLE hDst)
+{
+  nsCOMPtr<nsIConsoleService>
+    consoleCustom(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
+
+  if (m3DVStreaming) {
+    bool rv = m3DVStreaming->Nv3DVMetaData((DWORD)dwWidth, (DWORD)dwHeight, hSrcLuma, hDst);
+    if (consoleCustom) {
+      nsString msg;
+      if (rv) {
+        msg += NS_LITERAL_STRING("Nv3DVMetaData() SUCCEEDED!\n");
+      } else {
+        msg += NS_LITERAL_STRING("Nv3DVMetaData() FAILED!\n");
+      }
+      consoleCustom->LogStringMessage(msg.get());
+    }
+  } // m3DVStreaming
+}
+
+} /* namespace layers */
+} /* namespace mozilla */
new file mode 100644
--- /dev/null
+++ b/gfx/layers/d3d9/Nv3DVUtils.h
@@ -0,0 +1,117 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Corporation code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Atul Apte <aapte135@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * 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 ***** */
+
+#ifndef GFX_NV3DVUTILS_H
+#define GFX_NV3DVUTILS_H
+
+#include "Layers.h"
+#include <windows.h>
+#include <d3d9.h>
+
+namespace mozilla {
+namespace layers {
+
+#define FIREFOX_3DV_APP_HANDLE    0xECB992B6
+
+typedef enum _Stereo_Mode {
+  STEREO_MODE_LEFT_RIGHT = 0,
+  STEREO_MODE_RIGHT_LEFT = 1,
+  STEREO_MODE_TOP_BOTTOM = 2,
+  STEREO_MODE_BOTTOM_TOP = 3,
+  STEREO_MODE_LAST       = 4 
+} Stereo_Mode;
+
+class INv3DVStreaming : public IUnknown {
+
+public:
+  virtual bool Nv3DVInitialize()                  = 0;
+  virtual bool Nv3DVRelease()                     = 0;
+  virtual bool Nv3DVSetDevice(IUnknown* pDevice)  = 0;
+  virtual bool Nv3DVControl(Stereo_Mode eStereoMode, bool bEnableStereo, DWORD dw3DVAppHandle) = 0;
+  virtual bool Nv3DVMetaData(DWORD dwWidth, DWORD dwHeight, HANDLE hSrcLuma, HANDLE hDst) = 0;
+};
+
+/*
+ * Nv3DVUtils class
+ */
+class Nv3DVUtils {
+
+public:
+  Nv3DVUtils();
+  ~Nv3DVUtils();
+
+  /*
+   * Initializes the Nv3DVUtils object.
+   */
+  void Initialize();
+
+  /*
+   * Release any resources if needed
+   *
+   */
+  void UnInitialize();
+
+  /*
+   * Sets the device info, along with any other initialization that is needed after device creation
+   * Pass the D3D9 device pointer is an IUnknown input argument
+   */
+  void SetDeviceInfo(IUnknown *devUnknown);
+
+  /*
+   * Send Stereo Control Information. Used mainly to re-route 
+   * calls from ImageLayerD3D9 to the 3DV COM object
+   */
+  void SendNv3DVControl(Stereo_Mode eStereoMode, bool bEnableStereo, DWORD dw3DVAppHandle);
+
+  /*
+   * Send Stereo Metadata. Used mainly to re-route calls 
+   * from ImageLayerD3D9 to the 3DV COM object
+   */
+  void SendNv3DVMetaData(unsigned int dwWidth, unsigned int dwHeight, HANDLE hSrcLuma, HANDLE hDst);
+
+private:
+
+  /* Nv3DVStreaming interface pointer */
+  nsRefPtr<INv3DVStreaming> m3DVStreaming;
+
+};
+
+
+} /* layers */
+} /* mozilla */
+
+#endif /* GFX_NV3DVUTILS_H */
--- a/gfx/tests/Makefile.in
+++ b/gfx/tests/Makefile.in
@@ -34,16 +34,17 @@
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK *****
 
 DEPTH		= ../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
+relativesrcdir = gfx/tests
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= gfx
 MOZILLA_INTERNAL_API = 1
 
 XPCSHELL_TESTS = unit
 
--- a/gfx/thebes/Makefile.in
+++ b/gfx/thebes/Makefile.in
@@ -260,17 +260,17 @@ CPPSRCS	+= gfxDWriteFonts.cpp \
 	   gfxDWriteFontList.cpp \
 	   $(NULL)
 endif
 CPPSRCS	+= gfxGDIFont.cpp \
 	   gfxGDIFontList.cpp \
 	   gfxGDIShaper.cpp \
 	   gfxUniscribeShaper.cpp \
 	   $(NULL)
-_OS_LIBS = usp10 msimg32
+_OS_LIBS = usp10 msimg32 ole32
 endif
 
 CPPSRCS +=	gfxPDFSurface.cpp
 
 ifdef GNU_CXX
 _OS_LIBS	+= uuid
 endif
 OS_LIBS		+= $(call EXPAND_LIBNAME,$(_OS_LIBS))
--- a/gfx/thebes/gfxGDIFont.cpp
+++ b/gfx/thebes/gfxGDIFont.cpp
@@ -255,17 +255,18 @@ gfxGDIFont::GetSpaceGlyph()
 }
 
 PRBool
 gfxGDIFont::SetupCairoFont(gfxContext *aContext)
 {
     if (!mMetrics) {
         Initialize();
     }
-    if (cairo_scaled_font_status(mScaledFont) != CAIRO_STATUS_SUCCESS) {
+    if (!mScaledFont ||
+        cairo_scaled_font_status(mScaledFont) != CAIRO_STATUS_SUCCESS) {
         // Don't cairo_set_scaled_font as that would propagate the error to
         // the cairo_t, precluding any further drawing.
         return PR_FALSE;
     }
     cairo_set_scaled_font(aContext->GetCairo(), mScaledFont);
     cairo_win32_scaled_font_select_font(mScaledFont, DCFromContext(aContext));
     return PR_TRUE;
 }
@@ -418,22 +419,23 @@ gfxGDIFont::Initialize()
     if (mAntialiasOption != kAntialiasDefault) {
         cairo_font_options_set_antialias(fontOptions,
             GetCairoAntialiasOption(mAntialiasOption));
     }
     mScaledFont = cairo_scaled_font_create(mFontFace, &sizeMatrix,
                                            &ctm, fontOptions);
     cairo_font_options_destroy(fontOptions);
 
-    cairo_status_t cairoerr = cairo_scaled_font_status(mScaledFont);
-    if (cairoerr != CAIRO_STATUS_SUCCESS) {
+    if (!mScaledFont ||
+        cairo_scaled_font_status(mScaledFont) != CAIRO_STATUS_SUCCESS) {
 #ifdef DEBUG
         char warnBuf[1024];
         sprintf(warnBuf, "Failed to create scaled font: %s status: %d",
-                NS_ConvertUTF16toUTF8(mFontEntry->Name()).get(), cairoerr);
+                NS_ConvertUTF16toUTF8(mFontEntry->Name()).get(),
+                mScaledFont ? cairo_scaled_font_status(mScaledFont) : 0);
         NS_WARNING(warnBuf);
 #endif
     }
 
     mIsValid = PR_TRUE;
 
 #if 0
     printf("Font: %p (%s) size: %f\n", this,
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -193,16 +193,21 @@ BuildKeyNameFromFontName(nsAString &aNam
 
 gfxWindowsPlatform::gfxWindowsPlatform()
 {
     mPrefFonts.Init(50);
 
     mUseClearTypeForDownloadableFonts = UNINITIALIZED_VALUE;
     mUseClearTypeAlways = UNINITIALIZED_VALUE;
 
+    /* 
+     * Initialize COM 
+     */ 
+    CoInitialize(NULL); 
+
     mScreenDC = GetDC(NULL);
 
 #ifdef MOZ_FT2_FONTS
     FT_Init_FreeType(&gPlatformFTLibrary);
 #endif
 
 /* Pick the default render mode differently between
  * desktop, Windows Mobile, and Windows CE.
@@ -351,16 +356,22 @@ gfxWindowsPlatform::~gfxWindowsPlatform(
     ::ReleaseDC(NULL, mScreenDC);
     // not calling FT_Done_FreeType because cairo may still hold references to
     // these FT_Faces.  See bug 458169.
 #ifdef CAIRO_HAS_D2D_SURFACE
     if (mD2DDevice) {
 	cairo_release_device(mD2DDevice);
     }
 #endif
+
+    /* 
+     * Uninitialize COM 
+     */ 
+    CoUninitialize(); 
+
 }
 
 gfxPlatformFontList*
 gfxWindowsPlatform::CreatePlatformFontList()
 {
 #ifdef MOZ_FT2_FONTS
     return new gfxFT2FontList();
 #else
--- a/gfx/thebes/gfxWindowsPlatform.h
+++ b/gfx/thebes/gfxWindowsPlatform.h
@@ -66,16 +66,17 @@
 #include "nsTArray.h"
 #include "nsDataHashtable.h"
 
 #ifdef MOZ_FT2_FONTS
 typedef struct FT_LibraryRec_ *FT_Library;
 #endif
 
 #include <windows.h>
+#include <objbase.h>
 
 // Utility to get a Windows HDC from a thebes context,
 // used by both GDI and Uniscribe font shapers
 struct DCFromContext {
     DCFromContext(gfxContext *aContext) {
         dc = NULL;
         nsRefPtr<gfxASurface> aSurface = aContext->CurrentSurface();
         NS_ASSERTION(aSurface, "DCFromContext: null surface");
--- a/intl/locale/src/unix/tests/Makefile.in
+++ b/intl/locale/src/unix/tests/Makefile.in
@@ -34,15 +34,16 @@
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK *****
 
 DEPTH		= ../../../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
+relativesrcdir = intl/locale/src/unix/tests
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE          = test_intl_locale_unix
 XPCSHELL_TESTS  = unit
 
 include $(topsrcdir)/config/rules.mk
--- a/intl/locale/tests/Makefile.in
+++ b/intl/locale/tests/Makefile.in
@@ -34,15 +34,16 @@
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK *****
 
 DEPTH		= ../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
+relativesrcdir = intl/locale/tests
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE          = test_intl_locale
 XPCSHELL_TESTS  = unit
 
 include $(topsrcdir)/config/rules.mk
--- a/intl/locale/tests_multilocale/Makefile.in
+++ b/intl/locale/tests_multilocale/Makefile.in
@@ -34,15 +34,16 @@
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK *****
 
 DEPTH		= ../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
+relativesrcdir = intl/locale/tests_multilocale
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE          = test_intl_locale_multilocale
 XPCSHELL_TESTS  = unit
 
 include $(topsrcdir)/config/rules.mk
--- a/intl/strres/tests/Makefile.in
+++ b/intl/strres/tests/Makefile.in
@@ -34,16 +34,17 @@
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK *****
 
 DEPTH		= ../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
+relativesrcdir = intl/strres/tests
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		 = test_intl_strres
 
 XPCSHELL_TESTS	 = unit
 
 include $(topsrcdir)/config/rules.mk
--- a/intl/uconv/tests/Makefile.in
+++ b/intl/uconv/tests/Makefile.in
@@ -34,16 +34,17 @@
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK *****
 
 DEPTH		= ../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
+relativesrcdir = intl/uconv/tests
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE          = test_intl_uconv
 XPCSHELL_TESTS	= unit
 
 ifndef MOZ_ENABLE_LIBXUL
 MOZILLA_INTERNAL_API = 1
--- a/intl/unicharutil/tests/Makefile.in
+++ b/intl/unicharutil/tests/Makefile.in
@@ -34,16 +34,17 @@
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK *****
 
 DEPTH		= ../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
+relativesrcdir = intl/unicharutil/tests
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= test_intl_unicharutil
 XPCSHELL_TESTS	= unit
 
 
 CPPSRCS		= UnicharSelfTest.cpp \
--- a/ipc/chromium/src/base/process_util.h
+++ b/ipc/chromium/src/base/process_util.h
@@ -12,16 +12,18 @@
 
 #if defined(OS_WIN)
 #include <windows.h>
 #include <tlhelp32.h>
 #elif defined(OS_LINUX)
 #include <dirent.h>
 #include <limits.h>
 #include <sys/types.h>
+#elif defined(OS_MACOSX)
+#include <mach/mach.h>
 #endif
 
 #include <map>
 #include <string>
 #include <vector>
 
 #include "base/command_line.h"
 #include "base/process.h"
@@ -124,29 +126,46 @@ bool LaunchApp(const std::wstring& cmdli
 // close-on-exec.  |fds_to_remap| defines a mapping of src fd->dest fd to
 // propagate FDs into the child process.
 //
 // As above, if wait is true, execute synchronously. The pid will be stored
 // in process_handle if that pointer is non-null.
 //
 // Note that the first argument in argv must point to the filename,
 // and must be fully specified.
+#ifdef OS_MACOSX
+typedef std::vector<std::pair<int, int> > file_handle_mapping_vector;
+bool LaunchApp(const std::vector<std::string>& argv,
+               const file_handle_mapping_vector& fds_to_remap,
+               bool wait, ProcessHandle* process_handle,
+               task_t* task_handle);
+
+#if defined(OS_LINUX) || defined(OS_MACOSX)
+typedef std::map<std::string, std::string> environment_map;
+bool LaunchApp(const std::vector<std::string>& argv,
+               const file_handle_mapping_vector& fds_to_remap,
+               const environment_map& env_vars_to_set,
+               bool wait, ProcessHandle* process_handle,
+               task_t* task_handle);
+#endif
+#else // !OS_MACOSX
 typedef std::vector<std::pair<int, int> > file_handle_mapping_vector;
 bool LaunchApp(const std::vector<std::string>& argv,
                const file_handle_mapping_vector& fds_to_remap,
                bool wait, ProcessHandle* process_handle);
 
-#if defined(CHROMIUM_MOZILLA_BUILD) && (defined(OS_LINUX) || defined(OS_MACOSX))
+#if defined(OS_LINUX) || defined(OS_MACOSX)
 typedef std::map<std::string, std::string> environment_map;
 bool LaunchApp(const std::vector<std::string>& argv,
                const file_handle_mapping_vector& fds_to_remap,
                const environment_map& env_vars_to_set,
                bool wait, ProcessHandle* process_handle);
 #endif
 #endif
+#endif
 
 // Executes the application specified by cl. This function delegates to one
 // of the above two platform-specific functions.
 bool LaunchApp(const CommandLine& cl,
                bool wait, bool start_hidden, ProcessHandle* process_handle);
 
 #if defined(OS_WIN)
 // Executes the application specified by |cmd_line| and copies the contents
--- a/ipc/chromium/src/base/process_util_mac.mm
+++ b/ipc/chromium/src/base/process_util_mac.mm
@@ -11,157 +11,218 @@
 #include <sys/sysctl.h>
 #include <sys/types.h>
 #include <sys/wait.h>
 
 #include <string>
 
 #include "base/eintr_wrapper.h"
 #include "base/logging.h"
+#include "base/rand_util.h"
+#include "base/scoped_ptr.h"
 #include "base/string_util.h"
 #include "base/time.h"
+#include "chrome/common/mach_ipc_mac.h"
 
 namespace base {
 
-#if defined(CHROMIUM_MOZILLA_BUILD)
-bool LaunchApp(const std::vector<std::string>& argv,
-               const file_handle_mapping_vector& fds_to_remap,
-               bool wait, ProcessHandle* process_handle) {
-  return LaunchApp(argv, fds_to_remap, environment_map(),
-                   wait, process_handle);
+static std::string MachErrorCode(kern_return_t err) {
+  return StringPrintf("0x%x %s", err, mach_error_string(err));
 }
-#endif
+
+// Forks the current process and returns the child's |task_t| in the parent
+// process.
+static pid_t fork_and_get_task(task_t* child_task) {
+  const int kTimeoutMs = 100;
+  kern_return_t err;
+
+  // Put a random number into the channel name, so that a compromised renderer
+  // can't pretend being the child that's forked off.
+  std::string mach_connection_name = StringPrintf(
+      "org.mozilla.samplingfork.%p.%d",
+      child_task, base::RandInt(0, std::numeric_limits<int>::max()));
+  ReceivePort parent_recv_port(mach_connection_name.c_str());
+
+  // Error handling philosophy: If Mach IPC fails, don't touch |child_task| but
+  // return a valid pid. If IPC fails in the child, the parent will have to wait
+  // until kTimeoutMs is over. This is not optimal, but I've never seen it
+  // happen, and stuff should still mostly work.
+  pid_t pid = fork();
+  switch (pid) {
+    case -1:
+      return pid;
+    case 0: {  // child
+      ReceivePort child_recv_port;
+
+      MachSendMessage child_message(/* id= */0);
+      if (!child_message.AddDescriptor(mach_task_self())) {
+        LOG(ERROR) << "child AddDescriptor(mach_task_self()) failed.";
+        return pid;
+      }
+      mach_port_t raw_child_recv_port = child_recv_port.GetPort();
+      if (!child_message.AddDescriptor(raw_child_recv_port)) {
+        LOG(ERROR) << "child AddDescriptor(" << raw_child_recv_port
+                   << ") failed.";
+        return pid;
+      }
+
+      MachPortSender child_sender(mach_connection_name.c_str());
+      err = child_sender.SendMessage(child_message, kTimeoutMs);
+      if (err != KERN_SUCCESS) {
+        LOG(ERROR) << "child SendMessage() failed: " << MachErrorCode(err);
+        return pid;
+      }
+
+      MachReceiveMessage parent_message;
+      err = child_recv_port.WaitForMessage(&parent_message, kTimeoutMs);
+      if (err != KERN_SUCCESS) {
+        LOG(ERROR) << "child WaitForMessage() failed: " << MachErrorCode(err);
+        return pid;
+      }
+
+      if (parent_message.GetTranslatedPort(0) == MACH_PORT_NULL) {
+        LOG(ERROR) << "child GetTranslatedPort(0) failed.";
+        return pid;
+      }
+      err = task_set_bootstrap_port(mach_task_self(),
+                                    parent_message.GetTranslatedPort(0));
+      if (err != KERN_SUCCESS) {
+        LOG(ERROR) << "child task_set_bootstrap_port() failed: "
+                   << MachErrorCode(err);
+        return pid;
+      }
+      break;
+    }
+    default: {  // parent
+      MachReceiveMessage child_message;
+      err = parent_recv_port.WaitForMessage(&child_message, kTimeoutMs);
+      if (err != KERN_SUCCESS) {
+        LOG(ERROR) << "parent WaitForMessage() failed: " << MachErrorCode(err);
+        return pid;
+      }
+
+      if (child_message.GetTranslatedPort(0) == MACH_PORT_NULL) {
+        LOG(ERROR) << "parent GetTranslatedPort(0) failed.";
+        return pid;
+      }
+      *child_task = child_message.GetTranslatedPort(0);
+
+      if (child_message.GetTranslatedPort(1) == MACH_PORT_NULL) {
+        LOG(ERROR) << "parent GetTranslatedPort(1) failed.";
+        return pid;
+      }
+      MachPortSender parent_sender(child_message.GetTranslatedPort(1));
+
+      MachSendMessage parent_message(/* id= */0);
+      if (!parent_message.AddDescriptor(bootstrap_port)) {
+        LOG(ERROR) << "parent AddDescriptor(" << bootstrap_port << ") failed.";
+        return pid;
+      }
+
+      err = parent_sender.SendMessage(parent_message, kTimeoutMs);
+      if (err != KERN_SUCCESS) {
+        LOG(ERROR) << "parent SendMessage() failed: " << MachErrorCode(err);
+        return pid;
+      }
+      break;
+    }
+  }
+  return pid;
+}
 
 bool LaunchApp(const std::vector<std::string>& argv,
                const file_handle_mapping_vector& fds_to_remap,
-#if defined(CHROMIUM_MOZILLA_BUILD)
-               const environment_map& env_vars_to_set,
-#endif
-               bool wait, ProcessHandle* process_handle) {
-  bool retval = true;
-
-  char* argv_copy[argv.size() + 1];
-  for (size_t i = 0; i < argv.size(); i++) {
-    argv_copy[i] = const_cast<char*>(argv[i].c_str());
-  }
-  argv_copy[argv.size()] = NULL;
-
-  // Make sure we don't leak any FDs to the child process by marking all FDs
-  // as close-on-exec.
-  SetAllFDsToCloseOnExec();
+               bool wait, ProcessHandle* process_handle,
+               task_t* process_task) {
+  return LaunchApp(argv, fds_to_remap, environment_map(),
+                   wait, process_handle, process_task);
+}
 
-#if defined(CHROMIUM_MOZILLA_BUILD)
-  // Copy _NSGetEnviron() to a new char array and add the variables
-  // in env_vars_to_set.
-  // Existing variables are overwritten by env_vars_to_set.
-  int pos = 0;
-  environment_map combined_env_vars = env_vars_to_set;
-  while((*_NSGetEnviron())[pos] != NULL) {
-    std::string varString = (*_NSGetEnviron())[pos];
-    std::string varName = varString.substr(0, varString.find_first_of('='));
-    std::string varValue = varString.substr(varString.find_first_of('=') + 1);
-    if (combined_env_vars.find(varName) == combined_env_vars.end()) {
-      combined_env_vars[varName] = varValue;
-    }
-    pos++;
-  }
-  int varsLen = combined_env_vars.size() + 1;
+bool LaunchApp(
+    const std::vector<std::string>& argv,
+    const file_handle_mapping_vector& fds_to_remap,
+    const environment_map& environ,
+    bool wait,
+    ProcessHandle* process_handle,
+    task_t* task_handle) {
+  pid_t pid;
 
-  char** vars = new char*[varsLen];
-  int i = 0;
-  for (environment_map::const_iterator it = combined_env_vars.begin();
-       it != combined_env_vars.end(); ++it) {
-    std::string entry(it->first);
-    entry += "=";
-    entry += it->second;
-    vars[i] = strdup(entry.c_str());
-    i++;
-  }
-  vars[i] = NULL;
-#endif
-
-  posix_spawn_file_actions_t file_actions;
-  if (posix_spawn_file_actions_init(&file_actions) != 0) {
-#if defined(CHROMIUM_MOZILLA_BUILD)
-    for(int j = 0; j < varsLen; j++) {
-      free(vars[j]);
-    }  
-    delete[] vars;
-#endif
-    return false;
+  if (task_handle == NULL) {
+    pid = fork();
+  } else {
+    // On OS X, the task_t for a process is needed for several reasons. Sadly,
+    // the function task_for_pid() requires privileges a normal user doesn't
+    // have. Instead, a short-lived Mach IPC connection is opened between parent
+    // and child, and the child sends its task_t to the parent at fork time.
+    *task_handle = MACH_PORT_NULL;
+    pid = fork_and_get_task(task_handle);
   }
 
-  // Turn fds_to_remap array into a set of dup2 calls.
-  for (file_handle_mapping_vector::const_iterator it = fds_to_remap.begin();
-       it != fds_to_remap.end();
-       ++it) {
-    int src_fd = it->first;
-    int dest_fd = it->second;
+  if (pid < 0)
+    return false;
+
+  if (pid == 0) {
+    // Child process
 
-    if (src_fd == dest_fd) {
-      int flags = fcntl(src_fd, F_GETFD);
-      if (flags != -1) {
-        fcntl(src_fd, F_SETFD, flags & ~FD_CLOEXEC);
-      }
-    } else {
-      if (posix_spawn_file_actions_adddup2(&file_actions, src_fd, dest_fd) != 0)
-          {
-        posix_spawn_file_actions_destroy(&file_actions);
-#if defined(CHROMIUM_MOZILLA_BUILD)
-        for(int j = 0; j < varsLen; j++) {
-          free(vars[j]);
-        }  
-        delete[] vars;
-#endif
-        return false;
+    InjectiveMultimap fd_shuffle;
+    for (file_handle_mapping_vector::const_iterator
+        it = fds_to_remap.begin(); it != fds_to_remap.end(); ++it) {
+      fd_shuffle.push_back(InjectionArc(it->first, it->second, false));
+    }
+
+    for (environment_map::const_iterator it = environ.begin();
+         it != environ.end(); ++it) {
+      if (it->first.empty())
+        continue;
+
+      if (it->second.empty()) {
+        unsetenv(it->first.c_str());
+      } else {
+        setenv(it->first.c_str(), it->second.c_str(), 1);
       }
     }
-  }
 
+    // Obscure fork() rule: in the child, if you don't end up doing exec*(),
+    // you call _exit() instead of exit(). This is because _exit() does not
+    // call any previously-registered (in the parent) exit handlers, which
+    // might do things like block waiting for threads that don't even exist
+    // in the child.
+    if (!ShuffleFileDescriptors(fd_shuffle))
+      _exit(127);
 
-  int pid = 0;
-  int spawn_succeeded = (posix_spawnp(&pid,
-                                      argv_copy[0],
-                                      &file_actions,
-                                      NULL,
-                                      argv_copy,
-#if defined(CHROMIUM_MOZILLA_BUILD)
-                                      vars) == 0);
-#else
-                                      *_NSGetEnviron()) == 0);
-#endif
+    // If we are using the SUID sandbox, it sets a magic environment variable
+    // ("SBX_D"), so we remove that variable from the environment here on the
+    // off chance that it's already set.
+    unsetenv("SBX_D");
+
+    CloseSuperfluousFds(fd_shuffle);
 
-#if defined(CHROMIUM_MOZILLA_BUILD)
-  for(int j = 0; j < varsLen; j++) {
-    free(vars[j]);
-  }  
-  delete[] vars;
-#endif
-
-  posix_spawn_file_actions_destroy(&file_actions);
-
-  bool process_handle_valid = pid > 0;
-  if (!spawn_succeeded || !process_handle_valid) {
-    retval = false;
+    scoped_array<char*> argv_cstr(new char*[argv.size() + 1]);
+    for (size_t i = 0; i < argv.size(); i++)
+      argv_cstr[i] = const_cast<char*>(argv[i].c_str());
+    argv_cstr[argv.size()] = NULL;
+    execvp(argv_cstr[0], argv_cstr.get());
+    _exit(127);
   } else {
+    // Parent process
     if (wait)
       HANDLE_EINTR(waitpid(pid, 0, 0));
 
     if (process_handle)
       *process_handle = pid;
   }
 
-  return retval;
+  return true;
 }
 
 bool LaunchApp(const CommandLine& cl,
                bool wait, bool start_hidden, ProcessHandle* process_handle) {
   // TODO(playmobil): Do we need to respect the start_hidden flag?
   file_handle_mapping_vector no_files;
-  return LaunchApp(cl.argv(), no_files, wait, process_handle);
+  return LaunchApp(cl.argv(), no_files, wait, process_handle, NULL);
 }
 
 NamedProcessIterator::NamedProcessIterator(const std::wstring& executable_name,
                                            const ProcessFilter* filter)
   : executable_name_(executable_name),
     index_of_kinfo_proc_(0),
     filter_(filter) {
   // Get a snapshot of all of my processes (yes, as we loop it can go stale, but
--- a/ipc/glue/GeckoChildProcessHost.cpp
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -78,16 +78,19 @@ GeckoChildProcessHost::GeckoChildProcess
                                              base::WaitableEventWatcher::Delegate* aDelegate)
   : ChildProcessHost(RENDER_PROCESS), // FIXME/cjones: we should own this enum
     mProcessType(aProcessType),
     mMonitor("mozilla.ipc.GeckChildProcessHost.mMonitor"),
     mLaunched(false),
     mChannelInitialized(false),
     mDelegate(aDelegate),
     mChildProcessHandle(0)
+#if defined(XP_MACOSX)
+  , mChildTask(MACH_PORT_NULL)
+#endif
 {
     MOZ_COUNT_CTOR(GeckoChildProcessHost);
     
     MessageLoop* ioLoop = XRE_GetIOMessageLoop();
     ioLoop->PostTask(FROM_HERE,
                      NewRunnableMethod(this,
                                        &GeckoChildProcessHost::InitializeChannel));
 }
@@ -100,16 +103,21 @@ GeckoChildProcessHost::~GeckoChildProces
   MOZ_COUNT_DTOR(GeckoChildProcessHost);
 
   if (mChildProcessHandle > 0)
     ProcessWatcher::EnsureProcessTerminated(mChildProcessHandle
 #if defined(NS_BUILD_REFCNT_LOGGING)
                                             , false // don't "force"
 #endif
     );
+
+#if defined(XP_MACOSX)
+  if (mChildTask != MACH_PORT_NULL)
+    mach_port_deallocate(mach_task_self(), mChildTask);
+#endif
 }
 
 #ifdef XP_WIN
 void GeckoChildProcessHost::InitWindowsGroupID()
 {
   // On Win7+, pass the application user model to the child, so it can
   // register with it. This insures windows created by the container
   // properly group with the parent app on the Win7 taskbar.
@@ -211,16 +219,19 @@ GeckoChildProcessHost::PerformAsyncLaunc
 
   // We rely on the fact that InitializeChannel() has already been processed
   // on the IO thread before this point is reached.
   if (!GetChannel()) {
     return false;
   }
 
   base::ProcessHandle process;
+#if defined(XP_MACOSX)
+  task_t child_task;
+#endif
 
   // send the child the PID so that it can open a ProcessHandle back to us.
   // probably don't want to do this in the long run
   char pidstring[32];
   PR_snprintf(pidstring, sizeof(pidstring) - 1,
 	      "%ld", base::Process::Current().pid());
 
   const char* const childProcessType =
@@ -321,17 +332,21 @@ GeckoChildProcessHost::PerformAsyncLaunc
   childArgv.push_back(CrashReporter::GetChildNotificationPipe());
 #  endif  // OS_LINUX
 #endif
 
   base::LaunchApp(childArgv, mFileMap,
 #if defined(OS_LINUX) || defined(OS_MACOSX)
                   newEnvVars,
 #endif
-                  false, &process);
+                  false, &process
+#if defined(XP_MACOSX)
+                  , &child_task
+#endif
+                  );
 
 //--------------------------------------------------
 #elif defined(OS_WIN)
 
   FilePath exePath =
     FilePath::FromWStringHack(CommandLine::ForCurrentProcess()->program());
   exePath = exePath.DirName();
 
@@ -371,16 +386,19 @@ GeckoChildProcessHost::PerformAsyncLaunc
 #else
 #  error Sorry
 #endif
 
   if (!process) {
     return false;
   }
   SetHandle(process);
+#if defined(XP_MACOSX)
+  mChildTask = child_task;
+#endif
 
   return true;
 }
 
 void
 GeckoChildProcessHost::OnChannelConnected(int32 peer_pid)
 {
   MonitorAutoEnter mon(mMonitor);
--- a/ipc/glue/GeckoChildProcessHost.h
+++ b/ipc/glue/GeckoChildProcessHost.h
@@ -85,16 +85,23 @@ public:
   base::WaitableEvent* GetShutDownEvent() {
     return GetProcessEvent();
   }
 
   ProcessHandle GetChildProcessHandle() {
     return mChildProcessHandle;
   }
 
+#ifdef XP_MACOSX
+  task_t GetChildTask() {
+    return mChildTask;
+  }
+#endif
+
+
 protected:
   GeckoProcessType mProcessType;
   Monitor mMonitor;
   bool mLaunched;
   bool mChannelInitialized;
   FilePath mProcessPath;
 
 #ifdef XP_WIN
@@ -104,16 +111,19 @@ protected:
 
 #if defined(OS_POSIX)
   base::file_handle_mapping_vector mFileMap;
 #endif
 
   base::WaitableEventWatcher::Delegate* mDelegate;
 
   ProcessHandle mChildProcessHandle;
+#if defined(OS_MACOSX)
+  task_t mChildTask;
+#endif
 
 private:
   DISALLOW_EVIL_CONSTRUCTORS(GeckoChildProcessHost);
 };
 
 } /* namespace ipc */
 } /* namespace mozilla */
 
--- a/ipc/testshell/Makefile.in
+++ b/ipc/testshell/Makefile.in
@@ -33,16 +33,17 @@
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK *****
 
 DEPTH = ../..
 topsrcdir = @top_srcdir@
 srcdir = @srcdir@
 VPATH = @srcdir@
+relativesrcdir = ipc/testshell
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE = ipcshell
 LIBRARY_NAME = ipcshell_s
 FORCE_STATIC_LIB = 1
 LIBXUL_LIBRARY = 1
 EXPORT_LIBRARY = 1
--- a/js/jetpack/tests/Makefile.in
+++ b/js/jetpack/tests/Makefile.in
@@ -34,16 +34,17 @@
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK *****
 
 DEPTH     = ../../..
 topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH     = @srcdir@
+relativesrcdir = js/jetpack/tests
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE = test_jetpack
 
 XPCSHELL_TESTS = unit
 
 include $(topsrcdir)/config/config.mk
--- a/js/jetpack/tests/unit/test_jetpack_ctypes.js
+++ b/js/jetpack/tests/unit/test_jetpack_ctypes.js
@@ -1,12 +1,12 @@
 function run_test() {
   var jetpack = createJetpack({
     scriptFile: do_get_file("impl_jetpack_ctypes.js")
   });
   jetpack.registerReceiver("onCTypesTested", function(name, ok) {
     do_check_true(ok, "onCTypesTested");
     do_test_finished();
   });
-  var jetpacktestdir = do_get_file('../../jsctypes-test/unit').path;
+  var jetpacktestdir = do_get_file('../../../../toolkit/components/ctypes/tests/unit').path;
   jetpack.sendMessage("testCTypes", jetpacktestdir);
   do_test_pending();
 }
--- a/js/src/config/rules.mk
+++ b/js/src/config/rules.mk
@@ -142,82 +142,82 @@ endif
 
 ################################################################################
 # Testing frameworks support
 ################################################################################
 
 ifdef ENABLE_TESTS
 
 ifdef XPCSHELL_TESTS
-ifndef MODULE
-$(error Must define MODULE when defining XPCSHELL_TESTS.)
-endif
+#ifndef MODULE
+#$(error Must define MODULE when defining XPCSHELL_TESTS.)
+#endif
 
 testxpcobjdir = $(DEPTH)/_tests/xpcshell
 
 # Test file installation
 ifneq (,$(filter WINNT os2-emx,$(HOST_OS_ARCH)))
 # Windows and OS/2 nsinstall can't recursively copy directories, so use nsinstall.py
 TEST_INSTALLER = $(PYTHON) $(topsrcdir)/config/nsinstall.py
 else
 TEST_INSTALLER = $(INSTALL)
 endif
 
 define _INSTALL_TESTS
-$(TEST_INSTALLER) $(wildcard $(srcdir)/$(dir)/*) $(testxpcobjdir)/$(MODULE)/$(dir)
+$(TEST_INSTALLER) $(wildcard $(srcdir)/$(dir)/*) $(testxpcobjdir)/$(relativesrcdir)/$(dir)
 
 endef # do not remove the blank line!
 
 SOLO_FILE ?= $(error Specify a test filename in SOLO_FILE when using check-interactive or check-one)
 
 libs::
 	$(foreach dir,$(XPCSHELL_TESTS),$(_INSTALL_TESTS))
 	$(PYTHON) $(MOZILLA_DIR)/config/buildlist.py \
           $(testxpcobjdir)/all-test-dirs.list \
-          $(addprefix $(MODULE)/,$(XPCSHELL_TESTS))
+          $(addprefix $(relativesrcdir)/,$(XPCSHELL_TESTS))
 
 testxpcsrcdir = $(topsrcdir)/testing/xpcshell
 
 # Execute all tests in the $(XPCSHELL_TESTS) directories.
 # See also testsuite-targets.mk 'xpcshell-tests' target for global execution.
 xpcshell-tests:
 	$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
           -I$(topsrcdir)/build \
           $(testxpcsrcdir)/runxpcshelltests.py \
           --symbols-path=$(DIST)/crashreporter-symbols \
           $(EXTRA_TEST_ARGS) \
           $(DIST)/bin/xpcshell \
-          $(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(MODULE)/$(dir))
+          $(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(relativesrcdir)/$(dir))
 
 # Execute a single test, specified in $(SOLO_FILE), but don't automatically
 # start the test. Instead, present the xpcshell prompt so the user can
 # attach a debugger and then start the test.
 check-interactive:
 	$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
           -I$(topsrcdir)/build \
           $(testxpcsrcdir)/runxpcshelltests.py \
           --symbols-path=$(DIST)/crashreporter-symbols \
           --test-path=$(SOLO_FILE) \
           --profile-name=$(MOZ_APP_NAME) \
           --interactive \
           $(DIST)/bin/xpcshell \
-          $(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(MODULE)/$(dir))
+          $(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(relativesrcdir)/$(dir))
 
 # Execute a single test, specified in $(SOLO_FILE)
 check-one:
 	$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
           -I$(topsrcdir)/build \
           $(testxpcsrcdir)/runxpcshelltests.py \
           --symbols-path=$(DIST)/crashreporter-symbols \
           --test-path=$(SOLO_FILE) \
           --profile-name=$(MOZ_APP_NAME) \
           --verbose \
           $(EXTRA_TEST_ARGS) \
           $(DIST)/bin/xpcshell \
-          $(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(MODULE)/$(dir))
+          $(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(relativesrcdir)/$(dir))
 
 endif # XPCSHELL_TESTS
 
 ifdef CPP_UNIT_TESTS
 
 # Compile the tests to $(DIST)/bin.  Make lots of niceties available by default
 # through TestHarness.h, by modifying the list of includes and the libs against
 # which stuff links.
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -1722,17 +1722,17 @@ case "$target" in
 	    MKSHLIB_UNFORCE_ALL=''
 	;;
     esac
     ;;
 
 *-darwin*) 
     MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -o $@'
     MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -o $@'
-    MOZ_OPTIMIZE_FLAGS="-O3"
+    MOZ_OPTIMIZE_FLAGS="-O3 -fomit-frame-pointer"
     _PEDANTIC=
     CFLAGS="$CFLAGS -fpascal-strings -fno-common"
     CXXFLAGS="$CXXFLAGS -fpascal-strings -fno-common"
     DLL_SUFFIX=".dylib"
     DSO_LDOPTS=''
     STRIP="$STRIP -x -S"
     _PLATFORM_DEFAULT_TOOLKIT='cairo-cocoa'
     TARGET_NSPR_MDCPUCFG='\"md/_darwin.cfg\"'
@@ -5059,17 +5059,19 @@ AC_SUBST(MOZ_OS2_USE_DECLSPEC)
 AC_SUBST(MOZ_POST_DSO_LIB_COMMAND)
 AC_SUBST(MOZ_POST_PROGRAM_COMMAND)
 AC_SUBST(MOZ_TIMELINE)
 AC_SUBST(WINCE)
 AC_SUBST(WINCE_WINDOWS_MOBILE)
 
 AC_SUBST(MOZ_APP_NAME)
 AC_SUBST(MOZ_APP_DISPLAYNAME)
+AC_SUBST(MOZ_APP_UA_NAME)
 AC_SUBST(MOZ_APP_VERSION)
+AC_SUBST(FIREFOX_VERSION)
 
 AC_SUBST(MOZ_PKG_SPECIAL)
 
 AC_SUBST(MOZILLA_OFFICIAL)
 
 dnl win32 options
 AC_SUBST(MOZ_MAPINFO)
 AC_SUBST(MOZ_BROWSE_INFO)
--- a/js/src/xpconnect/tests/Makefile.in
+++ b/js/src/xpconnect/tests/Makefile.in
@@ -35,16 +35,17 @@
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK *****
 
 DEPTH		= ../../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
+relativesrcdir = js/src/xpconnect/tests
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= TestXPC
 SIMPLE_PROGRAMS = TestXPC$(BIN_SUFFIX)
 
 
 DIRS		= idl mochitest chrome
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -282,19 +282,34 @@ nsDisplayList::GetBounds(nsDisplayListBu
   nsRect bounds;
   for (nsDisplayItem* i = GetBottom(); i != nsnull; i = i->GetAbove()) {
     bounds.UnionRect(bounds, i->GetBounds(aBuilder));
   }
   return bounds;
 }
 
 PRBool
-nsDisplayList::ComputeVisibility(nsDisplayListBuilder* aBuilder,
-                                 nsRegion* aVisibleRegion) {
-  mVisibleRect = aVisibleRegion->GetBounds();
+nsDisplayList::ComputeVisibilityForRoot(nsDisplayListBuilder* aBuilder,
+                                        nsRegion* aVisibleRegion) {
+  nsRegion r;
+  r.And(*aVisibleRegion, GetBounds(aBuilder));
+  return ComputeVisibilityForSublist(aBuilder, aVisibleRegion, r.GetBounds());
+}
+
+PRBool
+nsDisplayList::ComputeVisibilityForSublist(nsDisplayListBuilder* aBuilder,
+                                           nsRegion* aVisibleRegion,
+                                           const nsRect& aListVisibleBounds) {
+#ifdef DEBUG
+  nsRegion r;
+  r.And(*aVisibleRegion, GetBounds(aBuilder));
+  NS_ASSERTION(r.GetBounds() == aListVisibleBounds,
+               "bad aListVisibleBounds");
+#endif
+  mVisibleRect = aListVisibleBounds;
   PRBool anyVisible = PR_FALSE;
 
   nsAutoTArray<nsDisplayItem*, 512> elements;
   FlattenTo(&elements);
 
   for (PRInt32 i = elements.Length() - 1; i >= 0; --i) {
     nsDisplayItem* item = elements[i];
     nsDisplayItem* belowItem = i < 1 ? nsnull : elements[i - 1];
@@ -317,17 +332,17 @@ nsDisplayList::ComputeVisibility(nsDispl
       if (item->IsOpaque(aBuilder) && f) {
         // Subtract opaque item from the visible region
         aBuilder->SubtractFromVisibleRegion(aVisibleRegion, nsRegion(bounds));
       }
     }
     AppendToBottom(item);
   }
 
-  mIsOpaque = aVisibleRegion->IsEmpty();
+  mIsOpaque = !aVisibleRegion->Intersects(mVisibleRect);
 #ifdef DEBUG
   mDidComputeVisibility = PR_TRUE;
 #endif
   return anyVisible;
 }
 
 void nsDisplayList::PaintRoot(nsDisplayListBuilder* aBuilder,
                               nsIRenderingContext* aCtx,
@@ -1050,24 +1065,23 @@ nsDisplayWrapList::HitTest(nsDisplayList
 nsRect
 nsDisplayWrapList::GetBounds(nsDisplayListBuilder* aBuilder) {
   return mList.GetBounds(aBuilder);
 }
 
 PRBool
 nsDisplayWrapList::ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                      nsRegion* aVisibleRegion) {
-  return mList.ComputeVisibility(aBuilder, aVisibleRegion);
+  return mList.ComputeVisibilityForSublist(aBuilder, aVisibleRegion,
+                                           mVisibleRect);
 }
 
 PRBool
 nsDisplayWrapList::IsOpaque(nsDisplayListBuilder* aBuilder) {
-  // We could try to do something but let's conservatively just return PR_FALSE.
-  // We reimplement ComputeVisibility and that's what really matters
-  return PR_FALSE;
+  return mList.IsOpaque();
 }
 
 PRBool nsDisplayWrapList::IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor) {
   // We could try to do something but let's conservatively just return PR_FALSE.
   return PR_FALSE;
 }