Merge last green changeset of mozilla-inbound to mozilla-central
authorEd Morley <bmo@edmorley.co.uk>
Fri, 07 Oct 2011 11:37:04 +0100
changeset 79647 bd1411e362fbfbc0d1b732db503ae0389b2cce51
parent 79576 c3a50afc2243098d819365833ada1bf37f07d478 (current diff)
parent 79646 b2ac6a043d7884d6429163023e8ac4b9424ff1cc (diff)
child 79696 35954e6f3167b3bc823126e3ec340b5a26e93274
push idunknown
push userunknown
push dateunknown
milestone10.0a1
Merge last green changeset of mozilla-inbound to mozilla-central
browser/base/content/tabview/content.js
browser/base/content/tabview/drag.js
browser/base/content/tabview/groupitems.js
browser/base/content/tabview/iq.js
browser/base/content/tabview/items.js
browser/base/content/tabview/modules/utils.jsm
browser/base/content/tabview/search.js
browser/base/content/tabview/storage.js
browser/base/content/tabview/storagePolicy.js
browser/base/content/tabview/tabitems.js
browser/base/content/tabview/tabview.css
browser/base/content/tabview/tabview.html
browser/base/content/tabview/tabview.js
browser/base/content/tabview/thumbnailStorage.js
browser/base/content/tabview/trench.js
browser/base/content/tabview/ui.js
browser/base/content/test/tabview/Makefile.in
browser/base/content/test/tabview/browser_tabview_alltabs.js
browser/base/content/test/tabview/browser_tabview_apptabs.js
browser/base/content/test/tabview/browser_tabview_bug580412.js
browser/base/content/test/tabview/browser_tabview_bug586553.js
browser/base/content/test/tabview/browser_tabview_bug587043.js
browser/base/content/test/tabview/browser_tabview_bug587231.js
browser/base/content/test/tabview/browser_tabview_bug587276.js
browser/base/content/test/tabview/browser_tabview_bug587351.js
browser/base/content/test/tabview/browser_tabview_bug587503.js
browser/base/content/test/tabview/browser_tabview_bug587990.js
browser/base/content/test/tabview/browser_tabview_bug588265.js
browser/base/content/test/tabview/browser_tabview_bug589324.js
browser/base/content/test/tabview/browser_tabview_bug590606.js
browser/base/content/test/tabview/browser_tabview_bug591706.js
browser/base/content/test/tabview/browser_tabview_bug594958.js
browser/base/content/test/tabview/browser_tabview_bug595020.js
browser/base/content/test/tabview/browser_tabview_bug595191.js
browser/base/content/test/tabview/browser_tabview_bug595436.js
browser/base/content/test/tabview/browser_tabview_bug595518.js
browser/base/content/test/tabview/browser_tabview_bug595521.js
browser/base/content/test/tabview/browser_tabview_bug595560.js
browser/base/content/test/tabview/browser_tabview_bug595601.js
browser/base/content/test/tabview/browser_tabview_bug595804.js
browser/base/content/test/tabview/browser_tabview_bug595930.js
browser/base/content/test/tabview/browser_tabview_bug595943.js
browser/base/content/test/tabview/browser_tabview_bug595965.js
browser/base/content/test/tabview/browser_tabview_bug596781.js
browser/base/content/test/tabview/browser_tabview_bug597248.js
browser/base/content/test/tabview/browser_tabview_bug597360.js
browser/base/content/test/tabview/browser_tabview_bug597399.js
browser/base/content/test/tabview/browser_tabview_bug597980.js
browser/base/content/test/tabview/browser_tabview_bug598375.js
browser/base/content/test/tabview/browser_tabview_bug598600.js
browser/base/content/test/tabview/browser_tabview_bug599626.js
browser/base/content/test/tabview/browser_tabview_bug600645.js
browser/base/content/test/tabview/browser_tabview_bug600812.js
browser/base/content/test/tabview/browser_tabview_bug602432.js
browser/base/content/test/tabview/browser_tabview_bug604098.js
browser/base/content/test/tabview/browser_tabview_bug606657.js
browser/base/content/test/tabview/browser_tabview_bug606905.js
browser/base/content/test/tabview/browser_tabview_bug607108.js
browser/base/content/test/tabview/browser_tabview_bug608037.js
browser/base/content/test/tabview/browser_tabview_bug608158.js
browser/base/content/test/tabview/browser_tabview_bug608184.js
browser/base/content/test/tabview/browser_tabview_bug608405.js
browser/base/content/test/tabview/browser_tabview_bug610208.js
browser/base/content/test/tabview/browser_tabview_bug610242.js
browser/base/content/test/tabview/browser_tabview_bug612470.js
browser/base/content/test/tabview/browser_tabview_bug613541.js
browser/base/content/test/tabview/browser_tabview_bug616729.js
browser/base/content/test/tabview/browser_tabview_bug616967.js
browser/base/content/test/tabview/browser_tabview_bug618816.js
browser/base/content/test/tabview/browser_tabview_bug618828.js
browser/base/content/test/tabview/browser_tabview_bug619937.js
browser/base/content/test/tabview/browser_tabview_bug622835.js
browser/base/content/test/tabview/browser_tabview_bug624265.js
browser/base/content/test/tabview/browser_tabview_bug624727.js
browser/base/content/test/tabview/browser_tabview_bug624847.js
browser/base/content/test/tabview/browser_tabview_bug624953.js
browser/base/content/test/tabview/browser_tabview_bug625195.js
browser/base/content/test/tabview/browser_tabview_bug625269.js
browser/base/content/test/tabview/browser_tabview_bug625424.js
browser/base/content/test/tabview/browser_tabview_bug626368.js
browser/base/content/test/tabview/browser_tabview_bug626791.js
browser/base/content/test/tabview/browser_tabview_bug627288.js
browser/base/content/test/tabview/browser_tabview_bug627736.js
browser/base/content/test/tabview/browser_tabview_bug628061.js
browser/base/content/test/tabview/browser_tabview_bug628165.js
browser/base/content/test/tabview/browser_tabview_bug628270.js
browser/base/content/test/tabview/browser_tabview_bug629189.js
browser/base/content/test/tabview/browser_tabview_bug629195.js
browser/base/content/test/tabview/browser_tabview_bug630102.js
browser/base/content/test/tabview/browser_tabview_bug630157.js
browser/base/content/test/tabview/browser_tabview_bug631752.js
browser/base/content/test/tabview/browser_tabview_bug633190.js
browser/base/content/test/tabview/browser_tabview_bug635696.js
browser/base/content/test/tabview/browser_tabview_bug641802.js
browser/base/content/test/tabview/browser_tabview_bug642793.js
browser/base/content/test/tabview/browser_tabview_bug643392.js
browser/base/content/test/tabview/browser_tabview_bug648882.js
browser/base/content/test/tabview/browser_tabview_bug649006.js
browser/base/content/test/tabview/browser_tabview_bug649319.js
browser/base/content/test/tabview/browser_tabview_bug650280.js
browser/base/content/test/tabview/browser_tabview_bug655269.js
browser/base/content/test/tabview/browser_tabview_bug656778.js
browser/base/content/test/tabview/browser_tabview_bug662266.js
browser/base/content/test/tabview/browser_tabview_bug663421.js
browser/base/content/test/tabview/browser_tabview_bug665502.js
browser/base/content/test/tabview/browser_tabview_bug681599.js
browser/base/content/test/tabview/browser_tabview_click_group.js
browser/base/content/test/tabview/browser_tabview_dragdrop.js
browser/base/content/test/tabview/browser_tabview_exit_button.js
browser/base/content/test/tabview/browser_tabview_expander.js
browser/base/content/test/tabview/browser_tabview_firstrun_pref.js
browser/base/content/test/tabview/browser_tabview_group.js
browser/base/content/test/tabview/browser_tabview_launch.js
browser/base/content/test/tabview/browser_tabview_layout.js
browser/base/content/test/tabview/browser_tabview_multiwindow_search.js
browser/base/content/test/tabview/browser_tabview_privatebrowsing.js
browser/base/content/test/tabview/browser_tabview_rtl.js
browser/base/content/test/tabview/browser_tabview_search.js
browser/base/content/test/tabview/browser_tabview_snapping.js
browser/base/content/test/tabview/browser_tabview_startup_transitions.js
browser/base/content/test/tabview/browser_tabview_storage_policy.js
browser/base/content/test/tabview/browser_tabview_undo_group.js
browser/base/content/test/tabview/head.js
browser/components/safebrowsing/src/Makefile.in
browser/components/sessionstore/test/Makefile.in
browser/components/sidebar/Makefile.in
browser/components/sidebar/src/Makefile.in
browser/components/sidebar/src/nsSidebar.js
browser/components/sidebar/src/nsSidebar.manifest
browser/components/test/Makefile.in
browser/devtools/scratchpad/Makefile.in
browser/devtools/styleinspector/test/Makefile.in
browser/devtools/webconsole/test/Makefile.in
caps/tests/Makefile.in
content/mathml/Makefile.in
content/mathml/content/Makefile.in
content/svg/document/Makefile.in
content/xml/content/Makefile.in
content/xul/templates/tests/Makefile.in
docshell/resources/Makefile.in
dom/tests/mochitest/ajax/scriptaculous/test/Makefile.in
embedding/components/appstartup/Makefile.in
embedding/components/printingui/Makefile.in
embedding/tests/Makefile.in
extensions/spellcheck/tests/Makefile.in
js/ductwork/Makefile.in
mobile/themes/Makefile.in
testing/mochitest/redirect.js
toolkit/components/aboutmemory/tests/Makefile.in
toolkit/components/passwordmgr/content/Makefile.in
toolkit/components/startup/tests/Makefile.in
toolkit/themes/faststripe/Makefile.in
toolkit/themes/pmstripe/Makefile.in
xpcom/base/nsSystemInfo.cpp
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -176,16 +176,17 @@ include $(topsrcdir)/config/rules.mk
                  browser_customize_popupNotification.js \
                  browser_disablechrome.js \
                  browser_discovery.js \
                  browser_duplicateIDs.js \
                  browser_gestureSupport.js \
                  browser_getshortcutoruri.js \
                  browser_hide_removing.js \
                  browser_overflowScroll.js \
+                 browser_locationBarCommand.js \
                  browser_locationBarExternalLoad.js \
                  browser_pageInfo.js \
                  browser_page_style_menu.js \
                  browser_pinnedTabs.js \
                  browser_plainTextLinks.js \
                  browser_pluginnotification.js \
                  browser_relatedTabs.js \
                  browser_sanitize-passwordDisabledHosts.js \
--- a/browser/base/content/test/browser_bug647886.js
+++ b/browser/base/content/test/browser_bug647886.js
@@ -1,14 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function test() {
   waitForExplicitFinish();
 
+  gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", function () {
     gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
 
     content.history.pushState({}, "2", "2.html");
 
     testBackButton();
   }, true);
 
@@ -21,14 +22,15 @@ function testBackButton() {
 
   info("waiting for the history menu to open");
 
   backButton.addEventListener("popupshown", function (event) {
     backButton.removeEventListener("popupshown", arguments.callee, false);
 
     ok(true, "history menu opened");
     event.target.hidePopup();
+    gBrowser.removeTab(gBrowser.selectedTab);
     finish();
   }, false);
 
   EventUtils.synthesizeMouseAtCenter(backButton, {type: "mousedown"});
   EventUtils.synthesizeMouse(backButton, rect.width / 2, rect.height, {type: "mouseup"});
 }
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_locationBarCommand.js
@@ -0,0 +1,188 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const TEST_VALUE = "example.com";
+const START_VALUE = "example.org";
+
+let gFocusManager = Cc["@mozilla.org/focus-manager;1"].
+                    getService(Ci.nsIFocusManager);
+
+function test() {
+  waitForExplicitFinish();
+  runAltLeftClickTest();
+}
+
+// Monkey patch saveURL to avoid dealing with file save code paths
+var oldSaveURL = saveURL;
+saveURL = function() {
+  ok(true, "SaveURL was called");
+  is(gURLBar.value, "", "Urlbar reverted to original value");
+  saveURL = oldSaveURL;
+  runShiftLeftClickTest();
+}
+function runAltLeftClickTest() {
+  info("Running test: Alt left click");
+  triggerCommand(true, { altKey: true });
+}
+
+function runShiftLeftClickTest() {
+  let listener = new WindowListener("chrome://browser/content/browser.xul", function(aWindow) {
+    Services.wm.removeListener(listener);
+    addPageShowListener(aWindow.gBrowser, function() {
+      info("URL should be loaded in a new window");
+      is(gURLBar.value, "", "Urlbar reverted to original value");       
+      is(gFocusManager.focusedElement, null, "There should be no focused element");
+      is(gFocusManager.focusedWindow, aWindow.gBrowser.contentWindow, "Content window should be focused");
+      is(aWindow.gURLBar.value, TEST_VALUE, "New URL is loaded in new window");
+
+      aWindow.close();
+      runNextTest();
+    });
+  });
+  Services.wm.addListener(listener);
+
+  info("Running test: Shift left click");
+  triggerCommand(true, { shiftKey: true });
+}
+
+function runNextTest() {
+  let test = gTests.shift();
+  if (!test) {
+    finish();
+    return;
+  }
+  
+  info("Running test: " + test.desc);
+  // Tab will be blank if test.startValue is null
+  let tab = gBrowser.selectedTab = gBrowser.addTab(test.startValue);
+  addPageShowListener(gBrowser, function() {
+    triggerCommand(test.click, test.event);
+    test.check(tab);
+
+    // Clean up
+    while (gBrowser.tabs.length > 1)
+      gBrowser.removeTab(gBrowser.selectedTab)
+    runNextTest();
+  });
+}
+
+let gTests = [
+  { desc: "Right click on go button",
+    click: true,
+    event: { button: 2 },
+    check: function(aTab) {
+      // Right click should do nothing (context menu will be shown)
+      is(gURLBar.value, TEST_VALUE, "Urlbar still has the value we entered");
+      ok(gURLBar.focused, "Urlbar is still focused after click");
+    }
+  },
+
+  { desc: "Left click on go button",
+    click: true,
+    event: {},
+    check: checkCurrent
+  },
+
+  { desc: "Ctrl/Cmd left click on go button",
+    click: true,
+    event: { accelKey: true },
+    check: checkNewTab
+  },
+
+  { desc: "Shift+Ctrl/Cmd left click on go button",
+    click: true,
+    event: { accelKey: true, shiftKey: true },
+    check: function(aTab) {
+      info("URL should be loaded in a new background tab");
+      is(gURLBar.value, "", "Urlbar reverted to original value");
+      ok(!gURLBar.focused, "Urlbar is no longer focused after urlbar command");
+      is(gBrowser.selectedTab, aTab, "Focus did not change to the new tab");
+    
+      // Select the new background tab
+      gBrowser.selectedTab = gBrowser.selectedTab.nextSibling;
+      is(gURLBar.value, TEST_VALUE, "New URL is loaded in new tab");
+    }
+  },
+
+  { desc: "Simple return keypress",
+    event: {},
+    check: checkCurrent
+  },
+
+  { desc: "Alt+Return keypress in a blank tab",
+    event: { altKey: true },
+    check: checkCurrent
+  },
+
+  { desc: "Alt+Return keypress in a dirty tab",
+    event: { altKey: true },
+    check: checkNewTab,
+    startValue: START_VALUE
+  },
+
+  { desc: "Ctrl/Cmd+Return keypress",
+    event: { accelKey: true },
+    check: checkCurrent
+  }
+]
+
+let gGoButton = document.getElementById("urlbar-go-button");
+function triggerCommand(aClick, aEvent) {
+  gURLBar.value = TEST_VALUE;
+  gURLBar.focus();
+
+  if (aClick)
+    EventUtils.synthesizeMouseAtCenter(gGoButton, aEvent); 
+  else
+    EventUtils.synthesizeKey("VK_RETURN", aEvent);
+}
+
+/* Checks that the URL was loaded in the current tab */
+function checkCurrent(aTab) {
+  info("URL should be loaded in the current tab");
+  is(gURLBar.value, TEST_VALUE, "Urlbar still has the value we entered");
+  is(gFocusManager.focusedElement, null, "There should be no focused element");
+  is(gFocusManager.focusedWindow, gBrowser.contentWindow, "Content window should be focused");
+  is(gBrowser.selectedTab, aTab, "New URL was loaded in the current tab");
+}
+
+/* Checks that the URL was loaded in a new focused tab */
+function checkNewTab(aTab) {
+  info("URL should be loaded in a new focused tab");
+  is(gURLBar.value, TEST_VALUE, "Urlbar still has the value we entered");
+  is(gFocusManager.focusedElement, null, "There should be no focused element");
+  is(gFocusManager.focusedWindow, gBrowser.contentWindow, "Content window should be focused");
+  isnot(gBrowser.selectedTab, aTab, "New URL was loaded in a new tab");
+}
+
+function addPageShowListener(aBrowser, aFunc) {
+  aBrowser.selectedBrowser.addEventListener("pageshow", function loadListener() {
+    aBrowser.selectedBrowser.removeEventListener("pageshow", loadListener, false);
+    aFunc();
+  });
+}
+
+function WindowListener(aURL, aCallback) {
+  this.callback = aCallback;
+  this.url = aURL;
+}
+WindowListener.prototype = {
+  onOpenWindow: function(aXULWindow) {
+    var domwindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                              .getInterface(Ci.nsIDOMWindow);
+    var self = this;
+    domwindow.addEventListener("load", function() {
+      domwindow.removeEventListener("load", arguments.callee, false);
+
+      if (domwindow.document.location.href != self.url)
+        return;
+
+      // Allow other window load listeners to execute before passing to callback
+      executeSoon(function() {
+        self.callback(domwindow);
+      });
+    }, false);
+  },
+  onCloseWindow: function(aXULWindow) {},
+  onWindowTitleChange: function(aXULWindow, aNewTitle) {}
+}
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -329,36 +329,45 @@
             gBrowser.loadURIWithFlags(url, flags, null, null, postData);
           }
 
           // Focus the content area before triggering loads, since if the load
           // occurs in a new tab, we want focus to be restored to the content
           // area when the current tab is re-selected.
           gBrowser.selectedBrowser.focus();
 
-          if (aTriggeringEvent instanceof MouseEvent) {
-            // We have a mouse event (from the go button), so use the standard
-            // UI link behaviors
-            let where = whereToOpenLink(aTriggeringEvent, false, false);
+          let isMouseEvent = aTriggeringEvent instanceof MouseEvent;
+          let altEnter = !isMouseEvent && aTriggeringEvent && aTriggeringEvent.altKey;
+
+          if (altEnter) {
+            // XXX This was added a long time ago, and I'm not sure why it is
+            // necessary. Alt+Enter's default action might cause a system beep,
+            // or something like that?
+            aTriggeringEvent.preventDefault();
+            aTriggeringEvent.stopPropagation();
+          }
+
+          // If the current tab is empty, ignore Alt+Enter (just reuse this tab)
+          altEnter = altEnter && !isTabEmpty(gBrowser.selectedTab);
+
+          if (isMouseEvent || altEnter) {
+            // Use the standard UI link behaviors for clicks or Alt+Enter
+            let where = "tab";
+            if (isMouseEvent)
+              where = whereToOpenLink(aTriggeringEvent, false, false);
+
             if (where == "current") {
               loadCurrent();
             } else {
               this.handleRevert();
-              openUILinkIn(url, where,
-                           { allowThirdPartyFixup: true, postData: postData });
+              let params = { allowThirdPartyFixup: true, postData: postData };
+              if (altEnter)
+                params.inBackground = false;
+              openUILinkIn(url, where, params);
             }
-          } else if (aTriggeringEvent && aTriggeringEvent.altKey &&
-                     !isTabEmpty(gBrowser.selectedTab)) {
-            this.handleRevert();
-            gBrowser.loadOneTab(url, {
-                                postData: postData,
-                                inBackground: false,
-                                allowThirdPartyFixup: true});
-            aTriggeringEvent.preventDefault();
-            aTriggeringEvent.stopPropagation();
           } else {
             loadCurrent();
           }
         ]]></body>
       </method>
 
       <method name="_canonizeURL">
         <parameter name="aTriggeringEvent"/>
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -190,16 +190,17 @@ function openLinkIn(url, where, params) 
     return;
 
   var aFromChrome           = params.fromChrome;
   var aAllowThirdPartyFixup = params.allowThirdPartyFixup;
   var aPostData             = params.postData;
   var aCharset              = params.charset;
   var aReferrerURI          = params.referrerURI;
   var aRelatedToCurrent     = params.relatedToCurrent;
+  var aInBackground         = params.inBackground;
 
   if (where == "save") {
     saveURL(url, null, null, true, null, aReferrerURI);
     return;
   }
   const Cc = Components.classes;
   const Ci = Components.interfaces;
 
@@ -235,19 +236,22 @@ function openLinkIn(url, where, params) 
     sa.AppendElement(aPostData);
     sa.AppendElement(allowThirdPartyFixupSupports);
 
     Services.ww.openWindow(w || window, getBrowserURL(),
                            null, "chrome,dialog=no,all", sa);
     return;
   }
 
-  var loadInBackground = aFromChrome ?
+  let loadInBackground = aInBackground;
+  if (loadInBackground == null) {
+    loadInBackground = aFromChrome ?
                          getBoolPref("browser.tabs.loadBookmarksInBackground") :
                          getBoolPref("browser.tabs.loadInBackground");
+  }
 
   if (where == "current" && w.gBrowser.selectedTab.pinned) {
     try {
       let uriObj = Services.io.newURI(url, null, null);
       if (!uriObj.schemeIs("javascript") &&
           w.gBrowser.currentURI.host != uriObj.host) {
         where = "tab";
         loadInBackground = false;
--- a/browser/components/Makefile.in
+++ b/browser/components/Makefile.in
@@ -64,30 +64,30 @@ PARALLEL_DIRS = \
   dirprovider \
   feeds \
   places \
   preferences \
   privatebrowsing \
   search \
   sessionstore \
   shell \
-  sidebar \
+  sidebar/src \
   migration \
   $(NULL)
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),windows) 
 PARALLEL_DIRS += wintaskbar
 endif
 
 ifdef MOZ_SAFE_BROWSING
 PARALLEL_DIRS += safebrowsing
 endif
 
 ifdef ENABLE_TESTS
-DIRS += test
+DIRS += test/browser
 endif
 
 DIRS += build
 
 ifdef MOZILLA_OFFICIAL
 DEFINES += -DOFFICIAL_BUILD=1
 endif
 
--- a/browser/components/preferences/aboutPermissions.js
+++ b/browser/components/preferences/aboutPermissions.js
@@ -361,24 +361,36 @@ let PermissionDefaults = {
  */
 let AboutPermissions = {
   /**
    * Number of sites to return from the places database.
    */  
   PLACES_SITES_LIMIT: 50,
 
   /**
+   * When adding sites to the dom sites-list, divide workload into intervals.
+   */
+  LIST_BUILD_CHUNK: 5, // interval size
+  LIST_BUILD_DELAY: 100, // delay between intervals
+
+  /**
    * Stores a mapping of host strings to Site objects.
    */
   _sites: {},
 
   sitesList: null,
   _selectedSite: null,
 
   /**
+   * For testing, track initializations so we can send notifications
+   */
+  _initPlacesDone: false,
+  _initServicesDone: false,
+
+  /**
    * This reflects the permissions that we expose in the UI. These correspond
    * to permission type strings in the permission manager, PermissionDefaults,
    * and element ids in aboutPermissions.xul.
    *
    * Potential future additions: "sts/use", "sts/subd"
    */
   _supportedPermissions: ["password", "cookie", "geo", "indexedDB", "popup"],
 
@@ -392,31 +404,34 @@ let AboutPermissions = {
 
   /**
    * Called on page load.
    */
   init: function() {
     this.sitesList = document.getElementById("sites-list");
 
     this.getSitesFromPlaces();
-    this.enumerateServices();
+
+    this.enumerateServicesGenerator = this.getEnumerateServicesGenerator();
+    setTimeout(this.enumerateServicesDriver.bind(this), this.LIST_BUILD_DELAY);
 
     // Attach observers in case data changes while the page is open.
     Services.prefs.addObserver("signon.rememberSignons", this, false);
     Services.prefs.addObserver("network.cookie.", this, false);
     Services.prefs.addObserver("geo.enabled", this, false);
     Services.prefs.addObserver("dom.indexedDB.enabled", this, false);
     Services.prefs.addObserver("dom.disable_open_during_load", this, false);
 
     Services.obs.addObserver(this, "perm-changed", false);
     Services.obs.addObserver(this, "passwordmgr-storage-changed", false);
     Services.obs.addObserver(this, "cookie-changed", false);
     Services.obs.addObserver(this, "browser:purge-domain-data", false);
     
     this._observersInitialized = true;
+    Services.obs.notifyObservers(null, "browser-permissions-preinit", null);
   },
 
   /**
    * Called on page unload.
    */
   cleanUp: function() {
     if (this._observersInitialized) {
       Services.prefs.removeObserver("signon.rememberSignons", this, false);
@@ -496,61 +511,93 @@ let AboutPermissions = {
         }
         AboutPermissions.endSitesListBatch();
       },
       handleError: function(aError) {
         Cu.reportError("AboutPermissions: " + aError);
       },
       handleCompletion: function(aReason) {
         // Notify oberservers for testing purposes.
+        AboutPermissions._initPlacesDone = true;
+        if (AboutPermissions._initServicesDone) {
+          Services.obs.notifyObservers(null, "browser-permissions-initialized", null);
+        }
+      }
+    });
+  },
+
+  /**
+   * Drives getEnumerateServicesGenerator to work in intervals.
+   */
+  enumerateServicesDriver: function() {
+    if (this.enumerateServicesGenerator.next()) {
+      // Build top sitesList items faster so that the list never seems sparse
+      let delay = Math.min(this.sitesList.itemCount * 5, this.LIST_BUILD_DELAY);
+      setTimeout(this.enumerateServicesDriver.bind(this), delay);
+    } else {
+      this.enumerateServicesGenerator.close();
+      this._initServicesDone = true;
+      if (this._initPlacesDone) {
         Services.obs.notifyObservers(null, "browser-permissions-initialized", null);
       }
-    });
+    }
   },
 
   /**
    * Finds sites that have non-default permissions and creates Site objects for
    * them if they are not already stored in _sites.
    */
-  enumerateServices: function() {
-    this.startSitesListBatch();
+  getEnumerateServicesGenerator: function() {
+    let itemCnt = 1;
 
     let logins = Services.logins.getAllLogins();
     logins.forEach(function(aLogin) {
+      if (itemCnt % this.LIST_BUILD_CHUNK == 0) {
+        yield true;
+      }
       try {
         // aLogin.hostname is a string in origin URL format (e.g. "http://foo.com")
         let uri = NetUtil.newURI(aLogin.hostname);
         this.addHost(uri.host);
       } catch (e) {
         // newURI will throw for add-ons logins stored in chrome:// URIs 
       }
+      itemCnt++;
     }, this);
 
     let disabledHosts = Services.logins.getAllDisabledHosts();
     disabledHosts.forEach(function(aHostname) {
+      if (itemCnt % this.LIST_BUILD_CHUNK == 0) {
+        yield true;
+      }
       try {
         // aHostname is a string in origin URL format (e.g. "http://foo.com")
         let uri = NetUtil.newURI(aHostname);
         this.addHost(uri.host);
       } catch (e) {
         // newURI will throw for add-ons logins stored in chrome:// URIs 
       }
+      itemCnt++;
     }, this);
 
     let (enumerator = Services.perms.enumerator) {
       while (enumerator.hasMoreElements()) {
+        if (itemCnt % this.LIST_BUILD_CHUNK == 0) {
+          yield true;
+        }
         let permission = enumerator.getNext().QueryInterface(Ci.nsIPermission);
         // Only include sites with exceptions set for supported permission types.
         if (this._supportedPermissions.indexOf(permission.type) != -1) {
           this.addHost(permission.host);
         }
+        itemCnt++;
       }
     }
 
-    this.endSitesListBatch();
+    yield false;
   },
 
   /**
    * Creates a new Site and adds it to _sites if it's not already there.
    *
    * @param aHost
    *        A host string.
    */
@@ -574,17 +621,21 @@ let AboutPermissions = {
     item.setAttribute("class", "site");
     item.setAttribute("value", aSite.host);
 
     aSite.getFavicon(function(aURL) {
       item.setAttribute("favicon", aURL);
     });
     aSite.listitem = item;
 
-    (this._listFragment || this.sitesList).appendChild(item);
+    // Make sure to only display relevant items when list is filtered
+    let filterValue = document.getElementById("sites-filter").value.toLowerCase();
+    item.collapsed = aSite.host.toLowerCase().indexOf(filterValue) == -1;
+
+    this.sitesList.appendChild(item);
   },
 
   startSitesListBatch: function () {
     if (!this._listFragment)
       this._listFragment = document.createDocumentFragment();
   },
 
   endSitesListBatch: function () {
@@ -635,17 +686,17 @@ let AboutPermissions = {
         if (site == this._selectedSite) {
           // Replace site-specific interface with "All Sites" interface.
           this.sitesList.selectedItem = document.getElementById("all-sites-item");
         }
 
         this.sitesList.removeChild(site.listitem);
         delete this._sites[site.host];
       }
-    }    
+    }
   },
 
   /**
    * Shows interface for managing site-specific permissions.
    */
   onSitesListSelect: function(event) {
     if (event.target.selectedItem.id == "all-sites-item") {
       // Clear the header label value from the previously selected site.
--- a/browser/components/preferences/tests/Makefile.in
+++ b/browser/components/preferences/tests/Makefile.in
@@ -51,12 +51,13 @@ include $(topsrcdir)/config/rules.mk
     browser_privacypane_2.js \
     browser_privacypane_3.js \
     browser_privacypane_4.js \
     browser_privacypane_5.js \
     browser_privacypane_6.js \
     browser_privacypane_7.js \
     browser_privacypane_8.js \
     browser_permissions.js \
+    browser_chunk_permissions.js \
     $(NULL)
 
 libs::	$(_BROWSER_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/browser/components/preferences/tests/browser_chunk_permissions.js
@@ -0,0 +1,151 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+Components.utils.import("resource://gre/modules/PlacesUtils.jsm");
+Components.utils.import("resource://gre/modules/NetUtil.jsm");
+
+const ABOUT_PERMISSIONS_SPEC = "about:permissions";
+
+const TEST_URI_1 = NetUtil.newURI("http://mozilla.com/");
+const TEST_URI_2 = NetUtil.newURI("http://mozilla.org/");
+const TEST_URI_3 = NetUtil.newURI("http://wikipedia.org/");
+
+// values from DefaultPermissions object
+const PERM_UNKNOWN = 0;
+const PERM_ALLOW = 1;
+const PERM_DENY = 2;
+const PERM_SESION = 8;
+
+// used to set permissions on test sites
+const TEST_PERMS = {
+  "password": PERM_ALLOW,
+  "cookie": PERM_ALLOW,
+  "geo": PERM_UNKNOWN,
+  "indexedDB": PERM_UNKNOWN,
+  "popup": PERM_DENY
+};
+
+function test() {
+  waitForExplicitFinish();
+  registerCleanupFunction(cleanUp);
+  setup();
+  runNextTest();
+}
+
+function setup() {
+  // add test history visit
+  PlacesUtils.history.addVisit(TEST_URI_1, Date.now() * 1000, null,
+    Ci.nsINavHistoryService.TRANSITION_LINK, false, 0);
+
+  // set permissions ourselves to avoid problems with different defaults
+  // from test harness configuration
+  for (let type in TEST_PERMS) {
+    if (type == "password") {
+      Services.logins.setLoginSavingEnabled(TEST_URI_2.prePath, true);
+    } else {
+      // set permissions on a site without history visits to test enumerateServices
+      Services.perms.add(TEST_URI_2, type, TEST_PERMS[type]);
+    }
+  }
+
+  Services.perms.add(TEST_URI_3, "popup", TEST_PERMS["popup"]);
+}
+
+function cleanUp() {
+  for (let type in TEST_PERMS) {
+    if (type != "password") {
+      Services.perms.remove(TEST_URI_1.host, type);
+      Services.perms.remove(TEST_URI_2.host, type);
+      Services.perms.remove(TEST_URI_3.host, type);
+    }
+  }
+}
+
+function runNextTest() {
+  if (gTestIndex == tests.length) {
+    waitForClearHistory(finish);
+    return;
+  }
+
+  let nextTest = tests[gTestIndex++];
+  info(nextTest.desc);
+
+  function preinit_observer() {
+    Services.obs.removeObserver(preinit_observer, "browser-permissions-preinit", false);
+    nextTest.preInit();
+  }
+  Services.obs.addObserver(preinit_observer, "browser-permissions-preinit", false);
+
+  function init_observer() {
+    Services.obs.removeObserver(init_observer, "browser-permissions-initialized", false);
+    nextTest.run();
+  }
+  Services.obs.addObserver(init_observer, "browser-permissions-initialized", false);
+
+  // open about:permissions
+  let tab = gBrowser.selectedTab = gBrowser.addTab("about:permissions");
+  registerCleanupFunction(function() {
+    gBrowser.removeTab(tab);
+  });
+}
+
+var gSitesList;
+
+var gTestIndex = 0;
+var tests = [
+  // 'preInit' occurs after opening about:permissions, before sites-list is populated
+  // 'run' occurs after sites-list is populated
+  {
+    desc: "test filtering before sites-list is fully constructed.",
+    preInit: function() {
+      let sitesFilter = gBrowser.contentDocument.getElementById("sites-filter");
+      sitesFilter.value = TEST_URI_2.host;
+      sitesFilter.doCommand();
+    },
+    run: function() {
+      let testSite1 = getSiteItem(TEST_URI_1.host);
+      ok(testSite1.collapsed, "test site 1 is collapsed after early filtering");
+      let testSite2 = getSiteItem(TEST_URI_2.host);
+      ok(!testSite2.collapsed, "test site 2 is not collapsed after early filtering");
+      let testSite3 = getSiteItem(TEST_URI_3.host);
+      ok(testSite3.collapsed, "test site 3 is collapsed after early filtering");
+
+      runNextTest();
+    }
+  },
+  {
+    desc: "test removing from sites-list before it is fully constructed.",
+    preInit: function() {
+      let pb = Cc["@mozilla.org/privatebrowsing;1"].
+                 getService(Ci.nsIPrivateBrowsingService);
+      pb.removeDataFromDomain(TEST_URI_2.host);
+    },
+    run: function() {
+      let testSite1 = getSiteItem(TEST_URI_1.host);
+      ok(!testSite2, "test site 1 was not removed from sites list");
+      let testSite2 = getSiteItem(TEST_URI_2.host);
+      ok(!testSite2, "test site 2 was pre-removed from sites list");
+      let testSite3 = getSiteItem(TEST_URI_3.host);
+      ok(!testSite2, "test site 3 was not removed from sites list");
+
+      runNextTest();
+    }
+  }
+];
+
+function getSiteItem(aHost) {
+  return gBrowser.contentDocument.
+                  querySelector(".site[value='" + aHost + "']");
+}
+
+// copied from toolkit/components/places/tests/head_common.js
+function waitForClearHistory(aCallback) {
+  let observer = {
+    observe: function(aSubject, aTopic, aData) {
+      Services.obs.removeObserver(this, PlacesUtils.TOPIC_EXPIRATION_FINISHED);
+      aCallback();
+    }
+  };
+  Services.obs.addObserver(observer, PlacesUtils.TOPIC_EXPIRATION_FINISHED, false);
+  PlacesUtils.bhistory.removeAllPages();
+}
--- a/browser/components/safebrowsing/Makefile.in
+++ b/browser/components/safebrowsing/Makefile.in
@@ -38,18 +38,16 @@
 
 DEPTH     = ../../..
 topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH     = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
-DIRS      = src
-
 ifdef ENABLE_TESTS
 DIRS += content/test
 endif
 
 ifdef MOZILLA_OFFICIAL
 DEFINES += -DOFFICIAL_BUILD=1
 endif
 
deleted file mode 100644
--- a/browser/components/safebrowsing/src/Makefile.in
+++ /dev/null
@@ -1,44 +0,0 @@
-# ***** 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.org code.
-#
-# The Initial Developer of the Original Code is Google Inc.
-# Portions created by the Initial Developer are Copyright (C) 2006
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-#
-# 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 *****
-
-
-DEPTH     = ../../../..
-topsrcdir = @top_srcdir@
-srcdir    = @srcdir@
-VPATH     = @srcdir@
-
-include $(DEPTH)/config/autoconf.mk
-
-include $(topsrcdir)/config/rules.mk
--- a/browser/components/sessionstore/Makefile.in
+++ b/browser/components/sessionstore/Makefile.in
@@ -46,12 +46,12 @@ XPIDL_MODULE = sessionstore
 XPIDLSRCS = \
 	nsISessionStartup.idl \
 	nsISessionStore.idl \
   $(NULL)
 
 DIRS = src
 
 ifdef ENABLE_TESTS
-DIRS += test
+DIRS += test/browser
 endif
 
 include $(topsrcdir)/config/rules.mk
deleted file mode 100644
--- a/browser/components/sessionstore/test/Makefile.in
+++ /dev/null
@@ -1,49 +0,0 @@
-# ***** 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.org code.
-#
-# The Initial Developer of the Original Code is
-# Mozilla Foundation.
-# Portions created by the Initial Developer are Copyright (C) 2007
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-#
-# Alternatively, the contents of this file may be used under the terms of
-# either of 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 *****
-
-DEPTH		= ../../../..
-topsrcdir	= @top_srcdir@
-srcdir		= @srcdir@
-VPATH		= @srcdir@
-
-include $(DEPTH)/config/autoconf.mk
-
-
-DIRS +=	browser \
-		$(NULL)
-
-include $(topsrcdir)/config/rules.mk
-
deleted file mode 100644
--- a/browser/components/sidebar/Makefile.in
+++ /dev/null
@@ -1,48 +0,0 @@
-#
-# ***** 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.org Code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 1999
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-#
-# 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 *****
-
-DEPTH		= ../../..
-topsrcdir	= @top_srcdir@
-srcdir		= @srcdir@
-VPATH		= @srcdir@
-
-include $(DEPTH)/config/autoconf.mk
-
-DIRS		= src
-
-include $(topsrcdir)/config/rules.mk
-
deleted file mode 100644
--- a/browser/components/test/Makefile.in
+++ /dev/null
@@ -1,48 +0,0 @@
-#
-# ***** 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.org code.
-#
-# The Initial Developer of the Original Code is
-# the Mozilla Foundation.
-# Portions created by the Initial Developer are Copyright (C) 2010
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-#   Robert Strong <robert.bugzilla@gmail.com> (Original Author)
-#
-# Alternatively, the contents of this file may be used under the terms of
-# either of 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 *****
-
-DEPTH     = ../../..
-topsrcdir = @top_srcdir@
-srcdir    = @srcdir@
-VPATH     = @srcdir@
-
-include $(DEPTH)/config/autoconf.mk
-
-DIRS = browser
-
-include $(topsrcdir)/config/rules.mk
--- a/browser/devtools/Makefile.in
+++ b/browser/devtools/Makefile.in
@@ -44,19 +44,18 @@ VPATH   = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 include $(topsrcdir)/config/config.mk
 
 DIRS = \
   highlighter \
   webconsole \
-  scratchpad \
   sourceeditor \
   styleinspector \
   shared \
   $(NULL)
 
 ifdef ENABLE_TESTS
-# DIRS += test # no tests yet
+DIRS += scratchpad/test
 endif
 
 include $(topsrcdir)/config/rules.mk
deleted file mode 100644
--- a/browser/devtools/scratchpad/Makefile.in
+++ /dev/null
@@ -1,50 +0,0 @@
-#
-# ***** 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 Scratchpad Build Code.
-#
-# The Initial Developer of the Original Code is The Mozilla Foundation.
-#
-# Portions created by the Initial Developer are Copyright (C) 2011
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-#   Rob Campbell <rcampbell@mozilla.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 *****
-
-DEPTH		= ../../..
-topsrcdir	= @top_srcdir@
-srcdir		= @srcdir@
-VPATH		= @srcdir@
-
-include $(DEPTH)/config/autoconf.mk
-
-ifdef ENABLE_TESTS
-	DIRS += test
-endif
-
-include $(topsrcdir)/config/rules.mk
--- a/browser/devtools/styleinspector/Makefile.in
+++ b/browser/devtools/styleinspector/Makefile.in
@@ -40,16 +40,16 @@ DEPTH		= ../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 ifdef ENABLE_TESTS
 ifneq (mobile,$(MOZ_BUILD_APP))
-	DIRS += test
+DIRS += test/browser
 endif
 endif
 
 include $(topsrcdir)/config/rules.mk
 
 libs::
-	$(NSINSTALL) $(srcdir)/*.jsm $(FINAL_TARGET)/modules/devtools
\ No newline at end of file
+	$(NSINSTALL) $(srcdir)/*.jsm $(FINAL_TARGET)/modules/devtools
deleted file mode 100644
--- a/browser/devtools/styleinspector/test/Makefile.in
+++ /dev/null
@@ -1,48 +0,0 @@
-#
-# ***** 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 Style Inspector code.
-#
-# The Initial Developer of the Original Code is
-# Mozilla Corporation.
-# Portions created by the Initial Developer are Copyright (C) 2010
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-#     Mike Ratcliffe <mratcliffe@mozilla.com>  (Original author)
-#
-# Alternatively, the contents of this file may be used under the terms of
-# either of 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 *****
-
-DEPTH		= ../../../..
-topsrcdir	= @top_srcdir@
-srcdir		= @srcdir@
-VPATH		= @srcdir@
-
-include $(DEPTH)/config/autoconf.mk
-
-DIRS = browser
-
-include $(topsrcdir)/config/rules.mk
--- a/browser/devtools/webconsole/Makefile.in
+++ b/browser/devtools/webconsole/Makefile.in
@@ -52,13 +52,13 @@ EXTRA_JS_MODULES = \
 		$(NULL)
 
 EXTRA_PP_JS_MODULES = \
 		HUDService.jsm \
 		$(NULL)
 
 ifdef ENABLE_TESTS
 ifneq (mobile,$(MOZ_BUILD_APP))
-	DIRS += test
+DIRS += test/browser
 endif
 endif
 
 include $(topsrcdir)/config/rules.mk
deleted file mode 100644
--- a/browser/devtools/webconsole/test/Makefile.in
+++ /dev/null
@@ -1,48 +0,0 @@
-#
-# ***** 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 HUDService code.
-#
-# The Initial Developer of the Original Code is
-# Mozilla Corporation.
-# Portions created by the Initial Developer are Copyright (C) 2010
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-#     David Dahl <ddahl@mozilla.com>  (Original author)
-#
-# Alternatively, the contents of this file may be used under the terms of
-# either of 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 *****
-
-DEPTH		= ../../../..
-topsrcdir	= @top_srcdir@
-srcdir		= @srcdir@
-VPATH		= @srcdir@
-
-include $(DEPTH)/config/autoconf.mk
-
-DIRS = browser
-
-include $(topsrcdir)/config/rules.mk
--- a/browser/makefiles.sh
+++ b/browser/makefiles.sh
@@ -51,21 +51,19 @@ browser/components/migration/Makefile
 browser/components/migration/public/Makefile
 browser/components/migration/src/Makefile
 browser/components/places/Makefile
 browser/components/places/src/Makefile
 browser/components/preferences/Makefile
 browser/components/privatebrowsing/Makefile
 browser/components/privatebrowsing/src/Makefile
 browser/components/safebrowsing/Makefile
-browser/components/safebrowsing/src/Makefile
 browser/components/search/Makefile
 browser/components/sessionstore/Makefile
 browser/components/sessionstore/src/Makefile
-browser/components/sidebar/Makefile
 browser/components/sidebar/src/Makefile
 browser/components/shell/Makefile
 browser/components/shell/public/Makefile
 browser/components/shell/src/Makefile
 browser/components/wintaskbar/Makefile
 browser/fuel/Makefile
 browser/fuel/public/Makefile
 browser/fuel/src/Makefile
@@ -85,17 +83,16 @@ browser/themes/winstripe/Makefile
 "
 
 if [ "$ENABLE_TESTS" ]; then
   add_makefiles "
     browser/base/content/test/Makefile
     browser/components/certerror/test/Makefile
     browser/components/preferences/tests/Makefile
     browser/components/search/test/Makefile
-    browser/components/sessionstore/test/Makefile
     browser/components/sessionstore/test/browser/Makefile
     browser/components/shell/test/Makefile
     browser/components/feeds/test/Makefile
     browser/components/feeds/test/chrome/Makefile
     browser/components/places/tests/Makefile
     browser/components/places/tests/chrome/Makefile
     browser/components/places/tests/browser/Makefile
     browser/components/privatebrowsing/test/Makefile
--- a/build/mobile/devicemanagerSUT.py
+++ b/build/mobile/devicemanagerSUT.py
@@ -679,17 +679,17 @@ class DeviceManagerSUT(DeviceManager):
 
     if filesize == -1:
       # read error message
       error_str, sep, buffer = read_until_char('\n', buffer, 'could not find error message')
       if not error_str:
         return None
       # prompt should follow
       read_exact(len(prompt), buffer, 'could not find prompt')
-      print 'DeviceManager: error pulling file: %s' % error_str
+      print "DeviceManager: error pulling file '%s': %s" % (remoteFile, error_str)
       return None
 
     # read file data
     total_to_recv = filesize + len(prompt)
     buffer = read_exact(total_to_recv, buffer, 'could not get all file data')
     if buffer == None:
       return None
     if buffer[-len(prompt):] != prompt:
--- a/build/mobile/remoteautomation.py
+++ b/build/mobile/remoteautomation.py
@@ -35,16 +35,19 @@
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK *****
 
 import time
 import sys
 import os
 import socket
+import automationutils
+import tempfile
+import shutil
 
 from automation import Automation
 from devicemanager import DeviceManager, NetworkTools
 
 class RemoteAutomation(Automation):
     _devicemanager = None
     
     def __init__(self, deviceManager, appName = '', remoteLog = None):
@@ -100,16 +103,25 @@ class RemoteAutomation(Automation):
         print proc.stdout
 
         if (status == 1 and self._devicemanager.processExist(proc.procName)):
             # Then we timed out, make sure Fennec is dead
             proc.kill()
 
         return status
 
+    def checkForCrashes(self, directory, symbolsPath):
+        dumpDir = tempfile.mkdtemp()
+        self._devicemanager.getDirectory(self._remoteProfile + '/minidumps/', dumpDir)
+        automationutils.checkForCrashes(dumpDir, symbolsPath, self.lastTestSeen)
+        try:
+          shutil.rmtree(dumpDir)
+        except:
+          print "WARNING: unable to remove directory: %s" % (dumpDir)
+
     def buildCommandLine(self, app, debuggerInfo, profileDir, testURL, extraArgs):
         # If remote profile is specified, use that instead
         if (self._remoteProfile):
             profileDir = self._remoteProfile
 
         cmd, args = Automation.buildCommandLine(self, app, debuggerInfo, profileDir, testURL, extraArgs)
         # Remove -foreground if it exists, if it doesn't this just returns
         try:
--- a/caps/Makefile.in
+++ b/caps/Makefile.in
@@ -41,13 +41,13 @@ srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= caps
 DIRS		= idl include src
 
 ifdef ENABLE_TESTS
-DIRS		+= tests
+DIRS += tests/mochitest
 endif
 
 include $(topsrcdir)/config/rules.mk
 
deleted file mode 100644
--- a/caps/tests/Makefile.in
+++ /dev/null
@@ -1,48 +0,0 @@
-#
-# ***** 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.org code.
-#
-# The Initial Developer of the Original Code is
-# Mozilla Foundation.
-# Portions created by the Initial Developer are Copyright (C) 2008
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-#   Mike Shaver <shaver@mozilla.com> (original cutter and paster)
-#
-# Alternatively, the contents of this file may be used under the terms of
-# either of 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 *****
-
-DEPTH		= ../..
-topsrcdir	= @top_srcdir@
-srcdir		= @srcdir@
-VPATH		= @srcdir@
-
-include $(DEPTH)/config/autoconf.mk
-
-DIRS = mochitest
-
-include $(topsrcdir)/config/rules.mk
--- a/content/Makefile.in
+++ b/content/Makefile.in
@@ -38,17 +38,17 @@
 DEPTH		= ..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= content
-PARALLEL_DIRS		= base canvas events html mathml smil svg xml xul xbl xslt
+PARALLEL_DIRS = base canvas events html mathml/content/src smil svg xml xul xbl xslt
 
 ifdef MOZ_MEDIA
 PARALLEL_DIRS	+= media
 endif
 
 ifdef MOZ_XTF
 PARALLEL_DIRS   += xtf
 endif
--- a/content/events/src/nsEventListenerManager.cpp
+++ b/content/events/src/nsEventListenerManager.cpp
@@ -328,16 +328,21 @@ nsEventListenerManager::RemoveEventListe
     ls = &mListeners.ElementAt(i);
     if (ls->mListener == aListener &&
         ((ls->mFlags & ~NS_PRIV_EVENT_UNTRUSTED_PERMITTED) == aFlags) &&
         EVENT_TYPE_EQUALS(ls, aType, aUserType)) {
       nsRefPtr<nsEventListenerManager> kungFuDeathGrip = this;
       mListeners.RemoveElementAt(i);
       mNoListenerForEvent = NS_EVENT_TYPE_NULL;
       mNoListenerForEventAtom = nsnull;
+      if (aType == NS_DEVICE_ORIENTATION) {
+        nsPIDOMWindow* window = GetInnerWindowForTarget();
+        if (window)
+          window->RemoveOrientationEventListener();
+      }
       break;
     }
   }
 }
 
 static inline bool
 ListenerCanHandle(nsListenerStruct* aLs, nsEvent* aEvent)
 {
--- a/content/events/test/test_bug648573.html
+++ b/content/events/test/test_bug648573.html
@@ -1,12 +1,12 @@
 <!DOCTYPE HTML>
 <html>
 <!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=648573
+https://bugzilla.mozilla.org/show_bug.cgi?id=648573 
 -->
 <head>
   <title>Test for Bug 648573</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.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=648573">Mozilla Bug 648573</a>
@@ -14,17 +14,19 @@ https://bugzilla.mozilla.org/show_bug.cg
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 648573 **/
 
-ok(!SpecialPowers.DOMWindowUtils.mayHaveTouchEventListeners,
+var utils = SpecialPowers.getDOMWindowUtils(window);
+
+ok(!utils.mayHaveTouchEventListeners,
   "There shouldn't be any touch event listeners yet.");
 
 ok("createTouch" in document, "Should have createTouch function");
 ok("createTouchList" in document, "Should have createTouchList function");
 ok(document.createEvent("touchevent"), "Should be able to create TouchEvent objects");
 
 var t1 = document.createTouch(window, document, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
 is(t1.target, document, "Wrong target");
@@ -96,15 +98,15 @@ function runEventTest(type) {
   t.dispatchEvent(e);
   ok(t.didCall, "Should have called the listener(2)");
 }
 
 for (var i = 0; i < events.length; ++i) {
   runEventTest(events[i]);
 }
 
-ok(SpecialPowers.DOMWindowUtils.mayHaveTouchEventListeners,
+ok(utils.mayHaveTouchEventListeners,
   "There should be touch event listeners.");
 </script>
 </pre>
 </body>
 </html>
 
--- a/content/events/test/test_bug662678.html
+++ b/content/events/test/test_bug662678.html
@@ -13,40 +13,44 @@ https://bugzilla.mozilla.org/show_bug.cg
 <p id="display"></p>
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 662678 **/
+SimpleTest.waitForExplicitFinish();
 
-window.addEventListener("devicemotion", function(event) {
+var checkMotion = function(event) {
+  window.removeEventListener("devicemotion", checkMotion, true);
+
   is(event.acceleration.x, 1.5);
   is(event.acceleration.y, 1.5);
   is(event.acceleration.z, 1.5);
 
   is(event.accelerationIncludingGravity.x, 1.5);
   is(event.accelerationIncludingGravity.y, 1.5);
   is(event.accelerationIncludingGravity.z, 1.5);
 
   is(event.rotationRate.alpha, 1.5);
   is(event.rotationRate.beta, 1.5);
   is(event.rotationRate.gamma, 1.5);
   SimpleTest.finish();
-}, true);
+};
+
+window.addEventListener("devicemotion", checkMotion, true);
 
 var event = DeviceMotionEvent;
 ok(!!event, "Should have seen DeviceMotionEvent!");
 
 event = document.createEvent("DeviceMotionEvent");
 event.initDeviceMotionEvent('devicemotion', true, true, 
                             {x:1.5,y:1.5,z:1.5},
                             {x:1.5,y:1.5,z:1.5},
                             {alpha:1.5,beta:1.5,gamma:1.5},
                             0);
 window.dispatchEvent(event);
-SimpleTest.waitForExplicitFinish();
 
 </script>
 </pre>
 </body>
 </html>
deleted file mode 100644
--- a/content/mathml/Makefile.in
+++ /dev/null
@@ -1,47 +0,0 @@
-#
-# ***** 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.org code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 1998
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-#
-# Alternatively, the contents of this file may be used under the terms of
-# either of 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 *****
-
-DEPTH		= ../..
-topsrcdir	= @top_srcdir@
-srcdir		= @srcdir@
-VPATH		= @srcdir@
-
-include $(DEPTH)/config/autoconf.mk
-
-DIRS		= content
-
-include $(topsrcdir)/config/rules.mk
deleted file mode 100644
--- a/content/mathml/content/Makefile.in
+++ /dev/null
@@ -1,48 +0,0 @@
-#
-# ***** 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.org code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 1998
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-#
-# Alternatively, the contents of this file may be used under the terms of
-# either of 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 *****
-
-DEPTH		= ../../..
-topsrcdir	= @top_srcdir@
-srcdir		= @srcdir@
-VPATH		= @srcdir@
-
-include $(DEPTH)/config/autoconf.mk
-
-DIRS		= src
-
-include $(topsrcdir)/config/rules.mk
-
--- a/content/svg/Makefile.in
+++ b/content/svg/Makefile.in
@@ -37,11 +37,11 @@
 
 DEPTH		= ../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
-PARALLEL_DIRS	= document content
+PARALLEL_DIRS = document/src content
 
 include $(topsrcdir)/config/rules.mk
--- a/content/svg/content/src/nsSVGString.cpp
+++ b/content/svg/content/src/nsSVGString.cpp
@@ -59,18 +59,18 @@ NS_INTERFACE_MAP_END
 
 void
 nsSVGString::SetBaseValue(const nsAString& aValue,
                           nsSVGElement *aSVGElement,
                           bool aDoSetAttr)
 {
   NS_ASSERTION(aSVGElement, "Null element passed to SetBaseValue");
 
+  mIsBaseSet = PR_TRUE;
   if (aDoSetAttr) {
-    mIsBaseSet = PR_TRUE;
     aSVGElement->SetStringBaseValue(mAttrEnum, aValue);
   }
 #ifdef MOZ_SMIL
   if (mAnimVal) {
     aSVGElement->AnimationNeedsResample();
   }
 #endif
 
deleted file mode 100644
--- a/content/svg/document/Makefile.in
+++ /dev/null
@@ -1,48 +0,0 @@
-#
-# ***** 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 the mozilla svg code.
-#
-# The Initial Developer of the Original Code is
-# Bradley Baetz.
-# Portions created by the Initial Developer are Copyright (C) 2001
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-#
-# Alternatively, the contents of this file may be used under the terms of
-# either of 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 *****
-
-DEPTH		= ../../..
-topsrcdir	= @top_srcdir@
-srcdir		= @srcdir@
-VPATH		= @srcdir@
-
-include $(DEPTH)/config/autoconf.mk
-
-DIRS		= src
-
-include $(topsrcdir)/config/rules.mk
-
--- a/content/xml/Makefile.in
+++ b/content/xml/Makefile.in
@@ -37,12 +37,12 @@
 
 DEPTH		= ../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
-PARALLEL_DIRS	= content document
+PARALLEL_DIRS = content/src document
 
 include $(topsrcdir)/config/rules.mk
 
deleted file mode 100644
--- a/content/xml/content/Makefile.in
+++ /dev/null
@@ -1,48 +0,0 @@
-#
-# ***** 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.org code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 1998
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-#
-# Alternatively, the contents of this file may be used under the terms of
-# either of 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 *****
-
-DEPTH		= ../../..
-topsrcdir	= @top_srcdir@
-srcdir		= @srcdir@
-VPATH		= @srcdir@
-
-include $(DEPTH)/config/autoconf.mk
-
-DIRS		= src
-
-include $(topsrcdir)/config/rules.mk
-
--- a/content/xul/templates/Makefile.in
+++ b/content/xul/templates/Makefile.in
@@ -41,13 +41,13 @@ srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= xultmpl
 PARALLEL_DIRS	= public src
 
 ifdef ENABLE_TESTS
-TOOL_DIRS += tests
+TOOL_DIRS += tests/chrome
 endif
 
 include $(topsrcdir)/config/rules.mk
 
deleted file mode 100644
--- a/content/xul/templates/tests/Makefile.in
+++ /dev/null
@@ -1,49 +0,0 @@
-#
-# ***** 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.org code.
-#
-# The Initial Developer of the Original Code is
-# Mozilla.org.
-# Portions created by the Initial Developer are Copyright (C) 2009
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-#
-# Alternatively, the contents of this file may be used under the terms of
-# either of 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 *****
-
-DEPTH		= ../../../..
-topsrcdir	= @top_srcdir@
-srcdir		= @srcdir@
-VPATH		= @srcdir@
-relativesrcdir = content/xul/templates/tests
-
-include $(DEPTH)/config/autoconf.mk
-
-DIRS = chrome
-
-include $(topsrcdir)/config/rules.mk
-
--- a/docshell/Makefile.in
+++ b/docshell/Makefile.in
@@ -43,16 +43,16 @@ VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE = docshell
 DIRS = \
 	base \
 	shistory \
 	build \
-	resources \
+	resources/content \
 	$(NULL)
 
 ifdef ENABLE_TESTS
 DIRS		+= test
 endif
 
 include $(topsrcdir)/config/rules.mk
deleted file mode 100644
--- a/docshell/resources/Makefile.in
+++ /dev/null
@@ -1,48 +0,0 @@
-# 
-# ***** 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 the Mozilla browser.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications, Inc.
-# Portions created by the Initial Developer are Copyright (C) 1999
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-#   Adam Lock <adamlock@netscape.com>
-#
-# Alternatively, the contents of this file may be used under the terms of
-# either of 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 *****
-
-DEPTH		= ../..
-topsrcdir	= @top_srcdir@
-srcdir		= @srcdir@
-VPATH		= @srcdir@
-
-include $(DEPTH)/config/autoconf.mk
-
-DIRS=content
-
-include $(topsrcdir)/config/rules.mk
--- a/docshell/test/chrome/bug293235_window.xul
+++ b/docshell/test/chrome/bug293235_window.xul
@@ -3,23 +3,29 @@
 
 <window id="293235Test"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         width="600"
         height="600"
         onload="setTimeout(nextTest,0);"
         title="bug 293235 test">
 
+  <script type="text/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/specialpowersAPI.js"/>
+  <script type="text/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SpecialPowersObserverAPI.js"/>
+  <script type="text/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/ChromePowers.js"/>
   <script type="application/javascript" src= "chrome://mochikit/content/chrome-harness.js" />
   <script type="application/javascript" src="docshell_helpers.js" />
   <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script>
 
   <script type="application/javascript"><![CDATA[
-    const Ci = Components.interfaces;
-    const Cc = Components.classes;
+    var Ci = Components.interfaces;
+    var Cc = Components.classes;
     Components.utils.import("resource://gre/modules/NetUtil.jsm");
 
     // Define the generator-iterator for the tests.
     var tests = testIterator();
 
     ////
     // Execute the next test in the generator function.
     //
--- a/docshell/test/chrome/bug298622_window.xul
+++ b/docshell/test/chrome/bug298622_window.xul
@@ -3,16 +3,22 @@
 
 <window id="298622Test"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         width="600"
         height="600"
         onload="setTimeout(nextTest,0);"
         title="bug 298622 test">
 
+  <script type="text/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/specialpowersAPI.js"/>
+  <script type="text/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SpecialPowersObserverAPI.js"/>
+  <script type="text/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/ChromePowers.js"/>
   <script type="application/javascript" src= "chrome://mochikit/content/chrome-harness.js" />
   <script type="application/javascript" src= "docshell_helpers.js" />
   <script type="application/javascript"><![CDATA[
     // Global variable that holds a reference to the find bar.
     var gFindBar;
   
     // Define the generator-iterator for the tests.
     var tests = testIterator();
--- a/docshell/test/chrome/bug301397_window.xul
+++ b/docshell/test/chrome/bug301397_window.xul
@@ -3,16 +3,22 @@
 
 <window id="301397Test"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         width="600"
         height="600"
         onload="setTimeout(nextTest,0);"
         title="bug 301397 test">
 
+  <script type="text/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/specialpowersAPI.js"/>
+  <script type="text/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SpecialPowersObserverAPI.js"/>
+  <script type="text/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/ChromePowers.js"/>
   <script type="application/javascript" src= "chrome://mochikit/content/chrome-harness.js" />
   <script type="application/javascript" src="docshell_helpers.js" />
   <script type="application/javascript"><![CDATA[
   
     // Define the generator-iterator for the tests.
     var tests = testIterator();
 
     ////
--- a/docshell/test/chrome/bug321671_window.xul
+++ b/docshell/test/chrome/bug321671_window.xul
@@ -3,16 +3,22 @@
 
 <window id="321671Test"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         width="600"
         height="600"
         onload="setTimeout(nextTest,0);"
         title="bug 321671 test">
 
+  <script type="text/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/specialpowersAPI.js"/>
+  <script type="text/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SpecialPowersObserverAPI.js"/>
+  <script type="text/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/ChromePowers.js"/>
   <script type="application/javascript" src= "chrome://mochikit/content/chrome-harness.js" />
   <script type="application/javascript" src="docshell_helpers.js" />
   <script type="application/javascript"><![CDATA[
   
     // Define the generator-iterator for the tests.
     var tests = testIterator();
     
     // Maximum number of entries in the bfcache for this session history.
--- a/docshell/test/chrome/bug396649_window.xul
+++ b/docshell/test/chrome/bug396649_window.xul
@@ -3,16 +3,22 @@
 
 <window id="396649Test"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         width="600"
         height="600"
         onload="setTimeout(nextTest,0);"
         title="bug 396649 test">
 
+  <script type="text/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/specialpowersAPI.js"/>
+  <script type="text/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SpecialPowersObserverAPI.js"/>
+  <script type="text/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/ChromePowers.js"/>
   <script type="application/javascript" src= "chrome://mochikit/content/chrome-harness.js" />
   <script type="application/javascript" src="docshell_helpers.js" />
   <script type="application/javascript"><![CDATA[
   
     // Define the generator-iterator for the tests.
     var tests = testIterator();
     
     // Maximum number of entries in the bfcache for this session history.
--- a/docshell/test/chrome/bug582176_window.xul
+++ b/docshell/test/chrome/bug582176_window.xul
@@ -3,16 +3,22 @@
 
 <window id="303267Test"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         width="600"
         height="600"
         onload="nextTestAsync();"
         title="bug 582176 test">
 
+  <script type="text/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/specialpowersAPI.js"/>
+  <script type="text/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SpecialPowersObserverAPI.js"/>
+  <script type="text/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/ChromePowers.js"/>
   <script type="application/javascript" src= "chrome://mochikit/content/chrome-harness.js" />
   <script type="application/javascript" src="docshell_helpers.js" />
   <script type="application/javascript"><![CDATA[
   
     // Define the generator-iterator for the tests.
     var tests = testIterator();
 
     ////
--- a/docshell/test/chrome/bug89419_window.xul
+++ b/docshell/test/chrome/bug89419_window.xul
@@ -4,16 +4,22 @@
 <window id="89419Test"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         width="600"
         height="600"
         onload="setTimeout(nextTest,0);"
         title="bug 89419 test">
 
   <script type="application/javascript" src= "chrome://mochikit/content/chrome-harness.js" />
+  <script type="text/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/specialpowersAPI.js"/>
+  <script type="text/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SpecialPowersObserverAPI.js"/>
+  <script type="text/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/ChromePowers.js"/>
   <script type="application/javascript" src="docshell_helpers.js" />
   <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script>
 
   <script type="application/javascript"><![CDATA[
     // Define the generator-iterator for the tests.
     var tests = testIterator();
 
     ////
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -10173,16 +10173,21 @@ nsGlobalWindow::TimeoutSuspendCount()
 
 void
 nsGlobalWindow::SetHasOrientationEventListener()
 {
   mHasDeviceMotion = PR_TRUE;
   EnableDeviceMotionUpdates();
 }
 
+void
+nsGlobalWindow::RemoveOrientationEventListener() {
+  DisableDeviceMotionUpdates();
+}
+
 NS_IMETHODIMP
 nsGlobalWindow::GetURL(nsIDOMMozURLProperty** aURL)
 {
   FORWARD_TO_INNER(GetURL, (aURL), NS_ERROR_UNEXPECTED);
 
   if (!mURLProperty) {
     mURLProperty = new nsDOMMozURLProperty(this);
   }
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -379,16 +379,17 @@ public:
 
   virtual NS_HIDDEN_(nsIDOMWindow*) EnterModalState();
   virtual NS_HIDDEN_(void) LeaveModalState(nsIDOMWindow* aWindow);
 
   virtual NS_HIDDEN_(bool) CanClose();
   virtual NS_HIDDEN_(nsresult) ForceClose();
 
   virtual NS_HIDDEN_(void) SetHasOrientationEventListener();
+  virtual NS_HIDDEN_(void) RemoveOrientationEventListener();
   virtual NS_HIDDEN_(void) MaybeUpdateTouchState();
   virtual NS_HIDDEN_(void) UpdateTouchState();
   virtual NS_HIDDEN_(bool) DispatchCustomEvent(const char *aEventName);
 
   // nsIDOMStorageIndexedDB
   NS_DECL_NSIDOMSTORAGEINDEXEDDB
 
   // nsIInterfaceRequestor
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -1868,18 +1868,18 @@ nsJSContext::CallEventHandler(nsISupport
 
   if (!mScriptsEnabled) {
     return NS_OK;
   }
 
 #ifdef NS_FUNCTION_TIMER
   {
     JSObject *obj = static_cast<JSObject *>(aHandler);
-    if (obj->isFunctionProxy())
-      obj = obj->unwrap(NULL);
+    if (js::IsFunctionProxy(obj))
+      obj = js::UnwrapObject(obj);
     JSString *id = JS_GetFunctionId(static_cast<JSFunction *>(JS_GetPrivate(mContext, obj)));
     JSAutoByteString bytes;
     const char *name = !id ? "anonymous" : bytes.encode(mContext, id) ? bytes.ptr() : "<error>";
     NS_TIME_FUNCTION_FMT(1.0, "%s (line %d) (function: %s)", MOZ_FUNCTION_NAME, __LINE__, name);
   }
 #endif
 
   JSAutoRequest ar(mContext);
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -572,16 +572,21 @@ public:
   virtual nsresult DispatchSyncPopState() = 0;
 
   /**
    * Tell this window that there is an observer for orientation changes
    */
   virtual void SetHasOrientationEventListener() = 0;
 
   /**
+   * Tell this window that we remove an orientation listener
+   */
+  virtual void RemoveOrientationEventListener() = 0;
+
+  /**
    * Set a arguments for this window. This will be set on the window
    * right away (if there's an existing document) and it will also be
    * installed on the window when the next document is loaded. Each
    * language impl is responsible for converting to an array of args
    * as appropriate for that language.
    */
   virtual nsresult SetArguments(nsIArray *aArguments, nsIPrincipal *aOrigin) = 0;
 
--- a/dom/indexedDB/test/error_events_abort_transactions_iframe.html
+++ b/dom/indexedDB/test/error_events_abort_transactions_iframe.html
@@ -45,17 +45,17 @@
     }
 
     window.onerror = function(event) {
     };
 
     function testSteps() {
       netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
 
-      let uri = SpecialPowers.getDocumentURIObject(window.document);
+      let uri = window.parent.SpecialPowers.getDocumentURIObject(window.document);
       Components.classes["@mozilla.org/permissionmanager;1"]
                 .getService(Components.interfaces.nsIPermissionManager)
                 .add(uri, "indexedDB",
                      Components.interfaces.nsIPermissionManager.ALLOW_ACTION);
 
       let request = mozIndexedDB.open(window.location.pathname);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
--- a/dom/indexedDB/test/event_propagation_iframe.html
+++ b/dom/indexedDB/test/event_propagation_iframe.html
@@ -84,17 +84,17 @@
           finishTest();
         }
       }
     }
 
     function testSteps() {
       netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
 
-      let uri = SpecialPowers.getDocumentURIObject(window.document);
+      let uri = window.parent.SpecialPowers.getDocumentURIObject(window.document);
       Components.classes["@mozilla.org/permissionmanager;1"]
                 .getService(Components.interfaces.nsIPermissionManager)
                 .add(uri, "indexedDB",
                      Components.interfaces.nsIPermissionManager.ALLOW_ACTION);
 
       let request = mozIndexedDB.open(window.location.pathname);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
--- a/dom/tests/mochitest/ajax/scriptaculous/Makefile.in
+++ b/dom/tests/mochitest/ajax/scriptaculous/Makefile.in
@@ -41,17 +41,17 @@ srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir	= dom/tests/mochitest/ajax/scriptaculous
 
 include $(DEPTH)/config/autoconf.mk
 
 DIRS	= \
 	lib \
 	src \
-	test \
+	test/unit \
 	$(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES	= \
 	manifest.json \
 	test_Scriptaculous.html \
 	$(NULL)
deleted file mode 100644
--- a/dom/tests/mochitest/ajax/scriptaculous/test/Makefile.in
+++ /dev/null
@@ -1,50 +0,0 @@
-#
-# ***** 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.org code.
-#
-# The Initial Developer of the Original Code is
-# Mozilla Foundation.
-# Portions created by the Initial Developer are Copyright (C) 2007
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-#
-# Alternatively, the contents of this file may be used under the terms of
-# either of 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 *****
-
-DEPTH		= ../../../../../..
-topsrcdir	= @top_srcdir@
-srcdir		= @srcdir@
-VPATH		= @srcdir@
-relativesrcdir	= dom/tests/mochitest/ajax/scriptaculous/test
-
-include $(DEPTH)/config/autoconf.mk
-
-DIRS	= \
-	unit \
-	$(NULL)
-
-include $(topsrcdir)/config/rules.mk
\ No newline at end of file
--- a/embedding/android/GeckoApp.java
+++ b/embedding/android/GeckoApp.java
@@ -82,17 +82,17 @@ abstract public class GeckoApp
     public static GeckoApp mAppContext;
     public static boolean mFullscreen = false;
     public static File sGREDir = null;
     static Thread mLibLoadThread = null;
     public Handler mMainHandler;
     private IntentFilter mConnectivityFilter;
     private BroadcastReceiver mConnectivityReceiver;
 
-    enum LaunchState {PreLaunch, Launching, WaitButton,
+    enum LaunchState {PreLaunch, Launching, WaitForDebugger,
                       Launched, GeckoRunning, GeckoExiting};
     private static LaunchState sLaunchState = LaunchState.PreLaunch;
     private static boolean sTryCatchAttached = false;
 
 
     static boolean checkLaunchState(LaunchState checkState) {
         synchronized(sLaunchState) {
             return sLaunchState == checkState;
@@ -422,31 +422,29 @@ abstract public class GeckoApp
         if (checkLaunchState(LaunchState.GeckoExiting)) {
             // We're exiting and shouldn't try to do anything else just incase
             // we're hung for some reason we'll force the process to exit
             System.exit(0);
             return;
         }
         final String action = intent.getAction();
         if (ACTION_DEBUG.equals(action) &&
-            checkAndSetLaunchState(LaunchState.Launching, LaunchState.WaitButton)) {
-            final Button launchButton = new Button(this);
-            launchButton.setText("Launch"); // don't need to localize
-            launchButton.setOnClickListener(new Button.OnClickListener() {
-                public void onClick (View v) {
-                    // hide the button so we can't be launched again
-                    mainLayout.removeView(launchButton);
+            checkAndSetLaunchState(LaunchState.Launching, LaunchState.WaitForDebugger)) {
+
+            mMainHandler.postDelayed(new Runnable() {
+                public void run() {
+                    Log.i(LOG_FILE_NAME, "Launching from debug intent after 5s wait");
                     setLaunchState(LaunchState.Launching);
                     launch(null);
                 }
-            });
-            mainLayout.addView(launchButton, 300, 200);
+            }, 1000 * 5 /* 5 seconds */);
+            Log.i(LOG_FILE_NAME, "Intent : ACTION_DEBUG - waiting 5s before launching");
             return;
         }
-        if (checkLaunchState(LaunchState.WaitButton) || launch(intent))
+        if (checkLaunchState(LaunchState.WaitForDebugger) || launch(intent))
             return;
 
         if (Intent.ACTION_MAIN.equals(action)) {
             Log.i(LOG_FILE_NAME, "Intent : ACTION_MAIN");
             GeckoAppShell.sendEventToGecko(new GeckoEvent(""));
         }
         else if (Intent.ACTION_VIEW.equals(action)) {
             String uri = intent.getDataString();
@@ -603,26 +601,16 @@ abstract public class GeckoApp
         Enumeration<? extends ZipEntry> zipEntries = zip.entries();
         while (zipEntries.hasMoreElements()) {
             ZipEntry entry = zipEntries.nextElement();
             if (entry.getName().startsWith("extensions/") && entry.getName().endsWith(".xpi")) {
                 Log.i("GeckoAppJava", "installing extension : " + entry.getName());
                 unpackFile(zip, buf, entry, entry.getName());
             }
         }
-
-        // copy any hyphenation dictionaries file into a hyphenation/ directory
-        Enumeration<? extends ZipEntry> hyphenEntries = zip.entries();
-        while (hyphenEntries.hasMoreElements()) {
-            ZipEntry entry = hyphenEntries.nextElement();
-            if (entry.getName().startsWith("hyphenation/")) {
-                Log.i("GeckoAppJava", "installing hyphenation : " + entry.getName());
-                unpackFile(zip, buf, entry, entry.getName());
-            }
-        }
     }
 
     void removeFiles() throws IOException {
         BufferedReader reader = new BufferedReader(
             new FileReader(new File(sGREDir, "removed-files")));
         try {
             for (String removedFileName = reader.readLine(); 
                  removedFileName != null; removedFileName = reader.readLine()) {
--- a/embedding/android/GeckoInputConnection.java
+++ b/embedding/android/GeckoInputConnection.java
@@ -274,16 +274,21 @@ public class GeckoInputConnection
         return true;
     }
 
     @Override
     public ExtractedText getExtractedText(ExtractedTextRequest req, int flags) {
         if (req == null)
             return null;
 
+        // Bail out here if gecko isn't running, otherwise we deadlock
+        // below when waiting for the reply to IME_GET_SELECTION.
+        if (!GeckoApp.checkLaunchState(GeckoApp.LaunchState.GeckoRunning))
+            return null;
+
         //Log.d("GeckoAppJava", "IME: getExtractedText");
 
         ExtractedText extract = new ExtractedText();
         extract.flags = 0;
         extract.partialStartOffset = -1;
         extract.partialEndOffset = -1;
 
         GeckoAppShell.sendEventToGecko(
--- a/embedding/components/Makefile.in
+++ b/embedding/components/Makefile.in
@@ -40,19 +40,19 @@ topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 # You'd think we could skip building ui if XUL is disabled,
 # but we need to export interface headers from those directories.
 
-DIRS = windowwatcher appstartup find webbrowserpersist commandhandler
+DIRS = windowwatcher appstartup/src find webbrowserpersist commandhandler
 
 ifdef MOZ_XUL
 ifdef NS_PRINTING
-DIRS += printingui
+DIRS += printingui/src
 endif
 endif
 
 DIRS += build
 
 include $(topsrcdir)/config/rules.mk
deleted file mode 100644
--- a/embedding/components/appstartup/Makefile.in
+++ /dev/null
@@ -1,47 +0,0 @@
-# 
-# ***** 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.org code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications, Inc.
-# Portions created by the Initial Developer are Copyright (C) 2001
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-#
-# 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 *****
-
-DEPTH		= ../../..
-topsrcdir	= @top_srcdir@
-srcdir		= @srcdir@
-VPATH		= @srcdir@
-
-include $(DEPTH)/config/autoconf.mk
-
-DIRS = src
-
-include $(topsrcdir)/config/rules.mk
deleted file mode 100644
--- a/embedding/components/printingui/Makefile.in
+++ /dev/null
@@ -1,50 +0,0 @@
-#! gmake
-# 
-# ***** 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.org code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 2000
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-#   Stuart Parmenter <pavlov@netscape.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 *****
-
-DEPTH		= ../../..
-topsrcdir	= @top_srcdir@
-srcdir		= @srcdir@
-VPATH		= @srcdir@
-
-include $(DEPTH)/config/autoconf.mk
-
-DIRS = src
-
-include $(topsrcdir)/config/rules.mk
-
deleted file mode 100644
--- a/embedding/tests/Makefile.in
+++ /dev/null
@@ -1,42 +0,0 @@
-# 
-# ***** BEGIN LICENSE BLOCK *****
-# Version: Mozilla-sample-code 1.0
-#
-# Copyright (c) 2002 Netscape Communications Corporation and
-# other contributors
-#
-# Permission is hereby granted, free of charge, to any person obtaining a
-# copy of this Mozilla sample software and associated documentation files
-# (the "Software"), to deal in the Software without restriction, including
-# without limitation the rights to use, copy, modify, merge, publish,
-# distribute, sublicense, and/or sell copies of the Software, and to permit
-# persons to whom the Software is furnished to do so, subject to the
-# following conditions:
-#
-# The above copyright notice and this permission notice shall be included
-# in all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-# DEALINGS IN THE SOFTWARE.
-#
-# Contributor(s):
-#
-# ***** END LICENSE BLOCK *****
-
-DEPTH		= ../..
-topsrcdir	= @top_srcdir@
-srcdir		= @srcdir@
-VPATH		= @srcdir@
-
-include $(DEPTH)/config/autoconf.mk
-
-ifeq ($(OS_ARCH),WINNT)
-DIRS = winEmbed
-endif
-
-include $(topsrcdir)/config/rules.mk
--- a/extensions/spellcheck/Makefile.in
+++ b/extensions/spellcheck/Makefile.in
@@ -40,12 +40,12 @@ srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= spellchecker
 DIRS		= idl locales hunspell src
 
 ifdef ENABLE_TESTS
-DIRS		+= tests
+DIRS += tests/chrome
 endif
 
 include $(topsrcdir)/config/rules.mk
--- a/extensions/spellcheck/hunspell/src/mozHunspell.cpp
+++ b/extensions/spellcheck/hunspell/src/mozHunspell.cpp
@@ -123,16 +123,17 @@ mozHunspell::Init()
   if (!mDictionaries.Init())
     return NS_ERROR_OUT_OF_MEMORY;
 
   LoadDictionaryList();
 
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (obs) {
     obs->AddObserver(this, "profile-do-change", PR_TRUE);
+    obs->AddObserver(this, "profile-after-change", PR_TRUE);
   }
 
   mHunspellReporter = new NS_MEMORY_REPORTER_NAME(Hunspell);
   NS_RegisterMemoryReporter(mHunspellReporter);
 
   return NS_OK;
 }
 
@@ -588,17 +589,18 @@ NS_IMETHODIMP mozHunspell::Suggest(const
   NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(*aSuggestionCount, wlst);
   return rv;
 }
 
 NS_IMETHODIMP
 mozHunspell::Observe(nsISupports* aSubj, const char *aTopic,
                     const PRUnichar *aData)
 {
-  NS_ASSERTION(!strcmp(aTopic, "profile-do-change"),
+  NS_ASSERTION(!strcmp(aTopic, "profile-do-change")
+               || !strcmp(aTopic, "profile-after-change"),
                "Unexpected observer topic");
 
   LoadDictionaryList();
 
   return NS_OK;
 }
 
 /* void addDirectory(in nsIFile dir); */
deleted file mode 100644
--- a/extensions/spellcheck/tests/Makefile.in
+++ /dev/null
@@ -1,48 +0,0 @@
-#
-# ***** 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.org code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 1998
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-#
-# Alternatively, the contents of this file may be used under the terms of
-# either of 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 *****
-
-DEPTH		= ../../..
-topsrcdir	= @top_srcdir@
-srcdir		= @srcdir@
-VPATH		= @srcdir@
-relativesrcdir  = extensions/spellcheck/tests
-
-include $(DEPTH)/config/autoconf.mk
-
-DIRS		= chrome
-
-include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/gfx/layers/DirectedGraph.h
@@ -0,0 +1,148 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * ***** 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) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Matt Woodrow <mwoodrow@mozilla.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_DIRECTEDGRAPH_H
+#define GFX_DIRECTEDGRAPH_H
+
+#include "gfxTypes.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace layers {
+
+template <typename T>
+class DirectedGraph {
+public:
+
+  class Edge {
+    public:
+    Edge(T aFrom, T aTo) : mFrom(aFrom), mTo(aTo) {}
+
+    bool operator==(const Edge& aOther) const
+    {
+      return mFrom == aOther.mFrom && mTo == aOther.mTo;
+    }
+
+    T mFrom;
+    T mTo;
+  };
+
+  class RemoveEdgesToComparator 
+  {
+  public:
+    PRBool Equals(const Edge& a, T const& b) const { return a.mTo == b; }
+  };
+
+  /**
+   * Add a new edge to the graph.
+   */
+  void AddEdge(Edge aEdge)
+  {
+    NS_ASSERTION(!mEdges.Contains(aEdge), "Adding a duplicate edge!");
+    mEdges.AppendElement(aEdge);
+  }
+
+  void AddEdge(T aFrom, T aTo)
+  {
+    AddEdge(Edge(aFrom, aTo));
+  }
+
+  /**
+   * Get the list of edges.
+   */
+  const nsTArray<Edge>& GetEdgeList() const
+  {
+    return mEdges; 
+  }
+
+  /**
+   * Remove the given edge from the graph.
+   */
+  void RemoveEdge(Edge aEdge)
+  {
+    mEdges.RemoveElement(aEdge);
+  }
+
+  /**
+   * Remove all edges going into aNode.
+   */
+  void RemoveEdgesTo(T aNode)
+  {
+    RemoveEdgesToComparator c;
+    while (mEdges.RemoveElement(aNode, c)) {}
+  }
+  
+  /**
+   * Get the number of edges going into aNode.
+   */
+  unsigned int NumEdgesTo(T aNode)
+  {
+    unsigned int count = 0;
+    for (unsigned int i = 0; i < mEdges.Length(); i++) {
+      if (mEdges.ElementAt(i).mTo == aNode) {
+        count++;
+      }
+    }
+    return count;
+  }
+
+  /**
+   * Get the list of all edges going from aNode
+   */
+  void GetEdgesFrom(T aNode, nsTArray<Edge>& aResult)
+  {
+    for (unsigned int i = 0; i < mEdges.Length(); i++) {
+      if (mEdges.ElementAt(i).mFrom == aNode) {
+        aResult.AppendElement(mEdges.ElementAt(i));
+      }
+    }
+  }
+
+  /**
+   * Get the total number of edges.
+   */
+  unsigned int GetEdgeCount() { return mEdges.Length(); }
+
+private:
+
+  nsTArray<Edge> mEdges;
+};
+
+}
+}
+
+#endif // GFX_DIRECTEDGRAPH_H
new file mode 100644
--- /dev/null
+++ b/gfx/layers/LayerSorter.cpp
@@ -0,0 +1,258 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * ***** 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) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Matt Woodrow <mwoodrow@mozilla.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 "LayerSorter.h"
+#include "DirectedGraph.h"
+#include "limits.h"
+
+namespace mozilla {
+namespace layers {
+
+enum LayerSortOrder {
+  Undefined,
+  ABeforeB,
+  BBeforeA,
+};
+
+/**
+ * Recover the z component from a 2d transformed point by finding the intersection
+ * of a line through the point in the z direction and the transformed plane.
+ *
+ * We want to solve:
+ *
+ * point = normal . (p0 - l0) / normal . l
+ */
+static gfxFloat RecoverZDepth(const gfx3DMatrix& aTransform, const gfxPoint& aPoint)
+{
+    const gfxPoint3D l(0, 0, 1);
+    gfxPoint3D l0 = gfxPoint3D(aPoint.x, aPoint.y, 0);
+    gfxPoint3D p0 = aTransform.Transform3D(gfxPoint3D(0, 0, 0));
+    gfxPoint3D normal = aTransform.GetNormalVector();
+
+    gfxFloat n = normal.DotProduct(p0 - l0); 
+    gfxFloat d = normal.DotProduct(l);
+
+    if (!d) {
+        return 0;
+    }
+
+    return n/d;
+}
+
+/**
+ * Determine if this transform layer should be drawn before another when they 
+ * are both preserve-3d children.
+ *
+ * We want to find the relative z depths of the 2 layers at points where they
+ * intersect when projected onto the 2d screen plane.
+ *
+ * If the ordering is consistent at all intersection points, then we have
+ * a definitive order, otherwise the 2 layers must actually intersect in 3d
+ * space, and we just order these arbitrarily.
+ */
+static LayerSortOrder CompareDepth(Layer* aOne, Layer* aTwo) {
+  gfxRect ourRect = aOne->GetEffectiveVisibleRegion().GetBounds();
+  gfxRect otherRect = aTwo->GetEffectiveVisibleRegion().GetBounds();
+
+  gfx3DMatrix ourTransform = aOne->GetTransform();
+  gfx3DMatrix otherTransform = aTwo->GetTransform();
+
+  // Transform both rectangles and project into 2d space.
+  gfxQuad ourTransformedRect = ourTransform.TransformRect(ourRect);
+  gfxQuad otherTransformedRect = otherTransform.TransformRect(otherRect);
+
+  // Make a list of all points that are within the other rect.
+  nsTArray<gfxPoint> points;
+  for (PRUint32 i=0; i<4; i++) {
+    if (ourTransformedRect.Contains(otherTransformedRect.mPoints[i])) {
+      points.AppendElement(otherTransformedRect.mPoints[i]);
+    }
+    if (otherTransformedRect.Contains(ourTransformedRect.mPoints[i])) {
+      points.AppendElement(ourTransformedRect.mPoints[i]);
+    }
+  }
+
+  // No intersections, no defined order between these layers.
+  if (points.IsEmpty()) {
+    return Undefined;
+  }
+
+  // Find the relative Z depths of each intersection point and check that the layers are in the same order.
+  bool drawBefore = false;
+  for (PRUint32 i = 0; i < points.Length(); i++) {
+    bool temp = RecoverZDepth(ourTransform, points.ElementAt(i)) <= RecoverZDepth(otherTransform, points.ElementAt(i));
+    if (i == 0) {
+      drawBefore = temp; 
+    } else if (drawBefore != temp) {
+      // Mixed ordering means an intersection in 3d space that we can't resolve without plane splitting
+      // or depth buffering. Store this as having no defined order for now.
+      return Undefined;
+    }
+  }
+  if (drawBefore) {
+    return ABeforeB;
+  }
+  return BBeforeA;
+}
+
+#ifdef DEBUG
+static bool gDumpLayerSortList = getenv("MOZ_DUMP_LAYER_SORT_LIST") != 0;
+
+static void DumpLayerList(nsTArray<Layer*>& aLayers)
+{
+  for (PRUint32 i = 0; i < aLayers.Length(); i++) {
+    fprintf(stderr, "%p, ", aLayers.ElementAt(i));
+  }
+  fprintf(stderr, "\n");
+}
+
+static void DumpEdgeList(DirectedGraph<Layer*>& aGraph)
+{
+  nsTArray<DirectedGraph<Layer*>::Edge> edges = aGraph.GetEdgeList();
+  
+  for (PRUint32 i = 0; i < edges.Length(); i++) {
+    fprintf(stderr, "From: %p, To: %p\n", edges.ElementAt(i).mFrom, edges.ElementAt(i).mTo);
+  }
+}
+#endif
+
+// The maximum number of layers that we will attempt to sort. Anything
+// greater than this will be left unsorted. We should consider enabling
+// depth buffering for the scene in this case.
+#define MAX_SORTABLE_LAYERS 100
+
+void SortLayersBy3DZOrder(nsTArray<Layer*>& aLayers)
+{
+  PRUint32 nodeCount = aLayers.Length();
+  if (nodeCount > MAX_SORTABLE_LAYERS) {
+    return;
+  }
+  DirectedGraph<Layer*> graph;
+
+#ifdef DEBUG
+  if (gDumpLayerSortList) {
+    fprintf(stderr, " --- Layers before sorting: --- \n");
+    DumpLayerList(aLayers);
+  }
+#endif
+
+  // Iterate layers and determine edges.
+  for (PRUint32 i = 0; i < nodeCount; i++) {
+    for (PRUint32 j = i + 1; j < nodeCount; j++) {
+      Layer* a = aLayers.ElementAt(i);
+      Layer* b = aLayers.ElementAt(j);
+      LayerSortOrder order = CompareDepth(a, b);
+      if (order == ABeforeB) {
+        graph.AddEdge(a, b);
+      } else if (order == BBeforeA) {
+        graph.AddEdge(b, a);
+      }
+    }
+  }
+
+#ifdef DEBUG
+  if (gDumpLayerSortList) {
+    fprintf(stderr, " --- Edge List: --- \n");
+    DumpEdgeList(graph);
+  }
+#endif
+
+  // Build a new array using the graph.
+  nsTArray<Layer*> noIncoming;
+  nsTArray<Layer*> sortedList;
+
+  // Make a list of all layers with no incoming edges.
+  noIncoming.AppendElements(aLayers);
+  const nsTArray<DirectedGraph<Layer*>::Edge>& edges = graph.GetEdgeList();
+  for (PRUint32 i = 0; i < edges.Length(); i++) {
+    noIncoming.RemoveElement(edges.ElementAt(i).mTo);
+  }
+
+  // Move each item without incoming edges into the sorted list,
+  // and remove edges from it.
+  while (!noIncoming.IsEmpty()) {
+    PRUint32 last = noIncoming.Length() - 1;
+
+    Layer* layer = noIncoming.ElementAt(last);
+
+    noIncoming.RemoveElementAt(last);
+    sortedList.AppendElement(layer);
+
+    nsTArray<DirectedGraph<Layer*>::Edge> outgoing;
+    graph.GetEdgesFrom(layer, outgoing);
+    for (PRUint32 i = 0; i < outgoing.Length(); i++) {
+      DirectedGraph<Layer*>::Edge edge = outgoing.ElementAt(i);
+      graph.RemoveEdge(edge);
+      if (!graph.NumEdgesTo(edge.mTo)) {
+        // If this node also has no edges now, add it to the list
+        noIncoming.AppendElement(edge.mTo);
+      }
+    }
+
+    // If there are no nodes without incoming edges, but there
+    // are still edges, then we have a cycle.
+    if (noIncoming.IsEmpty() && graph.GetEdgeCount()) {
+      // Find the node with the least incoming edges.
+      PRUint32 minEdges = UINT_MAX;
+      Layer* minNode = nsnull;
+      for (PRUint32 i = 0; i < aLayers.Length(); i++) {
+        PRUint32 edgeCount = graph.NumEdgesTo(aLayers.ElementAt(i));
+        if (edgeCount && edgeCount < minEdges) {
+          minEdges = edgeCount;
+          minNode = aLayers.ElementAt(i);
+        }
+      }
+
+      // Remove all of them!
+      graph.RemoveEdgesTo(minNode);
+      noIncoming.AppendElement(minNode);
+    }
+  }
+  NS_ASSERTION(!graph.GetEdgeCount(), "Cycles detected!");
+#ifdef DEBUG
+  if (gDumpLayerSortList) {
+    fprintf(stderr, " --- Layers after sorting: --- \n");
+    DumpLayerList(sortedList);
+  }
+#endif
+
+  aLayers.Clear();
+  aLayers.AppendElements(sortedList);
+}
+
+}
+}
new file mode 100644
--- /dev/null
+++ b/gfx/layers/LayerSorter.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * ***** 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) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Matt Woodrow <mwoodrow@mozilla.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_LAYERSORTER_H
+#define GFX_LAYERSORTER_H
+
+#include "Layers.h"
+
+namespace mozilla {
+namespace layers {
+
+void SortLayersBy3DZOrder(nsTArray<Layer*>& aLayers);
+
+}
+}
+#endif /* GFX_LAYERSORTER_H */
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -43,16 +43,17 @@
 
 #include "ImageLayers.h"
 #include "Layers.h"
 #include "gfxPlatform.h"
 #include "ReadbackLayer.h"
 #include "gfxUtils.h"
 #include "nsPrintfCString.h"
 #include "mozilla/Util.h"
+#include "LayerSorter.h"
 
 using namespace mozilla::layers;
 using namespace mozilla::gfx;
 
 typedef FrameMetrics::ViewID ViewID;
 const ViewID FrameMetrics::NULL_SCROLL_ID = 0;
 const ViewID FrameMetrics::ROOT_SCROLL_ID = 1;
 const ViewID FrameMetrics::START_SCROLL_ID = 2;
@@ -411,32 +412,54 @@ ContainerLayer::HasMultipleChildren()
     if (count > 1)
       return PR_TRUE;
   }
 
   return PR_FALSE;
 }
 
 void
+ContainerLayer::SortChildrenBy3DZOrder(nsTArray<Layer*>& aArray)
+{
+  nsAutoTArray<Layer*, 10> toSort;
+
+  for (Layer* l = GetFirstChild(); l; l = l->GetNextSibling()) {
+    ContainerLayer* container = l->AsContainerLayer();
+    if (container && container->GetContentFlags() & CONTENT_PRESERVE_3D) {
+      toSort.AppendElement(l);
+    } else {
+      if (toSort.Length() > 0) {
+        SortLayersBy3DZOrder(toSort);
+        aArray.MoveElementsFrom(toSort);
+      }
+      aArray.AppendElement(l);
+    }
+  }
+  if (toSort.Length() > 0) {
+    SortLayersBy3DZOrder(toSort);
+    aArray.MoveElementsFrom(toSort);
+  }
+}
+
+void
 ContainerLayer::DefaultComputeEffectiveTransforms(const gfx3DMatrix& aTransformToSurface)
 {
   gfxMatrix residual;
   gfx3DMatrix idealTransform = GetLocalTransform()*aTransformToSurface;
+  idealTransform.ProjectTo2D();
   mEffectiveTransform = SnapTransform(idealTransform, gfxRect(0, 0, 0, 0), &residual);
 
   bool useIntermediateSurface;
   float opacity = GetEffectiveOpacity();
   if (opacity != 1.0f && HasMultipleChildren()) {
     useIntermediateSurface = PR_TRUE;
   } else {
     useIntermediateSurface = PR_FALSE;
     gfxMatrix contTransform;
-    if (!mEffectiveTransform.Is2D(&contTransform)) {
-     useIntermediateSurface = PR_TRUE;   
-    } else if (
+    if (!mEffectiveTransform.Is2D(&contTransform) ||
 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
         !contTransform.PreservesAxisAlignedRectangles()) {
 #else
         contTransform.HasNonIntegerTranslation()) {
 #endif
       for (Layer* child = GetFirstChild(); child; child = child->GetNextSibling()) {
         const nsIntRect *clipRect = child->GetEffectiveClipRect();
         /* We can't (easily) forward our transform to children with a non-empty clip
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -43,16 +43,17 @@
 #include "nsRegion.h"
 #include "nsPoint.h"
 #include "nsRect.h"
 #include "nsISupportsImpl.h"
 #include "nsAutoPtr.h"
 #include "gfx3DMatrix.h"
 #include "gfxColor.h"
 #include "gfxPattern.h"
+#include "nsTArray.h"
 
 #include "mozilla/gfx/2D.h"
 
 #if defined(DEBUG) || defined(PR_LOGGING)
 #  include <stdio.h>            // FILE
 #  include "prlog.h"
 #  define MOZ_LAYERS_HAVE_LOG
 #  define MOZ_LAYERS_LOG(_args)                             \
@@ -572,17 +573,23 @@ public:
     CONTENT_OPAQUE = 0x01,
     /**
      * If this is set, the caller is notifying that the contents of this layer
      * require per-component alpha for optimal fidelity. However, there is no
      * guarantee that component alpha will be supported for this layer at
      * paint time.
      * This should never be set at the same time as CONTENT_OPAQUE.
      */
-    CONTENT_COMPONENT_ALPHA = 0x02
+    CONTENT_COMPONENT_ALPHA = 0x02,
+
+    /**
+     * If this is set then this layer is part of a preserve-3d group, and should
+     * be sorted with sibling layers that are also part of the same group.
+     */
+    CONTENT_PRESERVE_3D = 0x04
   };
   /**
    * CONSTRUCTION PHASE ONLY
    * This lets layout make some promises about what will be drawn into the
    * visible region of the ThebesLayer. This enables internal quality
    * and performance optimizations.
    */
   void SetContentFlags(PRUint32 aFlags)
@@ -1090,16 +1097,18 @@ public:
   void SetFrameMetrics(const FrameMetrics& aFrameMetrics)
   {
     mFrameMetrics = aFrameMetrics;
     Mutated();
   }
 
   virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs);
 
+  void SortChildrenBy3DZOrder(nsTArray<Layer*>& aArray);
+
   // These getters can be used anytime.
 
   virtual ContainerLayer* AsContainerLayer() { return this; }
 
   virtual Layer* GetFirstChild() { return mFirstChild; }
   virtual Layer* GetLastChild() { return mLastChild; }
   const FrameMetrics& GetFrameMetrics() { return mFrameMetrics; }
 
--- a/gfx/layers/Makefile.in
+++ b/gfx/layers/Makefile.in
@@ -61,30 +61,32 @@ endif
 
 EXPORTS = \
         BasicLayers.h \
         ImageLayers.h \
         Layers.h \
         LayerManagerOGL.h \
         LayerManagerOGLProgram.h \
         ReadbackLayer.h \
+        LayerSorter.h \
         $(NULL)
 
 CPPSRCS = \
         BasicImages.cpp \
         BasicLayers.cpp \
         Layers.cpp \
         ReadbackProcessor.cpp \
         ThebesLayerBuffer.cpp \
         CanvasLayerOGL.cpp \
         ColorLayerOGL.cpp \
         ContainerLayerOGL.cpp \
         ImageLayerOGL.cpp \
         LayerManagerOGL.cpp \
         ThebesLayerOGL.cpp \
+        LayerSorter.cpp \
         $(NULL)
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)
 CMMSRCS = \
         MacIOSurfaceImageOGL.mm \
         $(NULL)
 endif
 
--- a/gfx/layers/basic/BasicLayers.cpp
+++ b/gfx/layers/basic/BasicLayers.cpp
@@ -527,16 +527,19 @@ public:
         mResidualTranslation = gfxPoint(0,0);
         mValidRegion.SetEmpty();
       }
       return;
     }
     ThebesLayer::ComputeEffectiveTransforms(aTransformToSurface);
   }
 
+  // Sync front/back buffers content
+  virtual void SyncFrontBufferToBackBuffer() {}
+
 protected:
   BasicLayerManager* BasicManager()
   {
     return static_cast<BasicLayerManager*>(mManager);
   }
 
   virtual void
   PaintBuffer(gfxContext* aContext,
@@ -651,16 +654,17 @@ BasicThebesLayer::PaintThebes(gfxContext
   NS_ASSERTION(BasicManager()->InDrawing(),
                "Can only draw in drawing phase");
   nsRefPtr<gfxASurface> targetSurface = aContext->CurrentSurface();
 
   nsTArray<ReadbackProcessor::Update> readbackUpdates;
   if (aReadback && UsedForReadback()) {
     aReadback->GetThebesLayerUpdates(this, &readbackUpdates);
   }
+  SyncFrontBufferToBackBuffer();
 
   bool canUseOpaqueSurface = CanUseOpaqueSurface();
   Buffer::ContentType contentType =
     canUseOpaqueSurface ? gfxASurface::CONTENT_COLOR :
                           gfxASurface::CONTENT_COLOR_ALPHA;
   float opacity = GetEffectiveOpacity();
 
   if (!BasicManager()->IsRetained() ||
@@ -1882,23 +1886,26 @@ BasicLayerManager::PaintLayer(gfxContext
 #endif
     if (aLayer->AsThebesLayer()) {
       data->PaintThebes(groupTarget, aCallback, aCallbackData, aReadback);
     } else {
       data->Paint(groupTarget);
     }
   } else {
     ReadbackProcessor readback;
+    ContainerLayer* container = static_cast<ContainerLayer*>(aLayer);
     if (IsRetained()) {
-      ContainerLayer* container = static_cast<ContainerLayer*>(aLayer);
       readback.BuildUpdates(container);
     }
-
-    for (; child; child = child->GetNextSibling()) {
-      PaintLayer(groupTarget, child, aCallback, aCallbackData, &readback);
+  
+    nsAutoTArray<Layer*, 12> children;
+    container->SortChildrenBy3DZOrder(children);
+
+    for (PRUint32 i = 0; i < children.Length(); i++) {
+      PaintLayer(groupTarget, children.ElementAt(i), aCallback, aCallbackData, &readback);
       if (mTransactionIncomplete)
         break;
     }
   }
 
   if (needsGroup) {
     bool blitComplete = false;
     if (is2D) {
@@ -2149,48 +2156,50 @@ class BasicShadowableThebesLayer : publi
                                    public BasicShadowableLayer
 {
   typedef BasicThebesLayer Base;
 
 public:
   BasicShadowableThebesLayer(BasicShadowLayerManager* aManager)
     : BasicThebesLayer(aManager)
     , mIsNewBuffer(false)
+    , mFrontAndBackBufferDiffer(false)
   {
     MOZ_COUNT_CTOR(BasicShadowableThebesLayer);
   }
   virtual ~BasicShadowableThebesLayer()
   {
-    if (IsSurfaceDescriptorValid(mBackBuffer))
-      BasicManager()->ShadowLayerForwarder::DestroySharedSurface(&mBackBuffer);
+    DestroyBackBuffer();
     MOZ_COUNT_DTOR(BasicShadowableThebesLayer);
   }
 
   virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs)
   {
     aAttrs = ThebesLayerAttributes(GetValidRegion());
   }
 
   virtual Layer* AsLayer() { return this; }
   virtual ShadowableLayer* AsShadowableLayer() { return this; }
   virtual bool MustRetainContent() { return HasShadow(); }
 
-  void SetBackBufferAndAttrs(const ThebesBuffer& aBuffer,
+  void SetBackBufferAndAttrs(const OptionalThebesBuffer& aBuffer,
                              const nsIntRegion& aValidRegion,
                              const OptionalThebesBuffer& aReadOnlyFrontBuffer,
                              const nsIntRegion& aFrontUpdatedRegion);
 
   virtual void Disconnect()
   {
     mBackBuffer = SurfaceDescriptor();
     BasicShadowableLayer::Disconnect();
   }
 
   virtual BasicShadowableThebesLayer* AsThebes() { return this; }
 
+  virtual void SyncFrontBufferToBackBuffer();
+
 private:
   BasicShadowLayerManager* BasicManager()
   {
     return static_cast<BasicShadowLayerManager*>(mManager);
   }
 
   NS_OVERRIDE virtual void
   PaintBuffer(gfxContext* aContext,
@@ -2199,56 +2208,99 @@ private:
               const nsIntRegion& aRegionToInvalidate,
               bool aDidSelfCopy,
               LayerManager::DrawThebesLayerCallback aCallback,
               void* aCallbackData);
 
   NS_OVERRIDE virtual already_AddRefed<gfxASurface>
   CreateBuffer(Buffer::ContentType aType, const nsIntSize& aSize);
 
+  void DestroyBackBuffer()
+  {
+    if (IsSurfaceDescriptorValid(mBackBuffer)) {
+      BasicManager()->ShadowLayerForwarder::DestroySharedSurface(&mBackBuffer);
+    }
+  }
+
   // This describes the gfxASurface we hand to mBuffer.  We keep a
   // copy of the descriptor here so that we can call
   // DestroySharedSurface() on the descriptor.
   SurfaceDescriptor mBackBuffer;
+  nsIntRect mBackBufferRect;
+  nsIntPoint mBackBufferRectRotation;
 
   bool mIsNewBuffer;
+  OptionalThebesBuffer mROFrontBuffer;
+  nsIntRegion mFrontUpdatedRegion;
+  nsIntRegion mFrontValidRegion;
+  PRPackedBool mFrontAndBackBufferDiffer;
 };
 
 void
-BasicShadowableThebesLayer::SetBackBufferAndAttrs(const ThebesBuffer& aBuffer,
+BasicShadowableThebesLayer::SetBackBufferAndAttrs(const OptionalThebesBuffer& aBuffer,
                                                   const nsIntRegion& aValidRegion,
                                                   const OptionalThebesBuffer& aReadOnlyFrontBuffer,
                                                   const nsIntRegion& aFrontUpdatedRegion)
 {
-  mBackBuffer = aBuffer.buffer();
-  nsRefPtr<gfxASurface> backBuffer = BasicManager()->OpenDescriptor(mBackBuffer);
-
-  if (OptionalThebesBuffer::Tnull_t == aReadOnlyFrontBuffer.type()) {
+  if (OptionalThebesBuffer::Tnull_t == aBuffer.type()) {
+    mBackBuffer = SurfaceDescriptor();
+  } else {
+    mBackBuffer = aBuffer.get_ThebesBuffer().buffer();
+    mBackBufferRect = aBuffer.get_ThebesBuffer().rect();
+    mBackBufferRectRotation = aBuffer.get_ThebesBuffer().rotation();
+  }
+  mFrontAndBackBufferDiffer = true;
+  mROFrontBuffer = aReadOnlyFrontBuffer;
+  mFrontUpdatedRegion = aFrontUpdatedRegion;
+  mFrontValidRegion = aValidRegion;
+}
+
+void
+BasicShadowableThebesLayer::SyncFrontBufferToBackBuffer()
+{
+  if (!mFrontAndBackBufferDiffer) {
+    return;
+  }
+
+  nsRefPtr<gfxASurface> backBuffer;
+  if (!IsSurfaceDescriptorValid(mBackBuffer)) {
+    NS_ABORT_IF_FALSE(mROFrontBuffer.type() == OptionalThebesBuffer::TThebesBuffer,
+                      "should have a front RO buffer by now");
+    const ThebesBuffer roFront = mROFrontBuffer.get_ThebesBuffer();
+    nsRefPtr<gfxASurface> roFrontBuffer = BasicManager()->OpenDescriptor(roFront.buffer());
+    backBuffer = CreateBuffer(roFrontBuffer->GetContentType(), roFrontBuffer->GetSize());
+  } else {
+    backBuffer = BasicManager()->OpenDescriptor(mBackBuffer);
+  }
+  mFrontAndBackBufferDiffer = false;
+
+  if (OptionalThebesBuffer::Tnull_t == mROFrontBuffer.type()) {
     // We didn't get back a read-only ref to our old back buffer (the
     // parent's new front buffer).  If the parent is pushing updates
     // to a texture it owns, then we probably got back the same buffer
     // we pushed in the update and all is well.  If not, ...
-    mValidRegion = aValidRegion;
-    mBuffer.SetBackingBuffer(backBuffer, aBuffer.rect(), aBuffer.rotation());
+    mValidRegion = mFrontValidRegion;
+    mBuffer.SetBackingBuffer(backBuffer, mBackBufferRect, mBackBufferRectRotation);
     return;
   }
 
   MOZ_LAYERS_LOG(("BasicShadowableThebes(%p): reading back <x=%d,y=%d,w=%d,h=%d>",
                   this,
-                  aFrontUpdatedRegion.GetBounds().x,
-                  aFrontUpdatedRegion.GetBounds().y,
-                  aFrontUpdatedRegion.GetBounds().width,
-                  aFrontUpdatedRegion.GetBounds().height));
-
-  const ThebesBuffer roFront = aReadOnlyFrontBuffer.get_ThebesBuffer();
+                  mFrontUpdatedRegion.GetBounds().x,
+                  mFrontUpdatedRegion.GetBounds().y,
+                  mFrontUpdatedRegion.GetBounds().width,
+                  mFrontUpdatedRegion.GetBounds().height));
+
+  const ThebesBuffer roFront = mROFrontBuffer.get_ThebesBuffer();
   nsRefPtr<gfxASurface> roFrontBuffer = BasicManager()->OpenDescriptor(roFront.buffer());
   mBuffer.SetBackingBufferAndUpdateFrom(
     backBuffer,
     roFrontBuffer, roFront.rect(), roFront.rotation(),
-    aFrontUpdatedRegion);
+    mFrontUpdatedRegion);
+  mIsNewBuffer = false;
   // Now the new back buffer has the same (interesting) pixels as the
   // new front buffer, and mValidRegion et al. are correct wrt the new
   // back buffer (i.e. as they were for the old back buffer)
 }
 
 void
 BasicShadowableThebesLayer::PaintBuffer(gfxContext* aContext,
                                         const nsIntRegion& aRegionToDraw,
@@ -2300,46 +2352,31 @@ BasicShadowableThebesLayer::CreateBuffer
     return BasicThebesLayer::CreateBuffer(aType, aSize);
   }
 
   MOZ_LAYERS_LOG(("BasicShadowableThebes(%p): creating %d x %d buffer(x2)",
                   this,
                   aSize.width, aSize.height));
 
   if (IsSurfaceDescriptorValid(mBackBuffer)) {
-    BasicManager()->DestroyedThebesBuffer(BasicManager()->Hold(this),
-                                          mBackBuffer);
     mBackBuffer = SurfaceDescriptor();
   }
 
   // XXX error handling
-  SurfaceDescriptor tmpFront;
-  if (BasicManager()->ShouldDoubleBuffer()) {
-    if (!BasicManager()->AllocDoubleBuffer(gfxIntSize(aSize.width, aSize.height),
-                                           aType,
-                                           &tmpFront,
-                                           &mBackBuffer)) {
+  if (!BasicManager()->AllocBuffer(gfxIntSize(aSize.width, aSize.height),
+                                   aType,
+                                   &mBackBuffer)) {
       NS_RUNTIMEABORT("creating ThebesLayer 'back buffer' failed!");
-    }
-  } else {
-    if (!BasicManager()->AllocBuffer(gfxIntSize(aSize.width, aSize.height),
-                                     aType,
-                                     &mBackBuffer)) {
-      NS_RUNTIMEABORT("creating ThebesLayer 'back buffer' failed!");
-    }
   }
 
   NS_ABORT_IF_FALSE(!mIsNewBuffer,
                     "Bad! Did we create a buffer twice without painting?");
+
   mIsNewBuffer = true;
 
-  BasicManager()->CreatedThebesBuffer(BasicManager()->Hold(this),
-                                      nsIntRegion(),
-                                      nsIntRect(),
-                                      tmpFront);
   return BasicManager()->OpenDescriptor(mBackBuffer);
 }
 
 
 class BasicShadowableImageLayer : public BasicImageLayer,
                                   public BasicShadowableLayer
 {
 public:
@@ -2676,44 +2713,42 @@ public:
   virtual ~BasicShadowThebesLayer()
   {
     // If Disconnect() wasn't called on us, then we assume that the
     // remote side shut down and IPC is disconnected, so we let IPDL
     // clean up our front surface Shmem.
     MOZ_COUNT_DTOR(BasicShadowThebesLayer);
   }
 
-  virtual void SetFrontBuffer(const OptionalThebesBuffer& aNewFront,
-                              const nsIntRegion& aValidRegion);
-
   virtual void SetValidRegion(const nsIntRegion& aRegion)
   {
     mOldValidRegion = mValidRegion;
     ShadowThebesLayer::SetValidRegion(aRegion);
   }
 
   virtual void Disconnect()
   {
     DestroyFrontBuffer();
     ShadowThebesLayer::Disconnect();
   }
 
   virtual void
   Swap(const ThebesBuffer& aNewFront, const nsIntRegion& aUpdatedRegion,
-       ThebesBuffer* aNewBack, nsIntRegion* aNewBackValidRegion,
+       OptionalThebesBuffer* aNewBack, nsIntRegion* aNewBackValidRegion,
        OptionalThebesBuffer* aReadOnlyFront, nsIntRegion* aFrontUpdatedRegion);
 
   virtual void DestroyFrontBuffer()
   {
     mFrontBuffer.Clear();
     mValidRegion.SetEmpty();
     mOldValidRegion.SetEmpty();
 
     if (IsSurfaceDescriptorValid(mFrontBufferDescriptor)) {
       mAllocator->DestroySharedSurface(&mFrontBufferDescriptor);
+      mFrontBufferDescriptor = SurfaceDescriptor();
     }
   }
 
   virtual void PaintThebes(gfxContext* aContext,
                            LayerManager::DrawThebesLayerCallback aCallback,
                            void* aCallbackData,
                            ReadbackProcessor* aReadback);
 
@@ -2729,57 +2764,55 @@ private:
   // When we receive an update from our remote partner, we stow away
   // our previous parameters that described our previous front buffer.
   // Then when we Swap() back/front buffers, we can return these
   // parameters to our partner (adjusted as needed).
   nsIntRegion mOldValidRegion;
 };
 
 void
-BasicShadowThebesLayer::SetFrontBuffer(const OptionalThebesBuffer& aNewFront,
-                                       const nsIntRegion& aValidRegion)
-{
-  mValidRegion = mOldValidRegion = aValidRegion;
-
-  NS_ABORT_IF_FALSE(OptionalThebesBuffer::Tnull_t != aNewFront.type(),
-                    "aNewFront must be valid here!");
-
-  const ThebesBuffer newFront = aNewFront.get_ThebesBuffer();
-  nsRefPtr<gfxASurface> newFrontBuffer =
-    BasicManager()->OpenDescriptor(newFront.buffer());
-
-  nsRefPtr<gfxASurface> unused;
-  nsIntRect unusedRect;
-  nsIntPoint unusedRotation;
-  mFrontBuffer.Swap(newFrontBuffer, newFront.rect(), newFront.rotation(),
-                    getter_AddRefs(unused), &unusedRect, &unusedRotation);
-  mFrontBufferDescriptor = newFront.buffer();
-}
-
-void
 BasicShadowThebesLayer::Swap(const ThebesBuffer& aNewFront,
                              const nsIntRegion& aUpdatedRegion,
-                             ThebesBuffer* aNewBack,
+                             OptionalThebesBuffer* aNewBack,
                              nsIntRegion* aNewBackValidRegion,
                              OptionalThebesBuffer* aReadOnlyFront,
                              nsIntRegion* aFrontUpdatedRegion)
 {
+  nsRefPtr<gfxASurface> newFrontBuffer =
+    BasicManager()->OpenDescriptor(aNewFront.buffer());
+
+  if (IsSurfaceDescriptorValid(mFrontBufferDescriptor)) {
+    nsRefPtr<gfxASurface> currentFront = BasicManager()->OpenDescriptor(mFrontBufferDescriptor);
+    if (currentFront->GetSize() != newFrontBuffer->GetSize()) {
+      // Current front buffer is obsolete
+      DestroyFrontBuffer();
+    }
+  }
   // This code relies on Swap() arriving *after* attribute mutations.
-  aNewBack->buffer() = mFrontBufferDescriptor;
+  if (IsSurfaceDescriptorValid(mFrontBufferDescriptor)) {
+    *aNewBack = ThebesBuffer();
+    aNewBack->get_ThebesBuffer().buffer() = mFrontBufferDescriptor;
+  } else {
+    *aNewBack = null_t();
+  }
   // We have to invalidate the pixels painted into the new buffer.
   // They might overlap with our old pixels.
   aNewBackValidRegion->Sub(mOldValidRegion, aUpdatedRegion);
 
-  nsRefPtr<gfxASurface> newFrontBuffer =
-    BasicManager()->OpenDescriptor(aNewFront.buffer());
-
   nsRefPtr<gfxASurface> unused;
+  nsIntRect backRect;
+  nsIntPoint backRotation;
   mFrontBuffer.Swap(
     newFrontBuffer, aNewFront.rect(), aNewFront.rotation(),
-    getter_AddRefs(unused), &aNewBack->rect(), &aNewBack->rotation());
+    getter_AddRefs(unused), &backRect, &backRotation);
+
+  if (aNewBack->type() != OptionalThebesBuffer::Tnull_t) {
+    aNewBack->get_ThebesBuffer().rect() = backRect;
+    aNewBack->get_ThebesBuffer().rotation() = backRotation;
+  }
 
   mFrontBufferDescriptor = aNewFront.buffer();
 
   *aReadOnlyFront = aNewFront;
   *aFrontUpdatedRegion = aUpdatedRegion;
 }
 
 void
--- a/gfx/layers/d3d10/ContainerLayerD3D10.cpp
+++ b/gfx/layers/d3d10/ContainerLayerD3D10.cpp
@@ -260,22 +260,24 @@ ContainerLayerD3D10::RenderLayer()
   device()->RSGetScissorRects(&numRects, &oldD3D10Scissor);
   // Convert scissor to an nsIntRect. D3D10_RECT's are exclusive
   // on the bottom and right values.
   nsIntRect oldScissor(oldD3D10Scissor.left,
                        oldD3D10Scissor.top,
                        oldD3D10Scissor.right - oldD3D10Scissor.left,
                        oldD3D10Scissor.bottom - oldD3D10Scissor.top);
 
+  nsAutoTArray<Layer*, 12> children;
+  SortChildrenBy3DZOrder(children);
+
   /*
    * Render this container's contents.
    */
-  for (LayerD3D10* layerToRender = GetFirstChildD3D10();
-       layerToRender != nsnull;
-       layerToRender = GetNextSiblingD3D10(layerToRender)) {
+  for (PRUint32 i = 0; i < children.Length(); i++) {
+    LayerD3D10* layerToRender = static_cast<LayerD3D10*>(children.ElementAt(i)->ImplData());
 
     if (layerToRender->GetLayer()->GetEffectiveVisibleRegion().IsEmpty()) {
       continue;
     }
     
     nsIntRect scissorRect =
         layerToRender->GetLayer()->CalculateScissorRect(oldScissor, nsnull);
     if (scissorRect.IsEmpty()) {
--- a/gfx/layers/d3d10/LayerManagerD3D10.cpp
+++ b/gfx/layers/d3d10/LayerManagerD3D10.cpp
@@ -724,21 +724,16 @@ LayerManagerD3D10::Render()
     }
 
     nsRefPtr<WindowLayer> windowLayer =
         static_cast<WindowLayer*>(mRootForShadowTree->GetFirstChild());
     if (!windowLayer) {
         windowLayer = new WindowLayer(this);
         windowLayer->SetShadow(ConstructShadowFor(windowLayer));
         CreatedThebesLayer(windowLayer);
-        ShadowLayerForwarder::CreatedThebesBuffer(windowLayer,
-                                                  contentRect,
-                                                  contentRect,
-                                                  SurfaceDescriptor());
-
         mRootForShadowTree->InsertAfter(windowLayer, nsnull);
         ShadowLayerForwarder::InsertAfter(mRootForShadowTree, windowLayer);
     }
 
     if (!mRootForShadowTree->GetVisibleRegion().IsEqual(contentRect)) {
         mRootForShadowTree->SetVisibleRegion(contentRect);
         windowLayer->SetVisibleRegion(contentRect);
 
--- a/gfx/layers/d3d10/ThebesLayerD3D10.cpp
+++ b/gfx/layers/d3d10/ThebesLayerD3D10.cpp
@@ -465,37 +465,29 @@ ShadowThebesLayerD3D10::ShadowThebesLaye
   mImplData = static_cast<LayerD3D10*>(this);
 }
 
 ShadowThebesLayerD3D10::~ShadowThebesLayerD3D10()
 {
 }
 
 void
-ShadowThebesLayerD3D10::SetFrontBuffer(const OptionalThebesBuffer& aNewFront,
-                                       const nsIntRegion& aValidRegion)
-{
-  NS_ABORT_IF_FALSE(OptionalThebesBuffer::Tnull_t == aNewFront.type(),
-                    "Expected dummy front buffer initially");
-}
-
-void
 ShadowThebesLayerD3D10::Swap(
   const ThebesBuffer& aNewFront, const nsIntRegion& aUpdatedRegion,
-  ThebesBuffer* aNewBack, nsIntRegion* aNewBackValidRegion,
+  OptionalThebesBuffer* aNewBack, nsIntRegion* aNewBackValidRegion,
   OptionalThebesBuffer* aReadOnlyFront, nsIntRegion* aFrontUpdatedRegion)
 {
   nsRefPtr<ID3D10Texture2D> newBackBuffer = mTexture;
 
   mTexture = OpenForeign(mD3DManager->device(), aNewFront.buffer());
   NS_ABORT_IF_FALSE(mTexture, "Couldn't open foreign texture");
 
   // The content process tracks back/front buffers on its own, so
   // the newBack is in essence unused.
-  aNewBack->buffer() = aNewFront.buffer();
+  aNewBack->get_ThebesBuffer().buffer() = aNewFront.buffer();
 
   // The content process doesn't need to read back from the front
   // buffer (yet).
   *aReadOnlyFront = null_t();
 
   // FIXME/bug 662109: synchronize using KeyedMutex
 }
 
--- a/gfx/layers/d3d10/ThebesLayerD3D10.h
+++ b/gfx/layers/d3d10/ThebesLayerD3D10.h
@@ -103,22 +103,19 @@ private:
 
 class ShadowThebesLayerD3D10 : public ShadowThebesLayer,
                                public LayerD3D10
 {
 public:
   ShadowThebesLayerD3D10(LayerManagerD3D10* aManager);
   virtual ~ShadowThebesLayerD3D10();
 
-  // ShadowThebesLayer impl
-  virtual void SetFrontBuffer(const OptionalThebesBuffer& aNewFront,
-                              const nsIntRegion& aValidRegion);
   virtual void
   Swap(const ThebesBuffer& aNewFront, const nsIntRegion& aUpdatedRegion,
-       ThebesBuffer* aNewBack, nsIntRegion* aNewBackValidRegion,
+       OptionalThebesBuffer* aNewBack, nsIntRegion* aNewBackValidRegion,
        OptionalThebesBuffer* aReadOnlyFront, nsIntRegion* aFrontUpdatedRegion);
   virtual void DestroyFrontBuffer();
 
   virtual void Disconnect();
 
   /* LayerD3D10 implementation */
   virtual Layer* GetLayer() { return this; }
   virtual void RenderLayer();
--- a/gfx/layers/d3d9/ContainerLayerD3D9.cpp
+++ b/gfx/layers/d3d9/ContainerLayerD3D9.cpp
@@ -242,22 +242,24 @@ ContainerRender(Container* aContainer,
       SetVertexShaderConstantF(CBmProjection, &viewMatrix._11, 4);
   } else {
     aContainer->mSupportsComponentAlphaChildren = 
         (aContainer->GetContentFlags() & aContainer->CONTENT_OPAQUE) ||
         (aContainer->mParent && 
          aContainer->mParent->SupportsComponentAlphaChildren());
   }
 
+  nsAutoTArray<Layer*, 12> children;
+  aContainer->SortChildrenBy3DZOrder(children);
+
   /*
    * Render this container's contents.
    */
-  for (LayerD3D9* layerToRender = aContainer->GetFirstChildD3D9();
-       layerToRender != nsnull;
-       layerToRender = GetNextSiblingD3D9(layerToRender)) {
+  for (PRUint32 i = 0; i < children.Length(); i++) {
+    LayerD3D9* layerToRender = static_cast<LayerD3D9*>(children.ElementAt(i)->ImplData());
 
     if (layerToRender->GetLayer()->GetEffectiveVisibleRegion().IsEmpty()) {
       continue;
     }
     
     nsIntRect scissorRect =
       layerToRender->GetLayer()->CalculateScissorRect(oldScissor, nsnull);
     if (scissorRect.IsEmpty()) {
--- a/gfx/layers/d3d9/ThebesLayerD3D9.cpp
+++ b/gfx/layers/d3d9/ThebesLayerD3D9.cpp
@@ -614,35 +614,27 @@ ShadowThebesLayerD3D9::ShadowThebesLayer
 {
   mImplData = static_cast<LayerD3D9*>(this);
 }
 
 ShadowThebesLayerD3D9::~ShadowThebesLayerD3D9()
 {}
 
 void
-ShadowThebesLayerD3D9::SetFrontBuffer(const OptionalThebesBuffer& aNewFront,
-                                     const nsIntRegion& aValidRegion)
+ShadowThebesLayerD3D9::Swap(const ThebesBuffer& aNewFront,
+                           const nsIntRegion& aUpdatedRegion,
+                           OptionalThebesBuffer* aNewBack,
+                           nsIntRegion* aNewBackValidRegion,
+                           OptionalThebesBuffer* aReadOnlyFront,
+                           nsIntRegion* aFrontUpdatedRegion)
 {
   if (!mBuffer) {
     mBuffer = new ShadowBufferD3D9(this);
   }
 
-  NS_ASSERTION(OptionalThebesBuffer::Tnull_t == aNewFront.type(),
-               "Only one system-memory buffer expected");
-}
-
-void
-ShadowThebesLayerD3D9::Swap(const ThebesBuffer& aNewFront,
-                           const nsIntRegion& aUpdatedRegion,
-                           ThebesBuffer* aNewBack,
-                           nsIntRegion* aNewBackValidRegion,
-                           OptionalThebesBuffer* aReadOnlyFront,
-                           nsIntRegion* aFrontUpdatedRegion)
-{
   if (mBuffer) {
     nsRefPtr<gfxASurface> surf = ShadowLayerForwarder::OpenDescriptor(aNewFront.buffer());
     mBuffer->Upload(surf, GetVisibleRegion().GetBounds());
   }
 
   *aNewBack = aNewFront;
   *aNewBackValidRegion = mValidRegion;
   *aReadOnlyFront = null_t();
--- a/gfx/layers/d3d9/ThebesLayerD3D9.h
+++ b/gfx/layers/d3d9/ThebesLayerD3D9.h
@@ -115,22 +115,19 @@ private:
 
 class ShadowThebesLayerD3D9 : public ShadowThebesLayer,
                               public LayerD3D9
 {
 public:
   ShadowThebesLayerD3D9(LayerManagerD3D9 *aManager);
   virtual ~ShadowThebesLayerD3D9();
 
-  // ShadowThebesLayer impl
-  virtual void SetFrontBuffer(const OptionalThebesBuffer& aNewFront,
-                              const nsIntRegion& aValidRegion);
   virtual void
   Swap(const ThebesBuffer& aNewFront, const nsIntRegion& aUpdatedRegion,
-       ThebesBuffer* aNewBack, nsIntRegion* aNewBackValidRegion,
+       OptionalThebesBuffer* aNewBack, nsIntRegion* aNewBackValidRegion,
        OptionalThebesBuffer* aReadOnlyFront, nsIntRegion* aFrontUpdatedRegion);
   virtual void DestroyFrontBuffer();
 
   virtual void Disconnect();
 
   // LayerD3D9 impl
   Layer* GetLayer();
   virtual bool IsEmpty();
--- a/gfx/layers/ipc/PLayers.ipdl
+++ b/gfx/layers/ipc/PLayers.ipdl
@@ -104,27 +104,16 @@ struct ThebesBuffer {
 };
 union OptionalThebesBuffer { ThebesBuffer; null_t; };
 
 union CanvasSurface {
   SurfaceDescriptor;
   null_t;
 };
 
-// For the "buffer creation" operations, we send an initial front
-// buffer that only contains (transparent) black pixels just so that
-// we can swap it back after the first OpPaint without a special case.
-
-struct OpCreateThebesBuffer {
-  PLayer layer;
-  OptionalThebesBuffer initialFront;
-  nsIntRegion frontValidRegion;
-};
-struct OpDestroyThebesFrontBuffer { PLayer layer; };
-
 // Change a layer's attributes
 struct CommonLayerAttributes {
   nsIntRegion visibleRegion;
   gfx3DMatrix transform;
   PRUint32 contentFlags;
   float opacity;
   bool useClipRect;
   nsIntRect clipRect;
@@ -189,18 +178,16 @@ struct OpPaintImage  {
 
 // A unit of a changeset; a set of these comprise a changeset
 union Edit {
   OpCreateThebesLayer;
   OpCreateContainerLayer;
   OpCreateImageLayer;
   OpCreateColorLayer;
   OpCreateCanvasLayer;
-  OpCreateThebesBuffer;
-  OpDestroyThebesFrontBuffer;
 
   OpSetLayerAttributes;
 
   OpSetRoot;
   OpInsertAfter;
   OpAppendChild;
   OpRemoveChild;
 
@@ -212,17 +199,17 @@ union Edit {
 
 // Replies to operations
 struct OpBufferSwap   { PLayer layer; CanvasSurface newBackBuffer; };
 
 struct OpImageSwap { PLayer layer; SharedImage newBackImage; };
 
 struct OpThebesBufferSwap {
   PLayer layer;
-  ThebesBuffer newBackBuffer;
+  OptionalThebesBuffer newBackBuffer;
   nsIntRegion newValidRegion;
   // If the parent took the child's old back buffer and returned its
   // old front buffer, |readOnlyFrontBuffer| may (if non-null) contain
   // the child's old back buffer (parent's new front buffer).  This
   // buffer can be used to read back the newly updated region into the
   // child's new back buffer.  This buffer must be considered
   // read-only, but sadly that's not enforced.
   OptionalThebesBuffer readOnlyFrontBuffer;
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -180,41 +180,16 @@ ShadowLayerForwarder::CreatedColorLayer(
 }
 void
 ShadowLayerForwarder::CreatedCanvasLayer(ShadowableLayer* aCanvas)
 {
   CreatedLayer<OpCreateCanvasLayer>(mTxn, aCanvas);
 }
 
 void
-ShadowLayerForwarder::CreatedThebesBuffer(ShadowableLayer* aThebes,
-                                          const nsIntRegion& aFrontValidRegion,
-                                          const nsIntRect& aBufferRect,
-                                          const SurfaceDescriptor& aTempFrontBuffer)
-{
-  OptionalThebesBuffer buffer = null_t();
-  if (IsSurfaceDescriptorValid(aTempFrontBuffer)) {
-    buffer = ThebesBuffer(aTempFrontBuffer,
-                          aBufferRect,
-                          nsIntPoint(0, 0));
-  }
-  mTxn->AddEdit(OpCreateThebesBuffer(NULL, Shadow(aThebes),
-                                     buffer,
-                                     aFrontValidRegion));
-}
-
-void
-ShadowLayerForwarder::DestroyedThebesBuffer(ShadowableLayer* aThebes,
-                                            const SurfaceDescriptor& aBackBufferToDestroy)
-{
-  mTxn->AddEdit(OpDestroyThebesFrontBuffer(NULL, Shadow(aThebes)));
-  mTxn->AddBufferToDestroy(aBackBufferToDestroy);
-}
-
-void
 ShadowLayerForwarder::Mutated(ShadowableLayer* aMutant)
 {
   mTxn->AddMutant(aMutant);
 }
 
 void
 ShadowLayerForwarder::SetRoot(ShadowableLayer* aRoot)
 {
--- a/gfx/layers/ipc/ShadowLayers.h
+++ b/gfx/layers/ipc/ShadowLayers.h
@@ -136,48 +136,16 @@ public:
    */
   void CreatedThebesLayer(ShadowableLayer* aThebes);
   void CreatedContainerLayer(ShadowableLayer* aContainer);
   void CreatedImageLayer(ShadowableLayer* aImage);
   void CreatedColorLayer(ShadowableLayer* aColor);
   void CreatedCanvasLayer(ShadowableLayer* aCanvas);
 
   /**
-   * Notify the shadow manager that a buffer has been created for the
-   * specificed layer.  |aInitialFrontSurface| is one of the newly
-   * created, transparent black buffers for the layer; the "real"
-   * layer holds on to the other as its back buffer.  We send it
-   * across on buffer creation to avoid special cases in the buffer
-   * swapping logic for Painted*() operations.
-   *
-   * It is expected that Created*Buffer() will be followed by a
-   * Painted*Buffer() in the same transaction, so that
-   * |aInitialFrontBuffer| is never actually drawn to screen.  It is
-   * OK if it is drawn though.
-   */
-  /**
-   * |aBufferRect| is the screen rect covered by |aInitialFrontBuffer|.
-   */
-  void CreatedThebesBuffer(ShadowableLayer* aThebes,
-                           const nsIntRegion& aFrontValidRegion,
-                           const nsIntRect& aBufferRect,
-                           const SurfaceDescriptor& aInitialFrontBuffer);
-
-  /**
-   * The specified layer is destroying its buffers.
-   * |aBackBufferToDestroy| is deallocated when this transaction is
-   * posted to the parent.  During the parent-side transaction, the
-   * shadow is told to destroy its front buffer.  This can happen when
-   * a new front/back buffer pair have been created because of a layer
-   * resize, e.g.
-   */
-  void DestroyedThebesBuffer(ShadowableLayer* aThebes,
-                             const SurfaceDescriptor& aBackBufferToDestroy);
-
-  /**
    * At least one attribute of |aMutant| has changed, and |aMutant|
    * needs to sync to its shadow layer.  This initial implementation
    * forwards all attributes when any is mutated.
    */
   void Mutated(ShadowableLayer* aMutant);
 
   void SetRoot(ShadowableLayer* aRoot);
   /**
@@ -501,25 +469,16 @@ protected:
   bool mUseShadowClipRect;
 };
 
 
 class ShadowThebesLayer : public ShadowLayer,
                           public ThebesLayer
 {
 public:
-  /**
-   * CONSTRUCTION PHASE ONLY
-   *
-   * Override the front buffer and its valid region with the specified
-   * values.  This is called when a new buffer has been created.
-   */
-  virtual void SetFrontBuffer(const OptionalThebesBuffer& aNewFront,
-                              const nsIntRegion& aValidRegion) = 0;
-
   virtual void InvalidateRegion(const nsIntRegion& aRegion)
   {
     NS_RUNTIMEABORT("ShadowThebesLayers can't fill invalidated regions");
   }
 
   /**
    * CONSTRUCTION PHASE ONLY
    */
@@ -533,17 +492,17 @@ public:
    * CONSTRUCTION PHASE ONLY
    *
    * Publish the remote layer's back ThebesLayerBuffer to this shadow,
    * swapping out the old front ThebesLayerBuffer (the new back buffer
    * for the remote layer).
    */
   virtual void
   Swap(const ThebesBuffer& aNewFront, const nsIntRegion& aUpdatedRegion,
-       ThebesBuffer* aNewBack, nsIntRegion* aNewBackValidRegion,
+       OptionalThebesBuffer* aNewBack, nsIntRegion* aNewBackValidRegion,
        OptionalThebesBuffer* aReadOnlyFront, nsIntRegion* aFrontUpdatedRegion) = 0;
 
   /**
    * CONSTRUCTION PHASE ONLY
    *
    * Destroy the current front buffer.
    */
   virtual void DestroyFrontBuffer() = 0;
--- a/gfx/layers/ipc/ShadowLayersParent.cpp
+++ b/gfx/layers/ipc/ShadowLayersParent.cpp
@@ -198,39 +198,17 @@ ShadowLayersParent::RecvUpdate(const Inf
       MOZ_LAYERS_LOG(("[ParentSide] CreateCanvasLayer"));
 
       nsRefPtr<ShadowCanvasLayer> layer = 
         layer_manager()->CreateShadowCanvasLayer();
       layer->SetAllocator(this);
       AsShadowLayer(edit.get_OpCreateCanvasLayer())->Bind(layer);
       break;
     }
-    case Edit::TOpCreateThebesBuffer: {
-      MOZ_LAYERS_LOG(("[ParentSide] CreateThebesBuffer"));
 
-      const OpCreateThebesBuffer& otb = edit.get_OpCreateThebesBuffer();
-      ShadowThebesLayer* thebes = static_cast<ShadowThebesLayer*>(
-        AsShadowLayer(otb)->AsLayer());
-
-      thebes->SetFrontBuffer(otb.initialFront(), otb.frontValidRegion());
-
-      break;
-    }
-    case Edit::TOpDestroyThebesFrontBuffer: {
-      MOZ_LAYERS_LOG(("[ParentSide] DestroyThebesFrontBuffer"));
-
-      const OpDestroyThebesFrontBuffer& odfb =
-        edit.get_OpDestroyThebesFrontBuffer();
-      ShadowThebesLayer* thebes = static_cast<ShadowThebesLayer*>(
-        AsShadowLayer(odfb)->AsLayer());
-
-      thebes->DestroyFrontBuffer();
-
-      break;
-    }
       // Attributes
     case Edit::TOpSetLayerAttributes: {
       MOZ_LAYERS_LOG(("[ParentSide] SetLayerAttributes"));
 
       const OpSetLayerAttributes& osla = edit.get_OpSetLayerAttributes();
       Layer* layer = AsShadowLayer(osla)->AsLayer();
       const LayerAttributes& attrs = osla.attrs();
 
@@ -334,17 +312,17 @@ ShadowLayersParent::RecvUpdate(const Inf
       MOZ_LAYERS_LOG(("[ParentSide] Paint ThebesLayer"));
 
       const OpPaintThebesBuffer& op = edit.get_OpPaintThebesBuffer();
       ShadowLayerParent* shadow = AsShadowLayer(op);
       ShadowThebesLayer* thebes =
         static_cast<ShadowThebesLayer*>(shadow->AsLayer());
       const ThebesBuffer& newFront = op.newFrontBuffer();
 
-      ThebesBuffer newBack;
+      OptionalThebesBuffer newBack;
       nsIntRegion newValidRegion;
       OptionalThebesBuffer readonlyFront;
       nsIntRegion frontUpdatedRegion;
       thebes->Swap(newFront, op.updatedRegion(),
                    &newBack, &newValidRegion,
                    &readonlyFront, &frontUpdatedRegion);
       replyv.push_back(
         OpThebesBufferSwap(
--- a/gfx/layers/opengl/ContainerLayerOGL.cpp
+++ b/gfx/layers/opengl/ContainerLayerOGL.cpp
@@ -170,17 +170,16 @@ ContainerRender(Container* aContainer,
 
   nsIntRect cachedScissor = aContainer->gl()->ScissorRect();
   aContainer->gl()->PushScissorRect();
   aContainer->mSupportsComponentAlphaChildren = PR_FALSE;
 
   float opacity = aContainer->GetEffectiveOpacity();
   const gfx3DMatrix& transform = aContainer->GetEffectiveTransform();
   bool needsFramebuffer = aContainer->UseIntermediateSurface();
-  gfxMatrix contTransform;
   if (needsFramebuffer) {
     LayerManagerOGL::InitMode mode = LayerManagerOGL::InitModeClear;
     nsIntRect framebufferRect = visibleRect;
     if (aContainer->GetEffectiveVisibleRegion().GetNumRects() == 1 && 
         (aContainer->GetContentFlags() & Layer::CONTENT_OPAQUE))
     {
       // don't need a background, we're going to paint all opaque stuff
       aContainer->mSupportsComponentAlphaChildren = PR_TRUE;
@@ -208,29 +207,26 @@ ContainerRender(Container* aContainer,
                                    &frameBuffer,
                                    &containerSurface);
     childOffset.x = visibleRect.x;
     childOffset.y = visibleRect.y;
   } else {
     frameBuffer = aPreviousFrameBuffer;
     aContainer->mSupportsComponentAlphaChildren = (aContainer->GetContentFlags() & Layer::CONTENT_OPAQUE) ||
       (aContainer->GetParent() && aContainer->GetParent()->SupportsComponentAlphaChildren());
-#ifdef DEBUG
-    bool is2d =
-#endif
-    transform.Is2D(&contTransform);
-    NS_ASSERTION(is2d, "Transform must be 2D");
   }
 
+  nsAutoTArray<Layer*, 12> children;
+  aContainer->SortChildrenBy3DZOrder(children);
+
   /**
    * Render this container's contents.
    */
-  for (LayerOGL* layerToRender = aContainer->GetFirstChildOGL();
-       layerToRender != nsnull;
-       layerToRender = GetNextSibling(layerToRender)) {
+  for (PRUint32 i = 0; i < children.Length(); i++) {
+    LayerOGL* layerToRender = static_cast<LayerOGL*>(children.ElementAt(i)->ImplData());
 
     if (layerToRender->GetLayer()->GetEffectiveVisibleRegion().IsEmpty()) {
       continue;
     }
 
     nsIntRect scissorRect = layerToRender->GetLayer()->
         CalculateScissorRect(cachedScissor, &aManager->GetWorldTransform());
     if (scissorRect.IsEmpty()) {
--- a/gfx/layers/opengl/ThebesLayerOGL.cpp
+++ b/gfx/layers/opengl/ThebesLayerOGL.cpp
@@ -874,40 +874,27 @@ ShadowThebesLayerOGL::ShadowThebesLayerO
 {
   mImplData = static_cast<LayerOGL*>(this);
 }
 
 ShadowThebesLayerOGL::~ShadowThebesLayerOGL()
 {}
 
 void
-ShadowThebesLayerOGL::SetFrontBuffer(const OptionalThebesBuffer& aNewFront,
-                                     const nsIntRegion& aValidRegion)
-{
-  if (mDestroyed) {
-    return;
-  }
-
-  if (!mBuffer) {
-    mBuffer = new ShadowBufferOGL(this);
-  }
-
-  NS_ASSERTION(OptionalThebesBuffer::Tnull_t == aNewFront.type(),
-               "Only one system-memory buffer expected");
-}
-
-void
 ShadowThebesLayerOGL::Swap(const ThebesBuffer& aNewFront,
                            const nsIntRegion& aUpdatedRegion,
-                           ThebesBuffer* aNewBack,
+                           OptionalThebesBuffer* aNewBack,
                            nsIntRegion* aNewBackValidRegion,
                            OptionalThebesBuffer* aReadOnlyFront,
                            nsIntRegion* aFrontUpdatedRegion)
 {
-  if (!mDestroyed && mBuffer) {
+  if (!mDestroyed) {
+    if (!mBuffer) {
+      mBuffer = new ShadowBufferOGL(this);
+    }
     nsRefPtr<gfxASurface> surf = ShadowLayerForwarder::OpenDescriptor(aNewFront.buffer());
     mBuffer->Upload(surf, aUpdatedRegion, aNewFront.rect(), aNewFront.rotation());
   }
 
   *aNewBack = aNewFront;
   *aNewBackValidRegion = mValidRegion;
   *aReadOnlyFront = null_t();
   aFrontUpdatedRegion->SetEmpty();
--- a/gfx/layers/opengl/ThebesLayerOGL.h
+++ b/gfx/layers/opengl/ThebesLayerOGL.h
@@ -86,22 +86,19 @@ private:
 
 class ShadowThebesLayerOGL : public ShadowThebesLayer,
                              public LayerOGL
 {
 public:
   ShadowThebesLayerOGL(LayerManagerOGL *aManager);
   virtual ~ShadowThebesLayerOGL();
 
-  // ShadowThebesLayer impl
-  virtual void SetFrontBuffer(const OptionalThebesBuffer& aNewFront,
-                              const nsIntRegion& aValidRegion);
   virtual void
   Swap(const ThebesBuffer& aNewFront, const nsIntRegion& aUpdatedRegion,
-       ThebesBuffer* aNewBack, nsIntRegion* aNewBackValidRegion,
+       OptionalThebesBuffer* aNewBack, nsIntRegion* aNewBackValidRegion,
        OptionalThebesBuffer* aReadOnlyFront, nsIntRegion* aFrontUpdatedRegion);
   virtual void DestroyFrontBuffer();
 
   virtual void Disconnect();
 
   // LayerOGL impl
   void Destroy();
   Layer* GetLayer();
--- a/gfx/thebes/Makefile.in
+++ b/gfx/thebes/Makefile.in
@@ -29,16 +29,17 @@ EXPORTS	= \
 	gfxImageSurface.h \
 	gfxMatrix.h \
 	gfxPath.h \
 	gfxPattern.h \
 	gfxPlatform.h \
 	gfxPoint.h \
 	gfxPoint3D.h \
 	gfxPointH3D.h \
+	gfxQuad.h \
 	gfxQuaternion.h \
 	gfxRect.h \
 	gfxSkipChars.h \
 	gfxTeeSurface.h \
 	gfxTypes.h \
 	gfxTextRunCache.h \
 	gfxTextRunWordCache.h \
 	gfxUnicodeProperties.h \
--- a/gfx/thebes/gfx3DMatrix.cpp
+++ b/gfx/thebes/gfx3DMatrix.cpp
@@ -662,16 +662,32 @@ gfx3DMatrix::TransformBounds(const gfxRe
     max_x = max(points[i].x, max_x);
     min_y = min(points[i].y, min_y);
     max_y = max(points[i].y, max_y);
   }
 
   return gfxRect(min_x, min_y, max_x - min_x, max_y - min_y);
 }
 
+gfxQuad 
+gfx3DMatrix::TransformRect(const gfxRect& aRect) const
+{
+  gfxPoint points[4];
+
+  points[0] = Transform(aRect.TopLeft());
+  points[1] = Transform(gfxPoint(aRect.X() + aRect.Width(), aRect.Y()));
+  points[2] = Transform(gfxPoint(aRect.X() + aRect.Width(),
+                                 aRect.Y() + aRect.Height()));
+  points[3] = Transform(gfxPoint(aRect.X(), aRect.Y() + aRect.Height()));
+
+
+  // Could this ever result in lines that intersect? I don't think so.
+  return gfxQuad(points[0], points[1], points[2], points[3]);
+}
+
 bool
 gfx3DMatrix::Is2D() const
 {
   if (_13 != 0.0f || _14 != 0.0f ||
       _23 != 0.0f || _24 != 0.0f ||
       _31 != 0.0f || _32 != 0.0f || _33 != 1.0f || _34 != 0.0f ||
       _43 != 0.0f || _44 != 1.0f) {
     return PR_FALSE;
@@ -709,16 +725,29 @@ gfx3DMatrix::CanDraw2D(gfxMatrix* aMatri
     aMatrix->xy = _21;
     aMatrix->yy = _22;
     aMatrix->x0 = _41;
     aMatrix->y0 = _42;
   }
   return PR_TRUE;
 }
 
+gfx3DMatrix&
+gfx3DMatrix::ProjectTo2D()
+{
+  _31 = 0.0f;
+  _32 = 0.0f;
+  _13 = 0.0f; 
+  _23 = 0.0f; 
+  _33 = 1.0f; 
+  _43 = 0.0f; 
+  _34 = 0.0f;
+  return *this;
+}
+
 gfxPoint gfx3DMatrix::ProjectPoint(const gfxPoint& aPoint) const
 {
   // Define a ray of the form P + Ut where t is a real number
   // w is assumed to always be 1 when transforming 3d points with our
   // 4x4 matrix.
   // p is our click point, q is another point on the same ray.
   // 
   // Note: since the transformation is a general projective transformation and is not
--- a/gfx/thebes/gfx3DMatrix.h
+++ b/gfx/thebes/gfx3DMatrix.h
@@ -38,16 +38,17 @@
 
 #ifndef GFX_3DMATRIX_H
 #define GFX_3DMATRIX_H
 
 #include <gfxTypes.h>
 #include <gfxPoint3D.h>
 #include <gfxPointH3D.h>
 #include <gfxMatrix.h>
+#include <gfxQuad.h>
 
 /**
  * This class represents a 3D transformation. The matrix is laid
  * out as follows:
  *
  * _11 _12 _13 _14
  * _21 _22 _23 _24
  * _31 _32 _33 _34
@@ -116,16 +117,22 @@ public:
    * rendering, not for intermediate calculations.
    *
    * Since drawing is to a 2d plane, any 3d transform without perspective
    * can be reduced by dropping the z row and column.
    */
   bool CanDraw2D(gfxMatrix* aMatrix = nsnull) const;
 
   /**
+   * Converts the matrix to one that doesn't modify the z coordinate of points,
+   * but leaves the rest of the transformation unchanged.
+   */
+  gfx3DMatrix& ProjectTo2D();
+
+  /**
    * Returns true if the matrix is the identity matrix. The most important
    * property we require is that gfx3DMatrix().IsIdentity() returns true.
    */
   bool IsIdentity() const;
 
   /**
    * Pre-multiplication transformation functions:
    *
@@ -242,16 +249,19 @@ public:
    */
   gfxPoint Transform(const gfxPoint& point) const;
 
   /**
    * Transforms a rectangle according to this matrix
    */
   gfxRect TransformBounds(const gfxRect& rect) const;
 
+
+  gfxQuad TransformRect(const gfxRect& aRect) const;
+
   /** 
    * Transforms a 3D vector according to this matrix.
    */
   gfxPoint3D Transform3D(const gfxPoint3D& point) const;
   gfxPointH3D Transform4D(const gfxPointH3D& aPoint) const;
   gfxPointH3D TransposeTransform4D(const gfxPointH3D& aPoint) const;
 
   gfxPoint ProjectPoint(const gfxPoint& aPoint) const;
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/gfxQuad.h
@@ -0,0 +1,87 @@
+/* -*- 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 Oracle Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2005
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Matt Woodrow <mwoodrow@mozilla.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_QUAD_H
+#define GFX_QUAD_H
+
+#include "nsMathUtils.h"
+#include "mozilla/gfx/BaseSize.h"
+#include "mozilla/gfx/BasePoint.h"
+#include "nsSize.h"
+#include "nsPoint.h"
+
+#include "gfxTypes.h"
+
+static PRBool SameSideOfLine(const gfxPoint& aPoint1, const gfxPoint& aPoint2, const gfxPoint& aTest, const gfxPoint& aRef)
+{
+  // Solve the equation y - aPoint1.y - ((aPoint2.y - aPoint1.y)/(aPoint2.x - aPoint1.x))(x - aPoint1.x) for both test and ref
+  
+  gfxFloat deltaY = (aPoint2.y - aPoint1.y);
+  gfxFloat deltaX = (aPoint2.x - aPoint1.x);
+  
+  gfxFloat test = deltaX * (aTest.y - aPoint1.y) - deltaY * (aTest.x - aPoint1.x);
+  gfxFloat ref = deltaX * (aRef.y - aPoint1.y) - deltaY * (aRef.x - aPoint1.x);
+
+  // If both results have the same sign, then we're on the correct side of the line.
+  // 0 (on the line) is always considered in.
+
+  if ((test >= 0 && ref >= 0) || (test <= 0 && ref <= 0))
+    return PR_TRUE;
+  return PR_FALSE;
+}
+
+struct THEBES_API gfxQuad {
+    gfxQuad(const gfxPoint& aOne, const gfxPoint& aTwo, const gfxPoint& aThree, const gfxPoint& aFour)
+    {
+        mPoints[0] = aOne;
+        mPoints[1] = aTwo;
+        mPoints[2] = aThree;
+        mPoints[3] = aFour;
+    }
+
+    PRBool Contains(const gfxPoint& aPoint)
+    {
+        return (SameSideOfLine(mPoints[0], mPoints[1], aPoint, mPoints[2]) &&
+                SameSideOfLine(mPoints[1], mPoints[2], aPoint, mPoints[3]) &&
+                SameSideOfLine(mPoints[2], mPoints[3], aPoint, mPoints[0]) &&
+                SameSideOfLine(mPoints[3], mPoints[0], aPoint, mPoints[1]));
+    }
+
+    gfxPoint mPoints[4];
+};
+
+#endif /* GFX_QUAD_H */
--- a/intl/hyphenation/public/nsHyphenationManager.h
+++ b/intl/hyphenation/public/nsHyphenationManager.h
@@ -36,19 +36,21 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsHyphenationManager_h__
 #define nsHyphenationManager_h__
 
 #include "nsInterfaceHashtable.h"
 #include "nsRefPtrHashtable.h"
 #include "nsHashKeys.h"
+#include "mozilla/Omnijar.h"
 
 class nsHyphenator;
 class nsIAtom;
+class nsIURI;
 
 class nsHyphenationManager
 {
 public:
   nsHyphenationManager();
 
   already_AddRefed<nsHyphenator> GetHyphenator(nsIAtom *aLocale);
 
@@ -56,19 +58,20 @@ public:
 
   static void Shutdown();
 
 private:
   ~nsHyphenationManager();
 
 protected:
   void LoadPatternList();
+  void LoadPatternListFromOmnijar(mozilla::Omnijar::Type aType);
   void LoadPatternListFromDir(nsIFile *aDir);
   void LoadAliases();
 
   nsInterfaceHashtable<nsISupportsHashKey,nsIAtom> mHyphAliases;
-  nsInterfaceHashtable<nsISupportsHashKey,nsIFile> mPatternFiles;
+  nsInterfaceHashtable<nsISupportsHashKey,nsIURI> mPatternFiles;
   nsRefPtrHashtable<nsISupportsHashKey,nsHyphenator> mHyphenators;
 
   static nsHyphenationManager *sInstance;
 };
 
 #endif // nsHyphenationManager_h__
--- a/intl/hyphenation/public/nsHyphenator.h
+++ b/intl/hyphenation/public/nsHyphenator.h
@@ -37,22 +37,23 @@
 
 #ifndef nsHyphenator_h__
 #define nsHyphenator_h__
 
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "nsTArray.h"
 
+class nsIURI;
 class nsIUGenCategory;
 
 class nsHyphenator
 {
 public:
-  nsHyphenator(nsIFile *aFile);
+  nsHyphenator(nsIURI *aURI);
 
   NS_INLINE_DECL_REFCOUNTING(nsHyphenator)
 
   bool IsValid();
 
   nsresult Hyphenate(const nsAString& aText, nsTArray<bool>& aHyphens);
 
 private:
--- a/intl/hyphenation/src/nsHyphenationManager.cpp
+++ b/intl/hyphenation/src/nsHyphenationManager.cpp
@@ -34,22 +34,25 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsHyphenationManager.h"
 #include "nsHyphenator.h"
 #include "nsIAtom.h"
 #include "nsIFile.h"
+#include "nsIURI.h"
 #include "nsIProperties.h"
 #include "nsISimpleEnumerator.h"
 #include "nsIDirectoryEnumerator.h"
 #include "nsDirectoryServiceDefs.h"
+#include "nsNetUtil.h"
 #include "nsUnicharUtils.h"
 #include "mozilla/Preferences.h"
+#include "nsZipArchive.h"
 
 using namespace mozilla;
 
 #define INTL_HYPHENATIONALIAS_PREFIX "intl.hyphenation-alias."
 
 nsHyphenationManager *nsHyphenationManager::sInstance = nsnull;
 
 nsHyphenationManager*
@@ -84,30 +87,30 @@ nsHyphenationManager::~nsHyphenationMana
 already_AddRefed<nsHyphenator>
 nsHyphenationManager::GetHyphenator(nsIAtom *aLocale)
 {
   nsRefPtr<nsHyphenator> hyph;
   mHyphenators.Get(aLocale, getter_AddRefs(hyph));
   if (hyph) {
     return hyph.forget();
   }
-  nsCOMPtr<nsIFile> file = mPatternFiles.Get(aLocale);
-  if (!file) {
+  nsCOMPtr<nsIURI> uri = mPatternFiles.Get(aLocale);
+  if (!uri) {
     nsCOMPtr<nsIAtom> alias = mHyphAliases.Get(aLocale);
     if (alias) {
       mHyphenators.Get(alias, getter_AddRefs(hyph));
       if (hyph) {
         return hyph.forget();
       }
-      file = mPatternFiles.Get(alias);
-      if (file) {
+      uri = mPatternFiles.Get(alias);
+      if (uri) {
         aLocale = alias;
       }
     }
-    if (!file) {
+    if (!uri) {
       // In the case of a locale such as "de-DE-1996", we try replacing
       // successive trailing subtags with "-*" to find fallback patterns,
       // so "de-DE-1996" -> "de-DE-*" (and then recursively -> "de-*")
       nsAtomCString localeStr(aLocale);
       if (StringEndsWith(localeStr, NS_LITERAL_CSTRING("-*"))) {
         localeStr.Truncate(localeStr.Length() - 2);
       }
       PRInt32 i = localeStr.RFindChar('-');
@@ -115,92 +118,149 @@ nsHyphenationManager::GetHyphenator(nsIA
         localeStr.Replace(i, localeStr.Length() - i, "-*");
         nsCOMPtr<nsIAtom> fuzzyLocale = do_GetAtom(localeStr);
         return GetHyphenator(fuzzyLocale);
       } else {
         return nsnull;
       }
     }
   }
-  hyph = new nsHyphenator(file);
+  hyph = new nsHyphenator(uri);
   if (hyph->IsValid()) {
     mHyphenators.Put(aLocale, hyph);
     return hyph.forget();
   }
 #ifdef DEBUG
   nsCString msg;
-  file->GetNativePath(msg);
+  uri->GetSpec(msg);
   msg.Insert("failed to load patterns from ", 0);
   NS_WARNING(msg.get());
 #endif
   mPatternFiles.Remove(aLocale);
   return nsnull;
 }
 
 void
 nsHyphenationManager::LoadPatternList()
 {
   mPatternFiles.Clear();
   mHyphenators.Clear();
-  
-  nsresult rv;
-  
+
+  LoadPatternListFromOmnijar(Omnijar::GRE);
+  LoadPatternListFromOmnijar(Omnijar::APP);
+
   nsCOMPtr<nsIProperties> dirSvc =
     do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
   if (!dirSvc) {
     return;
   }
-  
+
+  nsresult rv;
   nsCOMPtr<nsIFile> greDir;
   rv = dirSvc->Get(NS_GRE_DIR,
                    NS_GET_IID(nsIFile), getter_AddRefs(greDir));
   if (NS_SUCCEEDED(rv)) {
     greDir->AppendNative(NS_LITERAL_CSTRING("hyphenation"));
     LoadPatternListFromDir(greDir);
   }
-  
+
   nsCOMPtr<nsIFile> appDir;
   rv = dirSvc->Get(NS_XPCOM_CURRENT_PROCESS_DIR,
                    NS_GET_IID(nsIFile), getter_AddRefs(appDir));
   if (NS_SUCCEEDED(rv)) {
     appDir->AppendNative(NS_LITERAL_CSTRING("hyphenation"));
     bool equals;
     if (NS_SUCCEEDED(appDir->Equals(greDir, &equals)) && !equals) {
       LoadPatternListFromDir(appDir);
     }
   }
 }
 
 void
+nsHyphenationManager::LoadPatternListFromOmnijar(Omnijar::Type aType)
+{
+  nsCString base;
+  nsresult rv = Omnijar::GetURIString(aType, base);
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  nsZipArchive *zip = Omnijar::GetReader(aType);
+  if (!zip) {
+    return;
+  }
+
+  nsZipFind *find;
+  zip->FindInit("hyphenation/hyph_*.dic", &find);
+  if (!find) {
+    return;
+  }
+
+  const char *result;
+  PRUint16 len;
+  while (NS_SUCCEEDED(find->FindNext(&result, &len))) {
+    nsCString uriString(base);
+    uriString.Append(result, len);
+    nsCOMPtr<nsIURI> uri;
+    rv = NS_NewURI(getter_AddRefs(uri), uriString);
+    if (NS_FAILED(rv)) {
+      continue;
+    }
+    nsCString locale;
+    rv = uri->GetPath(locale);
+    if (NS_FAILED(rv)) {
+      continue;
+    }
+    ToLowerCase(locale);
+    locale.SetLength(locale.Length() - 4); // strip ".dic"
+    locale.Cut(0, locale.RFindChar('/') + 1); // strip directory
+    if (StringBeginsWith(locale, NS_LITERAL_CSTRING("hyph_"))) {
+      locale.Cut(0, 5);
+    }
+    for (PRUint32 i = 0; i < locale.Length(); ++i) {
+      if (locale[i] == '_') {
+        locale.Replace(i, 1, '-');
+      }
+    }
+    nsCOMPtr<nsIAtom> localeAtom = do_GetAtom(locale);
+    if (NS_SUCCEEDED(rv)) {
+      mPatternFiles.Put(localeAtom, uri);
+    }
+  }
+
+  delete find;
+}
+
+void
 nsHyphenationManager::LoadPatternListFromDir(nsIFile *aDir)
 {
   nsresult rv;
-  
+
   bool check = false;
   rv = aDir->Exists(&check);
   if (NS_FAILED(rv) || !check) {
     return;
   }
-  
+
   rv = aDir->IsDirectory(&check);
   if (NS_FAILED(rv) || !check) {
     return;
   }
 
   nsCOMPtr<nsISimpleEnumerator> e;
   rv = aDir->GetDirectoryEntries(getter_AddRefs(e));
   if (NS_FAILED(rv)) {
     return;
   }
-  
+
   nsCOMPtr<nsIDirectoryEnumerator> files(do_QueryInterface(e));
   if (!files) {
     return;
   }
-  
+
   nsCOMPtr<nsIFile> file;
   while (NS_SUCCEEDED(files->GetNextFile(getter_AddRefs(file))) && file){
     nsAutoString dictName;
     file->GetLeafName(dictName);
     NS_ConvertUTF16toUTF8 locale(dictName);
     ToLowerCase(locale);
     if (!StringEndsWith(locale, NS_LITERAL_CSTRING(".dic"))) {
       continue;
@@ -214,17 +274,21 @@ nsHyphenationManager::LoadPatternListFro
         locale.Replace(i, 1, '-');
       }
     }
 #ifdef DEBUG_hyph
     printf("adding hyphenation patterns for %s: %s\n", locale.get(),
            NS_ConvertUTF16toUTF8(dictName).get());
 #endif
     nsCOMPtr<nsIAtom> localeAtom = do_GetAtom(locale);
-    mPatternFiles.Put(localeAtom, file);
+    nsCOMPtr<nsIURI> uri;
+    nsresult rv = NS_NewFileURI(getter_AddRefs(uri), file);
+    if (NS_SUCCEEDED(rv)) {
+      mPatternFiles.Put(localeAtom, uri);
+    }
   }
 }
 
 void
 nsHyphenationManager::LoadAliases()
 {
   nsIPrefBranch* prefRootBranch = Preferences::GetRootBranch();
   if (!prefRootBranch) {
--- a/intl/hyphenation/src/nsHyphenator.cpp
+++ b/intl/hyphenation/src/nsHyphenator.cpp
@@ -35,32 +35,32 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsHyphenator.h"
 #include "nsIFile.h"
 #include "nsUTF8Utils.h"
 #include "nsIUGenCategory.h"
 #include "nsUnicharUtilCIID.h"
-#include "nsNetUtil.h"
+#include "nsIURI.h"
 
 #include "hyphen.h"
 
-nsHyphenator::nsHyphenator(nsIFile *aFile)
+nsHyphenator::nsHyphenator(nsIURI *aURI)
   : mDict(nsnull)
 {
-  nsCString urlSpec;
-  nsresult rv = NS_GetURLSpecFromFile(aFile, urlSpec);
+  nsCString uriSpec;
+  nsresult rv = aURI->GetSpec(uriSpec);
   if (NS_FAILED(rv)) {
     return;
   }
-  mDict = hnj_hyphen_load(urlSpec.get());
+  mDict = hnj_hyphen_load(uriSpec.get());
 #ifdef DEBUG
   if (mDict) {
-    printf("loaded hyphenation patterns from %s\n", urlSpec.get());
+    printf("loaded hyphenation patterns from %s\n", uriSpec.get());
   }
 #endif
   mCategories = do_GetService(NS_UNICHARCATEGORY_CONTRACTID, &rv);
   NS_ASSERTION(NS_SUCCEEDED(rv), "failed to get category service");
 }
 
 nsHyphenator::~nsHyphenator()
 {
deleted file mode 100644
--- a/js/ductwork/Makefile.in
+++ /dev/null
@@ -1,49 +0,0 @@
-# ***** 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 the Mozilla Browser code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 2011
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-#  Jason Orendorff <jorendorff@mozilla.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 *****
-
-DEPTH     = ../..
-topsrcdir = @top_srcdir@
-srcdir    = @srcdir@
-VPATH     = @srcdir@
-
-include $(topsrcdir)/config/config.mk
-
-PARALLEL_DIRS += \
-  debugger \
-  $(NULL)
-
-include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug690292.js
@@ -0,0 +1,12 @@
+
+done = false;
+try {
+    function x() {}
+    print(this.watch("d", Object.create))
+    var d = <x></x>
+} catch (e) {}
+try {
+  eval("d = ''")
+  done = true;
+} catch (e) {}
+assertEq(done, false);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/splice-675164.js
@@ -0,0 +1,15 @@
+function NPList() {}
+NPList.prototype = new Array;
+
+var list = new NPList();
+list.push('a');
+
+var cut = list.splice(0, 1);
+
+assertEq(cut[0], 'a');
+assertEq(cut.length, 1);
+assertEq(list.length, 0);
+
+var desc = Object.getOwnPropertyDescriptor(list, "0");
+assertEq(desc, undefined);
+assertEq("0" in list, false);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/splice-call-plain-object-590780.js
@@ -0,0 +1,8 @@
+var o = { 0: 1, 1: 2, 2: 3, length: 3 };
+Array.prototype.splice.call(o, 0, 1);
+
+assertEq(o[0], 2);
+assertEq(o[1], 3);
+assertEq(Object.getOwnPropertyDescriptor(o, 2), undefined);
+assertEq("2" in o, false);
+assertEq(o.length, 2);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/splice-check-steps.js
@@ -0,0 +1,327 @@
+/*
+ * Check the order of splice's internal operations, because the ordering is
+ * visible externally.
+ */
+
+function handlerMaker(obj, expected_exceptions) {
+  var order = [];
+  function note(trap, name)
+  {
+      order.push(trap + '-' + name);
+      if (expected_exceptions[trap] === name) {
+          throw ("fail");
+      }
+  }
+
+  return [{
+    /* this is the only trap we care about */
+    delete: function(name) {
+      note("del", name);
+      return delete obj[name];
+    },
+
+    // Fundamental traps
+    getOwnPropertyDescriptor: function(name) {
+      var desc = Object.getOwnPropertyDescriptor(obj, name);
+      // a trapping proxy's properties must always be configurable
+      if (desc !== undefined)
+        desc.configurable = true;
+      return desc;
+    },
+    getPropertyDescriptor:  function(name) {
+      var desc = Object.getPropertyDescriptor(obj, name); // not in ES5
+      // a trapping proxy's properties must always be configurable
+      if (desc !== undefined)
+        desc.configurable = true;
+      return desc;
+    },
+    getOwnPropertyNames: function() {
+      return Object.getOwnPropertyNames(obj);
+    },
+    getPropertyNames: function() {
+      return Object.getPropertyNames(obj);                // not in ES5
+    },
+    defineProperty: function(name, desc) {
+      note("def", name);
+      Object.defineProperty(obj, name, desc);
+    },
+    fix:          function() {
+      if (Object.isFrozen(obj)) {
+        return Object.getOwnPropertyNames(obj).map(function(name) {
+          return Object.getOwnPropertyDescriptor(obj, name);
+        });
+      }
+      // As long as obj is not frozen, the proxy won't allow itself to be fixed
+      return undefined; // will cause a TypeError to be thrown
+    },
+
+    // derived traps
+    has:          function(name) {
+      note("has", name);
+      return name in obj;
+    },
+    hasOwn:       function(name) { return Object.prototype.hasOwnProperty.call(obj, name); },
+    get:          function(receiver, name) {
+      note("get", name);
+      return obj[name];
+    },
+    set:          function(receiver, name, val) {
+      note("set", name);
+      obj[name] = val;
+      return true; // bad behavior when set fails in non-strict mode
+    },
+    enumerate:    function() {
+      var result = [];
+      for (name in obj)
+        result.push(name);
+      return result;
+    },
+    keys: function() { return Object.keys(obj) }
+  }, order];
+}
+
+// arr: the array to splice
+// expected_order: the expected order of operations on arr, stringified
+function check_splice_proxy(arr, expected_order, expected_exceptions, expected_array, expected_result) {
+    print (arr);
+    var [handler, store] = handlerMaker(arr, expected_exceptions);
+    var proxy = Proxy.create(handler);
+
+    try {
+        var args = Array.prototype.slice.call(arguments, 5);
+        var result = Array.prototype.splice.apply(proxy, args);
+        assertEq(Object.keys(expected_exceptions).length, 0);
+    } catch (e) {
+        assertEq(Object.keys(expected_exceptions).length > 0, true);
+    }
+
+    // check the order of the property accesses, etc
+    assertEq(store.toString(), expected_order);
+
+    // The deleted elements are returned in an object that's always an Array.
+    assertEq(Array.isArray(result) || result === undefined, true);
+
+    // check the return value
+    for (var i in expected_result) {
+        assertEq(result[i], expected_result[i]);
+    }
+    for (var i in result) {
+        assertEq(result[i], expected_result[i]);
+    }
+
+    // check the value of arr
+    for (var i in expected_array) {
+        assertEq(arr[i], expected_array[i]);
+    }
+    for (var i in arr) {
+        assertEq(arr[i], expected_array[i]);
+    }
+
+    return result;
+}
+
+// Shrinking array
+check_splice_proxy(
+        [10,1,2,3,4,5],
+        "get-length," +
+        "has-0,get-0,has-1,get-1,has-2,get-2," +
+        "has-3,get-3,set-0,has-4,get-4,set-1,has-5,get-5,set-2," +
+        "del-5,del-4,del-3," +
+        "set-length",
+        {},
+        [3,4,5],
+        [10,1,2],
+        0, 3
+);
+
+// Growing array
+check_splice_proxy(
+        [11,1,2,3,4,5],
+        "get-length," +
+        "has-0,get-0,has-1,get-1,has-2,get-2," +
+        "has-5,get-5,set-9,has-4,get-4,set-8,has-3,get-3,set-7," +
+        "set-0,set-1,set-2,set-3,set-4,set-5,set-6," +
+        "set-length",
+        {},
+        [9,9,9,9,9,9,9,3,4,5],
+        [11,1,2],
+        0, 3, 9, 9, 9, 9, 9, 9, 9
+);
+
+// Same sized array
+check_splice_proxy(
+        [12,1,2,3,4,5],
+        "get-length," +
+        "has-0,get-0,has-1,get-1,has-2,get-2," +
+        "set-0,set-1,set-2," +
+        "set-length",
+        {},
+        [9,9,9,3,4,5],
+        [12,1,2],
+        0, 3, 9, 9, 9
+);
+
+
+/*
+ * Check that if we fail at a particular step in the algorithm, we don't
+ * continue with the algorithm beyond that step.
+ */
+
+
+// Step 3: fail when getting length
+check_splice_proxy(
+        [13,1,2,3,4,5],
+        "get-length",
+        {get: 'length'},
+        [13,1,2,3,4,5],
+        undefined,
+        0, 3, 9, 9, 9
+);
+
+// Step 9b: fail when [[HasProperty]]
+check_splice_proxy(
+        [14,1,2,3,4,5],
+        "get-length," +
+        "has-0,get-0,has-1",
+        {has: '1'},
+        [14,1,2,3,4,5],
+        undefined,
+        0, 3, 9, 9, 9
+);
+
+// Step 9c(i): fail when [[Get]]
+check_splice_proxy(
+        [15,1,2,3,4,5],
+        "get-length," +
+        "has-0,get-0,has-1,get-1",
+        {get: '1'},
+        [15,1,2,3,4,5],
+        undefined,
+        0, 3, 9, 9, 9
+);
+
+// Step 12b(iii): fail when [[HasProperty]]
+check_splice_proxy(
+        [16,1,2,3,4,5],
+        "get-length," +
+        "has-0,get-0,has-1,get-1,has-2,get-2," +
+        "has-3,get-3,set-0,has-4",
+        {has: '4'},
+        [3,1,2,3,4,5],
+        undefined,
+        0, 3
+);
+
+
+// Step 12b(iv)1: fail when [[Get]]
+check_splice_proxy(
+        [17,1,2,3,4,5],
+        "get-length," +
+        "has-0,get-0,has-1,get-1,has-2,get-2," +
+        "has-3,get-3,set-0,has-4,get-4",
+        {get: '4'},
+        [3,1,2,3,4,5],
+        undefined,
+        0, 3
+);
+
+
+// Step 12b(iv)2: fail when [[Put]]
+check_splice_proxy(
+        [18,1,2,3,4,5],
+        "get-length," +
+        "has-0,get-0,has-1,get-1,has-2,get-2," +
+        "has-3,get-3,set-0,has-4,get-4,set-1",
+        {set: '1'},
+        [3,1,2,3,4,5],
+        undefined,
+        0, 3
+);
+
+// Step 12b(v)1: fail when [[Delete]]
+check_splice_proxy(
+        [19,1,2,3,,5],
+        "get-length," +
+        "has-0,get-0,has-1,get-1,has-2,get-2," +
+        "has-3,get-3,set-0,has-4,del-1",
+        {del: '1'},
+        [3,1,2,3,,5],
+        undefined,
+        0, 3
+);
+
+// Step 12d(i): fail when [[Delete]]
+check_splice_proxy(
+        [20,1,2,3,4,5],
+        "get-length," +
+        "has-0,get-0,has-1,get-1,has-2,get-2," +
+        "has-3,get-3,set-0,has-4,get-4,set-1,has-5,get-5,set-2," +
+        "del-5,del-4",
+        {del: '4'},
+        [3,4,5,3,4],
+        undefined,
+        0, 3
+);
+
+// Step 13b(iii): fail when [[HasProperty]]
+check_splice_proxy(
+        [21,1,2,3,4,5],
+        "get-length," +
+        "has-0,get-0,has-1,get-1,has-2,get-2," +
+        "has-5,get-5,set-8,has-4",
+        {has: '4'},
+        [21,1,2,3,4,5,,,5],
+        undefined,
+        0, 3, 9,9,9,9,9,9
+);
+
+
+// Step 13b(iv)1: fail when [[Get]]
+check_splice_proxy(
+        [22,1,2,3,4,5],
+        "get-length," +
+        "has-0,get-0,has-1,get-1,has-2,get-2," +
+        "has-5,get-5,set-8,has-4,get-4",
+        {get: '4'},
+        [22,1,2,3,4,5,,,5],
+        undefined,
+        0, 3, 9,9,9,9,9,9
+);
+
+
+// Step 13b(iv)2: fail when [[Put]]
+check_splice_proxy(
+        [23,1,2,3,4,5],
+        "get-length," +
+        "has-0,get-0,has-1,get-1,has-2,get-2," +
+        "has-5,get-5,set-8,has-4,get-4,set-7",
+        {set: '7'},
+        [23,1,2,3,4,5,,,5],
+        undefined,
+        0, 3, 9,9,9,9,9,9
+);
+
+// Step 13b(v)1: fail when [[Delete]]
+check_splice_proxy(
+        [24,1,2,3,,5],
+        "get-length," +
+        "has-0,get-0,has-1,get-1,has-2,get-2," +
+        "has-5,get-5,set-8,has-4,del-7",
+        {del: '7'},
+        [24,1,2,3,,5,,,5],
+        undefined,
+        0, 3, 9,9,9,9,9,9
+);
+
+// Step 15b: fail when [[Put]]
+check_splice_proxy(
+        [25,1,2,3,4,5],
+        "get-length," +
+        "has-0,get-0,has-1,get-1,has-2,get-2," +
+        "has-5,get-5,set-8,has-4,get-4,set-7,has-3,get-3,set-6," +
+        "set-0,set-1,set-2",
+        {set: '2'},
+        [9,9,2,3,4,5,3,4,5],
+        undefined,
+        0, 3, 9,9,9,9,9,9
+);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/splice-delete-non-configurable-during-shrink.js
@@ -0,0 +1,26 @@
+/* Test that splice causing deletion of a non-configurable property stops at exactly step 12(v) of ES5 15.4.4.12 */
+
+var O = [1,2,3,4,5,6];
+var A = undefined;
+Object.defineProperty(O, 3, { configurable: false });
+
+try
+{
+  A = O.splice(0, 6);
+  throw new Error("didn't throw, returned " + A);
+}
+catch (e)
+{
+  assertEq(e instanceof TypeError, true,
+           "deleting O[3] should have caused a TypeError");
+}
+
+assertEq(O.length, 6); // setting length not reached
+assertEq(A, undefined); // return value not reached
+
+assertEq(O[5], undefined); // deletion reached
+assertEq(O[4], undefined); // deletion reached
+assertEq(O[3], 4); // deletion caused exception
+assertEq(O[2], 3); // deletion not reached
+assertEq(O[1], 2); // deletion not reached
+assertEq(O[0], 1); // deletion not reached
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/splice-fail-step-16.js
@@ -0,0 +1,29 @@
+// |jit-test| error: InternalError
+// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Remove when [].length is redefinable!
+
+/* Test that arrays resize normally during splice, even if .length is non-writable. */
+
+var arr = [1, 2, 3, 4, 5, 6];
+
+Object.defineProperty(arr, "length", {writable: false});
+
+try
+{
+  var removed = arr.splice(3, 3, 9, 9, 9, 9);
+  throw new Error("splice didn't throw, returned [" + removed + "]");
+}
+catch (e)
+{
+  assertEq(e instanceof TypeError, true,
+           "should have thrown a TypeError, instead threw " + e);
+}
+
+// The exception should happen in step 16, which means we've already removed the array elements.
+assertEq(arr[0], 1);
+assertEq(arr[1], 2);
+assertEq(arr[2], 3);
+assertEq(arr[3], 9);
+assertEq(arr[4], 9);
+assertEq(arr[5], 9);
+assertEq(arr[6], 9);
+assertEq(arr.length, 6);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/splice-huge-array-finishes.js
@@ -0,0 +1,15 @@
+// Making the array huge and sparse shouldn't leave us iterating through the entire array.
+// But it does, sadly. Disable, because it takes too long.
+if (0) {
+    var arr = [1, 2, 3, 4, 5, 6, 7, 8];
+    arr.length = Math.pow(2, 32) - 2;
+    arr.splice(5); // also test overflow
+
+    assertEq(arr.length, 5);
+    assertEq(arr[0], 1);
+    assertEq(arr[1], 2);
+    assertEq(arr[2], 3);
+    assertEq(arr[3], 4);
+    assertEq(arr[4], 5);
+    assertEq(arr[5], undefined);
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/splice-on-arguments.js
@@ -0,0 +1,39 @@
+// test whether splice works on arguments
+
+function splice_args () {
+    args = arguments;
+    return Array.prototype.splice.apply(args, [0, 5]);
+}
+
+var args;
+var O = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
+var A = splice_args.apply(undefined, O)
+
+// args: [5, 6, 7, 8, 9]
+assertEq(args[0], 5);
+assertEq(args[1], 6);
+assertEq(args[2], 7);
+assertEq(args[3], 8);
+assertEq(args[4], 9);
+assertEq(args.length, 5);
+
+// A: [0, 1, 2, 3, 4]
+assertEq(A[0], 0);
+assertEq(A[1], 1);
+assertEq(A[2], 2);
+assertEq(A[3], 3);
+assertEq(A[4], 4);
+assertEq(A.length, 5);
+
+// O: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+assertEq(O[0], 0);
+assertEq(O[1], 1);
+assertEq(O[2], 2);
+assertEq(O[3], 3);
+assertEq(O[4], 4);
+assertEq(O[5], 5);
+assertEq(O[6], 6);
+assertEq(O[7], 7);
+assertEq(O[8], 8);
+assertEq(O[9], 9);
+assertEq(O.length, 10);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/splice-throwing-length-getter-668024.js
@@ -0,0 +1,9 @@
+try
+{
+  Array.prototype.splice.call({ get length() { throw 'error'; } });
+  throw new Error("should have thrown, didn't");
+}
+catch (e)
+{
+  assertEq(e, "error", "wrong error thrown: " + e);
+}
--- a/js/src/jit-test/tests/debug/Frame-script-01.js
+++ b/js/src/jit-test/tests/debug/Frame-script-01.js
@@ -11,24 +11,16 @@ function ApplyToFrameScript(code, skip, 
         while (skip-- > 0)
             frame = frame.older;
         assertEq(frame.type, "eval");
         f(frame.script);
     };
     g.eval(code);
 }
 
-var savedScript;
-
 ApplyToFrameScript('debugger;', 0,
                    function (script) {
                        assertEq(script instanceof Debugger.Script, true);
-                       assertEq(script.live, true);
-                       savedScript = script;
                    });
-assertEq(savedScript.live, false);
 ApplyToFrameScript("(function () { eval('debugger;'); })();", 0,
                    function (script) {
                        assertEq(script instanceof Debugger.Script, true);
-                       assertEq(script.live, true);
-                       savedScript = script;
                    });
-assertEq(savedScript.live, false);
--- a/js/src/jit-test/tests/debug/Frame-script-02.js
+++ b/js/src/jit-test/tests/debug/Frame-script-02.js
@@ -11,23 +11,18 @@ function ApplyToFrameScript(code, skip, 
         while (skip-- > 0)
             frame = frame.older;
         assertEq(frame.type, "call");
         f(frame.script);
     };
     g.eval(code);
 }
 
-var savedScript;
-
 ApplyToFrameScript('(function () { debugger; })();', 0,
                    function (script) {
                        assertEq(script instanceof Debugger.Script, true);
-                       assertEq(script.live, true);
-                       savedScript = script;
                    });
-assertEq(savedScript.live, true);
 
 // This would be nice, once we can get host call frames:
 // ApplyToFrameScript("(function () { debugger; }).call(null);", 1,
 //                    function (script) {
 //                        assertEq(script, null);
 //                   });
--- a/js/src/jit-test/tests/debug/Script-gc-02.js
+++ b/js/src/jit-test/tests/debug/Script-gc-02.js
@@ -5,10 +5,10 @@ var dbg = Debugger(g);
 var arr = [];
 dbg.onDebuggerStatement = function (frame) { arr.push(frame.script); };
 g.eval("for (var i = 0; i < 10; i++) Function('debugger;')();");
 assertEq(arr.length, 10);
 
 gc();
 
 for (var i = 0; i < arr.length; i++)
-    assertEq(arr[i].live, true); // XXX FIXME - replace with something that touches the script
+    assertEq(arr[i].lineCount, 1);
 
--- a/js/src/jit-test/tests/debug/Script-gc-03.js
+++ b/js/src/jit-test/tests/debug/Script-gc-03.js
@@ -5,11 +5,11 @@ var dbg = Debugger(g);
 var arr = [];
 dbg.onDebuggerStatement = function (frame) { arr.push(frame.script); };
 g.eval("for (var i = 0; i < 100; i++) Function('debugger;')();");
 assertEq(arr.length, 100);
 
 gc(g);
 
 for (var i = 0; i < arr.length; i++)
-    assertEq(arr[i].live, true);  // XXX FIXME replace with something that touches the script
+    assertEq(arr[i].lineCount, 1);
 
 gc();
--- a/js/src/jit-test/tests/debug/breakpoint-03.js
+++ b/js/src/jit-test/tests/debug/breakpoint-03.js
@@ -5,13 +5,12 @@ load(libdir + "asserts.js");
 var g = newGlobal('new-compartment');
 var dbg = Debugger();
 var gobj = dbg.addDebuggee(g);
 g.eval("function f() { return 2; }");
 
 var s;
 dbg.onDebuggerStatement = function (frame) { s = frame.eval("f").return.script; };
 g.eval("debugger;");
-assertEq(s.live, true);
 s.setBreakpoint(0, {});  // ok
 
 dbg.removeDebuggee(gobj);
 assertThrowsInstanceOf(function () { s.setBreakpoint(0, {}); }, Error);
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -643,18 +643,16 @@ JSRuntime::JSRuntime()
     state(),
     cxCallback(NULL),
     compartmentCallback(NULL),
     activityCallback(NULL),
     activityCallbackArg(NULL),
     protoHazardShape(0),
     gcSystemAvailableChunkListHead(NULL),
     gcUserAvailableChunkListHead(NULL),
-    gcEmptyChunkListHead(NULL),
-    gcEmptyChunkCount(0),
     gcKeepAtoms(0),
     gcBytes(0),
     gcTriggerBytes(0),
     gcLastBytes(0),
     gcMaxBytes(0),
     gcMaxMallocBytes(0),
     gcEmptyArenaPoolLifespan(0),
     gcNumber(0),
@@ -690,16 +688,17 @@ JSRuntime::JSRuntime()
     hadOutOfMemory(false),
     data(NULL),
 #ifdef JS_THREADSAFE
     gcLock(NULL),
     gcDone(NULL),
     requestDone(NULL),
     requestCount(0),
     gcThread(NULL),
+    gcHelperThread(this),
     rtLock(NULL),
 # ifdef DEBUG
     rtLockOwner(0),
 # endif
     stateChange(NULL),
 #endif
     debuggerMutations(0),
     securityCallbacks(NULL),
@@ -2787,19 +2786,19 @@ JS_GetGCParameter(JSRuntime *rt, JSGCPar
         return rt->gcMaxMallocBytes;
       case JSGC_STACKPOOL_LIFESPAN:
         return rt->gcEmptyArenaPoolLifespan;
       case JSGC_BYTES:
         return rt->gcBytes;
       case JSGC_MODE:
         return uint32(rt->gcMode);
       case JSGC_UNUSED_CHUNKS:
-        return uint32(rt->gcEmptyChunkCount);
+        return uint32(rt->gcChunkPool.getEmptyCount());
       case JSGC_TOTAL_CHUNKS:
-        return uint32(rt->gcChunkSet.count() + rt->gcEmptyChunkCount);
+        return uint32(rt->gcChunkSet.count() + rt->gcChunkPool.getEmptyCount());
       default:
         JS_ASSERT(key == JSGC_NUMBER);
         return rt->gcNumber;
     }
 }
 
 JS_PUBLIC_API(void)
 JS_SetGCParameterForThread(JSContext *cx, JSGCParamKey key, uint32 value)
@@ -4845,17 +4844,17 @@ CompileUCFunctionForPrincipalsCommon(JSC
     JSFunction *fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, obj, funAtom);
     if (!fun)
         return NULL;
 
     if (!Compiler::compileFunctionBody(cx, fun, principals, &bindings,
                                        chars, length, filename, lineno, version)) {
         return NULL;
     }
-    
+
     if (obj && funAtom &&
         !obj->defineProperty(cx, ATOM_TO_JSID(funAtom), ObjectValue(*fun),
                              NULL, NULL, JSPROP_ENUMERATE)) {
         return NULL;
     }
 
     return fun;
 }
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2850,17 +2850,17 @@ typedef enum JSGCParamKey {
     JSGC_NUMBER = 4,
 
     /* Max size of the code cache in bytes. */
     JSGC_MAX_CODE_CACHE_BYTES = 5,
 
     /* Select GC mode. */
     JSGC_MODE = 6,
 
-    /* Number of GC chunks waiting to expire. */
+    /* Number of cached empty GC chunks. */
     JSGC_UNUSED_CHUNKS = 7,
 
     /* Total number of allocated GC chunks. */
     JSGC_TOTAL_CHUNKS = 8
 } JSGCParamKey;
 
 typedef enum JSGCMode {
     /* Perform only global GCs. */
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -91,18 +91,22 @@
  * Dense arrays do not track property creation order, so unlike other native
  * objects and slow arrays, enumerating an array does not necessarily visit the
  * properties in the order they were created.  We could instead maintain the
  * scope to track property enumeration order, but still use the fast slot
  * access.  That would have the same memory cost as just using a
  * SlowArrayClass, but have the same performance characteristics as a dense
  * array for slot accesses, at some cost in code complexity.
  */
+#include <limits.h>
 #include <stdlib.h>
 #include <string.h>
+
+#include "mozilla/RangedPtr.h"
+
 #include "jstypes.h"
 #include "jsstdint.h"
 #include "jsutil.h"
 
 #include "jsapi.h"
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jsbit.h"
@@ -136,16 +140,17 @@
 #include "jsobjinlines.h"
 #include "jsscopeinlines.h"
 #include "jscntxtinlines.h"
 #include "jsstrinlines.h"
 
 #include "vm/ArgumentsObject-inl.h"
 #include "vm/Stack-inl.h"
 
+using namespace mozilla;
 using namespace js;
 using namespace js::gc;
 using namespace js::types;
 
 JSBool
 js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp)
 {
     if (obj->isArray()) {
@@ -519,17 +524,17 @@ DeleteArrayElement(JSContext *cx, JSObje
 {
     JS_ASSERT(index >= 0);
     if (obj->isDenseArray()) {
         if (index <= jsuint(-1)) {
             jsuint idx = jsuint(index);
             if (idx < obj->getDenseArrayInitializedLength()) {
                 obj->markDenseArrayNotPacked(cx);
                 obj->setDenseArrayElement(idx, MagicValue(JS_ARRAY_HOLE));
-                if (!js_SuppressDeletedIndexProperties(cx, obj, idx, idx+1))
+                if (!js_SuppressDeletedElement(cx, obj, idx))
                     return -1;
             }
         }
         return 1;
     }
 
     AutoIdRooter idr(cx);
 
@@ -1715,17 +1720,17 @@ InitArrayTypes(JSContext *cx, TypeObject
             Type valtype = GetValueType(cx, vector[i]);
             types->addType(cx, valtype);
         }
     }
     return true;
 }
 
 static JSBool
-InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint count, Value *vector, bool updateTypes)
+InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint count, const Value *vector, bool updateTypes)
 {
     JS_ASSERT(count <= MAX_ARRAY_INDEX);
 
     if (count == 0)
         return JS_TRUE;
 
     if (updateTypes && !InitArrayTypes(cx, obj->getType(cx), vector, count))
         return JS_FALSE;
@@ -1746,24 +1751,24 @@ InitArrayElements(JSContext *cx, JSObjec
                 return false;
             JS_ASSERT(result == JSObject::ED_SPARSE);
             break;
         }
         jsuint newlen = start + count;
         if (newlen > obj->getArrayLength())
             obj->setDenseArrayLength(newlen);
 
-        JS_ASSERT(count < uint32(-1) / sizeof(Value));
+        JS_ASSERT(count < UINT32_MAX / sizeof(Value));
         obj->copyDenseArrayElements(start, vector, count);
         JS_ASSERT_IF(count != 0, !obj->getDenseArrayElement(newlen - 1).isMagic(JS_ARRAY_HOLE));
         return true;
     } while (false);
 
-    Value* end = vector + count;
-    while (vector != end && start <= MAX_ARRAY_INDEX) {
+    const Value* end = vector + count;
+    while (vector < end && start <= MAX_ARRAY_INDEX) {
         if (!JS_CHECK_OPERATION_LIMIT(cx) ||
             !SetArrayElement(cx, obj, start++, *vector++)) {
             return JS_FALSE;
         }
     }
 
     if (vector == end)
         return JS_TRUE;
@@ -1783,16 +1788,17 @@ InitArrayElements(JSContext *cx, JSObjec
             return JS_FALSE;
         }
         idval.getDoubleRef() += 1;
     } while (vector != end);
 
     return JS_TRUE;
 }
 
+#if 0
 static JSBool
 InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, const Value *vector)
 {
     JS_ASSERT(obj->isArray());
 
     JS_ASSERT(obj->isDenseArray());
     obj->setArrayLength(cx, length);
     if (!vector || !length)
@@ -1815,16 +1821,17 @@ InitArrayObject(JSContext *cx, JSObject 
         obj->setDenseArrayElement(i, vector[i]);
         hole |= vector[i].isMagic(JS_ARRAY_HOLE);
     }
     if (hole)
         obj->markDenseArrayNotPacked(cx);
 
     return true;
 }
+#endif
 
 /*
  * Perl-inspired join, reverse, and sort.
  */
 static JSBool
 array_join(JSContext *cx, uintN argc, Value *vp)
 {
     JS_CHECK_RECURSION(cx, return false);
@@ -2687,164 +2694,233 @@ TryReuseArrayType(JSObject *obj, JSObjec
      */
     JS_ASSERT(nobj->isDenseArray());
     JS_ASSERT(nobj->type() == nobj->getProto()->newType);
 
     if (obj->isArray() && !obj->hasSingletonType() && obj->getProto() == nobj->getProto())
         nobj->setType(obj->type());
 }
 
+/*
+ * Returns true if this is a dense array whose |count| properties starting from
+ * |startingIndex| may be accessed (get, set, delete) directly through its
+ * contiguous vector of elements without fear of getters, setters, etc. along
+ * the prototype chain.
+ */
+static inline bool
+CanOptimizeForDenseStorage(JSObject *arr, uint32 startingIndex, uint32 count, JSContext *cx)
+{
+    JS_ASSERT(UINT32_MAX - startingIndex >= count);
+
+    uint32 length = startingIndex + count;
+    return arr->isDenseArray() &&
+           !arr->getType(cx)->hasAllFlags(OBJECT_FLAG_NON_PACKED_ARRAY) &&
+           !js_PrototypeHasIndexedProperties(cx, arr) &&
+           length <= arr->getDenseArrayInitializedLength();
+}
+
+static inline bool
+CopyArrayElement(JSContext *cx, JSObject *source, uint32 sourceIndex,
+                 JSObject *target, uint32 targetIndex)
+{
+    if (!JS_CHECK_OPERATION_LIMIT(cx))
+        return false;
+
+    JSBool hole;
+    Value fromValue;
+    return GetElement(cx, source, sourceIndex, &hole, &fromValue) &&
+           SetOrDeleteArrayElement(cx, target, targetIndex, hole, fromValue);
+}
+
+/* ES5 15.4.4.12. */
 static JSBool
 array_splice(JSContext *cx, uintN argc, Value *vp)
 {
-    JSObject *obj = ToObject(cx, &vp[1]);
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    /* Step 1. */
+    JSObject *obj = ToObject(cx, &args.thisv());
     if (!obj)
         return false;
 
-    jsuint length, begin, end, count, delta, last;
-    JSBool hole;
-
-    /* Create a new array value to return. */
-    JSObject *obj2 = NewDenseEmptyArray(cx);
-    if (!obj2)
-        return JS_FALSE;
-    TryReuseArrayType(obj, obj2);
-    vp->setObject(*obj2);
-
-    /* Nothing to do if no args.  Otherwise get length. */
-    if (argc == 0)
-        return JS_TRUE;
-    Value *argv = JS_ARGV(cx, vp);
-    if (!js_GetLengthProperty(cx, obj, &length))
-        return JS_FALSE;
-    jsuint origlength = length;
-
-    /* Convert the first argument into a starting index. */
-    jsdouble d;
-    if (!ToInteger(cx, *argv, &d))
-        return JS_FALSE;
-    if (d < 0) {
-        d += length;
-        if (d < 0)
-            d = 0;
-    } else if (d > length) {
-        d = length;
+    /* Steps 3-4. */
+    uint32 len;
+    if (!js_GetLengthProperty(cx, obj, &len))
+        return false;
+
+    /* Step 5. */
+    double relativeStart;
+    if (!ToInteger(cx, argc >= 1 ? args[0] : UndefinedValue(), &relativeStart))
+        return false;
+
+    /* Step 6. */
+    uint32 actualStart;
+    if (relativeStart < 0)
+        actualStart = JS_MAX(len + relativeStart, 0);
+    else
+        actualStart = JS_MIN(relativeStart, len);
+
+    /* Step 7. */
+    uint32 actualDeleteCount;
+    if (argc != 1) {
+        jsdouble deleteCountDouble;
+        if (!ToInteger(cx, argc >= 2 ? args[1] : Int32Value(0), &deleteCountDouble))
+            return false;
+        actualDeleteCount = JS_MIN(JS_MAX(deleteCountDouble, 0), len - actualStart);
+    } else {
+        /*
+         * Non-standard: if start was specified but deleteCount was omitted,
+         * delete to the end of the array.  See bug 668024 for discussion.
+         */
+        actualDeleteCount = len - actualStart;
     }
-    begin = (jsuint)d; /* d has been clamped to uint32 */
-    argc--;
-    argv++;
-
-    /* Convert the second argument from a count into a fencepost index. */
-    delta = length - begin;
-    if (argc == 0) {
-        count = delta;
-        end = length;
-    } else {
-        if (!ToInteger(cx, *argv, &d))
+
+    JS_ASSERT(len - actualStart >= actualDeleteCount);
+
+    /* Steps 2, 8-9. */
+    JSObject *arr;
+    if (CanOptimizeForDenseStorage(obj, actualStart, actualDeleteCount, cx)) {
+        arr = NewDenseCopiedArray(cx, actualDeleteCount,
+                                  obj->getDenseArrayElements() + actualStart);
+        if (!arr)
             return false;
-        if (d < 0)
-            d = 0;
-        else if (d > delta)
-            d = delta;
-        count = (jsuint)d;
-        end = begin + count;
-        argc--;
-        argv++;
-    }
-
-    AutoValueRooter tvr(cx);
-
-    /* If there are elements to remove, put them into the return value. */
-    if (count > 0) {
-        if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj) &&
-            end <= obj->getDenseArrayInitializedLength()) {
-            if (!InitArrayObject(cx, obj2, count, obj->getDenseArrayElements() + begin))
-                return JS_FALSE;
-        } else {
-            for (last = begin; last < end; last++) {
-                if (!JS_CHECK_OPERATION_LIMIT(cx) ||
-                    !GetElement(cx, obj, last, &hole, tvr.addr())) {
-                    return JS_FALSE;
-                }
-
-                /* Copy tvr.value() to the new array unless it's a hole. */
-                if (!hole && !SetArrayElement(cx, obj2, last - begin, tvr.value()))
-                    return JS_FALSE;
+        TryReuseArrayType(obj, arr);
+    } else {
+        arr = NewDenseAllocatedArray(cx, actualDeleteCount);
+        if (!arr)
+            return false;
+        TryReuseArrayType(obj, arr);
+
+        for (uint32 k = 0; k < actualDeleteCount; k++) {
+            JSBool hole;
+            Value fromValue;
+            if (!JS_CHECK_OPERATION_LIMIT(cx) ||
+                !GetElement(cx, obj, actualStart + k, &hole, &fromValue) ||
+                (!hole && !arr->defineElement(cx, k, fromValue)))
+            {
+                return false;
             }
-
-            if (!js_SetLengthProperty(cx, obj2, count))
-                return JS_FALSE;
         }
     }
 
-    /* Find the direction (up or down) to copy and make way for argv. */
-    if (argc > count) {
-        delta = (jsuint)argc - count;
-        last = length;
-        bool optimized = false;
-        do {
-            if (!obj->isDenseArray())
-                break;
-            if (js_PrototypeHasIndexedProperties(cx, obj))
-                break;
-            if (length > obj->getDenseArrayInitializedLength())
-                break;
-            if (length != 0 && obj->getDenseArrayElement(length - 1).isMagic(JS_ARRAY_HOLE))
-                break;
-            JSObject::EnsureDenseResult result = obj->ensureDenseArrayElements(cx, length, delta);
-            if (result != JSObject::ED_OK) {
-                if (result == JSObject::ED_FAILED)
+    /* Step 11. */
+    uint32 itemCount = (argc >= 2) ? (argc - 2) : 0;
+
+    if (itemCount < actualDeleteCount) {
+        /* Step 12: the array is being shrunk. */
+        uint32 sourceIndex = actualStart + actualDeleteCount;
+        uint32 targetIndex = actualStart + itemCount;
+        uint32 finalLength = len - actualDeleteCount + itemCount;
+
+        if (CanOptimizeForDenseStorage(obj, 0, len, cx)) {
+            /* Steps 12(a)-(b). */
+            obj->moveDenseArrayElements(targetIndex, sourceIndex, len - sourceIndex);
+
+            /* Steps 12(c)-(d). */
+            obj->shrinkDenseArrayElements(cx, finalLength);
+
+            /*
+             * The array's initialized length is now out of sync with the array
+             * elements: resynchronize it.
+             */
+            if (cx->typeInferenceEnabled())
+                obj->setDenseArrayInitializedLength(finalLength);
+
+            /* Fix running enumerators for the deleted items. */
+            if (!js_SuppressDeletedElements(cx, obj, finalLength, len))
+                return false;
+        } else {
+            /*
+             * This is all very slow if the length is very large. We don't yet
+             * have the ability to iterate in sorted order, so we just do the
+             * pessimistic thing and let JS_CHECK_OPERATION_LIMIT handle the
+             * fallout.
+             */
+
+            /* Steps 12(a)-(b). */
+            for (uint32 from = sourceIndex, to = targetIndex; from < len; from++, to++) {
+                JSBool hole;
+                Value fromValue;
+                if (!JS_CHECK_OPERATION_LIMIT(cx) ||
+                    !GetElement(cx, obj, from, &hole, &fromValue) ||
+                    !SetOrDeleteArrayElement(cx, obj, to, hole, fromValue))
+                {
                     return false;
-                JS_ASSERT(result == JSObject::ED_SPARSE);
-                break;
+                }
+            }
+
+            /* Steps 12(c)-(d). */
+            for (uint32 k = len; k > finalLength; k--) {
+                if (DeleteArrayElement(cx, obj, k - 1, true) < 0)
+                    return false;
             }
-            obj->moveDenseArrayElements(end + delta, end, last - end);
-
-            obj->setArrayLength(cx, obj->getArrayLength() + delta);
-            optimized = true;
-        } while (false);
-
-        if (!optimized) {
-            /* (uint) end could be 0, so we can't use a vanilla >= test. */
-            while (last-- > end) {
+        }
+    } else if (itemCount > actualDeleteCount) {
+        /* Step 13. */
+
+        /*
+         * Optimize only if the array is already dense and we can extend it to
+         * its new length.
+         */
+        if (obj->isDenseArray()) {
+            JSObject::EnsureDenseResult res =
+                obj->ensureDenseArrayElements(cx, obj->getArrayLength(),
+                                              itemCount - actualDeleteCount);
+            if (res == JSObject::ED_FAILED)
+                return false;
+
+            if (res == JSObject::ED_SPARSE) {
+                if (!obj->makeDenseArraySlow(cx))
+                    return false;
+            } else {
+                JS_ASSERT(res == JSObject::ED_OK);
+            }
+        }
+
+        if (CanOptimizeForDenseStorage(obj, len, itemCount - actualDeleteCount, cx)) {
+            obj->moveDenseArrayElements(actualStart + itemCount,
+                                        actualStart + actualDeleteCount,
+                                        len - (actualStart + actualDeleteCount));
+
+            if (cx->typeInferenceEnabled())
+                obj->setDenseArrayInitializedLength(len + itemCount - actualDeleteCount);
+        } else {
+            for (jsdouble k = len - actualDeleteCount; k > actualStart; k--) {
+                jsdouble from = k + actualDeleteCount - 1;
+                jsdouble to = k + itemCount - 1;
+
+                JSBool hole;
+                Value fromValue;
                 if (!JS_CHECK_OPERATION_LIMIT(cx) ||
-                    !GetElement(cx, obj, last, &hole, tvr.addr()) ||
-                    !SetOrDeleteArrayElement(cx, obj, last + delta, hole, tvr.value())) {
-                    return JS_FALSE;
+                    !GetElement(cx, obj, from, &hole, &fromValue) ||
+                    !SetOrDeleteArrayElement(cx, obj, to, hole, fromValue))
+                {
+                    return false;
                 }
             }
         }
-        length += delta;
-    } else if (argc < count) {
-        delta = count - (jsuint)argc;
-        if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj) &&
-            length <= obj->getDenseArrayInitializedLength()) {
-
-            obj->moveDenseArrayElements(end - delta, end, length - end);
-        } else {
-            for (last = end; last < length; last++) {
-                if (!JS_CHECK_OPERATION_LIMIT(cx) ||
-                    !GetElement(cx, obj, last, &hole, tvr.addr()) ||
-                    !SetOrDeleteArrayElement(cx, obj, last - delta, hole, tvr.value())) {
-                    return JS_FALSE;
-                }
-            }
-        }
-        length -= delta;
+    }
+
+    /* Step 10. */
+    Value *items = args.array() + 2;
+
+    /* Steps 14-15. */
+    for (uint32 k = actualStart, i = 0; i < itemCount; i++, k++) {
+        if (!SetArrayElement(cx, obj, k, items[i]))
+            return false;
     }
 
-    if (length < origlength && !js_SuppressDeletedIndexProperties(cx, obj, length, origlength))
-        return JS_FALSE;
-
-    /*
-     * Copy from argv into the hole to complete the splice, and update length in
-     * case we deleted elements from the end.
-     */
-    return InitArrayElements(cx, obj, begin, argc, argv, true) &&
-           js_SetLengthProperty(cx, obj, length);
+    /* Step 16. */
+    jsdouble finalLength = jsdouble(len) - actualDeleteCount + itemCount;
+    if (!js_SetLengthProperty(cx, obj, finalLength))
+        return false;
+
+    /* Step 17. */
+    args.rval().setObject(*arr);
+    return true;
 }
 
 /*
  * Python-esque sequence operations.
  */
 static JSBool
 array_concat(JSContext *cx, uintN argc, Value *vp)
 {
@@ -3490,18 +3566,25 @@ NewArray(JSContext *cx, jsuint length, J
 
     obj->setArrayLength(cx, length);
 
     if (!cx->typeInferenceEnabled()) {
         obj->markDenseArrayNotPacked(cx);
         obj->backfillDenseArrayHoles(cx);
     }
 
-    if (allocateCapacity && !obj->ensureSlots(cx, length))
-        return NULL;
+    if (allocateCapacity) {
+        /* If ensureSlots creates dynamically allocated slots, then having fixedSlots is a waste. */
+        DebugOnly<uint32> oldSlots = obj->numSlots();
+
+        if (!obj->ensureSlots(cx, length))
+            return NULL;
+
+        JS_ASSERT_IF(obj->numFixedSlots(), oldSlots == obj->numSlots());
+    }
 
     return obj;
 }
 
 JSObject * JS_FASTCALL
 NewDenseEmptyArray(JSContext *cx, JSObject *proto)
 {
     return NewArray<false>(cx, 0, proto);
@@ -3535,17 +3618,17 @@ mjit::stubs::NewDenseUnallocatedArray(VM
         js_ReportOutOfMemory(f.cx);
         THROWV(NULL);
     }
     return obj;
 }
 #endif
 
 JSObject *
-NewDenseCopiedArray(JSContext *cx, uintN length, const Value *vp, JSObject *proto)
+NewDenseCopiedArray(JSContext *cx, uint32 length, const Value *vp, JSObject *proto /* = NULL */)
 {
     JSObject* obj = NewArray<true>(cx, length, proto);
     if (!obj)
         return NULL;
 
     JS_ASSERT(obj->getDenseArrayCapacity() >= length);
 
     if (cx->typeInferenceEnabled())
--- a/js/src/jsarray.h
+++ b/js/src/jsarray.h
@@ -151,17 +151,17 @@ NewDenseAllocatedEmptyArray(JSContext *c
  * Create a dense array with a set length, but without allocating space for the
  * contents. This is useful, e.g., when accepting length from the user.
  */
 extern JSObject * JS_FASTCALL
 NewDenseUnallocatedArray(JSContext *cx, uint length, JSObject *proto=NULL);
 
 /* Create a dense array with a copy of vp. */
 extern JSObject *
-NewDenseCopiedArray(JSContext *cx, uint length, const Value *vp, JSObject *proto=NULL);
+NewDenseCopiedArray(JSContext *cx, uint32 length, const Value *vp, JSObject *proto = NULL);
 
 /* Create a sparse array. */
 extern JSObject *
 NewSlowEmptyArray(JSContext *cx);
 
 }
 
 extern JSBool
@@ -175,16 +175,18 @@ namespace js {
 extern JSBool
 array_defineProperty(JSContext *cx, JSObject *obj, jsid id, const Value *value,
                      PropertyOp getter, StrictPropertyOp setter, uintN attrs);
 
 extern JSBool
 array_deleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool strict);
 
 /*
+ * Copy 'length' elements from aobj to vp.
+ *
  * This function assumes 'length' is effectively the result of calling
  * js_GetLengthProperty on aobj.
  */
 extern bool
 GetElements(JSContext *cx, JSObject *aobj, jsuint length, js::Value *vp);
 
 }
 
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -471,20 +471,20 @@ js_DestroyContext(JSContext *cx, JSDestr
         rt->state = JSRTS_LANDING;
     if (last || mode == JSDCM_FORCE_GC || mode == JSDCM_MAYBE_GC
 #ifdef JS_THREADSAFE
         || cx->outstandingRequests != 0
 #endif
         ) {
         JS_ASSERT(!rt->gcRunning);
 
+#ifdef JS_THREADSAFE
+        rt->gcHelperThread.waitBackgroundSweepEnd();
+#endif
         JS_UNLOCK_GC(rt);
-#ifdef JS_THREADSAFE
-        rt->gcHelperThread.waitBackgroundSweepEnd(rt);
-#endif
 
         if (last) {
 #ifdef JS_THREADSAFE
             /*
              * If this thread is not in a request already, begin one now so
              * that we wait for any racing GC started on a not-last context to
              * finish, before we plow ahead and unpin atoms. Note that even
              * though we begin a request here if necessary, we end all
@@ -554,20 +554,20 @@ js_DestroyContext(JSContext *cx, JSDestr
     }
 #ifdef JS_THREADSAFE
 #ifdef DEBUG
     JSThread *t = cx->thread();
 #endif
     js_ClearContextThread(cx);
     JS_ASSERT_IF(JS_CLIST_IS_EMPTY(&t->contextList), !t->data.requestDepth);
 #endif
+#ifdef JS_THREADSAFE
+    rt->gcHelperThread.waitBackgroundSweepEnd();
+#endif
     JS_UNLOCK_GC(rt);
-#ifdef JS_THREADSAFE
-    rt->gcHelperThread.waitBackgroundSweepEnd(rt);
-#endif
     Foreground::delete_(cx);
 }
 
 JSContext *
 js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp)
 {
     JSContext *cx = *iterp;
 
@@ -1172,17 +1172,20 @@ js_InvokeOperationCallback(JSContext *cx
          * we check the quota and report OOM here when we are off trace.
          */
         if (checkOutOfMemory(rt)) {
 #ifdef JS_THREADSAFE
             /*
             * We have to wait until the background thread is done in order
             * to get a correct answer.
             */
-            rt->gcHelperThread.waitBackgroundSweepEnd(rt);
+            {
+                AutoLockGC lock(rt);
+                rt->gcHelperThread.waitBackgroundSweepEnd();
+            }
             if (checkOutOfMemory(rt)) {
                 js_ReportOutOfMemory(cx);
                 return false;
             }
 #else
             js_ReportOutOfMemory(cx);
             return false;
 #endif
@@ -1513,27 +1516,35 @@ JSRuntime::onTooMuchMalloc()
 #endif
     GCREASON(TOOMUCHMALLOC);
     TriggerGC(this);
 }
 
 JS_FRIEND_API(void *)
 JSRuntime::onOutOfMemory(void *p, size_t nbytes, JSContext *cx)
 {
+    /*
+     * Retry when we are done with the background sweeping and have stopped
+     * all the allocations and released the empty GC chunks.
+     */
+    {
 #ifdef JS_THREADSAFE
-    gcHelperThread.waitBackgroundSweepEnd(this);
+        AutoLockGC lock(this);
+        gcHelperThread.waitBackgroundSweepOrAllocEnd();
+#endif
+        gcChunkPool.expire(this, true);
+    }
     if (!p)
         p = OffTheBooks::malloc_(nbytes);
     else if (p == reinterpret_cast<void *>(1))
         p = OffTheBooks::calloc_(nbytes);
     else
       p = OffTheBooks::realloc_(p, nbytes);
     if (p)
         return p;
-#endif
     if (cx)
         js_ReportOutOfMemory(cx);
     return NULL;
 }
 
 void
 JSContext::purge()
 {
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -406,24 +406,17 @@ struct JSRuntime {
      * Doubly-linked lists of chunks from user and system compartments. The GC
      * allocates its arenas from the corresponding list and when all arenas
      * in the list head are taken, then the chunk is removed from the list.
      * During the GC when all arenas in a chunk become free, that chunk is
      * removed from the list and scheduled for release.
      */
     js::gc::Chunk       *gcSystemAvailableChunkListHead;
     js::gc::Chunk       *gcUserAvailableChunkListHead;
-
-    /*
-     * Singly-linked list of empty chunks and its length. We use the list not
-     * to release empty chunks immediately so they can be used for future
-     * allocations. This avoids very high overhead of chunk release/allocation.
-     */
-    js::gc::Chunk       *gcEmptyChunkListHead;
-    size_t              gcEmptyChunkCount;
+    js::gc::ChunkPool   gcChunkPool;
 
     js::RootedValueMap  gcRootsHash;
     js::GCLocks         gcLocksHash;
     jsrefcount          gcKeepAtoms;
     uint32              gcBytes;
     uint32              gcTriggerBytes;
     size_t              gcLastBytes;
     size_t              gcMaxBytes;
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -919,47 +919,42 @@ JSCompartment::clearTraps(JSContext *cx,
 bool
 JSCompartment::markTrapClosuresIteratively(JSTracer *trc)
 {
     bool markedAny = false;
     JSContext *cx = trc->context;
     for (BreakpointSiteMap::Range r = breakpointSites.all(); !r.empty(); r.popFront()) {
         BreakpointSite *site = r.front().value;
 
-        // Mark jsdbgapi state if any. But if we know the scriptObject, put off
-        // marking trap state until we know the scriptObject is live.
-        if (site->trapHandler &&
-            (!site->scriptObject || !IsAboutToBeFinalized(cx, site->scriptObject)))
-        {
+        // Put off marking trap state until we know the script is live.
+        if (site->trapHandler && !IsAboutToBeFinalized(cx, site->script)) {
             if (site->trapClosure.isMarkable() &&
                 IsAboutToBeFinalized(cx, site->trapClosure.toGCThing()))
             {
                 markedAny = true;
             }
             MarkValue(trc, site->trapClosure, "trap closure");
         }
     }
     return markedAny;
 }
 
 void
 JSCompartment::sweepBreakpoints(JSContext *cx)
 {
     for (BreakpointSiteMap::Enum e(breakpointSites); !e.empty(); e.popFront()) {
         BreakpointSite *site = e.front().value;
-        if (site->scriptObject) {
-            // clearTrap and nextbp are necessary here to avoid possibly
-            // reading *site or *bp after destroying it.
-            bool scriptGone = IsAboutToBeFinalized(cx, site->scriptObject);
-            bool clearTrap = scriptGone && site->hasTrap();
-
-            Breakpoint *nextbp;
-            for (Breakpoint *bp = site->firstBreakpoint(); bp; bp = nextbp) {
-                nextbp = bp->nextInSite();
-                if (scriptGone || IsAboutToBeFinalized(cx, bp->debugger->toJSObject()))
-                    bp->destroy(cx, &e);
-            }
-
-            if (clearTrap)
-                site->clearTrap(cx, &e);
+        // clearTrap and nextbp are necessary here to avoid possibly
+        // reading *site or *bp after destroying it.
+        bool scriptGone = IsAboutToBeFinalized(cx, site->script);
+        bool clearTrap = scriptGone && site->hasTrap();
+        
+        Breakpoint *nextbp;
+        for (Breakpoint *bp = site->firstBreakpoint(); bp; bp = nextbp) {
+            nextbp = bp->nextInSite();
+            if (scriptGone || IsAboutToBeFinalized(cx, bp->debugger->toJSObject()))
+                bp->destroy(cx, &e);
         }
+        
+        if (clearTrap)
+            site->clearTrap(cx, &e);
     }
 }
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -2431,17 +2431,17 @@ js_CloneFunctionObject(JSContext *cx, JS
             if (!cscript)
                 return NULL;
 
             cfun->setScript(cscript);
             if (!cfun->script()->typeSetFunction(cx, cfun))
                 return NULL;
 
             js_CallNewScriptHook(cx, cfun->script(), cfun);
-            Debugger::onNewScript(cx, cfun->script(), cfun, Debugger::NewHeldScript);
+            Debugger::onNewScript(cx, cfun->script(), cfun, NULL);
         }
     }
     return clone;
 }
 
 #ifdef JS_TRACER
 JS_DEFINE_CALLINFO_4(extern, OBJECT, js_CloneFunctionObject, CONTEXT, FUNCTION, OBJECT, OBJECT, 0,
                      nanojit::ACCSET_STORE_ANY)
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -409,18 +409,148 @@ FinalizeArenas(JSContext *cx, ArenaLists
 	FinalizeTypedArenas<JSShortString>(cx, al, thingKind);
         break;
       case FINALIZE_EXTERNAL_STRING:
 	FinalizeTypedArenas<JSExternalString>(cx, al, thingKind);
         break;
     }
 }
 
-} /* namespace gc */
-} /* namespace js */
+#ifdef JS_THREADSAFE
+inline bool
+ChunkPool::wantBackgroundAllocation(JSRuntime *rt) const
+{
+    /*
+     * To minimize memory waste we do not want to run the background chunk
+     * allocation if we have empty chunks or when the runtime needs just few
+     * of them.
+     */
+    return rt->gcHelperThread.canBackgroundAllocate() &&
+           emptyCount == 0 &&
+           rt->gcChunkSet.count() >= 4;
+}
+#endif
+
+/* Must be called with the GC lock taken. */
+inline Chunk *
+ChunkPool::get(JSRuntime *rt)
+{
+    JS_ASSERT(this == &rt->gcChunkPool);
+
+    Chunk *chunk = emptyChunkListHead;
+    if (chunk) {
+        JS_ASSERT(emptyCount);
+        emptyChunkListHead = chunk->info.next;
+        --emptyCount;
+    } else {
+        JS_ASSERT(!emptyCount);
+        chunk = Chunk::allocate();
+        if (!chunk)
+            return NULL;
+    }
+    JS_ASSERT(chunk->unused());
+    JS_ASSERT(!rt->gcChunkSet.has(chunk));
+
+#ifdef JS_THREADSAFE
+    if (wantBackgroundAllocation(rt))
+        rt->gcHelperThread.startBackgroundAllocationIfIdle();
+#endif
+
+    return chunk;
+}
+
+/* Must be called either during the GC or with the GC lock taken. */
+inline void
+ChunkPool::put(JSRuntime *rt, Chunk *chunk)
+{
+    JS_ASSERT(this == &rt->gcChunkPool);
+
+    size_t initialAge = 0;
+#ifdef JS_THREADSAFE
+    /*
+     * When we have not yet started the background finalization, we must keep
+     * empty chunks until we are done with all the sweeping and finalization
+     * that cannot be done in the background even if shouldShrink() is true.
+     * This way we can safely call IsAboutToBeFinalized and Cell::isMarked for
+     * finalized GC things in empty chunks. So we only release the chunk if we
+     * are called from the background thread.
+     */
+    if (rt->gcHelperThread.sweeping()) {
+        if (rt->gcHelperThread.shouldShrink()) {
+            Chunk::release(chunk);
+            return;
+        }
+
+        /*
+         * Set the age to one as we expire chunks early during the background
+         * sweep so this chunk already survived one GC cycle.
+         */
+        initialAge = 1;
+    }
+#endif
+
+    chunk->info.age = initialAge;
+    chunk->info.next = emptyChunkListHead;
+    emptyChunkListHead = chunk;
+    emptyCount++;
+}
+
+/* Must be called either during the GC or with the GC lock taken. */
+void
+ChunkPool::expire(JSRuntime *rt, bool releaseAll)
+{
+    JS_ASSERT(this == &rt->gcChunkPool);
+
+    /*
+     * Return old empty chunks to the system while preserving the order of
+     * other chunks in the list. This way, if the GC runs several times
+     * without emptying the list, the older chunks will stay at the tail
+     * and are more likely to reach the max age.
+     */
+    for (Chunk **chunkp = &emptyChunkListHead; *chunkp; ) {
+        JS_ASSERT(emptyCount);
+        Chunk *chunk = *chunkp;
+        JS_ASSERT(chunk->unused());
+        JS_ASSERT(!rt->gcChunkSet.has(chunk));
+        JS_ASSERT(chunk->info.age <= MAX_EMPTY_CHUNK_AGE);
+        if (releaseAll || chunk->info.age == MAX_EMPTY_CHUNK_AGE) {
+            *chunkp = chunk->info.next;
+            --emptyCount;
+            Chunk::release(chunk);
+        } else {
+            /* Keep the chunk but increase its age. */
+            ++chunk->info.age;
+            chunkp = &chunk->info.next;
+        }
+    }
+    JS_ASSERT_IF(releaseAll, !emptyCount);
+}
+
+/* static */ Chunk *
+Chunk::allocate()
+{
+    Chunk *chunk = static_cast<Chunk *>(AllocGCChunk());
+    if (!chunk)
+        return NULL;
+    chunk->init();
+#ifdef MOZ_GCTIMER
+    JS_ATOMIC_INCREMENT(&newChunkCount);
+#endif
+    return chunk;
+}
+
+/* static */ inline void
+Chunk::release(Chunk *chunk)
+{
+    JS_ASSERT(chunk);
+#ifdef MOZ_GCTIMER
+    JS_ATOMIC_INCREMENT(&destroyChunkCount);
+#endif
+    FreeGCChunk(chunk);
+}
 
 void
 Chunk::init()
 {
     JS_POISON(this, JS_FREE_PATTERN, GC_CHUNK_SIZE);
 
     /* Assemble all arenas into a linked list and mark them as not allocated. */
     ArenaHeader **prevp = &info.emptyArenaListHead;
@@ -506,25 +636,25 @@ void
 Chunk::releaseArena(ArenaHeader *aheader)
 {
     JS_ASSERT(aheader->allocated());
     JS_ASSERT(!aheader->hasDelayedMarking);
     JSCompartment *comp = aheader->compartment;
     JSRuntime *rt = comp->rt;
 #ifdef JS_THREADSAFE
     AutoLockGC maybeLock;
-    if (rt->gcHelperThread.sweeping)
+    if (rt->gcHelperThread.sweeping())
         maybeLock.lock(rt);
 #endif
 
     Probes::resizeHeap(comp, rt->gcBytes, rt->gcBytes - ArenaSize);
     JS_ASSERT(size_t(rt->gcBytes) >= ArenaSize);
     JS_ASSERT(size_t(comp->gcBytes) >= ArenaSize);
 #ifdef JS_THREADSAFE
-    if (rt->gcHelperThread.sweeping) {
+    if (rt->gcHelperThread.sweeping()) {
         rt->reduceGCTriggerBytes(GC_HEAP_GROWTH_FACTOR * ArenaSize);
         comp->reduceGCTriggerBytes(GC_HEAP_GROWTH_FACTOR * ArenaSize);
     }
 #endif
     JS_ATOMIC_ADD(&rt->gcBytes, -int32(ArenaSize));
     JS_ATOMIC_ADD(&comp->gcBytes, -int32(ArenaSize));
 
     aheader->setAsNotAllocated();
@@ -535,123 +665,57 @@ Chunk::releaseArena(ArenaHeader *aheader
         JS_ASSERT(!info.prevp);
         JS_ASSERT(!info.next);
         addToAvailableList(aheader->compartment);
     } else if (!unused()) {
         JS_ASSERT(info.prevp);
     } else {
         rt->gcChunkSet.remove(this);
         removeFromAvailableList();
-
-        /*
-         * We keep empty chunks until we are done with finalization to allow
-         * calling IsAboutToBeFinalized/Cell::isMarked for finalized GC things
-         * in empty chunks. So we add the chunk to the empty set even during
-         * GC_SHRINK.
-         */
-        info.age = 0;
-        info.next = rt->gcEmptyChunkListHead;
-        rt->gcEmptyChunkListHead = this;
-        rt->gcEmptyChunkCount++;
+        rt->gcChunkPool.put(rt, this);
     }
 }
 
-inline Chunk *
-AllocateGCChunk(JSRuntime *rt)
-{
-    Chunk *p = static_cast<Chunk *>(AllocGCChunk());
-#ifdef MOZ_GCTIMER
-    if (p)
-        JS_ATOMIC_INCREMENT(&newChunkCount);
-#endif
-    return p;
-}
-
-inline void
-ReleaseGCChunk(JSRuntime *rt, Chunk *p)
-{
-    JS_ASSERT(p);
-#ifdef MOZ_GCTIMER
-    JS_ATOMIC_INCREMENT(&destroyChunkCount);
-#endif
-    FreeGCChunk(p);
-}
+} /* namespace gc */
+} /* namespace js */
 
 /* The caller must hold the GC lock. */
 static Chunk *
 PickChunk(JSCompartment *comp)
 {
     JSRuntime *rt = comp->rt;
     Chunk **listHeadp = GetAvailableChunkList(comp);
     Chunk *chunk = *listHeadp;
     if (chunk)
         return chunk;
 
-    /*
-     * We do not have available chunks, either get one from the empty set or
-     * allocate one.
-     */
-    chunk = rt->gcEmptyChunkListHead;
-    if (chunk) {
-        JS_ASSERT(chunk->unused());
-        JS_ASSERT(!rt->gcChunkSet.has(chunk));
-        JS_ASSERT(rt->gcEmptyChunkCount >= 1);
-        rt->gcEmptyChunkListHead = chunk->info.next;
-        rt->gcEmptyChunkCount--;
-    } else {
-        chunk = AllocateGCChunk(rt);
-        if (!chunk)
-            return NULL;
-
-        chunk->init();
-        rt->gcChunkAllocationSinceLastGC = true;
-    }
+    chunk = rt->gcChunkPool.get(rt);
+    if (!chunk)
+        return NULL;
+
+    rt->gcChunkAllocationSinceLastGC = true;
 
     /*
      * FIXME bug 583732 - chunk is newly allocated and cannot be present in
      * the table so using ordinary lookupForAdd is suboptimal here.
      */
     GCChunkSet::AddPtr p = rt->gcChunkSet.lookupForAdd(chunk);
     JS_ASSERT(!p);
     if (!rt->gcChunkSet.add(p, chunk)) {
-        ReleaseGCChunk(rt, chunk);
+        Chunk::release(chunk);
         return NULL;
     }
 
     chunk->info.prevp = NULL;
     chunk->info.next = NULL;
     chunk->addToAvailableList(comp);
 
     return chunk;
 }
 
-static void
-ExpireGCChunks(JSRuntime *rt, JSGCInvocationKind gckind)
-{
-    AutoLockGC lock(rt);
-
-    /* Return old empty chunks to the system. */
-    for (Chunk **chunkp = &rt->gcEmptyChunkListHead; *chunkp; ) {
-        JS_ASSERT(rt->gcEmptyChunkCount);
-        Chunk *chunk = *chunkp;
-        JS_ASSERT(chunk->unused());
-        JS_ASSERT(!rt->gcChunkSet.has(chunk));
-        JS_ASSERT(chunk->info.age <= MAX_EMPTY_CHUNK_AGE);
-        if (gckind == GC_SHRINK || chunk->info.age == MAX_EMPTY_CHUNK_AGE) {
-            *chunkp = chunk->info.next;
-            --rt->gcEmptyChunkCount;
-            ReleaseGCChunk(rt, chunk);
-        } else {
-            /* Keep the chunk but increase its age. */
-            ++chunk->info.age;
-            chunkp = &chunk->info.next;
-        }
-    }
-}
-
 JS_FRIEND_API(bool)
 IsAboutToBeFinalized(JSContext *cx, const void *thing)
 {
     JS_ASSERT(cx);
 
     JSCompartment *thingCompartment = reinterpret_cast<const Cell *>(thing)->compartment();
     JSRuntime *rt = cx->runtime;
     JS_ASSERT(rt == thingCompartment->rt);
@@ -693,17 +757,17 @@ js_InitGC(JSRuntime *rt, uint32 maxbytes
     if (!rt->gcLock)
         return false;
     rt->gcDone = JS_NEW_CONDVAR(rt->gcLock);
     if (!rt->gcDone)
         return false;
     rt->requestDone = JS_NEW_CONDVAR(rt->gcLock);
     if (!rt->requestDone)
         return false;
-    if (!rt->gcHelperThread.init(rt))
+    if (!rt->gcHelperThread.init())
         return false;
 #endif
 
     /*
      * Separate gcMaxMallocBytes from gcMaxBytes but initialize to maxbytes
      * for default backward API compatibility.
      */
     rt->gcMaxBytes = maxbytes;
@@ -990,30 +1054,29 @@ js_FinishGC(JSRuntime *rt)
     for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
         Foreground::delete_(*c);
     rt->compartments.clear();
     rt->atomsCompartment = NULL;
 
     rt->gcSystemAvailableChunkListHead = NULL;
     rt->gcUserAvailableChunkListHead = NULL;
     for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront())
-        ReleaseGCChunk(rt, r.front());
+        Chunk::release(r.front());
     rt->gcChunkSet.clear();
-    for (Chunk *chunk = rt->gcEmptyChunkListHead; chunk; ) {
-        Chunk *next = chunk->info.next;
-        ReleaseGCChunk(rt, chunk);
-        chunk = next;
-    }
-    rt->gcEmptyChunkListHead = NULL;
-    rt->gcEmptyChunkCount = 0;
 
 #ifdef JS_THREADSAFE
-    rt->gcHelperThread.finish(rt);
+    rt->gcHelperThread.finish();
 #endif
 
+    /*
+     * Finish the pool after the background thread stops in case it was doing
+     * the background sweeping.
+     */
+    rt->gcChunkPool.expire(rt, true);
+
 #ifdef DEBUG
     if (!rt->gcRootsHash.empty())
         CheckLeakedRoots(rt);
 #endif
     rt->gcRootsHash.clear();
     rt->gcLocksHash.clear();
 }
 
@@ -1232,17 +1295,17 @@ ArenaLists::allocateFromArena(JSCompartm
                 break;
 
             /*
              * If the background finalization still runs, wait for it to
              * finish and retry to check if it populated the arena list or
              * added new empty arenas.
              */
             JS_ASSERT(*bfs == BFS_RUN);
-            comp->rt->gcHelperThread.waitBackgroundSweepEnd(comp->rt, false);
+            comp->rt->gcHelperThread.waitBackgroundSweepEnd();
             JS_ASSERT(*bfs == BFS_JUST_FINISHED || *bfs == BFS_DONE);
         }
     }
 #endif /* JS_THREADSAFE */
 
     if (!chunk) {
         if (ArenaHeader *aheader = *al->cursor) {
             JS_ASSERT(aheader->hasFreeThings());
@@ -1315,17 +1378,17 @@ ArenaLists::finalizeLater(JSContext *cx,
               thingKind == FINALIZE_OBJECT8_BACKGROUND  ||
               thingKind == FINALIZE_OBJECT12_BACKGROUND ||
               thingKind == FINALIZE_OBJECT16_BACKGROUND ||
               thingKind == FINALIZE_FUNCTION            ||
               thingKind == FINALIZE_SHORT_STRING        ||
               thingKind == FINALIZE_STRING);
 
 #ifdef JS_THREADSAFE
-    JS_ASSERT(!cx->runtime->gcHelperThread.sweeping);
+    JS_ASSERT(!cx->runtime->gcHelperThread.sweeping());
 
     ArenaList *al = &arenaLists[thingKind];
     if (!al->head) {
         JS_ASSERT(backgroundFinalizeState[thingKind] == BFS_DONE);
         JS_ASSERT(al->cursor == &al->head);
         return;
     }
 
@@ -1463,18 +1526,20 @@ RunLastDitchGC(JSContext *cx)
         maybeUnlockAtomsCompartment.construct(cx);
 #endif
     /* The last ditch GC preserves all atoms. */
     AutoKeepAtoms keep(rt);
     GCREASON(LASTDITCH);
     js_GC(cx, rt->gcTriggerCompartment, GC_NORMAL);
 
 #ifdef JS_THREADSAFE
-    if (rt->gcBytes >= rt->gcMaxBytes)
-        cx->runtime->gcHelperThread.waitBackgroundSweepEnd(cx->runtime);
+    if (rt->gcBytes >= rt->gcMaxBytes) {
+        AutoLockGC lock(rt);
+        cx->runtime->gcHelperThread.waitBackgroundSweepEnd();
+    }
 #endif
 }
 
 inline bool
 IsGCAllowed(JSContext *cx)
 {
     return !JS_ON_TRACE(cx) && !JS_THREAD_DATA(cx)->waiveGCQuota;
 }
@@ -1994,123 +2059,176 @@ MaybeGC(JSContext *cx)
     }
 
     /*
      * On 32 bit setting gcNextFullGCTime below is not atomic and a race condition
      * could trigger an GC. We tolerate this.
      */
     int64 now = PRMJ_Now();
     if (rt->gcNextFullGCTime && rt->gcNextFullGCTime <= now) {
-        if (rt->gcChunkAllocationSinceLastGC || rt->gcEmptyChunkListHead) {
+        if (rt->gcChunkAllocationSinceLastGC) {
             GCREASON(MAYBEGC);
             js_GC(cx, NULL, GC_SHRINK);
         } else {
             rt->gcNextFullGCTime = now + GC_IDLE_FULL_SPAN;
         }
     }
 }
 
 } /* namespace js */
 
 #ifdef JS_THREADSAFE
 
 namespace js {
 
 bool
-GCHelperThread::init(JSRuntime *rt)
+GCHelperThread::init()
 {
     if (!(wakeup = PR_NewCondVar(rt->gcLock)))
         return false;
-    if (!(sweepingDone = PR_NewCondVar(rt->gcLock)))
+    if (!(done = PR_NewCondVar(rt->gcLock)))
         return false;
 
-    thread = PR_CreateThread(PR_USER_THREAD, threadMain, rt, PR_PRIORITY_NORMAL,
+    thread = PR_CreateThread(PR_USER_THREAD, threadMain, this, PR_PRIORITY_NORMAL,
                              PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
-    return !!thread;
-
+    if (!thread)
+        return false;
+
+    backgroundAllocation = (js_GetCPUCount() >= 2);
+    return true;
 }
 
 void
-GCHelperThread::finish(JSRuntime *rt)
+GCHelperThread::finish()
 {
     PRThread *join = NULL;
     {
         AutoLockGC lock(rt);
-        if (thread && !shutdown) {
-            shutdown = true;
-            PR_NotifyCondVar(wakeup);
+        if (thread && state != SHUTDOWN) {
+            /* The allocation should have been stopped during the last GC. */
+            JS_ASSERT(state == IDLE || state == SWEEPING);
+            if (state == IDLE)
+                PR_NotifyCondVar(wakeup);
+            state = SHUTDOWN;
             join = thread;
         }
     }
     if (join) {
         /* PR_DestroyThread is not necessary. */
         PR_JoinThread(join);
     }
     if (wakeup)
         PR_DestroyCondVar(wakeup);
-    if (sweepingDone)
-        PR_DestroyCondVar(sweepingDone);
+    if (done)
+        PR_DestroyCondVar(done);
 }
 
 /* static */
 void
 GCHelperThread::threadMain(void *arg)
 {
-    JSRuntime *rt = static_cast<JSRuntime *>(arg);
-    rt->gcHelperThread.threadLoop(rt);
+    static_cast<GCHelperThread *>(arg)->threadLoop();
 }
 
 void
-GCHelperThread::threadLoop(JSRuntime *rt)
+GCHelperThread::threadLoop()
 {
     AutoLockGC lock(rt);
-    while (!shutdown) {
-        /*
-         * Sweeping can be true here on the first iteration if a GC and the
-         * corresponding startBackgroundSweep call happen before this thread
-         * has a chance to run.
-         */
-        if (!sweeping)
+
+    /*
+     * Even on the first iteration the state can be SHUTDOWN or SWEEPING if
+     * the stop request or the GC and the corresponding startBackgroundSweep call
+     * happen before this thread has a chance to run.
+     */
+    for (;;) {
+        switch (state) {
+          case SHUTDOWN:
+            return;
+          case IDLE:
             PR_WaitCondVar(wakeup, PR_INTERVAL_NO_TIMEOUT);
-        if (sweeping) {
-            AutoUnlockGC unlock(rt);
+            break;
+          case SWEEPING:
             doSweep();
+            if (state == SWEEPING)
+                state = IDLE;
+            PR_NotifyAllCondVar(done);
+            break;
+          case ALLOCATING:
+            do {
+                Chunk *chunk;
+                {
+                    AutoUnlockGC unlock(rt);
+                    chunk = Chunk::allocate();
+                }
+
+                /* OOM stops the background allocation. */
+                if (!chunk)
+                    break;
+                rt->gcChunkPool.put(rt, chunk);
+            } while (state == ALLOCATING && rt->gcChunkPool.wantBackgroundAllocation(rt));
+            if (state == ALLOCATING)
+                state = IDLE;
+            break;
+          case CANCEL_ALLOCATION:
+            state = IDLE;
+            PR_NotifyAllCondVar(done);
+            break;
         }
-        sweeping = false;
-        PR_NotifyAllCondVar(sweepingDone);
     }
 }
 
 bool
-GCHelperThread::prepareForBackgroundSweep(JSContext *context) {
-    size_t maxArenaLists = MAX_BACKGROUND_FINALIZE_KINDS * context->runtime->compartments.length();
+GCHelperThread::prepareForBackgroundSweep(JSContext *cx)
+{
+    JS_ASSERT(cx->runtime == rt);
+    JS_ASSERT(state == IDLE);
+    size_t maxArenaLists = MAX_BACKGROUND_FINALIZE_KINDS * rt->compartments.length();
     if (!finalizeVector.reserve(maxArenaLists))
         return false;
-    cx = context;
+    context = cx;
     return true;
 }
 
-void
-GCHelperThread::startBackgroundSweep(JSRuntime *rt, JSGCInvocationKind gckind)
+/* Must be called with the GC lock taken. */
+inline void
+GCHelperThread::startBackgroundSweep(bool shouldShrink)
 {
     /* The caller takes the GC lock. */
-    JS_ASSERT(!sweeping);
-    lastGCKind = gckind;
-    sweeping = true;
+    JS_ASSERT(state == IDLE);
+    shrinkFlag = shouldShrink;
+    state = SWEEPING;
     PR_NotifyCondVar(wakeup);
 }
 
+/* Must be called with the GC lock taken. */
 void
-GCHelperThread::waitBackgroundSweepEnd(JSRuntime *rt, bool gcUnlocked)
+GCHelperThread::waitBackgroundSweepEnd()
+{
+    while (state == SWEEPING)
+        PR_WaitCondVar(done, PR_INTERVAL_NO_TIMEOUT);
+}
+
+/* Must be called with the GC lock taken. */
+void
+GCHelperThread::waitBackgroundSweepOrAllocEnd()
 {
-    AutoLockGC maybeLock;
-    if (gcUnlocked)
-        maybeLock.lock(rt);
-    while (sweeping)
-        PR_WaitCondVar(sweepingDone, PR_INTERVAL_NO_TIMEOUT);
+    if (state == ALLOCATING)
+        state = CANCEL_ALLOCATION;
+    while (state == SWEEPING || state == CANCEL_ALLOCATION)
+        PR_WaitCondVar(done, PR_INTERVAL_NO_TIMEOUT);
+}
+
+/* Must be called with the GC lock taken. */
+inline void
+GCHelperThread::startBackgroundAllocationIfIdle()
+{
+    if (state == IDLE) {
+        state = ALLOCATING;
+        PR_NotifyCondVar(wakeup);
+    }
 }
 
 JS_FRIEND_API(void)
 GCHelperThread::replenishAndFreeLater(void *ptr)
 {
     JS_ASSERT(freeCursor == freeCursorEnd);
     do {
         if (freeCursor && !freeVector.append(freeCursorEnd - FREE_ARRAY_LENGTH))
@@ -2122,30 +2240,39 @@ GCHelperThread::replenishAndFreeLater(vo
         }
         freeCursorEnd = freeCursor + FREE_ARRAY_LENGTH;
         *freeCursor++ = ptr;
         return;
     } while (false);
     Foreground::free_(ptr);
 }
 
+/* Must be called with the GC lock taken. */
 void
 GCHelperThread::doSweep()
 {
-    JS_ASSERT(cx);
+    JS_ASSERT(context);
+
+    /*
+     * Expire the chunks released during the GC so they will be available to
+     * the rest of the system immediately.
+     */
+    rt->gcChunkPool.expire(rt, shouldShrink());
+
+    AutoUnlockGC unlock(rt);
 
     /*
      * We must finalize in the insert order, see comments in
      * finalizeObjects.
      */
     for (ArenaHeader **i = finalizeVector.begin(); i != finalizeVector.end(); ++i)
-        ArenaLists::backgroundFinalize(cx, *i);
+        ArenaLists::backgroundFinalize(context, *i);
     finalizeVector.resize(0);
-    ExpireGCChunks(cx->runtime, lastGCKind);
-    cx = NULL;
+
+    context = NULL;
 
     if (freeCursor) {
         void **array = freeCursorEnd - FREE_ARRAY_LENGTH;
         freeElementsAndArray(array, freeCursor);
         freeCursor = freeCursorEnd = NULL;
     } else {
         JS_ASSERT(!freeCursorEnd);
     }
@@ -2379,45 +2506,46 @@ SweepPhase(JSContext *cx, GCMarker *gcma
         SweepCompartments(cx, gckind);
 
 #ifndef JS_THREADSAFE
     /*
      * Destroy arenas after we finished the sweeping so finalizers can safely
      * use IsAboutToBeFinalized().
      * This is done on the GCHelperThread if JS_THREADSAFE is defined.
      */
-    ExpireGCChunks(rt, gckind);
+    rt->gcChunkPool.expire(rt, gckind == GC_SHRINK);
 #endif
     GCTIMESTAMP(sweepDestroyEnd);
 
     if (rt->gcCallback)
         (void) rt->gcCallback(cx, JSGC_FINALIZE_END);
 }
 
 /*
  * Perform mark-and-sweep GC.
  *
  * In a JS_THREADSAFE build, the calling thread must be rt->gcThread and each
  * other thread must be either outside all requests or blocked waiting for GC
- * to finish. Note that the caller does not hold rt->gcLock.
- * If comp is set, we perform a single-compartment GC.
+ * to finish. The caller must hold rt->gcLock.
  */
 static void
 MarkAndSweep(JSContext *cx, JSGCInvocationKind gckind GCTIMER_PARAM)
 {
     JSRuntime *rt = cx->runtime;
     rt->gcNumber++;
 
     /* Clear gcIsNeeded now, when we are about to start a normal GC cycle. */
     rt->gcIsNeeded = false;
     rt->gcTriggerCompartment = NULL;
 
     /* Reset malloc counter. */
     rt->resetGCMallocBytes();
 
+    AutoUnlockGC unlock(rt);
+
     GCMarker gcmarker(cx);
     JS_ASSERT(IS_GC_MARKING_TRACER(&gcmarker));
     JS_ASSERT(gcmarker.getMarkColor() == BLACK);
     rt->gcMarkingTracer = &gcmarker;
 
     BeginMarkPhase(cx, &gcmarker, gckind GCTIMER_ARG);
     gcmarker.drainMarkStack();
     EndMarkPhase(cx, &gcmarker, gckind GCTIMER_ARG);
@@ -2644,39 +2772,39 @@ GCCycle(JSContext *cx, JSCompartment *co
      * NULL to look for violations.
      */
     SwitchToCompartment sc(cx, (JSCompartment *)NULL);
 
     JS_ASSERT(!rt->gcCurrentCompartment);
     rt->gcCurrentCompartment = comp;
 
     rt->gcMarkAndSweep = true;
-    {
-        AutoUnlockGC unlock(rt);
 
 #ifdef JS_THREADSAFE
-        /*
-         * As we about to purge caches and clear the mark bits we must wait
-         * for any background finalization to finish.
-         */
-        JS_ASSERT(!cx->gcBackgroundFree);
-        rt->gcHelperThread.waitBackgroundSweepEnd(rt);
-        if (gckind != GC_LAST_CONTEXT && rt->state != JSRTS_LANDING) {
-            if (rt->gcHelperThread.prepareForBackgroundSweep(cx))
-                cx->gcBackgroundFree = &rt->gcHelperThread;
-        }
+    /*
+     * As we about to purge caches and clear the mark bits we must wait for
+     * any background finalization to finish. We must also wait for the
+     * background allocation to finish so we can avoid taking the GC lock
+     * when manipulating the chunks during the GC.
+     */
+    JS_ASSERT(!cx->gcBackgroundFree);
+    rt->gcHelperThread.waitBackgroundSweepOrAllocEnd();
+    if (gckind != GC_LAST_CONTEXT && rt->state != JSRTS_LANDING) {
+        if (rt->gcHelperThread.prepareForBackgroundSweep(cx))
+            cx->gcBackgroundFree = &rt->gcHelperThread;
+    }
 #endif
-        MarkAndSweep(cx, gckind  GCTIMER_ARG);
-    }
+
+    MarkAndSweep(cx, gckind  GCTIMER_ARG);
 
 #ifdef JS_THREADSAFE
     if (gckind != GC_LAST_CONTEXT && rt->state != JSRTS_LANDING) {
         JS_ASSERT(cx->gcBackgroundFree == &rt->gcHelperThread);
         cx->gcBackgroundFree = NULL;
-        rt->gcHelperThread.startBackgroundSweep(rt, gckind);
+        rt->gcHelperThread.startBackgroundSweep(gckind == GC_SHRINK);
     } else {
         JS_ASSERT(!cx->gcBackgroundFree);
     }
 #endif
 
     rt->gcMarkAndSweep = false;
     rt->gcRegenShapes = false;
     rt->setGCLastBytes(rt->gcBytes, gckind);
@@ -2739,19 +2867,16 @@ js_GC(JSContext *cx, JSCompartment *comp
          * on another thread.
          */
         if (JSGCCallback callback = rt->gcCallback) {
             if (!callback(cx, JSGC_BEGIN) && gckind != GC_LAST_CONTEXT)
                 return;
         }
 
         {
-#ifdef JS_THREADSAFE
-            rt->gcHelperThread.waitBackgroundSweepEnd(rt);
-#endif
             /* Lock out other GC allocator and collector invocations. */
             AutoLockGC lock(rt);
             rt->gcPoke = false;
             GCCycle(cx, comp, gckind  GCTIMER_ARG);
         }
 
         /* We re-sample the callback again as the finalizers can change it. */
         if (JSGCCallback callback = rt->gcCallback)
@@ -2798,17 +2923,17 @@ TraceRuntime(JSTracer *trc)
 #ifdef JS_THREADSAFE
     {
         JSContext *cx = trc->context;
         JSRuntime *rt = cx->runtime;
         if (rt->gcThread != cx->thread()) {
             AutoLockGC lock(rt);
             AutoGCSession gcsession(cx);
 
-            rt->gcHelperThread.waitBackgroundSweepEnd(rt, false);
+            rt->gcHelperThread.waitBackgroundSweepEnd();
             AutoUnlockGC unlock(rt);
 
             AutoCopyFreeListToArenas copy(rt);
             RecordNativeStackTopForGC(trc->context);
             MarkRuntime(trc);
             return;
         }
     }
@@ -2862,17 +2987,17 @@ IterateCompartmentsArenasCells(JSContext
     LeaveTrace(cx);
 
     JSRuntime *rt = cx->runtime;
     JS_ASSERT(!rt->gcRunning);
 
     AutoLockGC lock(rt);
     AutoGCSession gcsession(cx);
 #ifdef JS_THREADSAFE
-    rt->gcHelperThread.waitBackgroundSweepEnd(rt, false);
+    rt->gcHelperThread.waitBackgroundSweepEnd();
 #endif
     AutoUnlockGC unlock(rt);
 
     AutoCopyFreeListToArenas copy(rt);
     for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) {
         JSCompartment *compartment = *c;
         (*compartmentCallback)(cx, data, compartment);
 
@@ -2896,17 +3021,17 @@ IterateCells(JSContext *cx, JSCompartmen
     LeaveTrace(cx);
 
     JSRuntime *rt = cx->runtime;
     JS_ASSERT(!rt->gcRunning);
 
     AutoLockGC lock(rt);
     AutoGCSession gcsession(cx);
 #ifdef JS_THREADSAFE
-    rt->gcHelperThread.waitBackgroundSweepEnd(rt, false);
+    rt->gcHelperThread.waitBackgroundSweepEnd();
 #endif
     AutoUnlockGC unlock(rt);
 
     AutoCopyFreeListToArenas copy(rt);
 
     JSGCTraceKind traceKind = MapAllocToTraceKind(thingKind);
     size_t thingSize = Arena::thingSize(thingKind);
 
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -636,37 +636,66 @@ struct Chunk {
     }
 
     uintptr_t address() const {
         uintptr_t addr = reinterpret_cast<uintptr_t>(this);
         JS_ASSERT(!(addr & GC_CHUNK_MASK));
         return addr;
     }
 
-    void init();
-
     bool unused() const {
         return info.numFree == ArenasPerChunk;
     }
 
     bool hasAvailableArenas() const {
         return info.numFree > 0;
     }
 
     inline void addToAvailableList(JSCompartment *compartment);
     inline void removeFromAvailableList();
 
     ArenaHeader *allocateArena(JSCompartment *comp, AllocKind kind);
 
     void releaseArena(ArenaHeader *aheader);
+
+    static Chunk *allocate();
+    static inline void release(Chunk *chunk);
+
+  private:
+    inline void init();
 };
 
 JS_STATIC_ASSERT(sizeof(Chunk) <= GC_CHUNK_SIZE);
 JS_STATIC_ASSERT(sizeof(Chunk) + BytesPerArena > GC_CHUNK_SIZE);
 
+class ChunkPool {
+    Chunk   *emptyChunkListHead;
+    size_t  emptyCount;
+
+  public:
+    ChunkPool()
+      : emptyChunkListHead(NULL),
+        emptyCount(0) { }
+
+    size_t getEmptyCount() const {
+        return emptyCount;
+    }
+
+    inline bool wantBackgroundAllocation(JSRuntime *rt) const;
+
+    /* Must be called with the GC lock taken. */
+    inline Chunk *get(JSRuntime *rt);
+
+    /* Must be called either during the GC or with the GC lock taken. */
+    inline void put(JSRuntime *rt, Chunk *chunk);
+
+    /* Must be called either during the GC or with the GC lock taken. */
+    void expire(JSRuntime *rt, bool releaseAll);
+};
+
 inline uintptr_t
 Cell::address() const
 {
     uintptr_t addr = uintptr_t(this);
     JS_ASSERT(addr % Cell::CellSize == 0);
     JS_ASSERT(Chunk::withinArenasRange(addr));
     return addr;
 }
@@ -1283,88 +1312,131 @@ js_WaitForGC(JSRuntime *rt);
 # define js_WaitForGC(rt)    ((void) 0)
 
 #endif
 
 namespace js {
 
 #ifdef JS_THREADSAFE
 
-/*
- * During the finalization we do not free immediately. Rather we add the
- * corresponding pointers to a buffer which we later release on a separated
- * thread.
- *
- * The buffer is implemented as a vector of 64K arrays of pointers, not as a
- * simple vector, to avoid realloc calls during the vector growth and to not
- * bloat the binary size of the inlined freeLater method. Any OOM during
- * buffer growth results in the pointer being freed immediately.
- */
 class GCHelperThread {
+    enum State {
+        IDLE,
+        SWEEPING,
+        ALLOCATING,
+        CANCEL_ALLOCATION,
+        SHUTDOWN
+    };
+
+    /*
+     * During the finalization we do not free immediately. Rather we add the
+     * corresponding pointers to a buffer which we later release on a
+     * separated thread.
+     *
+     * The buffer is implemented as a vector of 64K arrays of pointers, not as
+     * a simple vector, to avoid realloc calls during the vector growth and to
+     * not bloat the binary size of the inlined freeLater method. Any OOM
+     * during buffer growth results in the pointer being freed immediately.
+     */
     static const size_t FREE_ARRAY_SIZE = size_t(1) << 16;
     static const size_t FREE_ARRAY_LENGTH = FREE_ARRAY_SIZE / sizeof(void *);
 
-    JSContext         *cx;
-    PRThread*         thread;
-    PRCondVar*        wakeup;
-    PRCondVar*        sweepingDone;
-    bool              shutdown;
-    JSGCInvocationKind lastGCKind;
+    JSRuntime         *const rt;
+    PRThread          *thread;
+    PRCondVar         *wakeup;
+    PRCondVar         *done;
+    volatile State    state;
+
+    JSContext         *context;
+    bool              shrinkFlag;
 
     Vector<void **, 16, js::SystemAllocPolicy> freeVector;
     void            **freeCursor;
     void            **freeCursorEnd;
 
     Vector<js::gc::ArenaHeader *, 64, js::SystemAllocPolicy> finalizeVector;
 
+    bool    backgroundAllocation;
+
     friend struct js::gc::ArenaLists;
 
     JS_FRIEND_API(void)
     replenishAndFreeLater(void *ptr);
 
     static void freeElementsAndArray(void **array, void **end) {
         JS_ASSERT(array <= end);
         for (void **p = array; p != end; ++p)
             js::Foreground::free_(*p);
         js::Foreground::free_(array);
     }
 
     static void threadMain(void* arg);
+    void threadLoop();
 
-    void threadLoop(JSRuntime *rt);
+    /* Must be called with the GC lock taken. */
     void doSweep();
 
   public:
-    GCHelperThread()
-      : thread(NULL),
+    GCHelperThread(JSRuntime *rt)
+      : rt(rt),
+        thread(NULL),
         wakeup(NULL),
-        sweepingDone(NULL),
-        shutdown(false),
+        done(NULL),
+        state(IDLE),
         freeCursor(NULL),
         freeCursorEnd(NULL),
-        sweeping(false) { }
+        backgroundAllocation(true)
+    { }
+
+    bool init();
+    void finish();
+
+    /* Must be called with the GC lock taken. */
+    inline void startBackgroundSweep(bool shouldShrink);
+
+    /* Must be called with the GC lock taken. */
+    void waitBackgroundSweepEnd();
+
+    /* Must be called with the GC lock taken. */
+    void waitBackgroundSweepOrAllocEnd();
+
+    /* Must be called with the GC lock taken. */
+    inline void startBackgroundAllocationIfIdle();
 
-    volatile bool     sweeping;
-    bool init(JSRuntime *rt);
-    void finish(JSRuntime *rt);
+    bool canBackgroundAllocate() const {
+        return backgroundAllocation;
+    }
+
+    void disableBackgroundAllocation() {
+        backgroundAllocation = false;
+    }
 
-    /* Must be called with GC lock taken. */
-    void startBackgroundSweep(JSRuntime *rt, JSGCInvocationKind gckind);
+    /*
+     * Outside the GC lock may give true answer when in fact the sweeping has
+     * been done.
+     */
+    bool sweeping() const {
+        return state == SWEEPING;
+    }
 
-    void waitBackgroundSweepEnd(JSRuntime *rt, bool gcUnlocked = true);
+    bool shouldShrink() const {
+        JS_ASSERT(sweeping());
+        return shrinkFlag;
+    }
 
     void freeLater(void *ptr) {
-        JS_ASSERT(!sweeping);
+        JS_ASSERT(!sweeping());
         if (freeCursor != freeCursorEnd)
             *freeCursor++ = ptr;
         else
             replenishAndFreeLater(ptr);
     }
 
-    bool prepareForBackgroundSweep(JSContext *context);
+    /* Must be called with the GC lock taken. */
+    bool prepareForBackgroundSweep(JSContext *cx);
 };
 
 #endif /* JS_THREADSAFE */
 
 struct GCChunkHasher {
     typedef gc::Chunk *Lookup;
 
     /*
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -2719,16 +2719,24 @@ TypeObject::addProperty(JSContext *cx, j
                     UpdatePropertyType(cx, &base->types, singleton, shape, true);
                 shape = shape->previous();
             }
         } else {
             const Shape *shape = singleton->nativeLookup(cx, id);
             if (shape)
                 UpdatePropertyType(cx, &base->types, singleton, shape, false);
         }
+
+        if (singleton->watched()) {
+            /*
+             * Mark the property as configured, to inhibit optimizations on it
+             * and avoid bypassing the watchpoint handler.
+             */
+            base->types.setOwnProperty(cx, true);
+        }
     }
 
     *pprop = base;
 
     InferSpew(ISpewOps, "typeSet: %sT%p%s property %s %s",
               InferSpewColor(&base->types), &base->types, InferSpewColorReset(),
               TypeObjectString(this), TypeIdString(id));
 
@@ -4931,24 +4939,16 @@ TypeMonitorCallSlow(JSContext *cx, JSObj
 
 static inline bool
 IsAboutToBeFinalized(JSContext *cx, TypeObjectKey *key)
 {
     /* Mask out the low bit indicating whether this is a type or JS object. */
     return !reinterpret_cast<const gc::Cell *>((jsuword) key & ~1)->isMarked();
 }
 
-inline bool
-ScriptIsAboutToBeFinalized(JSContext *cx, JSScript *script, JSFunction *fun)
-{
-    return script->isCachedEval ||
-        (script->u.object && IsAboutToBeFinalized(cx, script->u.object)) ||
-        (fun && IsAboutToBeFinalized(cx, fun));
-}
-
 void
 TypeDynamicResult(JSContext *cx, JSScript *script, jsbytecode *pc, Type type)
 {
     JS_ASSERT(cx->typeInferenceEnabled());
     AutoEnterTypeInference enter(cx);
 
     UntrapOpcode untrap(cx, script, pc);
 
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -173,16 +173,27 @@ NewKeyValuePair(JSContext *cx, jsid id, 
 }
 
 static inline bool
 Enumerate(JSContext *cx, JSObject *obj, JSObject *pobj, jsid id,
           bool enumerable, uintN flags, IdSet& ht, AutoIdVector *props)
 {
     JS_ASSERT_IF(flags & JSITER_OWNONLY, obj == pobj);
 
+    /*
+     * We implement __proto__ using a property on |Object.prototype|, but
+     * because __proto__ is highly deserving of removal, we don't want it to
+     * show up in property enumeration, even if only for |Object.prototype|
+     * (think introspection by Prototype-like frameworks that add methods to
+     * the built-in prototypes).  So exclude __proto__ if the object where the
+     * property was found has no [[Prototype]] and might be |Object.prototype|.
+     */
+    if (JS_UNLIKELY(!pobj->getProto() && JSID_IS_ATOM(id, cx->runtime->atomState.protoAtom)))
+        return true;
+
     if (!(flags & JSITER_OWNONLY) || pobj->isProxy() || pobj->getOps()->enumerate) {
         /* If we've already seen this, we definitely won't add it. */
         IdSet::AddPtr p = ht.lookupForAdd(id);
         if (JS_UNLIKELY(!!p))
             return true;
 
         /*
          * It's not necessary to add properties to the hash table at the end of
@@ -922,28 +933,41 @@ js_SuppressDeletedElement(JSContext *cx,
     jsid id;
     if (!IndexToId(cx, index, &id))
         return false;
     JS_ASSERT(id == js_CheckForStringIndex(id));
     return SuppressDeletedPropertyHelper(cx, obj, SingleIdPredicate(id));
 }
 
 class IndexRangePredicate {
-    jsint begin, end;
-public:
-    IndexRangePredicate(jsint begin, jsint end) : begin(begin), end(end) {}
+    uint32 begin, end;
+
+  public:
+    IndexRangePredicate(uint32 begin, uint32 end) : begin(begin), end(end) {}
 
     bool operator()(jsid id) {
-        return JSID_IS_INT(id) && begin <= JSID_TO_INT(id) && JSID_TO_INT(id) < end;
+        if (JSID_IS_INT(id)) {
+            jsint i = JSID_TO_INT(id);
+            return i > 0 && begin <= uint32(i) && uint32(i) < end;
+        }
+
+        if (JS_LIKELY(JSID_IS_ATOM(id))) {
+            JSAtom *atom = JSID_TO_ATOM(id);
+            uint32 index;
+            return atom->isIndex(&index) && begin <= index && index < end;
+        }
+
+        return false;
     }
+
     bool matchesAtMostOne() { return false; }
 };
 
 bool
-js_SuppressDeletedIndexProperties(JSContext *cx, JSObject *obj, jsint begin, jsint end)
+js_SuppressDeletedElements(JSContext *cx, JSObject *obj, uint32 begin, uint32 end)
 {
     return SuppressDeletedPropertyHelper(cx, obj, IndexRangePredicate(begin, end));
 }
 
 JSBool
 js_IteratorMore(JSContext *cx, JSObject *iterobj, Value *rval)
 {
     /* Fast path for native iterators */
--- a/js/src/jsiter.h
+++ b/js/src/jsiter.h
@@ -135,17 +135,17 @@ js_CloseIterator(JSContext *cx, JSObject
 
 bool
 js_SuppressDeletedProperty(JSContext *cx, JSObject *obj, jsid id);
 
 bool
 js_SuppressDeletedElement(JSContext *cx, JSObject *obj, uint32 index);
 
 bool
-js_SuppressDeletedIndexProperties(JSContext *cx, JSObject *obj, jsint begin, jsint end);
+js_SuppressDeletedElements(JSContext *cx, JSObject *obj, uint32 begin, uint32 end);
 
 /*
  * IteratorMore() indicates whether another value is available. It might
  * internally call iterobj.next() and then cache the value until its
  * picked up by IteratorNext(). The value is cached in the current context.
  */
 extern JSBool
 js_IteratorMore(JSContext *cx, JSObject *iterobj, js::Value *rval);
--- a/js/src/jslock.cpp
+++ b/js/src/jslock.cpp
@@ -39,16 +39,23 @@
 
 #ifdef JS_THREADSAFE
 
 /*
  * JS locking stubs.
  */
 #include <stdlib.h>
 #include <string.h>
+
+#ifdef XP_WIN
+# include "jswin.h"
+#else
+# include <unistd.h>
+#endif
+
 #include "jspubtd.h"
 #include "jsutil.h"
 #include "jstypes.h"
 #include "jsstdint.h"
 #include "jsbit.h"
 #include "jscntxt.h"
 #include "jsgc.h"
 #include "jslock.h"
@@ -68,17 +75,16 @@ using namespace js;
 
 #if defined(_MSC_VER) && defined(_M_IX86)
 #pragma warning( disable : 4035 )
 JS_BEGIN_EXTERN_C
 extern long __cdecl
 _InterlockedCompareExchange(long *volatile dest, long exchange, long comp);
 JS_END_EXTERN_C
 #pragma intrinsic(_InterlockedCompareExchange)
-
 JS_STATIC_ASSERT(sizeof(jsword) == sizeof(long));
 
 static JS_ALWAYS_INLINE int
 NativeCompareAndSwapHelper(volatile jsword *w, jsword ov, jsword nv)
 {
     _InterlockedCompareExchange((long*) w, nv, ov);
     __asm {
         sete al
@@ -87,21 +93,22 @@ NativeCompareAndSwapHelper(volatile jswo
 
 static JS_ALWAYS_INLINE int
 NativeCompareAndSwap(volatile jsword *w, jsword ov, jsword nv)
 {
     return (NativeCompareAndSwapHelper(w, ov, nv) & 1);
 }
 
 #elif defined(_MSC_VER) && (defined(_M_AMD64) || defined(_M_X64))
-JS_BEGIN_EXTERN_C
-extern long long __cdecl
-_InterlockedCompareExchange64(long long *volatile dest, long long exchange, long long comp);
-JS_END_EXTERN_C
+/*
+ * Compared with the _InterlockedCompareExchange in the 32 bit case above MSVC
+ * declares _InterlockedCompareExchange64 through <windows.h>.
+ */
 #pragma intrinsic(_InterlockedCompareExchange64)
+JS_STATIC_ASSERT(sizeof(jsword) == sizeof(long long));
 
 static JS_ALWAYS_INLINE int
 NativeCompareAndSwap(volatile jsword *w, jsword ov, jsword nv)
 {
     return _InterlockedCompareExchange64((long long *volatile)w, nv, ov) == ov;
 }
 
 #elif defined(XP_MACOSX) || defined(DARWIN)
@@ -299,16 +306,33 @@ js_AtomicClearMask(volatile jsword *w, j
     jsword ov, nv;
 
     do {
         ov = *w;
         nv = ov & ~mask;
     } while (!js_CompareAndSwap(w, ov, nv));
 }
 
+unsigned
+js_GetCPUCount()
+{
+    static unsigned ncpus = 0;
+    if (ncpus == 0) {
+# ifdef XP_WIN
+        SYSTEM_INFO sysinfo;
+        GetSystemInfo(&sysinfo);
+        ncpus = unsigned(sysinfo.dwNumberOfProcessors);
+# else
+        long n = sysconf(_SC_NPROCESSORS_ONLN);
+        ncpus = (n > 0) ? unsigned(n) : 1;
+# endif
+    }
+    return ncpus;
+}
+
 #ifndef NSPR_LOCK
 
 struct JSFatLock {
     int         susp;
     PRLock      *slock;
     PRCondVar   *svar;
     JSFatLock   *next;
     JSFatLock   **prevp;
--- a/js/src/jslock.h
+++ b/js/src/jslock.h
@@ -193,27 +193,36 @@ js_AtomicSetMask(volatile jsword *w, jsw
  * compare and swap.
  */
 extern void
 js_AtomicClearMask(volatile jsword *w, jsword mask);
 
 #define JS_ATOMIC_SET_MASK(w, mask) js_AtomicSetMask(w, mask)
 #define JS_ATOMIC_CLEAR_MASK(w, mask) js_AtomicClearMask(w, mask)
 
+extern  unsigned
+js_GetCPUCount();
+
 #else
 
 static inline JSBool
 js_CompareAndSwap(jsword *w, jsword ov, jsword nv)
 {
     return (*w == ov) ? *w = nv, JS_TRUE : JS_FALSE;
 }
 
 #define JS_ATOMIC_SET_MASK(w, mask) (*(w) |= (mask))
 #define JS_ATOMIC_CLEAR_MASK(w, mask) (*(w) &= ~(mask))
 
+static inline unsigned
+js_GetCPUCount()
+{
+    return 1;
+}
+
 #endif
 
 #ifdef __cplusplus
 
 namespace js {
 
 #ifdef JS_THREADSAFE
 class AutoLock {
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1246,21 +1246,21 @@ JSScript::NewScriptFromCG(JSContext *cx,
          */
         if ((cg->flags & TCF_NEED_SCRIPT_OBJECT) && !js_NewScriptObject(cx, script))
             return NULL;
     }
 
     /* Tell the debugger about this compiled script. */
     js_CallNewScriptHook(cx, script, fun);
     if (!cg->parent) {
-        Debugger::onNewScript(cx, script,
-                              fun ? fun : (script->u.object ? script->u.object : cg->scopeChain()),
-                              (fun || script->u.object)
-                              ? Debugger::NewHeldScript
-                              : Debugger::NewNonHeldScript);
+        JSObject *owner = fun ? fun : script->u.object;
+        GlobalObject *compileAndGoGlobal = NULL;
+        if (script->compileAndGo)
+            compileAndGoGlobal = (owner ? owner : cg->scopeChain())->getGlobal();
+        Debugger::onNewScript(cx, script, owner, compileAndGoGlobal);
     }
 
     return script;
 }
 
 size_t
 JSScript::dataSize()
 {
@@ -1325,17 +1325,16 @@ void
 js_CallDestroyScriptHook(JSContext *cx, JSScript *script)
 {
     if (!script->callDestroyHook)
         return;
 
     if (JSDestroyScriptHook hook = cx->debugHooks->destroyScriptHook)
         hook(cx, script, cx->debugHooks->destroyScriptHookData);
     script->callDestroyHook = false;
-    Debugger::onDestroyScript(script);
     JS_ClearScriptTraps(cx, script);
 }
 
 void
 JSScript::finalize(JSContext *cx)
 {
     CheckScript(this, NULL);
 
--- a/js/src/jsweakmap.h
+++ b/js/src/jsweakmap.h
@@ -286,24 +286,24 @@ class DefaultMarkPolicy<JSObject *, Valu
         return true;
     }
     void markEntry(const Value &v) {
         js::gc::MarkValue(tracer, v, "WeakMap entry value");
     }
 };
 
 template <>
-class DefaultMarkPolicy<JSObject *, JSObject *> {
+class DefaultMarkPolicy<gc::Cell *, JSObject *> {
   protected:
     JSTracer *tracer;
   public:
     DefaultMarkPolicy(JSTracer *t) : tracer(t) { }
-    bool keyMarked(JSObject *k)   { return !IsAboutToBeFinalized(tracer->context, k); }
+    bool keyMarked(gc::Cell *k)   { return !IsAboutToBeFinalized(tracer->context, k); }
     bool valueMarked(JSObject *v) { return !IsAboutToBeFinalized(tracer->context, v); }
-    bool markEntryIfLive(JSObject *k, JSObject *v) {
+    bool markEntryIfLive(gc::Cell *k, JSObject *v) {
         if (keyMarked(k) && !valueMarked(v)) {
             js::gc::MarkObject(tracer, *v, "WeakMap entry value");
             return true;
         }
         return false;
     }
     void markEntry(JSObject *v) {
         js::gc::MarkObject(tracer, *v, "WeakMap entry value");
@@ -312,16 +312,16 @@ class DefaultMarkPolicy<JSObject *, JSOb
 
 // A MarkPolicy for WeakMaps whose keys and values may be objects in arbitrary
 // compartments within a runtime.
 //
 // With the current GC, the implementation turns out to be identical to the
 // default mark policy. We give it a distinct name anyway, in case this ever
 // changes.
 //
-typedef DefaultMarkPolicy<JSObject *, JSObject *> CrossCompartmentMarkPolicy;
+typedef DefaultMarkPolicy<gc::Cell *, JSObject *> CrossCompartmentMarkPolicy;
 
 }
 
 extern JSObject *
 js_InitWeakMapClass(JSContext *cx, JSObject *obj);
 
 #endif
--- a/js/src/jsxdrapi.cpp
+++ b/js/src/jsxdrapi.cpp
@@ -713,20 +713,21 @@ JS_XDRScript(JSXDRState *xdr, JSScript *
         state.filename = script->filename;
     if (!JS_XDRCStringOrNull(xdr, (char **) &state.filename))
         return false;
 
     if (!js_XDRScript(xdr, &script))
         return false;
 
     if (xdr->mode == JSXDR_DECODE) {
+        JS_ASSERT(!script->compileAndGo);
         if (!js_NewScriptObject(xdr->cx, script))
             return false;
         js_CallNewScriptHook(xdr->cx, script, NULL);
-        Debugger::onNewScript(xdr->cx, script, script->u.object, Debugger::NewHeldScript);
+        Debugger::onNewScript(xdr->cx, script, script->u.object, NULL);
         *scriptp = script;
     }
 
     return true;
 }
 
 #define CLASS_REGISTRY_MIN      8
 #define CLASS_INDEX_TO_ID(i)    ((i)+1)
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -1582,16 +1582,18 @@ mjit::Compiler::generateMethod()
                 return Compile_Error;
             updateJoinVarTypes();
             fallthrough = true;
 
             if (!cx->typeInferenceEnabled()) {
                 /* All join points have synced state if we aren't doing cross-branch regalloc. */
                 opinfo->safePoint = true;
             }
+        } else if (opinfo->safePoint && !cx->typeInferenceEnabled()) {
+            frame.syncAndForgetEverything();
         }
         frame.assertValidRegisterState();
         a->jumpMap[uint32(PC - script->code)] = masm.label();
 
         // Now that we have the PC's register allocation, make sure it gets
         // explicitly updated if this is the loop entry and new loop registers
         // are allocated later on.
         if (loop && !a->parent)
--- a/js/src/methodjit/Compiler.h
+++ b/js/src/methodjit/Compiler.h
@@ -718,34 +718,37 @@ class Compiler : public BaseCompiler
     void jsop_getelem_args();
 #ifdef JS_METHODJIT_TYPED_ARRAY
     bool jsop_getelem_typed(int atype);
 #endif
     void jsop_toid();
     bool isCacheableBaseAndIndex(FrameEntry *obj, FrameEntry *id);
     void jsop_stricteq(JSOp op);
     bool jsop_equality(JSOp op, BoolStub stub, jsbytecode *target, JSOp fused);
+    CompileStatus jsop_equality_obj_obj(JSOp op, jsbytecode *target, JSOp fused);
     bool jsop_equality_int_string(JSOp op, BoolStub stub, jsbytecode *target, JSOp fused);
     void jsop_pos();
 
     static inline Assembler::Condition
     GetCompareCondition(JSOp op, JSOp fused)
     {
         bool ifeq = fused == JSOP_IFEQ;
         switch (op) {
           case JSOP_GT:
             return ifeq ? Assembler::LessThanOrEqual : Assembler::GreaterThan;
           case JSOP_GE:
             return ifeq ? Assembler::LessThan : Assembler::GreaterThanOrEqual;
           case JSOP_LT:
             return ifeq ? Assembler::GreaterThanOrEqual : Assembler::LessThan;
           case JSOP_LE:
             return ifeq ? Assembler::GreaterThan : Assembler::LessThanOrEqual;
+          case JSOP_STRICTEQ:
           case JSOP_EQ:
             return ifeq ? Assembler::NotEqual : Assembler::Equal;
+          case JSOP_STRICTNE:
           case JSOP_NE:
             return ifeq ? Assembler::Equal : Assembler::NotEqual;
           default:
             JS_NOT_REACHED("unrecognized op");
             return Assembler::Equal;
         }
     }
 
--- a/js/src/methodjit/FastOps.cpp
+++ b/js/src/methodjit/FastOps.cpp
@@ -398,16 +398,68 @@ static inline bool
 CheckNullOrUndefined(FrameEntry *fe)
 {
     if (!fe->isTypeKnown())
         return false;
     JSValueType type = fe->getKnownType();
     return type == JSVAL_TYPE_NULL || type == JSVAL_TYPE_UNDEFINED;
 }
 
+CompileStatus
+mjit::Compiler::jsop_equality_obj_obj(JSOp op, jsbytecode *target, JSOp fused)
+{
+    FrameEntry *rhs = frame.peek(-1);
+    FrameEntry *lhs = frame.peek(-2);
+
+    JS_ASSERT(cx->typeInferenceEnabled() &&
+              lhs->isType(JSVAL_TYPE_OBJECT) && rhs->isType(JSVAL_TYPE_OBJECT));
+
+    /*
+     * Handle equality between two objects. We have to ensure there is no
+     * special equality operator on either object, if that passes then
+     * this is a pointer comparison.
+     */
+    types::TypeSet *lhsTypes = analysis->poppedTypes(PC, 1);
+    types::TypeSet *rhsTypes = analysis->poppedTypes(PC, 0);
+    if (!lhsTypes->hasObjectFlags(cx, types::OBJECT_FLAG_SPECIAL_EQUALITY) &&
+        !rhsTypes->hasObjectFlags(cx, types::OBJECT_FLAG_SPECIAL_EQUALITY)) {
+        /* :TODO: Merge with jsop_relational_int? */
+        JS_ASSERT_IF(!target, fused != JSOP_IFEQ);
+        frame.forgetMismatchedObject(lhs);
+        frame.forgetMismatchedObject(rhs);
+        Assembler::Condition cond = GetCompareCondition(op, fused);
+        if (target) {
+            Jump sj = stubcc.masm.branchTest32(GetStubCompareCondition(fused),
+                                               Registers::ReturnReg, Registers::ReturnReg);
+            if (!frame.syncForBranch(target, Uses(2)))
+                return Compile_Error;
+            RegisterID lreg = frame.tempRegForData(lhs);
+            frame.pinReg(lreg);
+            RegisterID rreg = frame.tempRegForData(rhs);
+            frame.unpinReg(lreg);
+            Jump fast = masm.branchPtr(cond, lreg, rreg);
+            frame.popn(2);
+            return jumpAndTrace(fast, target, &sj) ? Compile_Okay : Compile_Error;
+        } else {
+            RegisterID result = frame.allocReg();
+            RegisterID lreg = frame.tempRegForData(lhs);
+            frame.pinReg(lreg);
+            RegisterID rreg = frame.tempRegForData(rhs);
+            frame.unpinReg(lreg);
+            masm.branchValue(cond, lreg, rreg, result);
+
+            frame.popn(2);
+            frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, result);
+            return Compile_Okay;
+        }
+    }
+
+    return Compile_Skipped;
+}
+
 bool
 mjit::Compiler::jsop_equality(JSOp op, BoolStub stub, jsbytecode *target, JSOp fused)
 {
     FrameEntry *rhs = frame.peek(-1);
     FrameEntry *lhs = frame.peek(-2);
 
     /* The compiler should have handled constant folding. */
     JS_ASSERT(!(rhs->isConstant() && lhs->isConstant()));
@@ -482,56 +534,21 @@ mjit::Compiler::jsop_equality(JSOp op, B
             masm.move(Imm32(op == JSOP_EQ), reg);
             j3.linkTo(masm.label(), &masm);
             frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, reg);
         }
         return true;
     }
 
     if (cx->typeInferenceEnabled() &&
-        lhs->isType(JSVAL_TYPE_OBJECT) && rhs->isType(JSVAL_TYPE_OBJECT)) {
-        /*
-         * Handle equality between two objects. We have to ensure there is no
-         * special equality operator on either object, if that passes then
-         * this is a pointer comparison.
-         */
-        types::TypeSet *lhsTypes = analysis->poppedTypes(PC, 1);
-        types::TypeSet *rhsTypes = analysis->poppedTypes(PC, 0);
-        if (!lhsTypes->hasObjectFlags(cx, types::OBJECT_FLAG_SPECIAL_EQUALITY) &&
-            !rhsTypes->hasObjectFlags(cx, types::OBJECT_FLAG_SPECIAL_EQUALITY)) {
-            /* :TODO: Merge with jsop_relational_int? */
-            JS_ASSERT_IF(!target, fused != JSOP_IFEQ);
-            frame.forgetMismatchedObject(lhs);
-            frame.forgetMismatchedObject(rhs);
-            Assembler::Condition cond = GetCompareCondition(op, fused);
-            if (target) {
-                Jump sj = stubcc.masm.branchTest32(GetStubCompareCondition(fused),
-                                                   Registers::ReturnReg, Registers::ReturnReg);
-                if (!frame.syncForBranch(target, Uses(2)))
-                    return false;
-                RegisterID lreg = frame.tempRegForData(lhs);
-                frame.pinReg(lreg);
-                RegisterID rreg = frame.tempRegForData(rhs);
-                frame.unpinReg(lreg);
-                Jump fast = masm.branchPtr(cond, lreg, rreg);
-                frame.popn(2);
-                return jumpAndTrace(fast, target, &sj);
-            } else {
-                RegisterID result = frame.allocReg();
-                RegisterID lreg = frame.tempRegForData(lhs);
-                frame.pinReg(lreg);
-                RegisterID rreg = frame.tempRegForData(rhs);
-                frame.unpinReg(lreg);
-                masm.branchValue(cond, lreg, rreg, result);
-
-                frame.popn(2);
-                frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, result);
-                return true;
-            }
-        }
+        lhs->isType(JSVAL_TYPE_OBJECT) && rhs->isType(JSVAL_TYPE_OBJECT))
+    {
+        CompileStatus status = jsop_equality_obj_obj(op, target, fused);
+        if (status == Compile_Okay) return true;
+        else if (status == Compile_Error) return false;
     }
 
     return emitStubCmpOp(stub, target, fused);
 }
 
 bool
 mjit::Compiler::jsop_relational(JSOp op, BoolStub stub,
                                 jsbytecode *target, JSOp fused)
@@ -2344,16 +2361,102 @@ mjit::Compiler::jsop_stricteq(JSOp op)
         if (data != result)
             frame.freeReg(data);
 
         frame.popn(2);
         frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, result);
         return;
     }
 
+    if (lhs->isType(JSVAL_TYPE_STRING) || rhs->isType(JSVAL_TYPE_STRING)) {
+        FrameEntry *maybeNotStr = lhs->isType(JSVAL_TYPE_STRING) ? rhs : lhs;
+
+        if (maybeNotStr->isNotType(JSVAL_TYPE_STRING)) {
+            frame.popn(2);
+            frame.push(BooleanValue(false));
+            return;
+        }
+
+        if (!maybeNotStr->isTypeKnown()) {
+            JS_ASSERT(!maybeNotStr->isConstant());
+            Jump j = frame.testString(Assembler::NotEqual, maybeNotStr);
+            stubcc.linkExit(j, Uses(2));
+        }
+
+        FrameEntry *op1 = lhs->isConstant() ? rhs : lhs;
+        FrameEntry *op2 = lhs->isConstant() ? lhs : rhs;
+        JS_ASSERT(!op1->isConstant());
+
+        /* ReturnReg is safely usable with set32, since %ah can be accessed. */
+        RegisterID resultReg = Registers::ReturnReg;
+        frame.takeReg(resultReg);
+        RegisterID tmpReg = frame.allocReg();
+        RegisterID reg1 = frame.tempRegForData(op1);
+        frame.pinReg(reg1);
+
+        RegisterID reg2;
+        if (op2->isConstant()) {
+            reg2 = frame.allocReg();
+            JSString *str = op2->getValue().toString();
+            JS_ASSERT(str->isAtom());
+            masm.move(ImmPtr(str), reg2);
+        } else {
+            reg2 = frame.tempRegForData(op2);
+            frame.pinReg(reg2);
+        }
+
+        JS_ASSERT(reg1 != resultReg);
+        JS_ASSERT(reg1 != tmpReg);
+        JS_ASSERT(reg2 != resultReg);
+        JS_ASSERT(reg2 != tmpReg);
+
+        /* JSString::isAtom === (lengthAndFlags & ATOM_MASK == 0) */
+        JS_STATIC_ASSERT(JSString::ATOM_FLAGS == 0);
+        Imm32 atomMask(JSString::ATOM_MASK);
+
+        masm.load32(Address(reg1, JSString::offsetOfLengthAndFlags()), tmpReg);
+        Jump op1NotAtomized = masm.branchTest32(Assembler::NonZero, tmpReg, atomMask);
+        stubcc.linkExit(op1NotAtomized, Uses(2));
+
+        if (!op2->isConstant()) {
+            masm.load32(Address(reg2, JSString::offsetOfLengthAndFlags()), tmpReg);
+            Jump op2NotAtomized = masm.branchTest32(Assembler::NonZero, tmpReg, atomMask);
+            stubcc.linkExit(op2NotAtomized, Uses(2));
+        }
+
+        masm.set32(cond, reg1, reg2, resultReg);
+
+        frame.unpinReg(reg1);
+        if (op2->isConstant())
+            frame.freeReg(reg2);
+        else
+            frame.unpinReg(reg2);
+        frame.freeReg(tmpReg);
+
+        stubcc.leave();
+        if (op == JSOP_STRICTEQ)
+            OOL_STUBCALL_USES(stubs::StrictEq, REJOIN_NONE, Uses(2));
+        else
+            OOL_STUBCALL_USES(stubs::StrictNe, REJOIN_NONE, Uses(2));
+
+        frame.popn(2);
+        frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, resultReg);
+
+        stubcc.rejoin(Changes(1));
+        return;
+    }
+
+    if (cx->typeInferenceEnabled() &&
+        lhs->isType(JSVAL_TYPE_OBJECT) && rhs->isType(JSVAL_TYPE_OBJECT))
+    {
+        CompileStatus status = jsop_equality_obj_obj(op, NULL, JSOP_NOP);
+        if (status == Compile_Okay) return;
+        JS_ASSERT(status == Compile_Skipped);
+    }
+
     /* Is it impossible that both Values are ints? */
     if ((lhs->isTypeKnown() && lhs->isNotType(JSVAL_TYPE_INT32)) ||
         (rhs->isTypeKnown() && rhs->isNotType(JSVAL_TYPE_INT32))) {
         prepareStubCall(Uses(2));
 
         if (op == JSOP_STRICTEQ)
             INLINE_STUBCALL_USES(stubs::StrictEq, REJOIN_NONE, Uses(2));
         else
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -1557,17 +1557,17 @@ js_InternalInterpret(void *returnData, v
           case JSOP_IFEQ:
           case JSOP_IFEQX:
             takeBranch = returnReg == NULL;
             break;
           default:
             JS_NOT_REACHED("Bad branch op");
         }
         if (takeBranch)
-            f.regs.pc = nextpc + GET_JUMP_OFFSET(nextpc);
+            f.regs.pc = nextpc + analyze::GetJumpOffset(nextpc, nextpc);
         else
             f.regs.pc = nextpc + analyze::GetBytecodeLength(nextpc);
         break;
       }
 
       default:
         JS_NOT_REACHED("Missing rejoin");
     }
--- a/js/src/tests/ecma_5/Array/jstests.list
+++ b/js/src/tests/ecma_5/Array/jstests.list
@@ -1,9 +1,11 @@
 url-prefix ../../jsreftest.html?test=ecma_5/Array/
 script length-01.js
 script length-set-object.js
 script regress-599159.js
 script sort-01.js
+script splice-return-array-elements-defined-not-set.js
+script splice-suppresses-unvisited-indexes.js
 script toLocaleString-01.js
 script toString-01.js
 script unshift-01.js
 script index-with-null-character.js
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/Array/splice-return-array-elements-defined-not-set.js
@@ -0,0 +1,46 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 668024;
+var summary =
+  'Array.prototype.splice should define, not set, the elements of the array ' +
+  'it returns';
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+Object.defineProperty(Object.prototype, 2,
+  {
+    set: function(v)
+    {
+      throw new Error("setter on Object.prototype called!");
+    },
+    get: function() { return "fnord"; },
+    enumerable: false,
+    configurable: true
+  });
+
+var arr = [0, 1, 2, 3, 4, 5];
+var removed = arr.splice(0, 6);
+
+assertEq(arr.length, 0);
+assertEq(removed.length, 6);
+assertEq(removed[0], 0);
+assertEq(removed[1], 1);
+assertEq(removed[2], 2);
+assertEq(removed[3], 3);
+assertEq(removed[4], 4);
+assertEq(removed[5], 5);
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
+
+print("Tests complete");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/Array/splice-suppresses-unvisited-indexes.js
@@ -0,0 +1,61 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 668024;
+var summary =
+  'Array.prototype.splice, when it deletes elements, should make sure any ' +
+  'deleted but not visited elements are suppressed from subsequent enumeration';
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+var arr = [0, 1, 2, 3, 4, 5, , 7];
+
+var seen = [];
+var sawOneBeforeThree = true;
+for (var p in arr)
+{
+  if (p === "1")
+  {
+    // The order of enumeration of properties is unspecified, so technically,
+    // it would be kosher to enumerate "1" last, say, such that all properties
+    // in the array actually were enumerated, including an index which splice
+    // would delete.  Don't flag that case as a failure.  (SpiderMonkey doesn't
+    // do this, and neither do any of the other browser engines, but it is
+    // permissible behavior.)
+    if (seen.indexOf("3") >= 0)
+    {
+      sawOneBeforeThree = false;
+      break;
+    }
+
+    arr.splice(2, 3);
+  }
+
+  seen.push(p);
+}
+
+if (sawOneBeforeThree)
+{
+  // ES5 12.6.4 states:
+  //
+  //   If a property that has not yet been visited during enumeration is
+  //   deleted, then it will not be visited.
+  //
+  // So if we haven't seen "3" by the time we see "1", the splice call above
+  // will delete "3", and therefore we must not see it.
+  assertEq(seen.indexOf("3"), -1);
+}
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
+
+print("Tests complete");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/extensions/getOwnPropertyNames-__proto__.js
@@ -0,0 +1,28 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 690031;
+var summary =
+  'Exclude __proto__ from showing up when enumerating properties of ' +
+  'Object.prototype again';
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+var keys = Object.getOwnPropertyNames(Object.prototype);
+assertEq(keys.indexOf("__proto__"), -1,
+         "shouldn't have gotten __proto__ as a property of Object.prototype " +
+         "(got these properties: " + keys + ")");
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
+
+print("Tests complete");
--- a/js/src/tests/ecma_5/extensions/jstests.list
+++ b/js/src/tests/ecma_5/extensions/jstests.list
@@ -10,16 +10,17 @@ script bug472534.js
 script bug496985.js
 script bug566661.js
 script array-toString-recursion.js
 skip-if(!xulRuntime.shell) script cross-global-eval-is-indirect.js # needs newGlobal()
 script eval-native-callback-is-indirect.js
 script extension-methods-reject-null-undefined-this.js
 skip-if(!xulRuntime.shell) script function-definition-with.js # needs evaluate()
 script function-properties.js
+script getOwnPropertyNames-__proto__.js
 script iterator-in-catch.js
 script JSON-string-replacer-overflow.js
 skip-if(!xulRuntime.shell) script legacy-JSON.js # needs parseLegacyJSON
 fails script nested-delete-name-in-evalcode.js # bug 604301, at a minimum
 script proxy-strict.js
 script regress-bug567606.js
 script regress-bug607284.js
 script regress-bug629723.js
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -307,17 +307,17 @@ Breakpoint::nextInSite()
     return (link == &site->breakpoints) ? NULL : fromSiteLinks(link);
 }
 
 
 /*** Debugger hook dispatch **********************************************************************/
 
 Debugger::Debugger(JSContext *cx, JSObject *dbg)
   : object(dbg), uncaughtExceptionHook(NULL), enabled(true),
-    frames(cx), objects(cx), heldScripts(cx), nonHeldScripts(cx)
+    frames(cx), objects(cx), scripts(cx)
 {
     assertSameCompartment(cx, dbg);
 
     JSRuntime *rt = cx->runtime;
     AutoLockGC lock(rt);
     JS_APPEND_LINK(&link, &rt->debuggerList);
     JS_INIT_CLIST(&breakpoints);
 }
@@ -329,21 +329,20 @@ Debugger::~Debugger()
     /* This always happens in the GC thread, so no locking is required. */
     JS_ASSERT(object->compartment()->rt->gcRunning);
     JS_REMOVE_LINK(&link);
 }
 
 bool
 Debugger::init(JSContext *cx)
 {
-    bool ok = (frames.init() &&
-               objects.init() &&
-               debuggees.init() &&
-               heldScripts.init() &&
-               nonHeldScripts.init());
+    bool ok = frames.init() &&
+              objects.init() &&
+              debuggees.init() &&
+              scripts.init();
     if (!ok)
         js_ReportOutOfMemory(cx);
     return ok;
 }
 
 JS_STATIC_ASSERT(uintN(JSSLOT_DEBUGFRAME_OWNER) == uintN(JSSLOT_DEBUGOBJECT_OWNER));
 JS_STATIC_ASSERT(uintN(JSSLOT_DEBUGFRAME_OWNER) == uintN(JSSLOT_DEBUGSCRIPT_OWNER));
 
@@ -400,23 +399,17 @@ Debugger::hasAnyLiveHooks(JSContext *cx)
         getHook(OnNewScript) ||
         getHook(OnEnterFrame))
     {
         return true;
     }
 
     /* If any breakpoints are in live scripts, return true. */
     for (Breakpoint *bp = firstBreakpoint(); bp; bp = bp->nextInDebugger()) {
-        /*
-         * If holder is non-null, examine it to see if the script will be
-         * collected. If holder is null, then bp->site->script is an eval
-         * script on the stack, so it is definitely live.
-         */
-        JSObject *holder = bp->site->getScriptObject();
-        if (!holder || !IsAboutToBeFinalized(cx, holder))
+        if (!IsAboutToBeFinalized(cx, bp->site->script))
             return true;
     }
 
     for (FrameMap::Range r = frames.all(); !r.empty(); r.popFront()) {
         if (!r.front().value->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER).isUndefined())
             return true;
     }
 
@@ -489,17 +482,17 @@ Debugger::slowPathOnLeaveFrame(JSContext
 bool
 Debugger::wrapDebuggeeValue(JSContext *cx, Value *vp)
 {
     assertSameCompartment(cx, object);
 
     if (vp->isObject()) {
         JSObject *obj = &vp->toObject();
 
-        ObjectWeakMap::AddPtr p = objects.lookupForAdd(obj);
+        CellWeakMap::AddPtr p = objects.lookupForAdd(obj);
         if (p) {
             vp->setObject(*p->value);
         } else {
             /* Create a new Debugger.Object for obj. */
             JSObject *proto = &object->getReservedSlot(JSSLOT_DEBUG_OBJECT_PROTO).toObject();
             JSObject *dobj =
                 NewNonFunction<WithProto::Given>(cx, &DebuggerObject_class, proto, NULL);
             if (!dobj || !dobj->ensureClassReservedSlots(cx))
@@ -733,28 +726,27 @@ Debugger::fireEnterFrame(JSContext *cx)
         return;
     }
     Value rv;
     if (!Invoke(cx, ObjectValue(*object), ObjectValue(*hook), 1, argv, &rv))
         handleUncaughtException(ac, NULL, true);
 }
 
 void
-Debugger::fireNewScript(JSContext *cx, JSScript *script, JSObject *obj, NewScriptKind kind)
+Debugger::fireNewScript(JSContext *cx, JSScript *script, JSObject *obj)
 {
     JSObject *hook = getHook(OnNewScript);
     JS_ASSERT(hook);
     JS_ASSERT(hook->isCallable());
 
     AutoCompartment ac(cx, object);
     if (!ac.enter())
         return;
 
-    JSObject *dsobj =
-        kind == NewHeldScript ? wrapHeldScript(cx, script, obj) : wrapNonHeldScript(cx, script);
+    JSObject *dsobj = wrapScript(cx, script, obj);
     if (!dsobj) {
         handleUncaughtException(ac, NULL, false);
         return;
     }
 
     Value argv[1];
     argv[0].setObject(*dsobj);
     Value rv;
@@ -817,49 +809,51 @@ AddNewScriptRecipients(GlobalObject::Deb
         {
             return false;
         }
     }
     return true;
 }
 
 void
-Debugger::slowPathOnNewScript(JSContext *cx, JSScript *script, JSObject *obj, NewScriptKind kind)
+Debugger::slowPathOnNewScript(JSContext *cx, JSScript *script, JSObject *obj,
+                              GlobalObject *compileAndGoGlobal)
 {
+    JS_ASSERT(script->compileAndGo == !!compileAndGoGlobal);
+
     /*
      * Build the list of recipients. For compile-and-go scripts, this is the
      * same as the generic Debugger::dispatchHook code, but non-compile-and-go
      * scripts are not tied to particular globals. We deliver them to every
      * debugger observing any global in the script's compartment.
      */
     AutoValueVector triggered(cx);
-    GlobalObject *global;
     if (script->compileAndGo) {
-        global = obj->getGlobal();
-        if (GlobalObject::DebuggerVector *debuggers = global->getDebuggers()) {
+        if (GlobalObject::DebuggerVector *debuggers = compileAndGoGlobal->getDebuggers()) {
             if (!AddNewScriptRecipients(debuggers, &triggered))
                 return;
         }
     } else {
-        global = NULL;
         GlobalObjectSet &debuggees = script->compartment()->getDebuggees();
         for (GlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront()) {
             if (!AddNewScriptRecipients(r.front()->getDebuggers(), &triggered))
                 return;
         }
     }
 
     /*
      * Deliver the event to each debugger, checking again as in
      * Debugger::dispatchHook.
      */
     for (Value *p = triggered.begin(); p != triggered.end(); p++) {
         Debugger *dbg = Debugger::fromJSObject(&p->toObject());
-        if ((!global || dbg->debuggees.has(global)) && dbg->enabled && dbg->getHook(OnNewScript))
-            dbg->fireNewScript(cx, script, obj, kind);
+        if ((!compileAndGoGlobal || dbg->debuggees.has(compileAndGoGlobal)) &&
+            dbg->enabled && dbg->getHook(OnNewScript)) {
+            dbg->fireNewScript(cx, script, obj);
+        }
     }
 }
 
 JSTrapStatus
 Debugger::onTrap(JSContext *cx, Value *vp)
 {
     StackFrame *fp = cx->fp();
     GlobalObject *scriptGlobal = fp->scopeChain().getGlobal();
@@ -1006,48 +1000,55 @@ Debugger::onSingleStep(JSContext *cx, Va
         cx->setPendingException(exception);
     return JSTRAP_CONTINUE;
 }
 
 
 /*** Debugger JSObjects **************************************************************************/
 
 void
-Debugger::markKeysInCompartment(JSTracer *tracer, const ObjectWeakMap &map)
+Debugger::markKeysInCompartment(JSTracer *tracer, const CellWeakMap &map, bool scripts)
 {
     JSCompartment *comp = tracer->context->runtime->gcCurrentCompartment;
     JS_ASSERT(comp);
 
     /*
      * WeakMap::Range is deliberately private, to discourage C++ code from
      * enumerating WeakMap keys. However in this case we need access, so we
      * make a base-class reference. Range is public in HashMap.
      */
-    typedef HashMap<JSObject *, JSObject *, DefaultHasher<JSObject *>, RuntimeAllocPolicy> Map;
+    typedef HashMap<gc::Cell *, JSObject *, DefaultHasher<gc::Cell *>, RuntimeAllocPolicy> Map;
     const Map &storage = map;
     for (Map::Range r = storage.all(); !r.empty(); r.popFront()) {
-        JSObject *key = r.front().key;
-        if (key->compartment() == comp && IsAboutToBeFinalized(tracer->context, key))
-            js::gc::MarkObject(tracer, *key, "cross-compartment WeakMap key");
+        gc::Cell *key = r.front().key;
+        if (key->compartment() == comp && IsAboutToBeFinalized(tracer->context, key)) {
+            if (scripts) {
+                js::gc::MarkScript(tracer, static_cast<JSScript *>(key),
+                                   "cross-compartment WeakMap key");
+            } else {
+                js::gc::MarkObject(tracer, *static_cast<JSObject *>(key),
+                                   "cross-compartment WeakMap key");
+            }
+        }
     }
 }
 
 /*
  * Ordinarily, WeakMap keys and values are marked because at some point it was
  * discovered that the WeakMap was live; that is, some object containing the
  * WeakMap was marked during mark phase.
  *
  * However, during single-compartment GC, we have to do something about
  * cross-compartment WeakMaps in other compartments. Since those compartments
  * aren't being GC'd, the WeakMaps definitely will not be found during mark
  * phase. If their keys and values might need to be marked, we have to do it
  * manually.
  *
  * Each Debugger object keeps two cross-compartment WeakMaps: objects and
- * heldScripts.  Both have the nice property that all their values are in the
+ * scripts. Both have the nice property that all their values are in the
  * same compartment as the Debugger object, so we only need to mark the
  * keys. We must simply mark all keys that are in the compartment being GC'd.
  *
  * We must scan all Debugger objects regardless of whether they *currently*
  * have any debuggees in the compartment being GC'd, because the WeakMap
  * entries persist even when debuggees are removed.
  *
  * This happens during the initial mark phase, not iterative marking, because
@@ -1061,18 +1062,18 @@ Debugger::markCrossCompartmentDebuggerOb
 
     /*
      * Mark all objects in comp that are referents of Debugger.Objects in other
      * compartments.
      */
     for (JSCList *p = &rt->debuggerList; (p = JS_NEXT_LINK(p)) != &rt->debuggerList;) {
         Debugger *dbg = Debugger::fromLinks(p);
         if (dbg->object->compartment() != comp) {
-            markKeysInCompartment(tracer, dbg->objects);
-            markKeysInCompartment(tracer, dbg->heldScripts);
+            markKeysInCompartment(tracer, dbg->objects, false);
+            markKeysInCompartment(tracer, dbg->scripts, true);
         }
     }
 }
 
 /*
  * This method has two tasks:
  *   1. Mark Debugger objects that are unreachable except for debugger hooks that
  *      may yet be called.
@@ -1140,18 +1141,17 @@ Debugger::markAllIteratively(GCMarker *t
                     MarkObject(trc, *dbgobj, "enabled Debugger");
                     markedAny = true;
                     dbgMarked = true;
                 }
 
                 if (dbgMarked) {
                     /* Search for breakpoints to mark. */
                     for (Breakpoint *bp = dbg->firstBreakpoint(); bp; bp = bp->nextInDebugger()) {
-                        JSObject *scriptObject = bp->site->getScriptObject();
-                        if (!scriptObject || !IsAboutToBeFinalized(cx, scriptObject)) {
+                        if (!IsAboutToBeFinalized(cx, bp->site->script)) {
                             /*
                              * The debugger and the script are both live.
                              * Therefore the breakpoint handler is live.
                              */
                             JSObject *handler = bp->getHandler();
                             if (IsAboutToBeFinalized(cx, handler)) {
                                 MarkObject(trc, *bp->getHandler(), "breakpoint handler");
                                 markedAny = true;
@@ -1190,33 +1190,18 @@ Debugger::trace(JSTracer *trc)
         JSObject *frameobj = r.front().value;
         JS_ASSERT(frameobj->getPrivate());
         MarkObject(trc, *frameobj, "live Debugger.Frame");
     }
 
     /* Trace the referent -> Debugger.Object weak map. */
     objects.trace(trc);
 
-    /*
-     * Trace the weak map from JSFunctions and "Script" JSObjects to
-     * Debugger.Script objects.
-     */
-    heldScripts.trace(trc);
-
-    /* Trace the map for non-held scripts, which are explicitly freed. */
-    for (ScriptMap::Range r = nonHeldScripts.all(); !r.empty(); r.popFront()) {
-        JSObject *scriptobj = r.front().value;
-
-        /*
-         * nonHeldScripts should only refer to Debugger.Script objects for
-         * scripts that haven't been freed yet.
-         */
-        JS_ASSERT(scriptobj->getPrivate());
-        MarkObject(trc, *scriptobj, "live eval Debugger.Script");
-    }
+    /* Trace the weak map from JSScript instances to Debugger.Script objects. */
+    scripts.trace(trc);
 }
 
 void
 Debugger::sweepAll(JSContext *cx)
 {
     JSRuntime *rt = cx->runtime;
     JS_ASSERT(!rt->gcCurrentCompartment);
 
@@ -1786,81 +1771,37 @@ JSFunctionSpec Debugger::methods[] = {
     JS_FN("getNewestFrame", Debugger::getNewestFrame, 0, 0),
     JS_FN("clearAllBreakpoints", Debugger::clearAllBreakpoints, 1, 0),
     JS_FS_END
 };
 
 
 /*** Debugger.Script *****************************************************************************/
 
-/*
- * JSScripts' lifetimes fall into to two categories:
- *
- * - "Held scripts": JSScripts belonging to JSFunctions and JSScripts created
- *   using JSAPI have lifetimes determined by the garbage collector. A JSScript
- *   itself has no mark bit of its own. Instead, its holding object manages the
- *   JSScript as part of its own structure: the holder has a mark bit; when the
- *   holder is marked it calls js_TraceScript on its JSScript; and when the
- *   holder is freed it explicitly frees its JSScript.
- *
- *   Debugger.Script instances for held scripts are strong references to the
- *   holder (and thus to the script). Debugger::heldScripts weakly maps
- *   debuggee holding objects to the Debugger.Script objects for their
- *   JSScripts. We needn't act on a destroyScript event for a held script: if
- *   we get such an event we know its Debugger.Script is dead anyway, and its
- *   entry in Debugger::heldScripts will be cleaned up by the standard weak
- *   table code.
- *
- * - "Non-held scripts": JSScripts generated temporarily for a call to eval or
- *   JS_Evaluate*, live until the call completes, at which point the script is
- *   destroyed.
- *
- *   A Debugger.Script instance for a non-held script has no influence on the
- *   JSScript's lifetime. Debugger::nonHeldScripts maps live JSScripts to to
- *   their Debugger.Script objects.  When a destroyScript event tells us that
- *   a non-held script is dead, we remove its table entry, and clear its
- *   Debugger.Script object's script pointer, thus marking it dead.
- *
- * A Debugger.Script's private pointer points directly to the JSScript, or is
- * NULL if the Debugger.Script is dead. The JSSLOT_DEBUGSCRIPT_HOLDER slot
- * refers to the holding object, or is null for non-held JSScripts. The private
- * pointer is not traced; the holding object reference, if present, is traced
- * via DebuggerScript_trace.
- *
- * (We consider a script saved in and retrieved from the eval cache to have
- * been destroyed, and then --- mirabile dictu --- re-created at the same
- * address. The newScriptHook and destroyScriptHook hooks cooperate with this
- * view.)
- */
 static inline JSScript *
 GetScriptReferent(JSObject *obj)
 {
     JS_ASSERT(obj->getClass() == &DebuggerScript_class);
     return (JSScript *) obj->getPrivate();
 }
 
-static inline void
-ClearScriptReferent(JSObject *obj)
-{
-    JS_ASSERT(obj->getClass() == &DebuggerScript_class);
-    obj->setPrivate(NULL);
-}
-
 static inline JSObject *
 GetScriptHolder(JSObject *obj)
 {
     JS_ASSERT(obj->getClass() == &DebuggerScript_class);
     Value v = obj->getReservedSlot(JSSLOT_DEBUGSCRIPT_HOLDER);
     return (JSObject *) v.toPrivate();
 }
 
 static void
 DebuggerScript_trace(JSTracer *trc, JSObject *obj)
 {
     if (!trc->context->runtime->gcCurrentCompartment) {
+        if (JSScript *script = GetScriptReferent(obj))
+            MarkScript(trc, script, "Debugger.Script referent");
         Value v = obj->getReservedSlot(JSSLOT_DEBUGSCRIPT_HOLDER);
         if (!v.isUndefined()) {
             if (JSObject *obj = (JSObject *) v.toPrivate())
                 MarkObject(trc, *obj, "Debugger.Script referent holder");
         }
     }
 }
 
@@ -1890,93 +1831,43 @@ Debugger::newDebuggerScript(JSContext *c
     scriptobj->setPrivate(script);
     scriptobj->setReservedSlot(JSSLOT_DEBUGSCRIPT_OWNER, ObjectValue(*object));
     scriptobj->setReservedSlot(JSSLOT_DEBUGSCRIPT_HOLDER, PrivateValue(holder));
 
     return scriptobj;
 }
 
 JSObject *
-Debugger::wrapHeldScript(JSContext *cx, JSScript *script, JSObject *obj)
+Debugger::wrapScript(JSContext *cx, JSScript *script, JSObject *obj)
 {
     assertSameCompartment(cx, object);
     JS_ASSERT(cx->compartment != script->compartment());
-    JS_ASSERT(script->compartment() == obj->compartment());
-
-    ScriptWeakMap::AddPtr p = heldScripts.lookupForAdd(obj);
+    JS_ASSERT_IF(obj, script->compartment() == obj->compartment());
+
+    CellWeakMap::AddPtr p = scripts.lookupForAdd(script);
     if (!p) {
         JSObject *scriptobj = newDebuggerScript(cx, script, obj);
 
         /* The allocation may have caused a GC, which can remove table entries. */
-        if (!scriptobj || !heldScripts.relookupOrAdd(p, obj, scriptobj))
+        if (!scriptobj || !scripts.relookupOrAdd(p, script, scriptobj))
             return NULL;
     }
 
     JS_ASSERT(GetScriptReferent(p->value) == script);
     return p->value;
 }
 
 JSObject *
 Debugger::wrapFunctionScript(JSContext *cx, JSFunction *fun)
 {
-    return wrapHeldScript(cx, fun->script(), fun);
-}
-
-JSObject *
-Debugger::wrapJSAPIScript(JSContext *cx, JSObject *obj)
-{
-    JS_ASSERT(obj->isScript());
-    return wrapHeldScript(cx, obj->getScript(), obj);
-}
-
-JSObject *
-Debugger::wrapNonHeldScript(JSContext *cx, JSScript *script)
-{
-    assertSameCompartment(cx, object);
-    JS_ASSERT(cx->compartment != script->compartment());
-
-    ScriptMap::AddPtr p = nonHeldScripts.lookupForAdd(script);
-    if (!p) {
-        JSObject *scriptobj = newDebuggerScript(cx, script, NULL);
-
-        /* The allocation may have caused a GC, which can remove table entries. */
-        if (!scriptobj || !nonHeldScripts.relookupOrAdd(p, script, scriptobj))
-            return NULL;
-    }
-
-    JS_ASSERT(GetScriptReferent(p->value) == script);
-    return p->value;
-}
-
-void
-Debugger::slowPathOnDestroyScript(JSScript *script)
-{
-    /* Find all debuggers that might have Debugger.Script referring to this script. */
-    js::GlobalObjectSet *debuggees = &script->compartment()->getDebuggees();
-    for (GlobalObjectSet::Range r = debuggees->all(); !r.empty(); r.popFront()) {
-        GlobalObject::DebuggerVector *debuggers = r.front()->getDebuggers();
-        for (Debugger **p = debuggers->begin(); p != debuggers->end(); p++)
-            (*p)->destroyNonHeldScript(script);
-    }
-}
-
-void
-Debugger::destroyNonHeldScript(JSScript *script)
-{
-    ScriptMap::Ptr p = nonHeldScripts.lookup(script);
-    if (p) {