Merge last PGO-green changeset of mozilla-inbound to mozilla-central
authorEd Morley <emorley@mozilla.com>
Thu, 20 Dec 2012 21:05:33 +0000
changeset 122208 868b21bed3eb74c0c998eac9bcb600e71566245a
parent 122096 d81af339bb7aa3a484c76382de70fff8828fec57 (current diff)
parent 122207 8a7b7f1ac53ace630e6f49c87bcdb3c465c5a00d (diff)
child 122209 beec8736b9ec7dd74c100aa2b0437dacdb4c6a08
child 124022 8108ebc84975fd1b166b9e97d92a221fbb72a494
push idunknown
push userunknown
push dateunknown
milestone20.0a1
Merge last PGO-green changeset of mozilla-inbound to mozilla-central
browser/components/places/content/download.css
browser/components/places/content/download.xml
browser/components/places/content/downloadsView.js
browser/components/sessionstore/test/Makefile.in
dom/indexedDB/CheckQuotaHelper.cpp
dom/indexedDB/CheckQuotaHelper.h
mobile/android/base/resources/drawable-hdpi/abouthome_logo.png
mobile/android/base/resources/drawable-xhdpi/abouthome_logo.png
mobile/android/base/resources/drawable/abouthome_logo.png
--- a/accessible/src/base/AccEvent.h
+++ b/accessible/src/base/AccEvent.h
@@ -205,17 +205,16 @@ class AccMutationEvent: public AccEvent
 {
 public:
   AccMutationEvent(uint32_t aEventType, Accessible* aTarget,
                    nsINode* aTargetNode) :
     AccEvent(aEventType, aTarget, eAutoDetect, eCoalesceMutationTextChange)
   {
     // Don't coalesce these since they are coalesced by reorder event. Coalesce
     // contained text change events.
-    mNode = aTargetNode;
     mParent = mAccessible->Parent();
   }
   virtual ~AccMutationEvent() { }
 
   // Event
   static const EventGroup kEventGroup = eMutationEvent;
   virtual unsigned int GetEventGroups() const
   {
--- a/accessible/src/base/NotificationController.cpp
+++ b/accessible/src/base/NotificationController.cpp
@@ -633,17 +633,17 @@ NotificationController::CoalesceTextChan
 
   aTailEvent->mTextChangeEvent.swap(aThisEvent->mTextChangeEvent);
 }
 
 void
 NotificationController::CreateTextChangeEventFor(AccMutationEvent* aEvent)
 {
   DocAccessible* document = aEvent->GetDocAccessible();
-  Accessible* container = document->GetContainerAccessible(aEvent->mNode);
+  Accessible* container = aEvent->mAccessible->Parent();
   if (!container)
     return;
 
   HyperTextAccessible* textAccessible = container->AsHyperText();
   if (!textAccessible)
     return;
 
   // Don't fire event for the first html:br in an editor.
--- a/b2g/chrome/content/forms.js
+++ b/b2g/chrome/content/forms.js
@@ -34,17 +34,16 @@ let HTMLOptionElement = Ci.nsIDOMHTMLOpt
 let FormAssistant = {
   init: function fa_init() {
     addEventListener("focus", this, true, false);
     addEventListener("blur", this, true, false);
     addEventListener("resize", this, true, false);
     addMessageListener("Forms:Select:Choice", this);
     addMessageListener("Forms:Input:Value", this);
     addMessageListener("Forms:Select:Blur", this);
-    Services.obs.addObserver(this, "ime-enabled-state-changed", false);
     Services.obs.addObserver(this, "xpcom-shutdown", false);
   },
 
   ignoredInputTypes: new Set([
     'button', 'file', 'checkbox', 'radio', 'reset', 'submit', 'image'
   ]),
 
   isKeyboardOpened: false,
@@ -60,71 +59,88 @@ let FormAssistant = {
     return this._focusedElement;
   },
 
   set focusedElement(val) {
     this._focusedElement = val;
   },
 
   setFocusedElement: function fa_setFocusedElement(element) {
+    if (element instanceof HTMLOptionElement)
+      element = element.parentNode;
+
     if (element === this.focusedElement)
       return;
 
     if (this.focusedElement) {
+      this.focusedElement.removeEventListener('click', this);
       this.focusedElement.removeEventListener('mousedown', this);
       this.focusedElement.removeEventListener('mouseup', this);
       if (!element) {
         this.focusedElement.blur();
       }
     }
 
     if (element) {
+      element.addEventListener('click', this);
       element.addEventListener('mousedown', this);
       element.addEventListener('mouseup', this);
     }
 
     this.focusedElement = element;
   },
 
   handleEvent: function fa_handleEvent(evt) {
     let focusedElement = this.focusedElement;
     let target = evt.target;
 
     switch (evt.type) {
       case "focus":
         if (this.isTextInputElement(target) && this.isIMEDisabled())
           return;
 
-        if (target && this.isFocusableElement(target))
-          this.handleIMEStateEnabled(target);
+        // We got input focus, but don't open the virtual keyboard unless we
+        // get a 'click' event, i.e. the user is tapping the input element.
+        if (target && this.isFocusableElement(target)) {
+          this.setFocusedElement(target);
+        }
         break;
 
       case "blur":
         if (this.focusedElement)
-          this.handleIMEStateDisabled();
+          this.hideKeyboard();
+        this.setFocusedElement(null);
         break;
 
       case 'mousedown':
         // We only listen for this event on the currently focused element.
         // When the mouse goes down, note the cursor/selection position
         this.selectionStart = this.focusedElement.selectionStart;
         this.selectionEnd = this.focusedElement.selectionEnd;
         break;
 
       case 'mouseup':
         // We only listen for this event on the currently focused element.
         // When the mouse goes up, see if the cursor has moved (or the
         // selection changed) since the mouse went down. If it has, we
         // need to tell the keyboard about it
         if (this.focusedElement.selectionStart !== this.selectionStart ||
             this.focusedElement.selectionEnd !== this.selectionEnd) {
-          this.tryShowIme(this.focusedElement);
+          this.sendKeyboardState(this.focusedElement);
         }
         break;
 
+      case 'click':
+        // We only listen for click events on the currently focused element.
+        // Gecko fires a click event if the user "taps" an input element
+        // without dragging. This is how we differentiate tap gestures to set
+        // input focus (and open the keyboard) from simply panning the page.
+        this.showKeyboard();
+        break;
+
       case "resize":
         if (!this.isKeyboardOpened)
           return;
 
         if (this.scrollIntoViewTimeout) {
           content.clearTimeout(this.scrollIntoViewTimeout);
           this.scrollIntoViewTimeout = null;
         }
@@ -190,66 +206,46 @@ let FormAssistant = {
         this.setFocusedElement(null);
         break;
       }
     }
   },
 
   observe: function fa_observe(subject, topic, data) {
     switch (topic) {
-      case "ime-enabled-state-changed":
-        let shouldOpen = parseInt(data);
-        let target = Services.fm.focusedElement;
-        if (!target || !this.isTextInputElement(target))
-          return;
-
-        if (shouldOpen) {
-          if (!this.focusedElement && this.isFocusableElement(target))
-            this.handleIMEStateEnabled(target);
-        } else if (this._focusedElement == target) {
-          this.handleIMEStateDisabled();
-        }
-        break;
-
       case "xpcom-shutdown":
-        Services.obs.removeObserver(this, "ime-enabled-state-changed", false);
         Services.obs.removeObserver(this, "xpcom-shutdown");
         removeMessageListener("Forms:Select:Choice", this);
         removeMessageListener("Forms:Input:Value", this);
         break;
     }
   },
 
   isIMEDisabled: function fa_isIMEDisabled() {
     let disabled = false;
     try {
       disabled = domWindowUtils.IMEStatus == domWindowUtils.IME_STATUS_DISABLED;
     } catch (e) {}
 
     return disabled;
   },
 
-  handleIMEStateEnabled: function fa_handleIMEStateEnabled(target) {
+  showKeyboard: function fa_showKeyboard() {
     if (this.isKeyboardOpened)
       return;
 
-    if (target instanceof HTMLOptionElement)
-      target = target.parentNode;
-
-    let kbOpened = this.tryShowIme(target);
+    let target = this.focusedElement;
+    let kbOpened = this.sendKeyboardState(target);
     if (this.isTextInputElement(target))
       this.isKeyboardOpened = kbOpened;
-
-    this.setFocusedElement(target);
   },
 
-  handleIMEStateDisabled: function fa_handleIMEStateDisabled() {
+  hideKeyboard: function fa_hideKeyboard() {
     sendAsyncMessage("Forms:Input", { "type": "blur" });
     this.isKeyboardOpened = false;
-    this.setFocusedElement(null);
   },
 
   isFocusableElement: function fa_isFocusableElement(element) {
     if (element.contentEditable && element.contentEditable == "true") {
       return true;
     }
 
     if (element instanceof HTMLSelectElement ||
@@ -265,17 +261,17 @@ let FormAssistant = {
   },
 
   isTextInputElement: function fa_isTextInputElement(element) {
     return element instanceof HTMLInputElement ||
            element instanceof HTMLTextAreaElement ||
            (element.contentEditable && element.contentEditable == "true");
   },
 
-  tryShowIme: function(element) {
+  sendKeyboardState: function(element) {
     // FIXME/bug 729623: work around apparent bug in the IME manager
     // in gecko.
     let readonly = element.getAttribute("readonly");
     if (readonly) {
       return false;
     }
 
     sendAsyncMessage("Forms:Input", getJSON(element));
--- a/b2g/confvars.sh
+++ b/b2g/confvars.sh
@@ -14,17 +14,16 @@ MOZ_B2G_VERSION=1.0.0-prerelease
 MOZ_B2G_OS_NAME=Boot2Gecko
 
 MOZ_BRANDING_DIRECTORY=b2g/branding/unofficial
 MOZ_OFFICIAL_BRANDING_DIRECTORY=b2g/branding/official
 # MOZ_APP_DISPLAYNAME is set by branding/configure.sh
 
 MOZ_SAFE_BROWSING=
 MOZ_SERVICES_COMMON=1
-MOZ_SERVICES_HEALTHREPORT=1
 MOZ_SERVICES_METRICS=1
 
 MOZ_WEBSMS_BACKEND=1
 MOZ_DISABLE_DOMCRYPTO=1
 MOZ_APP_STATIC_INI=1
 
 if test "$OS_TARGET" = "Android"; then
 MOZ_CAPTURE=1
--- a/browser/base/content/browser-plugins.js
+++ b/browser/base/content/browser-plugins.js
@@ -139,17 +139,16 @@ var gPluginHandler = {
         return "PluginPlayPreview";
       default:
         // Not all states map to a handler
         return null;
     }
   },
 
   handleEvent : function(event) {
-    let self = gPluginHandler;
     let plugin = event.target;
     let doc = plugin.ownerDocument;
 
     // We're expecting the target to be a plugin.
     if (!(plugin instanceof Ci.nsIObjectLoadingContent))
       return;
 
     let eventType = event.type;
@@ -159,98 +158,98 @@ var gPluginHandler = {
       // and make sure we don't handle it twice
       let overlay = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
       if (!overlay || overlay._bindingHandled) {
         return;
       }
       overlay._bindingHandled = true;
 
       // Lookup the handler for this binding
-      eventType = self._getBindingType(plugin);
+      eventType = this._getBindingType(plugin);
       if (!eventType) {
         // Not all bindings have handlers
         return;
       }
     }
 
     switch (eventType) {
       case "PluginCrashed":
-        self.pluginInstanceCrashed(plugin, event);
+        this.pluginInstanceCrashed(plugin, event);
         break;
 
       case "PluginNotFound":
         // For non-object plugin tags, register a click handler to install the
         // plugin. Object tags can, and often do, deal with that themselves,
         // so don't stomp on the page developers toes.
         if (!(plugin instanceof HTMLObjectElement)) {
           // We don't yet check to see if there's actually an installer available.
           let installStatus = doc.getAnonymousElementByAttribute(plugin, "class", "installStatus");
           installStatus.setAttribute("status", "ready");
           let iconStatus = doc.getAnonymousElementByAttribute(plugin, "class", "icon");
           iconStatus.setAttribute("status", "ready");
 
           let installLink = doc.getAnonymousElementByAttribute(plugin, "class", "installPluginLink");
-          self.addLinkClickCallback(installLink, "installSinglePlugin", plugin);
+          this.addLinkClickCallback(installLink, "installSinglePlugin", plugin);
         }
         /* FALLTHRU */
 
       case "PluginBlocklisted":
       case "PluginOutdated":
 #ifdef XP_MACOSX
       case "npapi-carbon-event-model-failure":
 #endif
-        self.pluginUnavailable(plugin, eventType);
+        this.pluginUnavailable(plugin, eventType);
         break;
 
       case "PluginVulnerableUpdatable":
         let updateLink = doc.getAnonymousElementByAttribute(plugin, "class", "checkForUpdatesLink");
-        self.addLinkClickCallback(updateLink, "openPluginUpdatePage");
+        this.addLinkClickCallback(updateLink, "openPluginUpdatePage");
         /* FALLTHRU */
 
       case "PluginVulnerableNoUpdate":
       case "PluginClickToPlay":
-        self._handleClickToPlayEvent(plugin);
+        this._handleClickToPlayEvent(plugin);
         let overlay = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
-        let pluginName = self._getPluginInfo(plugin).pluginName;
+        let pluginName = this._getPluginInfo(plugin).pluginName;
         let messageString = gNavigatorBundle.getFormattedString("PluginClickToPlay", [pluginName]);
         let overlayText = doc.getAnonymousElementByAttribute(plugin, "class", "msg msgClickToPlay");
         overlayText.textContent = messageString;
         if (eventType == "PluginVulnerableUpdatable" ||
             eventType == "PluginVulnerableNoUpdate") {
           let vulnerabilityString = gNavigatorBundle.getString(eventType);
           let vulnerabilityText = doc.getAnonymousElementByAttribute(plugin, "anonid", "vulnerabilityStatus");
           vulnerabilityText.textContent = vulnerabilityString;
         }
         break;
 
       case "PluginPlayPreview":
-        self._handlePlayPreviewEvent(plugin);
+        this._handlePlayPreviewEvent(plugin);
         break;
 
       case "PluginDisabled":
         let manageLink = doc.getAnonymousElementByAttribute(plugin, "class", "managePluginsLink");
-        self.addLinkClickCallback(manageLink, "managePlugins");
+        this.addLinkClickCallback(manageLink, "managePlugins");
         break;
 
       case "PluginScripted":
         let browser = gBrowser.getBrowserForDocument(doc.defaultView.top.document);
         if (browser._pluginScriptedState == this.PLUGIN_SCRIPTED_STATE_NONE) {
           browser._pluginScriptedState = this.PLUGIN_SCRIPTED_STATE_FIRED;
           setTimeout(function() {
             gPluginHandler.handlePluginScripted(this);
           }.bind(browser), 500);
         }
         break;
     }
 
     // Hide the in-content UI if it's too big. The crashed plugin handler already did this.
     if (eventType != "PluginCrashed") {
       let overlay = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
-      if (overlay != null && self.isTooSmall(plugin, overlay))
-          overlay.style.visibility = "hidden";
+      if (overlay != null && this.isTooSmall(plugin, overlay))
+        overlay.style.visibility = "hidden";
     }
   },
 
   _notificationDisplayedOnce: false,
   handlePluginScripted: function PH_handlePluginScripted(aBrowser) {
     let contentWindow = aBrowser.contentWindow;
     if (!contentWindow)
       return;
--- a/browser/base/content/browser-social.js
+++ b/browser/base/content/browser-social.js
@@ -713,17 +713,17 @@ var SocialToolbar = {
     this.updateProvider();
     this._dynamicResizer = new DynamicResizeWatcher();
   },
 
   // Called when the Social.provider changes
   updateProvider: function () {
     if (!Social.provider)
       return;
-    this.button.setAttribute("image", Social.provider.iconURL);
+    this.button.style.listStyleImage = "url(" + Social.provider.iconURL + ")";
     this.updateButton();
     this.updateProfile();
     this.populateProviderMenus();
   },
 
   get button() {
     return document.getElementById("social-provider-button");
   },
new file mode 100644
--- /dev/null
+++ b/browser/components/downloads/content/allDownloadsViewOverlay.css
@@ -0,0 +1,33 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+richlistitem.download {
+  -moz-binding: url('chrome://browser/content/downloads/download.xml#download-full-ui');
+}
+
+.download-state:not(          [state="0"]  /* Downloading        */)
+                                           .downloadPauseMenuItem,
+.download-state:not(          [state="4"]  /* Paused             */)
+                                           .downloadResumeMenuItem,
+.download-state:not(:-moz-any([state="2"], /* Failed             */
+                              [state="4"]) /* Paused             */)
+                                           .downloadCancelMenuItem,
+.download-state:not(:-moz-any([state="1"], /* Finished           */
+                              [state="2"], /* Failed             */
+                              [state="3"], /* Canceled           */
+                              [state="6"], /* Blocked (parental) */
+                              [state="8"], /* Blocked (dirty)    */
+                              [state="9"]) /* Blocked (policy)   */)
+                                           .downloadRemoveFromHistoryMenuItem,
+.download-state:not(:-moz-any([state="-1"],/* Starting (initial) */
+                              [state="5"], /* Starting (queued)  */
+                              [state="0"], /* Downloading        */
+                              [state="4"]) /* Paused             */)
+                                           .downloadShowMenuItem,
+
+.download-state[state="7"]                 .downloadCommandsSeparator
+
+{
+  display: none;
+}
rename from browser/components/places/content/downloadsView.js
rename to browser/components/downloads/content/allDownloadsViewOverlay.js
new file mode 100644
--- /dev/null
+++ b/browser/components/downloads/content/allDownloadsViewOverlay.xul
@@ -0,0 +1,102 @@
+<?xml version="1.0"?>
+
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+<?xml-stylesheet href="chrome://browser/content/downloads/allDownloadsViewOverlay.css"?>
+<?xml-stylesheet href="chrome://browser/skin/downloads/allDownloadsViewOverlay.css"?>
+
+<!DOCTYPE overlay [
+<!ENTITY % downloadsDTD SYSTEM "chrome://browser/locale/downloads/downloads.dtd">
+%downloadsDTD;
+]>
+
+<!-- This overlay provides a downloads view that lists both session downloads,
+     using the DownloadsView API, and history downloads, using places queries.
+     The view also implements a command controller and a context menu for
+     managing the downloads list.  In order to use this view:
+     1. Apply this overlay to your window.
+     2. Insert in all the overlay entry-points, namely:
+        <richlistbox id="downloadsRichListBox"/>
+        <commandset id="downloadCommands"/>
+        <menupopup id="downloadsContextMenu"/>
+    3. Make sure your window also has the editMenuOverlay overlay applied,
+       because the view implements cmd_copy and cmd_delete.
+    4. To initialize the view
+        let view = new DownloadsPlacesView(document.getElementById("downloadsRichListBox"));
+        // This is what the Places Library uses. It could be tweaked a bit as long as the
+        // transition-type is set correctly
+        view.places = "place:transition=7&sort=4";
+-->
+<overlay id="downloadsViewOverlay"
+         xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+  <script type="application/javascript"
+          src="chrome://browser/content/downloads/allDownloadsViewOverlay.js"/>
+  <script type="application/javascript"
+          src="chrome://global/content/contentAreaUtils.js"/>
+
+  <richlistbox flex="1"
+               seltype="multiple"
+               id="downloadsRichListBox" context="downloadsContextMenu"
+               onkeypress="return this._placesView.onKeyPress(event);"
+               oncontextmenu="return this._placesView.onContextMenu(event);"/>
+
+  <commandset id="downloadCommands"
+              commandupdater="true"
+              events="focus,select,contextmenu"
+              oncommandupdate="goUpdateDownloadCommands();">
+    <command id="downloadsCmd_pauseResume"
+             oncommand="goDoCommand('downloadsCmd_pauseResume')"/>
+    <command id="downloadsCmd_cancel"
+             oncommand="goDoCommand('downloadsCmd_cancel')"/>
+    <command id="downloadsCmd_open"
+             oncommand="goDoCommand('downloadsCmd_open')"/>
+    <command id="downloadsCmd_show"
+             oncommand="goDoCommand('downloadsCmd_show')"/>
+    <command id="downloadsCmd_retry"
+             oncommand="goDoCommand('downloadsCmd_retry')"/>
+    <command id="downloadsCmd_openReferrer"
+             oncommand="goDoCommand('downloadsCmd_openReferrer')"/>
+  </commandset>
+
+  <menupopup id="downloadsContextMenu" class="download-state">
+    <menuitem command="downloadsCmd_pauseResume"
+              class="downloadPauseMenuItem"
+              label="&cmd.pause.label;"
+              accesskey="&cmd.pause.accesskey;"/>
+    <menuitem command="downloadsCmd_pauseResume"
+              class="downloadResumeMenuItem"
+              label="&cmd.resume.label;"
+              accesskey="&cmd.resume.accesskey;"/>
+    <menuitem command="downloadsCmd_cancel"
+              class="downloadCancelMenuItem"
+              label="&cmd.cancel.label;"
+              accesskey="&cmd.cancel.accesskey;"/>
+    <menuitem command="cmd_delete"
+              class="downloadRemoveFromHistoryMenuItem"
+              label="&cmd.removeFromHistory.label;"
+              accesskey="&cmd.removeFromHistory.accesskey;"/>
+    <menuitem command="downloadsCmd_show"
+              class="downloadShowMenuItem"
+#ifdef XP_MACOSX
+              label="&cmd.showMac.label;"
+              accesskey="&cmd.showMac.accesskey;"
+#else
+              label="&cmd.show.label;"
+              accesskey="&cmd.show.accesskey;"
+#endif
+              />
+
+    <menuseparator class="downloadCommandsSeparator"/>
+
+    <menuitem command="downloadsCmd_openReferrer"
+              label="&cmd.goToDownloadPage.label;"
+              accesskey="&cmd.goToDownloadPage.accesskey;"/>
+    <menuitem command="cmd_copy"
+              label="&cmd.copyDownloadLink.label;"
+              accesskey="&cmd.copyDownloadLink.accesskey;"/>
+  </menupopup>
+</overlay>
rename from browser/components/places/content/download.css
rename to browser/components/downloads/content/download.css
--- a/browser/components/downloads/content/download.xml
+++ b/browser/components/downloads/content/download.xml
@@ -53,9 +53,48 @@
                     tooltiptext="&cmd.retry.label;"
                     oncommand="DownloadsView.onDownloadCommand(event, 'downloadsCmd_retry');"/>
         <xul:button class="downloadButton downloadShow"
                     tooltiptext="&cmd.show.label;"
                     oncommand="DownloadsView.onDownloadCommand(event, 'downloadsCmd_show');"/>
       </xul:stack>
     </content>
   </binding>
+
+  <binding id="download-full-ui"
+           extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
+    <resources>
+      <stylesheet src="chrome://browser/content/downloads/download.css"/>
+    </resources>
+
+    <content orient="horizontal" align="center">
+      <xul:image class="downloadTypeIcon"
+                 validate="always"
+                 xbl:inherits="src=image"/>
+      <xul:image class="downloadTypeIcon blockedIcon"/>
+      <xul:vbox pack="center" flex="1">
+        <xul:description class="downloadTarget"
+                         crop="center"
+                         xbl:inherits="value=displayName,tooltiptext=displayName"/>
+        <xul:progressmeter anonid="progressmeter"
+                           class="downloadProgress"
+                           min="0"
+                           max="100"
+                           xbl:inherits="mode=progressmode,value=progress"/>
+        <xul:description class="downloadDetails"
+                         style="width: &downloadDetails.width;"
+                         crop="end"
+                         xbl:inherits="value=status,tooltiptext=statusTip"/>
+      </xul:vbox>
+      <xul:stack>
+        <xul:button class="downloadButton downloadCancel"
+                    command="downloadsCmd_cancel"
+                    tooltiptext="&cmd.cancel.label;"/>
+        <xul:button class="downloadButton downloadRetry"
+                    command="downloadsCmd_retry"
+                    tooltiptext="&cmd.retry.label;"/>
+        <xul:button class="downloadButton downloadShow"
+                    command="downloadsCmd_show"
+                    tooltiptext="&cmd.show.label;"/>
+      </xul:stack>
+    </content>
+  </binding>
 </bindings>
--- a/browser/components/downloads/jar.mn
+++ b/browser/components/downloads/jar.mn
@@ -1,11 +1,15 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 browser.jar:
         content/browser/downloads/download.xml           (content/download.xml)
+        content/browser/downloads/download.css           (content/download.css)
         content/browser/downloads/downloads.css          (content/downloads.css)
 *       content/browser/downloads/downloads.js           (content/downloads.js)
 *       content/browser/downloads/downloadsOverlay.xul   (content/downloadsOverlay.xul)
         content/browser/downloads/indicator.js           (content/indicator.js)
         content/browser/downloads/indicatorOverlay.xul   (content/indicatorOverlay.xul)
+*       content/browser/downloads/allDownloadsViewOverlay.xul (content/allDownloadsViewOverlay.xul)
+        content/browser/downloads/allDownloadsViewOverlay.js  (content/allDownloadsViewOverlay.js)
+        content/browser/downloads/allDownloadsViewOverlay.css (content/allDownloadsViewOverlay.css)
deleted file mode 100644
--- a/browser/components/places/content/download.xml
+++ /dev/null
@@ -1,54 +0,0 @@
-<?xml version="1.0"?>
-<!-- -*- Mode: HTML; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- -->
-<!-- vim: set ts=2 et sw=2 tw=80: -->
-
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this file,
-   - You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-<!DOCTYPE bindings SYSTEM "chrome://browser/locale/downloads/downloads.dtd">
-
-<bindings id="downloadBindings"
-          xmlns="http://www.mozilla.org/xbl"
-          xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-          xmlns:xbl="http://www.mozilla.org/xbl">
-
-  <binding id="download"
-           extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
-   <resources>
-     <stylesheet src="chrome://browser/content/places/download.css"/>
-   </resources>
-
-    <content orient="horizontal" align="center">
-      <xul:image class="downloadTypeIcon"
-                 validate="always"
-                 xbl:inherits="src=image"/>
-      <xul:image class="downloadTypeIcon blockedIcon"/>
-      <xul:vbox pack="center" flex="1">
-        <xul:description class="downloadTarget"
-                         crop="center"
-                         xbl:inherits="value=displayName,tooltiptext=displayName"/>
-        <xul:progressmeter anonid="progressmeter"
-                           class="downloadProgress"
-                           min="0"
-                           max="100"
-                           xbl:inherits="mode=progressmode,value=progress"/>
-        <xul:description class="downloadDetails"
-                         style="width: &downloadDetails.width;"
-                         crop="end"
-                         xbl:inherits="value=status,tooltiptext=statusTip"/>
-      </xul:vbox>
-      <xul:stack>
-        <xul:button class="downloadButton downloadCancel"
-                    command="downloadsCmd_cancel"
-                    tooltiptext="&cmd.cancel.label;"/>
-        <xul:button class="downloadButton downloadRetry"
-                    command="downloadsCmd_retry"
-                    tooltiptext="&cmd.retry.label;"/>
-        <xul:button class="downloadButton downloadShow"
-                    command="downloadsCmd_show"
-                    tooltiptext="&cmd.show.label;"/>
-      </xul:stack>
-    </content>
-  </binding>
-</bindings>
--- a/browser/components/places/content/places.css
+++ b/browser/components/places/content/places.css
@@ -9,38 +9,8 @@ tree[type="places"] {
 .toolbar-drop-indicator {
   position: relative;
   z-index: 1;
 }
 
 menupopup[placespopup="true"] {
   -moz-binding: url("chrome://browser/content/places/menu.xml#places-popup-base");
 }
-
-richlistitem.download {
-	-moz-binding: url('chrome://browser/content/places/download.xml#download');
-}
-
-.download-state:not(          [state="0"]  /* Downloading        */)
-                                           .downloadPauseMenuItem,
-.download-state:not(          [state="4"]  /* Paused             */)
-                                           .downloadResumeMenuItem,
-.download-state:not(:-moz-any([state="2"], /* Failed             */
-                              [state="4"]) /* Paused             */)
-                                           .downloadCancelMenuItem,
-.download-state:not(:-moz-any([state="1"], /* Finished           */
-                              [state="2"], /* Failed             */
-                              [state="3"], /* Canceled           */
-                              [state="6"], /* Blocked (parental) */
-                              [state="8"], /* Blocked (dirty)    */
-                              [state="9"]) /* Blocked (policy)   */)
-                                           .downloadRemoveFromHistoryMenuItem,
-.download-state:not(:-moz-any([state="-1"],/* Starting (initial) */
-                              [state="5"], /* Starting (queued)  */
-                              [state="0"], /* Downloading        */
-                              [state="4"]) /* Paused             */)
-                                           .downloadShowMenuItem,
-
-.download-state[state="7"]                 .downloadCommandsSeparator
-
-{
-  display: none;
-}
--- a/browser/components/places/content/places.xul
+++ b/browser/components/places/content/places.xul
@@ -5,61 +5,55 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 <?xml-stylesheet href="chrome://browser/content/places/places.css"?>
 <?xml-stylesheet href="chrome://browser/content/places/organizer.css"?>
 
 <?xml-stylesheet href="chrome://global/skin/"?>
 <?xml-stylesheet href="chrome://browser/skin/places/places.css"?>
 <?xml-stylesheet href="chrome://browser/skin/places/organizer.css"?>
-<?xml-stylesheet href="chrome://browser/skin/downloads/downloads.css"?>
 
 <?xul-overlay href="chrome://browser/content/places/editBookmarkOverlay.xul"?>
+<?xul-overlay href="chrome://browser/content/downloads/allDownloadsViewOverlay.xul"?>
 
 #ifdef XP_MACOSX
 <?xul-overlay href="chrome://browser/content/macBrowserOverlay.xul"?>
 #else
 <?xul-overlay href="chrome://browser/content/baseMenuOverlay.xul"?>
 <?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
 <?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?>
 #endif
 
 <!DOCTYPE window [
 <!ENTITY % placesDTD SYSTEM "chrome://browser/locale/places/places.dtd">
 %placesDTD;
 <!ENTITY % editMenuOverlayDTD SYSTEM "chrome://global/locale/editMenuOverlay.dtd">
 %editMenuOverlayDTD;
 <!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
 %browserDTD;
-<!ENTITY % downloadsDTD SYSTEM "chrome://browser/locale/downloads/downloads.dtd">
-%downloadsDTD;
 ]>
 
 <window id="places"
         title="&places.library.title;"
         windowtype="Places:Organizer"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         xmlns:html="http://www.w3.org/1999/xhtml"
         onload="PlacesOrganizer.init();"
         onunload="PlacesOrganizer.destroy();"
         width="&places.library.width;" height="&places.library.height;"
         screenX="10" screenY="10"
         toggletoolbar="true"
         persist="width height screenX screenY sizemode">
 
   <script type="application/javascript"
-          src="chrome://browser/content/places/downloadsView.js"/>
-  <script type="application/javascript"
           src="chrome://browser/content/places/places.js"/>
   <script type="application/javascript"
           src="chrome://browser/content/utilityOverlay.js"/>
   <script type="application/javascript"
           src="chrome://browser/content/places/editBookmarkOverlay.js"/>
-  <script type="application/javascript"
-          src="chrome://global/content/contentAreaUtils.js"/>
 
   <stringbundleset id="placesStringSet">
     <stringbundle id="brandStrings" src="chrome://branding/locale/brand.properties"/>
   </stringbundleset>
 
 
 #ifdef XP_MACOSX
 #include ../../../base/content/browserMountPoints.inc
@@ -406,21 +400,17 @@
             <treecol label="&col.dateadded.label;" id="placesContentDateAdded" anonid="dateAdded" flex="1" hidden="true"
                       persist="width hidden ordinal sortActive sortDirection"/>
             <splitter class="tree-splitter"/>
             <treecol label="&col.lastmodified.label;" id="placesContentLastModified" anonid="lastModified" flex="1" hidden="true"
                       persist="width hidden ordinal sortActive sortDirection"/>
           </treecols>
           <treechildren flex="1"/>
         </tree>
-        <richlistbox flex="1"
-                     seltype="multiple"
-                     id="downloadsRichListBox" context="downloadsContextMenu"
-                     onkeypress="return this._placesView.onKeyPress(event);"
-                     oncontextmenu="return this._placesView.onContextMenu(event);"/>
+        <richlistbox id="downloadsRichListBox"/>
       </deck>
       <deck id="detailsDeck" style="height: 11em;">
         <vbox id="itemsCountBox" align="center">
           <spacer flex="3"/>
           <label id="itemsCountText"/>
           <spacer flex="1"/>
           <description id="selectItemDescription">
               &detailsPane.selectAnItemText.description;
@@ -446,65 +436,11 @@
                     control="infoBoxExpander"/>
 
           </hbox>
         </vbox>
       </deck>
     </vbox>
   </hbox>
 
-  <commandset id="downloadCommands"
-              commandupdater="true"
-              events="focus,select,contextmenu"
-              oncommandupdate="goUpdatePlacesCommands(); goUpdateDownloadCommands();">
-    <command id="downloadsCmd_pauseResume"
-             oncommand="goDoCommand('downloadsCmd_pauseResume')"/>
-    <command id="downloadsCmd_cancel"
-             oncommand="goDoCommand('downloadsCmd_cancel')"/>
-    <command id="downloadsCmd_open"
-             oncommand="goDoCommand('downloadsCmd_open')"/>
-    <command id="downloadsCmd_show"
-             oncommand="goDoCommand('downloadsCmd_show')"/>
-    <command id="downloadsCmd_retry"
-             oncommand="goDoCommand('downloadsCmd_retry')"/>
-    <command id="downloadsCmd_openReferrer"
-             oncommand="goDoCommand('downloadsCmd_openReferrer')"/>
-  </commandset>
-
-  <menupopup id="downloadsContextMenu"
-             class="download-state">
-    <menuitem command="downloadsCmd_pauseResume"
-              class="downloadPauseMenuItem"
-              label="&cmd.pause.label;"
-              accesskey="&cmd.pause.accesskey;"/>
-    <menuitem command="downloadsCmd_pauseResume"
-              class="downloadResumeMenuItem"
-              label="&cmd.resume.label;"
-              accesskey="&cmd.resume.accesskey;"/>
-    <menuitem command="downloadsCmd_cancel"
-              class="downloadCancelMenuItem"
-              label="&cmd.cancel.label;"
-              accesskey="&cmd.cancel.accesskey;"/>
-    <menuitem command="cmd_delete"
-              class="downloadRemoveFromHistoryMenuItem"
-              label="&cmd.removeFromHistory.label;"
-              accesskey="&cmd.removeFromHistory.accesskey;"/>
-    <menuitem command="downloadsCmd_show"
-              class="downloadShowMenuItem"
-#ifdef XP_MACOSX
-              label="&cmd.showMac.label;"
-              accesskey="&cmd.showMac.accesskey;"
-#else
-              label="&cmd.show.label;"
-              accesskey="&cmd.show.accesskey;"
-#endif
-              />
-
-    <menuseparator class="downloadCommandsSeparator"/>
-
-    <menuitem command="downloadsCmd_openReferrer"
-              label="&cmd.goToDownloadPage.label;"
-              accesskey="&cmd.goToDownloadPage.accesskey;"/>
-    <menuitem command="cmd_copy"
-              label="&cmd.copyDownloadLink.label;"
-              accesskey="&cmd.copyDownloadLink.accesskey;"/>
-  </menupopup>
+  <commandset id="downloadCommands"/>
+  <menupopup id="downloadsContextMenu"/>
 </window>
--- a/browser/components/places/jar.mn
+++ b/browser/components/places/jar.mn
@@ -3,19 +3,16 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 browser.jar:
 # Provide another URI for the bookmarkProperties dialog so we can persist the
 # attributes separately
     content/browser/places/bookmarkProperties2.xul       (content/bookmarkProperties.xul)
 *   content/browser/places/places.xul                    (content/places.xul) 
 *   content/browser/places/places.js                     (content/places.js)
-    content/browser/places/downloadsView.js              (content/downloadsView.js)
-    content/browser/places/download.xml                  (content/download.xml)
-    content/browser/places/download.css                  (content/download.css)
     content/browser/places/places.css                    (content/places.css)
     content/browser/places/organizer.css                 (content/organizer.css)
     content/browser/places/bookmarkProperties.xul        (content/bookmarkProperties.xul)
     content/browser/places/bookmarkProperties.js         (content/bookmarkProperties.js)
     content/browser/places/placesOverlay.xul             (content/placesOverlay.xul)
 *   content/browser/places/menu.xml                      (content/menu.xml)
     content/browser/places/tree.xml                      (content/tree.xml)
     content/browser/places/controller.js                 (content/controller.js)
--- a/browser/components/sessionstore/nsISessionStartup.idl
+++ b/browser/components/sessionstore/nsISessionStartup.idl
@@ -5,19 +5,25 @@
 #include "nsISupports.idl"
 
 /**
  * nsISessionStore keeps track of the current browsing state - i.e.
  * tab history, cookies, scroll state, form data, POSTDATA and window features
  * - and allows to restore everything into one window.
  */
 
-[scriptable, uuid(170c6857-7f71-46ce-bc9b-185723b1c3a8)]
+[scriptable, uuid(35235b39-7098-4b3b-8e28-cd004a88b06f)]
 interface nsISessionStartup: nsISupports
 {
+  /**
+   * Return a promise that is resolved once initialization
+   * is complete.
+   */
+  readonly attribute jsval onceInitialized;
+
   // Get session state
   readonly attribute jsval state;
 
   /**
    * Determine if session should be restored
    */
   boolean doRestore();
 
--- a/browser/components/sessionstore/src/Makefile.in
+++ b/browser/components/sessionstore/src/Makefile.in
@@ -7,25 +7,26 @@ topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH     = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/config.mk
 
 EXTRA_COMPONENTS = \
   nsSessionStore.manifest \
-	nsSessionStore.js \
-	nsSessionStartup.js \
+  nsSessionStore.js \
+  nsSessionStartup.js \
   $(NULL)
 
 JS_MODULES_PATH := $(FINAL_TARGET)/modules/sessionstore
 
 EXTRA_JS_MODULES := \
   DocumentUtils.jsm \
   SessionStorage.jsm \
   XPathGenerator.jsm \
+  _SessionFile.jsm \
   $(NULL)
 
 EXTRA_PP_JS_MODULES := \
 	SessionStore.jsm \
 	$(NULL)
 
 include $(topsrcdir)/config/rules.mk
--- a/browser/components/sessionstore/src/SessionStore.jsm
+++ b/browser/components/sessionstore/src/SessionStore.jsm
@@ -70,33 +70,39 @@ const TAB_EVENTS = [
   "TabOpen", "TabClose", "TabSelect", "TabShow", "TabHide", "TabPinned",
   "TabUnpinned"
 ];
 
 #ifndef XP_WIN
 #define BROKEN_WM_Z_ORDER
 #endif
 
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm", this);
+Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
 // debug.js adds NS_ASSERT. cf. bug 669196
-Cu.import("resource://gre/modules/debug.js");
-Cu.import("resource:///modules/TelemetryTimestamps.jsm");
-Cu.import("resource://gre/modules/TelemetryStopwatch.jsm");
-Cu.import("resource://gre/modules/osfile.jsm");
-Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
+Cu.import("resource://gre/modules/debug.js", this);
+Cu.import("resource:///modules/TelemetryTimestamps.jsm", this);
+Cu.import("resource://gre/modules/TelemetryStopwatch.jsm", this);
+Cu.import("resource://gre/modules/osfile.jsm", this);
+Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm", this);
+Cu.import("resource://gre/modules/commonjs/promise/core.js", this);
+
+XPCOMUtils.defineLazyServiceGetter(this, "gSessionStartup",
+  "@mozilla.org/browser/sessionstartup;1", "nsISessionStartup");
 
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
   "resource://gre/modules/NetUtil.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ScratchpadManager",
   "resource:///modules/devtools/scratchpad-manager.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "DocumentUtils",
   "resource:///modules/sessionstore/DocumentUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "SessionStorage",
   "resource:///modules/sessionstore/SessionStorage.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "_SessionFile",
+  "resource:///modules/sessionstore/_SessionFile.jsm");
 
 #ifdef MOZ_CRASHREPORTER
 XPCOMUtils.defineLazyServiceGetter(this, "CrashReporter",
   "@mozilla.org/xre/app-info;1", "nsICrashReporter");
 #endif
 
 function debug(aMsg) {
   aMsg = ("SessionStore: " + aMsg).replace(/\S{80}/g, "$&\n");
@@ -285,18 +291,21 @@ let SessionStoreInternal = {
   _restorePinnedTabsOnDemand: null,
 
   // The state from the previous session (after restoring pinned tabs). This
   // state is persisted and passed through to the next session during an app
   // restart to make the third party add-on warning not trash the deferred
   // session
   _lastSessionState: null,
 
-  // Whether we've been initialized
-  _initialized: false,
+  // A promise resolved once initialization is complete
+  _promiseInitialization: Promise.defer(),
+
+  // Whether session has been initialized
+  _sessionInitialized: false,
 
   // The original "sessionstore.resume_session_once" preference value before it
   // was modified by saveState.  saveState will set the
   // "sessionstore.resume_session_once" to true when the
   // the "sessionstore.resume_from_crash" preference is false (crash recovery
   // is disabled) so that pinned tabs will be restored in the case of a
   // crash.  This variable is used to restore the original value so the
   // previous session is not always restored when
@@ -321,16 +330,19 @@ let SessionStoreInternal = {
   },
 
   /* ........ Global Event Handlers .............. */
 
   /**
    * Initialize the component
    */
   initService: function ssi_initService() {
+    if (this._sessionInitialized) {
+      return;
+    }
     TelemetryTimestamps.add("sessionRestoreInitialized");
     OBSERVING.forEach(function(aTopic) {
       Services.obs.addObserver(this, aTopic, true);
     }, this);
 
 #ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
     var pbs = Cc["@mozilla.org/privatebrowsing;1"].
               getService(Ci.nsIPrivateBrowsingService);
@@ -353,25 +365,23 @@ let SessionStoreInternal = {
     this._restoreHiddenTabs =
       this._prefBranch.getBoolPref("sessionstore.restore_hidden_tabs");
     this._prefBranch.addObserver("sessionstore.restore_hidden_tabs", this, true);
 
     this._restorePinnedTabsOnDemand =
       this._prefBranch.getBoolPref("sessionstore.restore_pinned_tabs_on_demand");
     this._prefBranch.addObserver("sessionstore.restore_pinned_tabs_on_demand", this, true);
 
-    // get file references
-    this._sessionFile = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
-    this._sessionFileBackup = this._sessionFile.clone();
-    this._sessionFile.append("sessionstore.js");
-    this._sessionFileBackup.append("sessionstore.bak");
-
-    // get string containing session state
-    var ss = Cc["@mozilla.org/browser/sessionstartup;1"].
-             getService(Ci.nsISessionStartup);
+    gSessionStartup.onceInitialized.then(
+      this.initSession.bind(this)
+    );
+  },
+
+  initSession: function ssi_initSession() {
+    let ss = gSessionStartup;
     try {
       if (ss.doRestore() ||
           ss.sessionType == Ci.nsISessionStartup.DEFER_SESSION)
         this._initialState = ss.state;
     }
     catch(ex) { dump(ex + "\n"); } // no state to restore, which is ok
 
     if (this._initialState) {
@@ -432,35 +442,34 @@ let SessionStoreInternal = {
             delete aWindow.__lastSessionWindowID;
           });
         }
       }
       catch (ex) { debug("The session file is invalid: " + ex); }
     }
 
     if (this._resume_from_crash) {
-      // create a backup if the session data file exists
-      try {
-        if (this._sessionFileBackup.exists())
-          this._sessionFileBackup.remove(false);
-        if (this._sessionFile.exists())
-          this._sessionFile.copyTo(null, this._sessionFileBackup.leafName);
-      }
-      catch (ex) { Cu.reportError(ex); } // file was write-locked?
+      // Launch background copy of the session file. Note that we do
+      // not have race conditions here as _SessionFile guarantees
+      // that any I/O operation is completed before proceeding to
+      // the next I/O operation.
+      _SessionFile.createBackupCopy();
     }
 
     // at this point, we've as good as resumed the session, so we can
     // clear the resume_session_once flag, if it's set
     if (this._loadState != STATE_QUITTING &&
         this._prefBranch.getBoolPref("sessionstore.resume_session_once"))
       this._prefBranch.setBoolPref("sessionstore.resume_session_once", false);
 
     this._initEncoding();
 
-    this._initialized = true;
+    // Session is ready.
+    this._sessionInitialized = true;
+    this._promiseInitialization.resolve();
   },
 
   _initEncoding : function ssi_initEncoding() {
     // The (UTF-8) encoder used to write to files.
     XPCOMUtils.defineLazyGetter(this, "_writeFileEncoder", function () {
       return new TextEncoder();
     });
   },
@@ -490,26 +499,17 @@ let SessionStoreInternal = {
     });
 
     XPCOMUtils.defineLazyGetter(this, "_max_windows_undo", function () {
       this._prefBranch.addObserver("sessionstore.max_windows_undo", this, true);
       return this._prefBranch.getIntPref("sessionstore.max_windows_undo");
     });
   },
 
-  /**
-   * Start tracking a window.
-   * This function also initializes the component if it's not already
-   * initialized.
-   */
-  init: function ssi_init(aWindow) {
-    // Initialize the service if needed.
-    if (!this._initialized)
-      this.initService();
-
+  _initWindow: function ssi_initWindow(aWindow) {
     if (!aWindow || this._loadState == STATE_RUNNING) {
       // make sure that all browser windows which try to initialize
       // SessionStore are really tracked by it
       if (aWindow && (!aWindow.__SSi || !this._windows[aWindow.__SSi]))
         this.onLoad(aWindow);
       // If init is being called with a null window, it's possible that we
       // just want to tell sessionstore that a session is live (as is the case
       // with starting Firefox with -private, for example; see bug 568816),
@@ -520,22 +520,39 @@ let SessionStoreInternal = {
       return;
     }
 
     // As this is called at delayedStartup, restoration must be initiated here
     this.onLoad(aWindow);
   },
 
   /**
+   * Start tracking a window.
+   *
+   * This function also initializes the component if it is not
+   * initialized yet.
+   */
+  init: function ssi_init(aWindow) {
+    let self = this;
+    this.initService();
+    this._promiseInitialization.promise.then(
+      function onSuccess() {
+        self._initWindow(aWindow);
+      }
+    );
+  },
+
+  /**
    * Called on application shutdown, after notifications:
    * quit-application-granted, quit-application
    */
   _uninit: function ssi_uninit() {
     // save all data for session resuming
-    this.saveState(true);
+    if (this._sessionInitialized)
+      this.saveState(true);
 
     // clear out _tabsToRestore in case it's still holding refs
     this._tabsToRestore.priority = null;
     this._tabsToRestore.visible = null;
     this._tabsToRestore.hidden = null;
 
     // Make sure to break our cycle with the save timer
     if (this._saveTimer) {
@@ -993,17 +1010,17 @@ let SessionStoreInternal = {
     this._uninit();
   },
 
   /**
    * On purge of session history
    */
   onPurgeSessionHistory: function ssi_onPurgeSessionHistory() {
     var _this = this;
-    this._clearDisk();
+    _SessionFile.wipe();
     // If the browser is shutting down, simply return after clearing the
     // session data on disk as this notification fires after the
     // quit-application notification so the browser is about to exit.
     if (this._loadState == STATE_QUITTING)
       return;
     this._lastSessionState = null;
     let openWindows = {};
     this._forEachBrowserWindow(function(aWindow) {
@@ -1134,17 +1151,17 @@ let SessionStoreInternal = {
         if (this._resume_session_once_on_shutdown != null) {
           this._prefBranch.setBoolPref("sessionstore.resume_session_once",
                                        this._resume_session_once_on_shutdown);
           this._resume_session_once_on_shutdown = null;
         }
         // either create the file with crash recovery information or remove it
         // (when _loadState is not STATE_RUNNING, that file is used for session resuming instead)
         if (!this._resume_from_crash)
-          this._clearDisk();
+          _SessionFile.wipe();
         this.saveState(true);
         break;
       case "sessionstore.restore_on_demand":
         this._restoreOnDemand =
           this._prefBranch.getBoolPref("sessionstore.restore_on_demand");
         break;
       case "sessionstore.restore_hidden_tabs":
         this._restoreHiddenTabs =
@@ -3759,50 +3776,47 @@ let SessionStoreInternal = {
     this._saveStateObject(oState);
   },
 
   /**
    * write a state object to disk
    */
   _saveStateObject: function ssi_saveStateObject(aStateObj) {
     TelemetryStopwatch.start("FX_SESSION_RESTORE_SERIALIZE_DATA_MS");
-    var stateString = Cc["@mozilla.org/supports-string;1"].
-                        createInstance(Ci.nsISupportsString);
-    stateString.data = this._toJSONString(aStateObj);
+    let data = this._toJSONString(aStateObj);
     TelemetryStopwatch.finish("FX_SESSION_RESTORE_SERIALIZE_DATA_MS");
 
+    let stateString = this._createSupportsString(data);
     Services.obs.notifyObservers(stateString, "sessionstore-state-write", "");
-
-    // don't touch the file if an observer has deleted all state data
-    if (stateString.data)
-      this._writeFile(this._sessionFile, stateString.data);
-
-    this._lastSaveTime = Date.now();
-  },
-
-  /**
-   * delete session datafile and backup
-   */
-  _clearDisk: function ssi_clearDisk() {
-    if (this._sessionFile.exists()) {
-      try {
-        this._sessionFile.remove(false);
+    data = stateString.data;
+
+    // Don't touch the file if an observer has deleted all state data.
+    if (!data) {
+      return;
+    }
+
+    let self = this;
+    _SessionFile.write(data).then(
+      function onSuccess() {
+        self._lastSaveTime = Date.now();
+        Services.obs.notifyObservers(null, "sessionstore-state-write-complete", "");
       }
-      catch (ex) { dump(ex + '\n'); } // couldn't remove the file - what now?
-    }
-    if (this._sessionFileBackup.exists()) {
-      try {
-        this._sessionFileBackup.remove(false);
-      }
-      catch (ex) { dump(ex + '\n'); } // couldn't remove the file - what now?
-    }
+    );
   },
 
   /* ........ Auxiliary Functions .............. */
 
+  // Wrap a string as a nsISupports
+  _createSupportsString: function ssi_createSupportsString(aData) {
+    let string = Cc["@mozilla.org/supports-string;1"]
+                   .createInstance(Ci.nsISupportsString);
+    string.data = aData;
+    return string;
+  },
+
   /**
    * call a callback for all currently opened browser windows
    * (might miss the most recent one)
    * @param aFunc
    *        Callback each window is passed to
    */
   _forEachBrowserWindow: function ssi_forEachBrowserWindow(aFunc) {
     var windowsEnum = Services.wm.getEnumerator("navigator:browser");
@@ -4486,43 +4500,16 @@ let SessionStoreInternal = {
    */
   _removeSHistoryListener: function ssi_removeSHistoryListener(aTab) {
     let browser = aTab.linkedBrowser;
     if (browser.__SS_shistoryListener) {
       browser.webNavigation.sessionHistory.
                             removeSHistoryListener(browser.__SS_shistoryListener);
       delete browser.__SS_shistoryListener;
     }
-  },
-
-  /**
-   * write file to disk
-   * @param aFile
-   *        nsIFile
-   * @param aData
-   *        String data
-   */
-  _writeFile: function ssi_writeFile(aFile, aData) {
-    let refObj = {};
-    TelemetryStopwatch.start("FX_SESSION_RESTORE_WRITE_FILE_MS", refObj);
-    let path = aFile.path;
-    let encoded = this._writeFileEncoder.encode(aData);
-    let promise = OS.File.writeAtomic(path, encoded, {tmpPath: path + ".tmp"});
-
-    promise.then(
-      function onSuccess() {
-        TelemetryStopwatch.finish("FX_SESSION_RESTORE_WRITE_FILE_MS", refObj);
-        Services.obs.notifyObservers(null,
-                                      "sessionstore-state-write-complete",
-                                      "");
-      },
-      function onFailure(reason) {
-        TelemetryStopwatch.cancel("FX_SESSION_RESTORE_WRITE_FILE_MS", refObj);
-        Components.reportError("ssi_writeFile failure " + reason);
-      });
   }
 };
 
 // This is used to help meter the number of restoring tabs. This is the control
 // point for telling the next tab to restore. It gets attached to each gBrowser
 // via gBrowser.addTabsProgressListener
 let gRestoreTabsProgressListener = {
   onStateChange: function(aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/src/_SessionFile.jsm
@@ -0,0 +1,255 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+this.EXPORTED_SYMBOLS = ["_SessionFile"];
+
+/**
+ * Implementation of all the disk I/O required by the session store.
+ * This is a private API, meant to be used only by the session store.
+ * It will change. Do not use it for any other purpose.
+ *
+ * Note that this module implicitly depends on one of two things:
+ * 1. either the asynchronous file I/O system enqueues its requests
+ *   and never attempts to simultaneously execute two I/O requests on
+ *   the files used by this module from two distinct threads; or
+ * 2. the clients of this API are well-behaved and do not place
+ *   concurrent requests to the files used by this module.
+ *
+ * Otherwise, we could encounter bugs, especially under Windows,
+ *   e.g. if a request attempts to write sessionstore.js while
+ *   another attempts to copy that file.
+ *
+ * This implementation uses OS.File, which guarantees property 1.
+ */
+
+const Cu = Components.utils;
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/osfile.jsm");
+Cu.import("resource://gre/modules/commonjs/promise/core.js");
+
+XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStopwatch",
+  "resource://gre/modules/TelemetryStopwatch.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
+  "resource://gre/modules/NetUtil.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
+  "resource://gre/modules/FileUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Task",
+  "resource://gre/modules/Task.jsm");
+
+// An encoder to UTF-8.
+XPCOMUtils.defineLazyGetter(this, "gEncoder", function () {
+  return new TextEncoder();
+});
+
+this._SessionFile = {
+  /**
+   * A promise fulfilled once initialization (either synchronous or
+   * asynchronous) is complete.
+   */
+  promiseInitialized: function SessionFile_initialized() {
+    return SessionFileInternal.promiseInitialized;
+  },
+  /**
+   * Read the contents of the session file, asynchronously.
+   */
+  read: function SessionFile_read() {
+    return SessionFileInternal.read();
+  },
+  /**
+   * Read the contents of the session file, synchronously.
+   */
+  syncRead: function SessionFile_syncRead() {
+    return SessionFileInternal.syncRead();
+  },
+  /**
+   * Write the contents of the session file, asynchronously.
+   */
+  write: function SessionFile_write(aData) {
+    return SessionFileInternal.write(aData);
+  },
+  /**
+   * Create a backup copy, asynchronously.
+   */
+  createBackupCopy: function SessionFile_createBackupCopy() {
+    return SessionFileInternal.createBackupCopy();
+  },
+  /**
+   * Wipe the contents of the session file, asynchronously.
+   */
+  wipe: function SessionFile_wipe() {
+    return SessionFileInternal.wipe();
+  }
+};
+
+Object.freeze(_SessionFile);
+
+/**
+ * Utilities for dealing with promises and Task.jsm
+ */
+const TaskUtils = {
+  /**
+   * Add logging to a promise.
+   *
+   * @param {Promise} promise
+   * @return {Promise} A promise behaving as |promise|, but with additional
+   * logging in case of uncaught error.
+   */
+  captureErrors: function captureErrors(promise) {
+    return promise.then(
+      null,
+      function onError(reason) {
+        Cu.reportError("Uncaught asynchronous error: " + reason + " at\n" + reason.stack);
+        throw reason;
+      }
+    );
+  },
+  /**
+   * Spawn a new Task from a generator.
+   *
+   * This function behaves as |Task.spawn|, with the exception that it
+   * adds logging in case of uncaught error. For more information, see
+   * the documentation of |Task.jsm|.
+   *
+   * @param {generator} gen Some generator.
+   * @return {Promise} A promise built from |gen|, with the same semantics
+   * as |Task.spawn(gen)|.
+   */
+  spawn: function spawn(gen) {
+    return this.captureErrors(Task.spawn(gen));
+  }
+};
+
+let SessionFileInternal = {
+  /**
+   * A promise fulfilled once initialization is complete
+   */
+  promiseInitialized: Promise.defer(),
+
+  /**
+   * The path to sessionstore.js
+   */
+  path: OS.Path.join(OS.Constants.Path.profileDir, "sessionstore.js"),
+
+  /**
+   * The path to sessionstore.bak
+   */
+  backupPath: OS.Path.join(OS.Constants.Path.profileDir, "sessionstore.bak"),
+
+  /**
+   * Read the sessionstore file synchronously.
+   *
+   * This function is meant to serve as a fallback in case of race
+   * between a synchronous usage of the API and asynchronous
+   * initialization.
+   */
+  syncRead: function ssfi_syncRead() {
+    let text;
+    let exn;
+    TelemetryStopwatch.start("FX_SESSION_RESTORE_SYNC_READ_FILE_MS");
+    try {
+      let file = new FileUtils.File(this.path);
+      if (!file.exists()) {
+        return null;
+      }
+      let chan = NetUtil.newChannel(file);
+      let stream = chan.open();
+      text = NetUtil.readInputStreamToString(stream, stream.available(), {charset: "utf-8"});
+    } catch(ex) {
+      exn = ex;
+    } finally {
+      TelemetryStopwatch.finish("FX_SESSION_RESTORE_SYNC_READ_FILE_MS");
+    }
+    if (exn) {
+      Cu.reportError(exn);
+      return "";
+    }
+    return text;
+  },
+
+  read: function ssfi_read() {
+    let refObj = {};
+    let self = this;
+    return TaskUtils.spawn(function task() {
+      TelemetryStopwatch.start("FX_SESSION_RESTORE_READ_FILE_MS", refObj);
+      let text;
+      try {
+        let bytes = yield OS.File.read(self.path);
+        text = new TextDecoder().decode(bytes);
+        TelemetryStopwatch.finish("FX_SESSION_RESTORE_READ_FILE_MS", refObj);
+      } catch (ex) {
+        if (self._isNoSuchFile(ex)) {
+          // The file does not exist, this is perfectly valid
+          TelemetryStopwatch.finish("FX_SESSION_RESTORE_READ_FILE_MS", refObj);
+        } else {
+          // Any other error: let's not measure with telemetry
+          TelemetryStopwatch.cancel("FX_SESSION_RESTORE_READ_FILE_MS", refObj);
+          Cu.reportError(ex);
+        }
+        text = "";
+      }
+      throw new Task.Result(text);
+    });
+  },
+
+  write: function ssfi_write(aData) {
+    let refObj = {};
+    let self = this;
+    return TaskUtils.spawn(function task() {
+      TelemetryStopwatch.start("FX_SESSION_RESTORE_WRITE_FILE_MS", refObj);
+
+      let bytes = gEncoder.encode(aData);
+
+      try {
+        yield OS.File.writeAtomic(self.path, bytes, {tmpPath: self.path + ".tmp"});
+        TelemetryStopwatch.finish("FX_SESSION_RESTORE_WRITE_FILE_MS", refObj);
+      } catch (ex) {
+        TelemetryStopwatch.cancel("FX_SESSION_RESTORE_WRITE_FILE_MS", refObj);
+        Cu.reportError("Could not write session state file " + self.path
+                       + ": " + aReason);
+      }
+    });
+  },
+
+  createBackupCopy: function ssfi_createBackupCopy() {
+    let self = this;
+    return TaskUtils.spawn(function task() {
+      try {
+        yield OS.File.copy(self.path, self.backupPath);
+      } catch (ex) {
+        if (!self._isNoSuchFile(ex)) {
+          Cu.reportError("Could not backup session state file: " + ex);
+          throw ex;
+        }
+      }
+    });
+  },
+
+  wipe: function ssfi_wipe() {
+    let self = this;
+    return TaskUtils.spawn(function task() {
+      try {
+        yield OS.File.remove(self.path);
+      } catch (ex) {
+        Cu.reportError("Could not remove session state file: " + ex);
+        throw ex;
+      }
+      try {
+        yield OS.File.remove(self.backupPath);
+      } catch (ex) {
+        Cu.reportError("Could not remove session state backup file: " + ex);
+        throw ex;
+      }
+    });
+  },
+
+  _isNoSuchFile: function ssfi_isNoSuchFile(aReason) {
+    return aReason instanceof OS.File.Error && aReason.becauseNoSuchFile;
+  }
+};
--- a/browser/components/sessionstore/src/nsSessionStartup.js
+++ b/browser/components/sessionstore/src/nsSessionStartup.js
@@ -34,131 +34,163 @@
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cr = Components.results;
 const Cu = Components.utils;
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/TelemetryStopwatch.jsm");
 Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
+Cu.import("resource://gre/modules/commonjs/promise/core.js");
+
+XPCOMUtils.defineLazyModuleGetter(this, "_SessionFile",
+  "resource:///modules/sessionstore/_SessionFile.jsm");
 
 const STATE_RUNNING_STR = "running";
-const MAX_FILE_SIZE = 100 * 1024 * 1024; // 100 megabytes
 
 function debug(aMsg) {
   aMsg = ("SessionStartup: " + aMsg).replace(/\S{80}/g, "$&\n");
   Services.console.logStringMessage(aMsg);
 }
 
+let gOnceInitializedDeferred = Promise.defer();
+
 /* :::::::: The Service ::::::::::::::: */
 
 function SessionStartup() {
 }
 
 SessionStartup.prototype = {
 
   // the state to restore at startup
   _initialState: null,
   _sessionType: Ci.nsISessionStartup.NO_SESSION,
+  _initialized: false,
 
 /* ........ Global Event Handlers .............. */
 
   /**
    * Initialize the component
    */
   init: function sss_init() {
+    debug("init starting");
     // do not need to initialize anything in auto-started private browsing sessions
     let pbs = Cc["@mozilla.org/privatebrowsing;1"].
               getService(Ci.nsIPrivateBrowsingService);
     if (PrivateBrowsingUtils.permanentPrivateBrowsing ||
         pbs.lastChangedByCommandLine)
       return;
-
-    let prefBranch = Cc["@mozilla.org/preferences-service;1"].
-                     getService(Ci.nsIPrefService).getBranch("browser.");
+    // Session state is unknown until we read the file.
+    this._sessionType = null;
+    _SessionFile.read().then(
+      this._onSessionFileRead.bind(this)
+    );
+    debug("init launched");
+  },
 
-    // get file references
-    var dirService = Cc["@mozilla.org/file/directory_service;1"].
-                     getService(Ci.nsIProperties);
-    let sessionFile = dirService.get("ProfD", Ci.nsILocalFile);
-    sessionFile.append("sessionstore.js");
-
-    let doResumeSessionOnce = prefBranch.getBoolPref("sessionstore.resume_session_once");
-    let doResumeSession = doResumeSessionOnce ||
-                          prefBranch.getIntPref("startup.page") == 3;
-
-    // only continue if the session file exists
-    if (!sessionFile.exists())
-      return;
+  // Wrap a string as a nsISupports
+  _createSupportsString: function ssfi_createSupportsString(aData) {
+    let string = Cc["@mozilla.org/supports-string;1"]
+                   .createInstance(Ci.nsISupportsString);
+    string.data = aData;
+    return string;
+  },
 
-    // get string containing session state
-    let iniString = this._readStateFile(sessionFile);
-    if (!iniString)
+  _onSessionFileRead: function sss_onSessionFileRead(aStateString) {
+    debug("onSessionFileRead ");
+    if (this._initialized) {
+      debug("onSessionFileRead: Initialization is already complete");
+      // Initialization is complete, nothing else to do
       return;
-
-    // parse the session state into a JS object
-    // remove unneeded braces (added for compatibility with Firefox 2.0 and 3.0)
-    if (iniString.charAt(0) == '(')
-      iniString = iniString.slice(1, -1);
-    let corruptFile = false;
+    }
     try {
-      this._initialState = JSON.parse(iniString);
-    }
-    catch (ex) {
-      debug("The session file contained un-parse-able JSON: " + ex);
-      // Try to eval.
-      // evalInSandbox will throw if iniString is not parse-able.
+      this._initialized = true;
+
+      // Let observers modify the state before it is used
+      let supportsStateString = this._createSupportsString(aStateString);
+      Services.obs.notifyObservers(supportsStateString, "sessionstore-state-read", "");
+      aStateString = supportsStateString.data;
+
+      // No valid session found.
+      if (!aStateString) {
+        this._sessionType = Ci.nsISessionStartup.NO_SESSION;
+        return;
+      }
+
+      // parse the session state into a JS object
+      // remove unneeded braces (added for compatibility with Firefox 2.0 and 3.0)
+      if (aStateString.charAt(0) == '(')
+        aStateString = aStateString.slice(1, -1);
+      let corruptFile = false;
       try {
-        var s = new Cu.Sandbox("about:blank", {sandboxName: 'nsSessionStartup'});
-        this._initialState = Cu.evalInSandbox("(" + iniString + ")", s);
-      } catch(ex) {
-        debug("The session file contained un-eval-able JSON: " + ex);
-        corruptFile = true;
+        this._initialState = JSON.parse(aStateString);
       }
-    }
-    Services.telemetry.getHistogramById("FX_SESSION_RESTORE_CORRUPT_FILE").add(corruptFile);
-
-    // If this is a normal restore then throw away any previous session
-    if (!doResumeSessionOnce)
-      delete this._initialState.lastSessionState;
+      catch (ex) {
+        debug("The session file contained un-parse-able JSON: " + ex);
+        // This is not valid JSON, but this might still be valid JavaScript,
+        // as used in FF2/FF3, so we need to eval.
+        // evalInSandbox will throw if aStateString is not parse-able.
+        try {
+          var s = new Cu.Sandbox("about:blank", {sandboxName: 'nsSessionStartup'});
+          this._initialState = Cu.evalInSandbox("(" + aStateString + ")", s);
+        } catch(ex) {
+          debug("The session file contained un-eval-able JSON: " + ex);
+          corruptFile = true;
+        }
+      }
+      Services.telemetry.getHistogramById("FX_SESSION_RESTORE_CORRUPT_FILE").add(corruptFile);
 
-    let resumeFromCrash = prefBranch.getBoolPref("sessionstore.resume_from_crash");
-    let lastSessionCrashed =
-      this._initialState && this._initialState.session &&
-      this._initialState.session.state &&
-      this._initialState.session.state == STATE_RUNNING_STR;
+      let doResumeSessionOnce = Services.prefs.getBoolPref("browser.sessionstore.resume_session_once");
+      let doResumeSession = doResumeSessionOnce ||
+            Services.prefs.getIntPref("browser.startup.page") == 3;
 
-    // Report shutdown success via telemetry. Shortcoming here are
-    // being-killed-by-OS-shutdown-logic, shutdown freezing after
-    // session restore was written, etc.
-    Services.telemetry.getHistogramById("SHUTDOWN_OK").add(!lastSessionCrashed);
+      // If this is a normal restore then throw away any previous session
+      if (!doResumeSessionOnce)
+        delete this._initialState.lastSessionState;
+
+      let resumeFromCrash = Services.prefs.getBoolPref("browser.sessionstore.resume_from_crash");
+      let lastSessionCrashed =
+        this._initialState && this._initialState.session &&
+        this._initialState.session.state &&
+        this._initialState.session.state == STATE_RUNNING_STR;
 
-    // set the startup type
-    if (lastSessionCrashed && resumeFromCrash)
-      this._sessionType = Ci.nsISessionStartup.RECOVER_SESSION;
-    else if (!lastSessionCrashed && doResumeSession)
-      this._sessionType = Ci.nsISessionStartup.RESUME_SESSION;
-    else if (this._initialState)
-      this._sessionType = Ci.nsISessionStartup.DEFER_SESSION;
-    else
-      this._initialState = null; // reset the state
+      // Report shutdown success via telemetry. Shortcoming here are
+      // being-killed-by-OS-shutdown-logic, shutdown freezing after
+      // session restore was written, etc.
+      Services.telemetry.getHistogramById("SHUTDOWN_OK").add(!lastSessionCrashed);
+
+      // set the startup type
+      if (lastSessionCrashed && resumeFromCrash)
+        this._sessionType = Ci.nsISessionStartup.RECOVER_SESSION;
+      else if (!lastSessionCrashed && doResumeSession)
+        this._sessionType = Ci.nsISessionStartup.RESUME_SESSION;
+      else if (this._initialState)
+        this._sessionType = Ci.nsISessionStartup.DEFER_SESSION;
+      else
+        this._initialState = null; // reset the state
 
-    // wait for the first browser window to open
-    // Don't reset the initial window's default args (i.e. the home page(s))
-    // if all stored tabs are pinned.
-    if (this.doRestore() &&
-        (!this._initialState.windows ||
-        !this._initialState.windows.every(function (win)
-           win.tabs.every(function (tab) tab.pinned))))
-      Services.obs.addObserver(this, "domwindowopened", true);
+      // wait for the first browser window to open
+      // Don't reset the initial window's default args (i.e. the home page(s))
+      // if all stored tabs are pinned.
+      if (this.doRestore() &&
+          (!this._initialState.windows ||
+           !this._initialState.windows.every(function (win)
+             win.tabs.every(function (tab) tab.pinned))))
+        Services.obs.addObserver(this, "domwindowopened", true);
 
-    Services.obs.addObserver(this, "sessionstore-windows-restored", true);
+      Services.obs.addObserver(this, "sessionstore-windows-restored", true);
+
+      if (this._sessionType != Ci.nsISessionStartup.NO_SESSION)
+        Services.obs.addObserver(this, "browser:purge-session-history", true);
 
-    if (this._sessionType != Ci.nsISessionStartup.NO_SESSION)
-      Services.obs.addObserver(this, "browser:purge-session-history", true);
+    } finally {
+      // We're ready. Notify everyone else.
+      Services.obs.notifyObservers(null, "sessionstore-state-finalized", "");
+      gOnceInitializedDeferred.resolve();
+    }
   },
 
   /**
    * Handle notifications
    */
   observe: function sss_observe(aSubject, aTopic, aData) {
     switch (aTopic) {
     case "app-startup":
@@ -232,89 +264,62 @@ SessionStartup.prototype = {
     } catch (e) {
       // This might throw if we're removing the observer multiple times,
       // but this is safe to ignore.
     }
   },
 
 /* ........ Public API ................*/
 
+  get onceInitialized() {
+    return gOnceInitializedDeferred.promise;
+  },
+
   /**
    * Get the session state as a jsval
    */
   get state() {
+    this._ensureInitialized();
     return this._initialState;
   },
 
   /**
    * Determine whether there is a pending session restore.
    * @returns bool
    */
   doRestore: function sss_doRestore() {
+    this._ensureInitialized();
     return this._sessionType == Ci.nsISessionStartup.RECOVER_SESSION ||
            this._sessionType == Ci.nsISessionStartup.RESUME_SESSION;
   },
 
   /**
    * Get the type of pending session store, if any.
    */
   get sessionType() {
+    this._ensureInitialized();
     return this._sessionType;
   },
 
-/* ........ Storage API .............. */
-
-  /**
-   * Reads a session state file into a string and lets
-   * observers modify the state before it's being used
-   *
-   * @param aFile is any nsIFile
-   * @returns a session state string
-   */
-  _readStateFile: function sss_readStateFile(aFile) {
-    TelemetryStopwatch.start("FX_SESSION_RESTORE_READ_FILE_MS");
-    var stateString = Cc["@mozilla.org/supports-string;1"].
-                        createInstance(Ci.nsISupportsString);
-    stateString.data = this._readFile(aFile) || "";
-    TelemetryStopwatch.finish("FX_SESSION_RESTORE_READ_FILE_MS");
-
-    Services.obs.notifyObservers(stateString, "sessionstore-state-read", "");
-
-    return stateString.data;
-  },
-
-  /**
-   * reads a file into a string
-   * @param aFile
-   *        nsIFile
-   * @returns string
-   */
-  _readFile: function sss_readFile(aFile) {
+  // Ensure that initialization is complete.
+  // If initialization is not complete yet, fall back to a synchronous
+  // initialization and kill ongoing asynchronous initialization
+  _ensureInitialized: function sss__ensureInitialized() {
     try {
-      var stream = Cc["@mozilla.org/network/file-input-stream;1"].
-                   createInstance(Ci.nsIFileInputStream);
-      stream.init(aFile, 0x01, 0, 0);
-      var cvstream = Cc["@mozilla.org/intl/converter-input-stream;1"].
-                     createInstance(Ci.nsIConverterInputStream);
-
-      var fileSize = stream.available();
-      if (fileSize > MAX_FILE_SIZE)
-        throw "SessionStartup: sessionstore.js was not processed because it was too large.";
-
-      cvstream.init(stream, "UTF-8", fileSize, Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
-      var data = {};
-      cvstream.readString(fileSize, data);
-      var content = data.value;
-      cvstream.close();
-
-      return content.replace(/\r\n?/g, "\n");
+      debug("_ensureInitialized: " + this._initialState);
+      if (this._initialized) {
+        // Initialization is complete, nothing else to do
+        return;
+      }
+      let contents = _SessionFile.syncRead();
+      this._onSessionFileRead(contents);
+    } catch(ex) {
+      debug("ensureInitialized: could not read session " + ex + ", " + ex.stack);
+      throw ex;
     }
-    catch (ex) { Cu.reportError(ex); }
-
-    return null;
   },
 
   /* ........ QueryInterface .............. */
   QueryInterface : XPCOMUtils.generateQI([Ci.nsIObserver,
                                           Ci.nsISupportsWeakReference,
                                           Ci.nsISessionStartup]),
   classID:          Components.ID("{ec7a6c20-e081-11da-8ad9-0800200c9a66}"),
 };
--- a/browser/components/sessionstore/test/Makefile.in
+++ b/browser/components/sessionstore/test/Makefile.in
@@ -11,16 +11,20 @@ relativesrcdir  = @relativesrcdir@
 include $(DEPTH)/config/autoconf.mk
 
 # browser_506482.js is disabled because of frequent failures (bug 538672)
 # browser_526613.js is disabled because of frequent failures (bug 534489)
 # browser_589246.js is disabled for leaking browser windows (bug 752467)
 # browser_580512.js is disabled for leaking browser windows (bug 752467)
 # browser_586068-reload.js is disabled due to generally being broken (bug 809123, 797263)
 
+XPCSHELL_TESTS = \
+	unit \
+	$(NULL)
+
 MOCHITEST_BROWSER_FILES = \
 	head.js \
 	browser_form_restore_events.js \
 	browser_form_restore_events_sample.html \
 	browser_formdata_format.js \
 	browser_formdata_format_sample.html \
 	browser_248970_b_sample.html \
 	browser_339445.js \
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/unit/data/sessionstore_valid.js
@@ -0,0 +1,3 @@
+{
+  "windows": []
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/unit/head.js
@@ -0,0 +1,26 @@
+let Cu = Components.utils;
+let Cc = Components.classes;
+let Ci = Components.interfaces;
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+// Call a function once initialization of SessionStartup is complete
+let afterSessionStartupInitialization =
+  function afterSessionStartupInitialization(cb) {
+    do_print("Waiting for session startup initialization");
+    let observer = function() {
+      try {
+        do_print("Session startup initialization observed");
+        Services.obs.removeObserver(observer, "sessionstore-state-finalized");
+        cb();
+      } catch (ex) {
+        do_throw(ex);
+      }
+    };
+    let startup = Cc["@mozilla.org/browser/sessionstartup;1"].
+      getService(Ci.nsIObserver);
+    Services.obs.addObserver(startup, "final-ui-startup", false);
+    Services.obs.addObserver(startup, "quit-application", false);
+    Services.obs.notifyObservers(null, "final-ui-startup", "");
+    Services.obs.addObserver(observer, "sessionstore-state-finalized", false);
+};
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/unit/test_startup_nosession_async.js
@@ -0,0 +1,22 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+
+// Test nsISessionStartup.sessionType in the following scenario:
+// - no sessionstore.js;
+// - the session store has been loaded, so no need to go
+//    through the synchronous fallback
+
+function run_test() {
+  do_get_profile();
+  // Initialize the profile (the session startup uses it)
+
+  do_test_pending();
+  let startup = Cc["@mozilla.org/browser/sessionstartup;1"].
+    getService(Ci.nsISessionStartup);
+
+  afterSessionStartupInitialization(function cb() {
+    do_check_eq(startup.sessionType, Ci.nsISessionStartup.NO_SESSION);
+    do_test_finished();
+  });
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/unit/test_startup_nosession_sync.js
@@ -0,0 +1,15 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+
+// Test nsISessionStartup.sessionType in the following scenario:
+// - no sessionstore.js;
+// - the session store has not been loaded yet, so we have to trigger
+//    synchronous fallback
+
+function run_test() {
+  do_get_profile();
+  let startup = Cc["@mozilla.org/browser/sessionstartup;1"].
+    getService(Ci.nsISessionStartup);
+  do_check_eq(startup.sessionType, Ci.nsISessionStartup.NO_SESSION);
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/unit/test_startup_session_async.js
@@ -0,0 +1,23 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+
+// Test nsISessionStartup.sessionType in the following scenario:
+// - valid sessionstore.js;
+// - the session store has been loaded, so no need to go
+//    through the synchronous fallback
+
+function run_test() {
+  let profd = do_get_profile();
+  let source = do_get_file("data/sessionstore_valid.js");
+  source.copyTo(profd, "sessionstore.js");
+
+  do_test_pending();
+  let startup = Cc["@mozilla.org/browser/sessionstartup;1"].
+    getService(Ci.nsISessionStartup);
+
+  afterSessionStartupInitialization(function cb() {
+    do_check_eq(startup.sessionType, Ci.nsISessionStartup.DEFER_SESSION);
+    do_test_finished();
+  });
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/unit/test_startup_session_sync.js
@@ -0,0 +1,17 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+
+// Test nsISessionStartup.sessionType in the following scenario:
+// - valid sessionstore.js;
+// - the session store has not been loaded yet, so we have to trigger
+//    synchronous fallback
+
+function run_test() {
+  let profd = do_get_profile();
+  let source = do_get_file("data/sessionstore_valid.js");
+  source.copyTo(profd, "sessionstore.js");
+  let startup = Cc["@mozilla.org/browser/sessionstartup;1"].
+    getService(Ci.nsISessionStartup);
+  do_check_eq(startup.sessionType, Ci.nsISessionStartup.DEFER_SESSION);
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/unit/xpcshell.ini
@@ -0,0 +1,8 @@
+[DEFAULT]
+head = head.js
+tail =
+
+[test_startup_nosession_sync.js]
+[test_startup_nosession_async.js]
+[test_startup_session_sync.js]
+[test_startup_session_async.js]
\ No newline at end of file
--- a/browser/modules/test/browser_TelemetryTimestamps.js
+++ b/browser/modules/test/browser_TelemetryTimestamps.js
@@ -6,17 +6,17 @@ function getSimpleMeasurementsFromTeleme
   let ping = TelemetryPing.getPayload();
 
   return ping.simpleMeasurements;
 }
 
 function test() {
   waitForExplicitFinish()
   const Telemetry = Services.telemetry;
-  Telemetry.asyncReadShutdownTime(function () {
+  Telemetry.asyncFetchTelemetryData(function () {
     actualTest();
     finish();
   });
 }
 
 function actualTest() {
   // Test the module logic
   let tmp = {};
--- a/browser/themes/gnomestripe/browser.css
+++ b/browser/themes/gnomestripe/browser.css
@@ -2346,16 +2346,20 @@ html|*#gcli-output-frame {
 .social-notification-icon-hbox {
   pointer-events: none;
 }
 
 .social-status-button {
   list-style-image: none;
 }
 
+#social-provider-button {
+  -moz-image-region: rect(0, 16px, 16px, 0);
+}
+
 #social-provider-button > image {
   margin: 5px 3px;
 }
 
 #social-provider-button > .toolbarbutton-menu-dropmarker {
   display: none;
 }
 
copy from browser/themes/gnomestripe/downloads/downloads.css
copy to browser/themes/gnomestripe/downloads/allDownloadsViewOverlay.css
--- a/browser/themes/gnomestripe/jar.mn
+++ b/browser/themes/gnomestripe/jar.mn
@@ -46,16 +46,17 @@ browser.jar:
   skin/classic/browser/urlbar-arrow.png
   skin/classic/browser/webRTC-shareDevice-16.png
   skin/classic/browser/webRTC-shareDevice-64.png
   skin/classic/browser/downloads/buttons.png          (downloads/buttons.png)
   skin/classic/browser/downloads/download-glow.png    (downloads/download-glow.png)
   skin/classic/browser/downloads/download-glow-small.png (downloads/download-glow-small.png)
   skin/classic/browser/downloads/download-notification.png (downloads/download-notification.png)
   skin/classic/browser/downloads/downloads.css        (downloads/downloads.css)
+  skin/classic/browser/downloads/allDownloadsViewOverlay.css   (downloads/allDownloadsViewOverlay.css)
   skin/classic/browser/feeds/feedIcon.png             (feeds/feedIcon.png)
   skin/classic/browser/feeds/feedIcon16.png           (feeds/feedIcon16.png)
   skin/classic/browser/feeds/videoFeedIcon.png        (feeds/feedIcon.png)
   skin/classic/browser/feeds/videoFeedIcon16.png      (feeds/feedIcon16.png)
   skin/classic/browser/feeds/audioFeedIcon.png        (feeds/feedIcon.png)
   skin/classic/browser/feeds/audioFeedIcon16.png      (feeds/feedIcon16.png)
   skin/classic/browser/feeds/subscribe.css            (feeds/subscribe.css)
   skin/classic/browser/feeds/subscribe-ui.css         (feeds/subscribe-ui.css)
--- a/browser/themes/pinstripe/browser.css
+++ b/browser/themes/pinstripe/browser.css
@@ -3812,16 +3812,20 @@ html|*#gcli-output-frame {
 .social-notification-icon-hbox {
   pointer-events: none;
 }
 
 .social-status-button {
   list-style-image: none;
 }
 
+#social-provider-button {
+  -moz-image-region: rect(0, 16px, 16px, 0);
+}
+
 #social-provider-button > .toolbarbutton-menu-dropmarker {
   display: none;
 }
 
 .social-notification-icon-stack {
   padding: 0;
 }
 
new file mode 100644
--- /dev/null
+++ b/browser/themes/pinstripe/downloads/allDownloadsViewOverlay.css
@@ -0,0 +1,64 @@
+richlistitem.download {
+  height: 7em;
+  margin: 0;
+  padding: 8px;
+  -moz-padding-end: 0;
+}
+
+richlistitem.download:first-child {
+  border-top: 1px solid transparent;
+}
+
+richlistitem.download:last-child {
+  border-bottom: 1px solid transparent;
+}
+
+.downloadTypeIcon {
+  -moz-margin-end: 8px;
+  /* Prevent flickering when changing states. */
+  min-height: 32px;
+  min-width: 32px;
+}
+
+.blockedIcon {
+  list-style-image: url("chrome://global/skin/icons/Error.png");
+}
+
+.downloadTarget {
+  margin-bottom: 6px;
+  cursor: inherit;
+}
+
+.downloadDetails {
+  opacity: 0.7;
+  font-size: 95%;
+  cursor: inherit;
+}
+
+.downloadButton {
+  -moz-appearance: none;
+  min-width: 0;
+  min-height: 0;
+  margin: 3px;
+  border: none;
+  padding: 5px;
+  list-style-image: url("chrome://browser/skin/downloads/buttons.png");
+}
+
+.downloadButton > .button-box {
+  padding: 0;
+}
+
+/*** Button icons ***/
+
+.downloadButton.downloadCancel {
+  -moz-image-region: rect(0px, 16px, 16px, 0px);
+}
+
+.downloadButton.downloadShow {
+  -moz-image-region: rect(16px, 16px, 32px, 0px);
+}
+
+.downloadButton.downloadRetry {
+  -moz-image-region: rect(32px, 16px, 48px, 0px);
+}
--- a/browser/themes/pinstripe/jar.mn
+++ b/browser/themes/pinstripe/jar.mn
@@ -70,16 +70,17 @@ browser.jar:
   skin/classic/browser/webRTC-shareDevice-16.png
   skin/classic/browser/webRTC-shareDevice-16@2x.png
   skin/classic/browser/webRTC-shareDevice-64.png
   skin/classic/browser/downloads/buttons.png                (downloads/buttons.png)
   skin/classic/browser/downloads/download-glow.png          (downloads/download-glow.png)
   skin/classic/browser/downloads/download-glow@2x.png       (downloads/download-glow@2x.png)
   skin/classic/browser/downloads/download-notification.png  (downloads/download-notification.png)
   skin/classic/browser/downloads/downloads.css              (downloads/downloads.css)
+  skin/classic/browser/downloads/allDownloadsViewOverlay.css (downloads/allDownloadsViewOverlay.css)
   skin/classic/browser/feeds/subscribe.css                  (feeds/subscribe.css)
   skin/classic/browser/feeds/subscribe-ui.css               (feeds/subscribe-ui.css)
   skin/classic/browser/feeds/feedIcon.png                   (feeds/feedIcon.png)
   skin/classic/browser/feeds/feedIcon16.png                 (feeds/feedIcon16.png)
   skin/classic/browser/feeds/videoFeedIcon.png              (feeds/feedIcon.png)
   skin/classic/browser/feeds/videoFeedIcon16.png            (feeds/feedIcon16.png)
   skin/classic/browser/feeds/audioFeedIcon.png              (feeds/feedIcon.png)
   skin/classic/browser/feeds/audioFeedIcon16.png            (feeds/feedIcon16.png)
--- a/browser/themes/pinstripe/places/places.css
+++ b/browser/themes/pinstripe/places/places.css
@@ -197,76 +197,8 @@ treechildren::-moz-tree-twisty(title, se
 
 treechildren::-moz-tree-image(cutting) {
   opacity: 0.5;
 }
 
 treechildren::-moz-tree-cell-text(cutting) {
   opacity: 0.7;
 }
-
-
-/** Downloads View **/
-
-richlistitem.download {
-  height: 7em;
-  margin: 0;
-  padding: 8px;
-  -moz-padding-end: 0;
-}
-
-richlistitem.download:first-child {
-  border-top: 1px solid transparent;
-}
-
-richlistitem.download:last-child {
-  border-bottom: 1px solid transparent;
-}
-
-.downloadTypeIcon {
-  -moz-margin-end: 8px;
-  /* Prevent flickering when changing states. */
-  min-height: 32px;
-  min-width: 32px;
-}
-
-.blockedIcon {
-  list-style-image: url("chrome://global/skin/icons/Error.png");
-}
-
-.downloadTarget {
-  margin-bottom: 6px;
-  cursor: inherit;
-}
-
-.downloadDetails {
-  opacity: 0.7;
-  font-size: 95%;
-  cursor: inherit;
-}
-
-.downloadButton {
-  -moz-appearance: none;
-  min-width: 0;
-  min-height: 0;
-  margin: 3px;
-  border: none;
-  padding: 5px;
-  list-style-image: url("chrome://browser/skin/downloads/buttons.png");
-}
-
-.downloadButton > .button-box {
-  padding: 0;
-}
-
-/*** Button icons ***/
-
-.downloadButton.downloadCancel {
-  -moz-image-region: rect(0px, 16px, 16px, 0px);
-}
-
-.downloadButton.downloadShow {
-  -moz-image-region: rect(16px, 16px, 32px, 0px);
-}
-
-.downloadButton.downloadRetry {
-  -moz-image-region: rect(32px, 16px, 48px, 0px);
-}
--- a/browser/themes/winstripe/browser.css
+++ b/browser/themes/winstripe/browser.css
@@ -3013,16 +3013,19 @@ html|*#gcli-output-frame {
   min-width: 16px;
   text-shadow: none;
   background-image: -moz-linear-gradient(top, #B4211B, #8A1915);
   border-radius: 1px;
   -moz-margin-end: 5px;
 }
 
 /* Social toolbar item */
+#social-provider-button {
+  -moz-image-region: rect(0, 16px, 16px, 0);
+}
 
 #social-provider-button > .toolbarbutton-menu-dropmarker {
   display: none;
 }
 
 #social-toolbar-item > .toolbarbutton-1 {
   padding: 5px;
   -moz-appearance: toolbarbutton;
copy from browser/themes/winstripe/downloads/downloads.css
copy to browser/themes/winstripe/downloads/allDownloadsViewOverlay.css
--- a/browser/themes/winstripe/jar.mn
+++ b/browser/themes/winstripe/jar.mn
@@ -60,16 +60,17 @@ browser.jar:
         skin/classic/browser/webapps-16.png
         skin/classic/browser/webapps-64.png
         skin/classic/browser/webRTC-shareDevice-16.png
         skin/classic/browser/webRTC-shareDevice-64.png
         skin/classic/browser/downloads/buttons.png                   (downloads/buttons.png)
         skin/classic/browser/downloads/download-glow.png             (downloads/download-glow.png)
         skin/classic/browser/downloads/download-notification.png     (downloads/download-notification.png)
 *       skin/classic/browser/downloads/downloads.css                 (downloads/downloads.css)
+*       skin/classic/browser/downloads/allDownloadsViewOverlay.css   (downloads/allDownloadsViewOverlay.css)
         skin/classic/browser/feeds/feedIcon.png                      (feeds/feedIcon.png)
         skin/classic/browser/feeds/feedIcon16.png                    (feeds/feedIcon16.png)
         skin/classic/browser/feeds/audioFeedIcon.png                 (feeds/feedIcon.png)
         skin/classic/browser/feeds/audioFeedIcon16.png               (feeds/feedIcon16.png)
         skin/classic/browser/feeds/videoFeedIcon.png                 (feeds/feedIcon.png)
         skin/classic/browser/feeds/videoFeedIcon16.png               (feeds/feedIcon16.png)
         skin/classic/browser/feeds/subscribe.css                     (feeds/subscribe.css)
         skin/classic/browser/feeds/subscribe-ui.css                  (feeds/subscribe-ui.css)
@@ -272,16 +273,17 @@ browser.jar:
         skin/classic/aero/browser/webapps-16.png
         skin/classic/aero/browser/webapps-64.png
         skin/classic/aero/browser/webRTC-shareDevice-16.png
         skin/classic/aero/browser/webRTC-shareDevice-64.png
         skin/classic/aero/browser/downloads/buttons.png              (downloads/buttons-aero.png)
         skin/classic/aero/browser/downloads/download-glow.png        (downloads/download-glow.png)
         skin/classic/aero/browser/downloads/download-notification.png (downloads/download-notification.png)
 *       skin/classic/aero/browser/downloads/downloads.css            (downloads/downloads-aero.css)
+        skin/classic/aero/browser/downloads/allDownloadsViewOverlay.css (downloads/allDownloadsViewOverlay.css)
         skin/classic/aero/browser/feeds/feedIcon.png                 (feeds/feedIcon-aero.png)
         skin/classic/aero/browser/feeds/feedIcon16.png               (feeds/feedIcon16-aero.png)
         skin/classic/aero/browser/feeds/audioFeedIcon.png            (feeds/feedIcon-aero.png)
         skin/classic/aero/browser/feeds/audioFeedIcon16.png          (feeds/feedIcon16-aero.png)
         skin/classic/aero/browser/feeds/videoFeedIcon.png            (feeds/feedIcon-aero.png)
         skin/classic/aero/browser/feeds/videoFeedIcon16.png          (feeds/feedIcon16-aero.png)
         skin/classic/aero/browser/feeds/subscribe.css                (feeds/subscribe.css)
         skin/classic/aero/browser/feeds/subscribe-ui.css             (feeds/subscribe-ui.css)
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -459,18 +459,32 @@ public:
                                nsIPresShell** aInstancePtrResult) = 0;
   virtual void DeleteShell() = 0;
 
   nsIPresShell* GetShell() const
   {
     return GetBFCacheEntry() ? nullptr : mPresShell;
   }
 
+  void DisallowBFCaching()
+  {
+    NS_ASSERTION(!mBFCacheEntry, "We're already in the bfcache!");
+    mBFCacheDisallowed = true;
+  }
+
+  bool IsBFCachingAllowed() const
+  {
+    return !mBFCacheDisallowed;
+  }
+
   void SetBFCacheEntry(nsIBFCacheEntry* aEntry)
   {
+    NS_ASSERTION(IsBFCachingAllowed() || !aEntry,
+                 "You should have checked!");
+
     mBFCacheEntry = aEntry;
   }
 
   nsIBFCacheEntry* GetBFCacheEntry() const
   {
     return mBFCacheEntry;
   }
 
@@ -1911,16 +1925,19 @@ protected:
   bool mNeedStyleFlush;
 
   // True if a DOMMutationObserver is perhaps attached to a node in the document.
   bool mMayHaveDOMMutationObservers;
 
   // True if a document has loaded Mixed Active Script (see nsMixedContentBlocker.cpp)
   bool mHasMixedActiveContentLoaded;
 
+  // True if DisallowBFCaching has been called on this document.
+  bool mBFCacheDisallowed;
+
   // The document's script global object, the object from which the
   // document can get its script context and scope. This is the
   // *inner* window object.
   nsCOMPtr<nsIScriptGlobalObject> mScriptGlobalObject;
 
   // If mIsStaticDocument is true, mOriginalDocument points to the original
   // document.
   nsCOMPtr<nsIDocument> mOriginalDocument;
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -2515,29 +2515,30 @@ GetRequestBody(nsIInputStream* aStream, 
 
 static nsresult
 GetRequestBody(nsIXHRSendable* aSendable, nsIInputStream** aResult, uint64_t* aContentLength,
                nsACString& aContentType, nsACString& aCharset)
 {
   return aSendable->GetSendInfo(aResult, aContentLength, aContentType, aCharset);
 }
 
+// Used for array buffers and array buffer views
 static nsresult
-GetRequestBody(ArrayBuffer* aArrayBuffer, nsIInputStream** aResult, uint64_t* aContentLength,
+GetRequestBody(const uint8_t* aData, uint32_t aDataLength,
+               nsIInputStream** aResult, uint64_t* aContentLength,
                nsACString& aContentType, nsACString& aCharset)
 {
   aContentType.SetIsVoid(true);
   aCharset.Truncate();
 
-  int32_t length = aArrayBuffer->Length();
-  *aContentLength = length;
-  char* data = reinterpret_cast<char*>(aArrayBuffer->Data());
+  *aContentLength = aDataLength;
+  const char* data = reinterpret_cast<const char*>(aData);
 
   nsCOMPtr<nsIInputStream> stream;
-  nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream), data, length,
+  nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream), data, aDataLength,
                                       NS_ASSIGNMENT_COPY);
   NS_ENSURE_SUCCESS(rv, rv);
 
   stream.forget(aResult);
 
   return NS_OK;
 }
 
@@ -2590,17 +2591,18 @@ GetRequestBody(nsIVariant* aBody, nsIInp
     // ArrayBuffer?
     jsval realVal;
 
     nsresult rv = aBody->GetAsJSVal(&realVal);
     if (NS_SUCCEEDED(rv) && !JSVAL_IS_PRIMITIVE(realVal)) {
       JSObject *obj = JSVAL_TO_OBJECT(realVal);
       if (JS_IsArrayBufferObject(obj)) {
           ArrayBuffer buf(obj);
-          return GetRequestBody(&buf, aResult, aContentLength, aContentType, aCharset);
+          return GetRequestBody(buf.Data(), buf.Length(), aResult,
+                                aContentLength, aContentType, aCharset);
       }
     }
   }
   else if (dataType == nsIDataType::VTYPE_VOID ||
            dataType == nsIDataType::VTYPE_EMPTY) {
     // Makes us act as if !aBody, don't upload anything
     aContentType.AssignLiteral("text/plain");
     aCharset.AssignLiteral("UTF-8");
@@ -2632,18 +2634,25 @@ nsXMLHttpRequest::GetRequestBody(nsIVari
     return ::GetRequestBody(aVariant, aResult, aContentLength, aContentType, aCharset);
   }
 
   const RequestBody& body = aBody.Value();
   RequestBody::Value value = body.GetValue();
   switch (body.GetType()) {
     case nsXMLHttpRequest::RequestBody::ArrayBuffer:
     {
-      return ::GetRequestBody(value.mArrayBuffer, aResult, aContentLength,
-                              aContentType, aCharset);
+      return ::GetRequestBody(value.mArrayBuffer->Data(),
+                              value.mArrayBuffer->Length(), aResult,
+                              aContentLength, aContentType, aCharset);
+    }
+    case nsXMLHttpRequest::RequestBody::ArrayBufferView:
+    {
+      return ::GetRequestBody(value.mArrayBufferView->Data(),
+                              value.mArrayBufferView->Length(), aResult,
+                              aContentLength, aContentType, aCharset);
     }
     case nsXMLHttpRequest::RequestBody::Blob:
     {
       nsresult rv;
       nsCOMPtr<nsIXHRSendable> sendable = do_QueryInterface(value.mBlob, &rv);
       NS_ENSURE_SUCCESS(rv, rv);
 
       return ::GetRequestBody(sendable, aResult, aContentLength, aContentType, aCharset);
--- a/content/base/src/nsXMLHttpRequest.h
+++ b/content/base/src/nsXMLHttpRequest.h
@@ -271,16 +271,20 @@ private:
   public:
     RequestBody() : mType(Uninitialized)
     {
     }
     RequestBody(mozilla::dom::ArrayBuffer* aArrayBuffer) : mType(ArrayBuffer)
     {
       mValue.mArrayBuffer = aArrayBuffer;
     }
+    RequestBody(mozilla::dom::ArrayBufferView* aArrayBufferView) : mType(ArrayBufferView)
+    {
+      mValue.mArrayBufferView = aArrayBufferView;
+    }
     RequestBody(nsIDOMBlob* aBlob) : mType(Blob)
     {
       mValue.mBlob = aBlob;
     }
     RequestBody(nsIDocument* aDocument) : mType(Document)
     {
       mValue.mDocument = aDocument;
     }
@@ -295,24 +299,26 @@ private:
     RequestBody(nsIInputStream* aStream) : mType(InputStream)
     {
       mValue.mStream = aStream;
     }
 
     enum Type {
       Uninitialized,
       ArrayBuffer,
+      ArrayBufferView,
       Blob,
       Document,
       DOMString,
       FormData,
       InputStream
     };
     union Value {
       mozilla::dom::ArrayBuffer* mArrayBuffer;
+      mozilla::dom::ArrayBufferView* mArrayBufferView;
       nsIDOMBlob* mBlob;
       nsIDocument* mDocument;
       const nsAString* mString;
       nsFormData* mFormData;
       nsIInputStream* mStream;
     };
 
     Type GetType() const
@@ -352,16 +358,20 @@ public:
   void Send(ErrorResult& aRv)
   {
     aRv = Send(Nullable<RequestBody>());
   }
   void Send(mozilla::dom::ArrayBuffer& aArrayBuffer, ErrorResult& aRv)
   {
     aRv = Send(RequestBody(&aArrayBuffer));
   }
+  void Send(mozilla::dom::ArrayBufferView& aArrayBufferView, ErrorResult& aRv)
+  {
+    aRv = Send(RequestBody(&aArrayBufferView));
+  }
   void Send(nsIDOMBlob* aBlob, ErrorResult& aRv)
   {
     NS_ASSERTION(aBlob, "Null should go to string version");
     aRv = Send(RequestBody(aBlob));
   }
   void Send(nsIDocument* aDoc, ErrorResult& aRv)
   {
     NS_ASSERTION(aDoc, "Null should go to string version");
--- a/content/base/test/test_XHRSendData.html
+++ b/content/base/test/test_XHRSendData.html
@@ -52,16 +52,25 @@ var shortInt8View = new Uint8Array(short
 shortInt8View[0] = 3;
 
 var longArray = new ArrayBuffer(512);
 var longInt8View = new Uint8Array(longArray);
 for (var i = 0; i < longInt8View.length; i++) {
   longInt8View[i] = i % 255;
 }
 
+// arraybufferview test objects
+var longArraySlice = longArray.slice(256, 384);
+var longInt32View1 = new Int32Array(longArraySlice)
+var longInt32View2 = new Int32Array(longArray, 256, 32)
+var longInt16View1 = new Uint16Array(longArraySlice)
+var longInt16View2 = new Uint16Array(longArray, 256, 64)
+var longInt8View1 = new Int8Array(longArraySlice)
+var longInt8View2 = new Int8Array(longArray, 256, 128)
+
 extensions.forEach(
     function (extension) {
       var testFile = createFileWithDataExt(testData, extension);
       testFiles.push(testFile);
 
       var fileList = document.getElementById('fileList');
       fileList.value = testFile.path;
       testDOMFiles.push(fileList.files[0]);
@@ -164,16 +173,40 @@ tests = [{ body: null,
          { body: shortArray,
            resBody: shortArray,
            resType: "arraybuffer"
          },
          { body: longArray,
            resBody: longArray,
            resType: "arraybuffer"
          },
+         { body: longInt32View1,
+           resBody: longArraySlice,
+           resType: "arraybuffer"
+         },
+         { body: longInt32View2,
+           resBody: longArraySlice,
+           resType: "arraybuffer"
+         },
+         { body: longInt16View1,
+           resBody: longArraySlice,
+           resType: "arraybuffer"
+         },
+         { body: longInt16View2,
+           resBody: longArraySlice,
+           resType: "arraybuffer"
+         },
+         { body: longInt8View1,
+           resBody: longArraySlice,
+           resType: "arraybuffer"
+         },
+         { body: longInt8View2,
+           resBody: longArraySlice,
+           resType: "arraybuffer"
+         },
          ];
 
 for (var i = 0; i < testDOMFiles.length; i++) {
   tests.push({ body: testDOMFiles[i],
                resBody: testData,
                resContentType: fileTypes[i],
                resContentLength: testData.length,
               });
--- a/content/canvas/src/CanvasRenderingContext2D.h
+++ b/content/canvas/src/CanvasRenderingContext2D.h
@@ -570,26 +570,16 @@ protected:
   nsISupports* GetStyleAsStringOrInterface(nsAString& aStr, CanvasMultiGetterType& aType, Style aWhichStyle);
 
   // Returns whether a color was successfully parsed.
   bool ParseColor(const nsAString& aString, nscolor* aColor);
 
   static void StyleColorToString(const nscolor& aColor, nsAString& aStr);
 
   /**
-    * Creates the unpremultiply lookup table, if it doesn't exist.
-    */
-  void EnsureUnpremultiplyTable();
-
-  /**
-    * Creates the premultiply lookup table, if it doesn't exist.
-    */
-  void EnsurePremultiplyTable();
-
-  /**
    * Creates the error target, if it doesn't exist
    */
   static void EnsureErrorTarget();
 
   /* This function ensures there is a writable pathbuilder available, this
    * pathbuilder may be working in user space or in device space or
    * device space.
    * After calling this function mPathTransformWillUpdate will be false
--- a/content/media/MediaDecoderOwner.h
+++ b/content/media/MediaDecoderOwner.h
@@ -121,17 +121,19 @@ public:
   // The status of the next frame which might be available from the decoder
   enum NextFrameStatus {
     // The next frame of audio/video is available
     NEXT_FRAME_AVAILABLE,
     // The next frame of audio/video is unavailable because the decoder
     // is paused while it buffers up data
     NEXT_FRAME_UNAVAILABLE_BUFFERING,
     // The next frame of audio/video is unavailable for some other reasons
-    NEXT_FRAME_UNAVAILABLE
+    NEXT_FRAME_UNAVAILABLE,
+    // Sentinel value
+    NEXT_FRAME_UNINITIALIZED
   };
 
   // Called by the decoder when some data has been downloaded or
   // buffering/seeking has ended. aNextFrameAvailable is true when
   // the data for the next frame is available. This method will
   // decide whether to set the ready state to HAVE_CURRENT_DATA,
   // HAVE_FUTURE_DATA or HAVE_ENOUGH_DATA.
   virtual void UpdateReadyStateForData(NextFrameStatus aNextFrame) = 0;
--- a/content/media/MediaDecoderStateMachine.cpp
+++ b/content/media/MediaDecoderStateMachine.cpp
@@ -381,17 +381,18 @@ MediaDecoderStateMachine::MediaDecoderSt
   mIsRunning(false),
   mRunAgain(false),
   mDispatchedRunEvent(false),
   mDecodeThreadWaiting(false),
   mRealTime(aRealTime),
   mDidThrottleAudioDecoding(false),
   mDidThrottleVideoDecoding(false),
   mRequestedNewDecodeThread(false),
-  mEventManager(aDecoder)
+  mEventManager(aDecoder),
+  mLastFrameStatus(MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED)
 {
   MOZ_COUNT_CTOR(MediaDecoderStateMachine);
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
 
   StateMachineTracker::Instance().EnsureGlobalStateMachine();
 
   // only enable realtime mode when "media.realtime_decoder.enabled" is true.
   if (Preferences::GetBool("media.realtime_decoder.enabled", false) == false)
@@ -550,21 +551,21 @@ void MediaDecoderStateMachine::SendStrea
   nsRefPtr<SharedBuffer> buffer = aAudio->mAudioBuffer;
   aOutput->AppendFrames(buffer.forget(), aAudio->mFrames, int32_t(offset), aAudio->mFrames,
                         AUDIO_OUTPUT_FORMAT);
   LOG(PR_LOG_DEBUG, ("%p Decoder writing %d frames of data to MediaStream for AudioData at %lld",
                      mDecoder.get(), aAudio->mFrames - int32_t(offset), aAudio->mTime));
   aStream->mAudioFramesWritten += aAudio->mFrames - int32_t(offset);
 }
 
-static void WriteVideoToMediaStream(mozilla::layers::Image* aImage,
+static void WriteVideoToMediaStream(layers::Image* aImage,
                                     int64_t aDuration, const gfxIntSize& aIntrinsicSize,
                                     VideoSegment* aOutput)
 {
-  nsRefPtr<mozilla::layers::Image> image = aImage;
+  nsRefPtr<layers::Image> image = aImage;
   aOutput->AppendFrame(image.forget(), aDuration, aIntrinsicSize);
 }
 
 static const TrackID TRACK_AUDIO = 1;
 static const TrackID TRACK_VIDEO = 2;
 static const TrackRate RATE_VIDEO = USECS_PER_S;
 
 void MediaDecoderStateMachine::SendStreamData()
@@ -2464,18 +2465,24 @@ VideoData* MediaDecoderStateMachine::Fin
   mAudioStartTime = mStartTime;
   LOG(PR_LOG_DEBUG, ("%p Media start time is %lld", mDecoder.get(), mStartTime));
   return v;
 }
 
 void MediaDecoderStateMachine::UpdateReadyState() {
   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
 
+  MediaDecoderOwner::NextFrameStatus nextFrameStatus = GetNextFrameStatus();
+  if (nextFrameStatus == mLastFrameStatus) {
+    return;
+  }
+  mLastFrameStatus = nextFrameStatus;
+
   nsCOMPtr<nsIRunnable> event;
-  switch (GetNextFrameStatus()) {
+  switch (nextFrameStatus) {
     case MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING:
       event = NS_NewRunnableMethod(mDecoder, &MediaDecoder::NextFrameUnavailableBuffering);
       break;
     case MediaDecoderOwner::NEXT_FRAME_AVAILABLE:
       event = NS_NewRunnableMethod(mDecoder, &MediaDecoder::NextFrameAvailable);
       break;
     case MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE:
       event = NS_NewRunnableMethod(mDecoder, &MediaDecoder::NextFrameUnavailable);
@@ -2609,20 +2616,16 @@ void MediaDecoderStateMachine::TimeoutEx
     // can just run it from here.
     CallRunStateMachine();
   }
   // Otherwise, an event has already been dispatched to run the state machine
   // as soon as possible. Nothing else needed to do, the state machine is
   // going to run anyway.
 }
 
-nsresult MediaDecoderStateMachine::ScheduleStateMachine() {
-  return ScheduleStateMachine(0);
-}
-
 void MediaDecoderStateMachine::ScheduleStateMachineWithLockAndWakeDecoder() {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   mon.NotifyAll();
   ScheduleStateMachine(0);
 }
 
 nsresult MediaDecoderStateMachine::ScheduleStateMachine(int64_t aUsecs) {
   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
--- a/content/media/MediaDecoderStateMachine.h
+++ b/content/media/MediaDecoderStateMachine.h
@@ -275,30 +275,24 @@ public:
 
   // Sets the current frame buffer length for the MozAudioAvailable event.
   // Accessed on the main and state machine threads.
   void SetFrameBufferLength(uint32_t aLength);
 
   // Returns the shared state machine thread.
   static nsIThread* GetStateMachineThread();
 
-  // Schedules the shared state machine thread to run the state machine.
-  // If the state machine thread is the currently running the state machine,
-  // we wait until that has completely finished before running the state
-  // machine again.
-  nsresult ScheduleStateMachine();
-
   // Calls ScheduleStateMachine() after taking the decoder lock. Also
   // notifies the decoder thread in case it's waiting on the decoder lock.
   void ScheduleStateMachineWithLockAndWakeDecoder();
 
   // Schedules the shared state machine thread to run the state machine
   // in aUsecs microseconds from now, if it's not already scheduled to run
   // earlier, in which case the request is discarded.
-  nsresult ScheduleStateMachine(int64_t aUsecs);
+  nsresult ScheduleStateMachine(int64_t aUsecs = 0);
 
   // Creates and starts a new decode thread. Don't call this directly,
   // request a new decode thread by calling
   // StateMachineTracker::RequestCreateDecodeThread().
   // The decoder monitor must not be held. Called on the state machine thread.
   nsresult StartDecodeThread();
 
   // Timer function to implement ScheduleStateMachine(aUsecs).
@@ -792,12 +786,14 @@ private:
   // and takes care of synchronizing access to its internal queue.
   AudioAvailableEventManager mEventManager;
 
   // Stores presentation info required for playback. The decoder monitor
   // must be held when accessing this.
   VideoInfo mInfo;
 
   mozilla::MediaMetadataManager mMetadataManager;
+
+  MediaDecoderOwner::NextFrameStatus mLastFrameStatus;
 };
 
 } // namespace mozilla;
 #endif
--- a/content/xbl/src/nsXBLSerialize.cpp
+++ b/content/xbl/src/nsXBLSerialize.cpp
@@ -1,26 +1,39 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsXBLSerialize.h"
 #include "nsDOMScriptObjectHolder.h"
 #include "nsContentUtils.h"
+#include "jsdbgapi.h"
 
 nsresult
 XBL_SerializeFunction(nsIScriptContext* aContext,
                       nsIObjectOutputStream* aStream,
                       JSObject* aFunctionObject)
 {
   JSContext* cx = aContext->GetNativeContext();
   return nsContentUtils::XPConnect()->WriteFunction(aStream, cx, aFunctionObject);
 }
 
 nsresult
 XBL_DeserializeFunction(nsIScriptContext* aContext,
                         nsIObjectInputStream* aStream,
                         JSObject** aFunctionObjectp)
 {
   JSContext* cx = aContext->GetNativeContext();
-  return nsContentUtils::XPConnect()->ReadFunction(aStream, cx, aFunctionObjectp);
+  nsresult rv = nsContentUtils::XPConnect()->ReadFunction(aStream, cx, aFunctionObjectp);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Mark the script as XBL.
+  //
+  // This might be more elegantly handled as a flag via the XPConnect serialization
+  // code, but that would involve profile compat issues between different builds.
+  // Given that we know this code is XBL, just flag it as such.
+  JSAutoRequest ar(cx);
+  JSFunction* fun = JS_ValueToFunction(cx, JS::ObjectValue(**aFunctionObjectp));
+  NS_ENSURE_TRUE(fun, NS_ERROR_UNEXPECTED);
+  JS_SetScriptUserBit(JS_GetFunctionScript(cx, fun), true);
+  return NS_OK;
 }
--- a/docshell/shistory/src/nsSHistory.cpp
+++ b/docshell/shistory/src/nsSHistory.cpp
@@ -963,17 +963,17 @@ nsSHistory::EvictOutOfRangeWindowContent
        aIndex, mLength, startSafeIndex, endSafeIndex)); 
 
   // The content viewers in range aIndex -/+ gHistoryMaxViewers will not be
   // evicted.  Collect a set of them so we don't accidentally evict one of them
   // if it appears outside this range.
   nsCOMArray<nsIContentViewer> safeViewers;
   nsCOMPtr<nsISHTransaction> trans;
   GetTransactionAtIndex(startSafeIndex, getter_AddRefs(trans));
-  for (uint32_t i = startSafeIndex; trans && i <= endSafeIndex; i++) {
+  for (int32_t i = startSafeIndex; trans && i <= endSafeIndex; i++) {
     nsCOMPtr<nsIContentViewer> viewer = GetContentViewerForTransaction(trans);
     safeViewers.AppendObject(viewer);
     nsISHTransaction *temp = trans;
     temp->GetNext(getter_AddRefs(trans));
   }
 
   // Walk the SHistory list and evict any content viewers that aren't safe.
   GetTransactionAtIndex(0, getter_AddRefs(trans));
--- a/dom/apps/src/AppsUtils.jsm
+++ b/dom/apps/src/AppsUtils.jsm
@@ -260,16 +260,73 @@ this.AppsUtils = {
 
     let buildID = Services.appinfo.platformBuildID;
 
     aPrefBranch.setCharPref("gecko.mstone", mstone);
     aPrefBranch.setCharPref("gecko.buildID", buildID);
 
     return ((mstone != savedmstone) || (buildID != savedBuildID));
   },
+
+  /**
+   * Check if two manifests have the same set of properties and that the
+   * values of these properties are the same, in each locale.
+   * Manifests here are raw json ones.
+   */
+  compareManifests: function compareManifests(aManifest1, aManifest2) {
+    // 1. check if we have the same locales in both manifests.
+    let locales1 = [];
+    let locales2 = [];
+    if (aManifest1.locales) {
+      for (let locale in aManifest1.locales) {
+        locales1.push(locale);
+      }
+    }
+    if (aManifest2.locales) {
+      for (let locale in aManifest2.locales) {
+        locales2.push(locale);
+      }
+    }
+    if (locales1.sort().join() !== locales2.sort().join()) {
+      return false;
+    }
+
+    // Helper function to check the app name and developer information for
+    // two given roots.
+    let checkNameAndDev = function(aRoot1, aRoot2) {
+      let name1 = aRoot1.name;
+      let name2 = aRoot2.name;
+      if (name1 !== name2) {
+        return false;
+      }
+
+      let dev1 = aRoot1.developer;
+      let dev2 = aRoot2.developer;
+      if ((dev1 && !dev2) || (dev2 && !dev1)) {
+        return false;
+      }
+
+      return (dev1.name === dev2.name && dev1.url === dev2.url);
+    }
+
+    // 2. For each locale, check if the name and dev info are the same.
+    if (!checkNameAndDev(aManifest1, aManifest2)) {
+      return false;
+    }
+
+    for (let locale in aManifest1.locales) {
+      if (!checkNameAndDev(aManifest1.locales[locale],
+                           aManifest2.locales[locale])) {
+        return false;
+      }
+    }
+
+    // Nothing failed.
+    return true;
+  }
 }
 
 /**
  * Helper object to access manifest information with locale support
  */
 this.ManifestHelper = function(aManifest, aOrigin) {
   this._origin = Services.io.newURI(aOrigin, null, null);
   this._manifest = aManifest;
--- a/dom/apps/src/Webapps.jsm
+++ b/dom/apps/src/Webapps.jsm
@@ -1851,16 +1851,21 @@ this.DOMApplicationRegistry = {
               // Obtain a converter to read from a UTF-8 encoded input stream.
               let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
                                 .createInstance(Ci.nsIScriptableUnicodeConverter);
               converter.charset = "UTF-8";
 
               let manifest = JSON.parse(converter.ConvertToUnicode(NetUtil.readInputStreamToString(istream,
                                                                    istream.available()) || ""));
 
+              if (!AppsUtils.compareManifests(manifest,
+                                              aManifest._manifest)) {
+                throw "MANIFEST_MISMATCH";
+              }
+
               if (!AppsUtils.checkManifest(manifest)) {
                 throw "INVALID_MANIFEST";
               }
 
               if (!AppsUtils.checkInstallAllowed(manifest, aApp.installOrigin)) {
                 throw "INSTALL_FROM_DENIED";
               }
 
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -805,17 +805,16 @@ addExternalIface('DocumentType', nativeT
 addExternalIface('DOMRequest')
 addExternalIface('DOMStringList', nativeType='nsDOMStringList',
                  headerFile='nsDOMLists.h')
 addExternalIface('File')
 addExternalIface('HitRegionOptions', nativeType='nsISupports')
 addExternalIface('LockedFile')
 addExternalIface('MediaStream')
 addExternalIface('NamedNodeMap')
-addExternalIface('nsISupports', nativeType='nsISupports')
 addExternalIface('OutputStream', nativeType='nsIOutputStream',
                  notflattened=True)
 addExternalIface('PaintRequest')
 addExternalIface('Principal', nativeType='nsIPrincipal',
                  headerFile='nsIPrincipal.h', notflattened=True)
 addExternalIface('SVGLength')
 addExternalIface('SVGMatrix')
 addExternalIface('SVGNumber')
new file mode 100644
--- /dev/null
+++ b/dom/bindings/crashtests/822340-1.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<script>
+  var xhr = new XMLHttpRequest;
+  function f() {
+    var x = xhr.getResponseHeader;
+    x("abc");
+  }
+  for (var i = 0; i < 20000; ++i) {
+    try { f(); } catch (e) {}
+  }
+</script>
new file mode 100644
--- /dev/null
+++ b/dom/bindings/crashtests/822340-2.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<script>
+  var l = document.getElementsByTagName("*");
+  var count = 20000;
+  for (var i = 0; i < count; ++i) {
+    l.item(0);
+  }
+</script>
--- a/dom/bindings/crashtests/crashtests.list
+++ b/dom/bindings/crashtests/crashtests.list
@@ -1,1 +1,3 @@
 asserts-if(cocoaWidget,0-1) load 769464.html
+load 822340-1.html
+load 822340-2.html
--- a/dom/bluetooth/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/BluetoothHfpManager.cpp
@@ -18,39 +18,49 @@
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "nsContentUtils.h"
 #include "nsIAudioManager.h"
 #include "nsIObserverService.h"
 #include "nsISettingsService.h"
 #include "nsIRadioInterfaceLayer.h"
+#include "nsRadioInterfaceLayer.h"
 
 #include <unistd.h> /* usleep() */
 
 #define AUDIO_VOLUME_BT_SCO "audio.volume.bt_sco"
 #define MOZSETTINGS_CHANGED_ID "mozsettings-changed"
 #define MOBILE_CONNECTION_ICCINFO_CHANGED "mobile-connection-iccinfo-changed"
-#define NS_RILCONTENTHELPER_CONTRACTID "@mozilla.org/ril/content-helper;1"
+#define MOBILE_CONNECTION_VOICE_CHANGED "mobile-connection-voice-changed"
 
 #define TOA_UNKNOWN 0x81
 #define TOA_INTERNATIONAL 0x91
 
 /**
  * These constants are used in result code such as +CLIP and +CCWA. The value
  * of these constants is the same as TOA_INTERNATIONAL/TOA_UNKNOWN defined in
  * ril_consts.js
  */
 #define TOA_UNKNOWN 0x81
 #define TOA_INTERNATIONAL 0x91
 
 using namespace mozilla;
 using namespace mozilla::ipc;
 USING_BLUETOOTH_NAMESPACE
 
+namespace {
+  StaticRefPtr<BluetoothHfpManager> gBluetoothHfpManager;
+  StaticRefPtr<BluetoothHfpManagerObserver> sHfpObserver;
+  bool gInShutdown = false;
+  static bool sStopSendingRingFlag = true;
+
+  static int sRingInterval = 3000; //unit: ms
+} // anonymous namespace
+
 /* CallState for sCINDItems[CINDType::CALL].value
  * - NO_CALL: there are no calls in progress
  * - IN_PROGRESS: at least one call is in progress
  */
 enum CallState {
   NO_CALL,
   IN_PROGRESS
 };
@@ -82,32 +92,32 @@ enum CallHeldState {
 typedef struct {
   const char* name;
   const char* range;
   int value;
 } CINDItem;
 
 enum CINDType {
   BATTCHG = 1,
-  SIGNAL,
+  CALL,
+  CALLHELD,
+  CALLSETUP,
   SERVICE,
-  CALL,
-  CALLSETUP,
-  CALLHELD,
-  ROAM,
+  SIGNAL,
+  ROAM
 };
 
 static CINDItem sCINDItems[] = {
   {},
   {"battchg", "0-5", 5},
-  {"signal", "0-5", 5},
-  {"service", "0,1", 1},
   {"call", "0,1", CallState::NO_CALL},
+  {"callheld", "0-2", CallHeldState::NO_CALLHELD},
   {"callsetup", "0-3", CallSetupState::NO_CALLSETUP},
-  {"callheld", "0-2", CallHeldState::NO_CALLHELD},
+  {"service", "0,1", 0},
+  {"signal", "0-5", 0},
   {"roam", "0,1", 0}
 };
 
 class mozilla::dom::bluetooth::BluetoothHfpManagerObserver : public nsIObserver
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
@@ -130,26 +140,32 @@ public:
       return false;
     }
 
     if (NS_FAILED(obs->AddObserver(this, MOBILE_CONNECTION_ICCINFO_CHANGED, false))) {
       NS_WARNING("Failed to add mobile connection iccinfo change observer!");
       return false;
     }
 
+    if (NS_FAILED(obs->AddObserver(this, MOBILE_CONNECTION_VOICE_CHANGED, false))) {
+      NS_WARNING("Failed to add mobile connection voice change observer!");
+      return false;
+    }
+
     return true;
   }
 
   bool Shutdown()
   {
     nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
     if (!obs ||
         NS_FAILED(obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) ||
         NS_FAILED(obs->RemoveObserver(this, MOZSETTINGS_CHANGED_ID)) ||
-        NS_FAILED(obs->RemoveObserver(this, MOBILE_CONNECTION_ICCINFO_CHANGED))) {
+        NS_FAILED(obs->RemoveObserver(this, MOBILE_CONNECTION_ICCINFO_CHANGED)) ||
+        NS_FAILED(obs->RemoveObserver(this, MOBILE_CONNECTION_VOICE_CHANGED))) {
       NS_WARNING("Can't unregister observers, or already unregistered!");
       return false;
     }
     return true;
   }
 
   ~BluetoothHfpManagerObserver()
   {
@@ -186,46 +202,39 @@ public:
   {
     NS_WARNING("Unable to get value for '" AUDIO_VOLUME_BT_SCO "'");
     return NS_OK;
   }
 };
 
 NS_IMPL_ISUPPORTS1(GetVolumeTask, nsISettingsServiceCallback);
 
-namespace {
-  StaticRefPtr<BluetoothHfpManager> gBluetoothHfpManager;
-  StaticRefPtr<BluetoothHfpManagerObserver> sHfpObserver;
-  bool gInShutdown = false;
-  static bool sStopSendingRingFlag = true;
-
-  static int sRingInterval = 3000; //unit: ms
-} // anonymous namespace
-
-NS_IMPL_ISUPPORTS1(BluetoothHfpManagerObserver, nsIObserver)
-
 NS_IMETHODIMP
 BluetoothHfpManagerObserver::Observe(nsISupports* aSubject,
                                      const char* aTopic,
                                      const PRUnichar* aData)
 {
   MOZ_ASSERT(gBluetoothHfpManager);
 
   if (!strcmp(aTopic, MOZSETTINGS_CHANGED_ID)) {
     return gBluetoothHfpManager->HandleVolumeChanged(nsDependentString(aData));
   } else if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
     return gBluetoothHfpManager->HandleShutdown();
   } else if (!strcmp(aTopic, MOBILE_CONNECTION_ICCINFO_CHANGED)) {
     return gBluetoothHfpManager->HandleIccInfoChanged();
+  } else if (!strcmp(aTopic, MOBILE_CONNECTION_VOICE_CHANGED)) {
+    return gBluetoothHfpManager->HandleVoiceConnectionChanged();
   }
 
   MOZ_ASSERT(false, "BluetoothHfpManager got unexpected topic!");
   return NS_ERROR_UNEXPECTED;
 }
 
+NS_IMPL_ISUPPORTS1(BluetoothHfpManagerObserver, nsIObserver)
+
 class SendRingIndicatorTask : public Task
 {
 public:
   SendRingIndicatorTask(const char* aNumber, int aType = TOA_UNKNOWN)
     : mNumber(aNumber)
     , mType(aType)
   {
     MOZ_ASSERT(NS_IsMainThread());
@@ -498,16 +507,62 @@ BluetoothHfpManager::HandleVolumeChanged
   if (GetConnectionStatus() == SocketConnectionStatus::SOCKET_CONNECTED) {
     SendCommand("+VGS: ", mCurrentVgs);
   }
 
   return NS_OK;
 }
 
 nsresult
+BluetoothHfpManager::HandleVoiceConnectionChanged()
+{
+  nsCOMPtr<nsIMobileConnectionProvider> connection =
+    do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
+  NS_ENSURE_TRUE(connection, NS_ERROR_FAILURE);
+
+  nsIDOMMozMobileConnectionInfo* voiceInfo;
+  connection->GetVoiceConnectionInfo(&voiceInfo);
+  NS_ENSURE_TRUE(voiceInfo, NS_ERROR_FAILURE);
+
+  bool roaming;
+  voiceInfo->GetRoaming(&roaming);
+  if (roaming != sCINDItems[CINDType::ROAM].value) {
+    sCINDItems[CINDType::ROAM].value = roaming;
+    SendCommand("+CIEV: ", CINDType::ROAM);
+  }
+
+  bool service = false;
+  nsString regState;
+  voiceInfo->GetState(regState);
+  if (regState.EqualsLiteral("registered")) {
+    service = true;
+  }
+  if (service != sCINDItems[CINDType::SERVICE].value) {
+    sCINDItems[CINDType::SERVICE].value = service;
+    SendCommand("+CIEV: ", CINDType::SERVICE);
+  }
+
+  uint8_t signal;
+  JS::Value value;
+  voiceInfo->GetRelSignalStrength(&value);
+  if (!value.isNumber()) {
+    NS_WARNING("Failed to get relSignalStrength in BluetoothHfpManager");
+    return NS_ERROR_FAILURE;
+  }
+  signal = ceil(value.toNumber() / 20.0);
+
+  if (signal != sCINDItems[CINDType::SIGNAL].value) {
+    sCINDItems[CINDType::SIGNAL].value = signal;
+    SendCommand("+CIEV: ", CINDType::SIGNAL);
+  }
+
+  return NS_OK;
+}
+
+nsresult
 BluetoothHfpManager::HandleIccInfoChanged()
 {
   nsCOMPtr<nsIMobileConnectionProvider> connection =
     do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
   NS_ENSURE_TRUE(connection, NS_ERROR_FAILURE);
 
   nsIDOMMozMobileICCInfo* iccInfo;
   connection->GetIccInfo(&iccInfo);
@@ -536,17 +591,17 @@ BluetoothHfpManager::HandleShutdown()
 // Virtual function of class SocketConsumer
 void
 BluetoothHfpManager::ReceiveSocketData(UnixSocketRawData* aMessage)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   int currentCallState = mCurrentCallStateArray[mCurrentCallIndex];
 
-  nsAutoCString msg((const char*)aMessage->mData);
+  nsAutoCString msg((const char*)aMessage->mData.get());
   msg.StripWhitespace();
 
   nsTArray<nsCString> atCommandValues;
 
   // For more information, please refer to 4.34.1 "Bluetooth Defined AT
   // Capabilities" in Bluetooth hands-free profile 1.6
   if (msg.Find("AT+BRSF=") != -1) {
     SendCommand("+BRSF: ", 23);
@@ -790,39 +845,33 @@ BluetoothHfpManager::SendLine(const char
   msg += kHfpCrlf;
 
   return SendSocketData(msg);
 }
 
 bool
 BluetoothHfpManager::SendCommand(const char* aCommand, const int aValue)
 {
+  if (mSocketStatus != SocketConnectionStatus::SOCKET_CONNECTED) {
+    return false;
+  }
+
   nsAutoCString message;
   int value = aValue;
   message += aCommand;
 
   if (!strcmp(aCommand, "+CIEV: ")) {
+    if ((aValue < 1) || (aValue > ArrayLength(sCINDItems) - 1)) {
+      NS_WARNING("unexpected CINDType for CIEV command");
+      return false;
+    }
+
     message.AppendInt(aValue);
     message += ",";
-    switch (aValue) {
-      case CINDType::CALL:
-        message.AppendInt(sCINDItems[CINDType::CALL].value);
-        break;
-      case CINDType::CALLSETUP:
-        message.AppendInt(sCINDItems[CINDType::CALLSETUP].value);
-        break;
-      case CINDType::CALLHELD:
-        message.AppendInt(sCINDItems[CINDType::CALLHELD].value);
-        break;
-      default:
-#ifdef DEBUG
-        NS_WARNING("unexpected CINDType for CIEV command");
-#endif
-        return false;
-    }
+    message.AppendInt(sCINDItems[aValue].value);
   } else if (!strcmp(aCommand, "+CIND: ")) {
     if (!aValue) {
       for (uint8_t i = 1; i < ArrayLength(sCINDItems); i++) {
         message += "(\"";
         message += sCINDItems[i].name;
         message += "\",(";
         message += sCINDItems[i].range;
         message += ")";
@@ -1052,17 +1101,17 @@ BluetoothHfpManager::OnConnectSuccess()
   }
 
   // Cache device path for NotifySettings() since we can't get socket address
   // when a headset disconnect with us
   GetSocketAddr(mDevicePath);
   mSocketStatus = GetConnectionStatus();
 
   nsCOMPtr<nsIRILContentHelper> ril =
-    do_GetService("@mozilla.org/ril/content-helper;1");
+    do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
   NS_ENSURE_TRUE_VOID(ril);
   ril->EnumerateCalls(mListener->GetCallback());
 
   NotifySettings();
 }
 
 void
 BluetoothHfpManager::OnConnectError()
--- a/dom/bluetooth/BluetoothHfpManager.h
+++ b/dom/bluetooth/BluetoothHfpManager.h
@@ -37,19 +37,20 @@ public:
   void SetupCIND(int aCallIndex, int aCallState,
                  const char* aPhoneNumber, bool aInitial);
   bool Listen();
   void SetVolume(int aVolume);
 
 private:
   friend class BluetoothHfpManagerObserver;
   BluetoothHfpManager();
-  nsresult HandleVolumeChanged(const nsAString& aData);
+  nsresult HandleIccInfoChanged();
   nsresult HandleShutdown();
-  nsresult HandleIccInfoChanged();
+  nsresult HandleVolumeChanged(const nsAString& aData);
+  nsresult HandleVoiceConnectionChanged();
 
   bool Init();
   void Cleanup();
   void NotifyDialer(const nsAString& aCommand);
   void NotifySettings();
   virtual void OnConnectSuccess() MOZ_OVERRIDE;
   virtual void OnConnectError() MOZ_OVERRIDE;
   virtual void OnDisconnect() MOZ_OVERRIDE;
--- a/dom/bluetooth/BluetoothOppManager.cpp
+++ b/dom/bluetooth/BluetoothOppManager.cpp
@@ -12,16 +12,17 @@
 #include "BluetoothUtils.h"
 #include "BluetoothUuid.h"
 #include "ObexBase.h"
 
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
+#include "nsAutoPtr.h"
 #include "nsCExternalHandlerService.h"
 #include "nsIObserver.h"
 #include "nsIObserverService.h"
 #include "nsIDOMFile.h"
 #include "nsIFile.h"
 #include "nsIInputStream.h"
 #include "nsIMIMEService.h"
 #include "nsIOutputStream.h"
@@ -71,16 +72,23 @@ public:
   {
     Shutdown();
   }
 };
 
 namespace {
 // Sending system message "bluetooth-opp-update-progress" every 50kb
 static const uint32_t kUpdateProgressBase = 50 * 1024;
+
+/*
+ * The format of the header of an PUT request is
+ * [opcode:1][packet length:2][headerId:1][header length:2]
+ */
+static const uint32_t kPutRequestHeaderSize = 6;
+
 StaticRefPtr<BluetoothOppManager> sInstance;
 StaticRefPtr<BluetoothOppManagerObserver> sOppObserver;
 
 /*
  * FIXME / Bug 806749
  *
  * Currently Bluetooth*Manager inherits mozilla::ipc::UnixSocketConsumer,
  * which means that each Bluetooth*Manager can handle only one socket
@@ -109,52 +117,53 @@ BluetoothOppManagerObserver::Observe(nsI
 
   MOZ_ASSERT(false, "BluetoothOppManager got unexpected topic!");
   return NS_ERROR_UNEXPECTED;
 }
 
 class ReadFileTask : public nsRunnable
 {
 public:
-  ReadFileTask(nsIInputStream* aInputStream) : mInputStream(aInputStream)
+  ReadFileTask(nsIInputStream* aInputStream,
+               uint32_t aRemoteMaxPacketSize) : mInputStream(aInputStream)
   {
     MOZ_ASSERT(NS_IsMainThread());
+
+    mAvailablePacketSize = aRemoteMaxPacketSize - kPutRequestHeaderSize;
   }
 
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(!NS_IsMainThread());
 
-    /*
-     * 255 is the Minimum OBEX Packet Length (See section 3.3.1.4,
-     * IrOBEX ver 1.2)
-     */
-    char buf[255];
     uint32_t numRead;
+    nsAutoArrayPtr<char> buf;
+    buf = new char[mAvailablePacketSize];
 
     // function inputstream->Read() only works on non-main thread
-    nsresult rv = mInputStream->Read(buf, sizeof(buf), &numRead);
+    nsresult rv = mInputStream->Read(buf.get(), mAvailablePacketSize, &numRead);
     if (NS_FAILED(rv)) {
       // Needs error handling here
       return NS_ERROR_FAILURE;
     }
 
     if (numRead > 0) {
       if (sSentFileLength + numRead >= sFileLength) {
         sWaitingToSendPutFinal = true;
       }
-      sInstance->SendPutRequest((uint8_t*)buf, numRead);
+      sInstance->SendPutRequest((uint8_t*)buf.get(), numRead);
       sSentFileLength += numRead;
     }
 
     return NS_OK;
   };
 
 private:
   nsCOMPtr<nsIInputStream> mInputStream;
+  uint32_t mAvailablePacketSize;
 };
 
 BluetoothOppManager::BluetoothOppManager() : mConnected(false)
                                            , mConnectionId(1)
                                            , mRemoteObexVersion(0)
                                            , mRemoteConnectionFlags(0)
                                            , mRemoteMaxPacketLength(0)
                                            , mLastCommand(0)
@@ -824,17 +833,18 @@ BluetoothOppManager::ClientDataHandler(U
       rv = mBlob->GetInternalStream(getter_AddRefs(mInputStream));
       if (NS_FAILED(rv)) {
         NS_WARNING("Can't get internal stream of blob");
         SendDisconnectRequest();
         return;
       }
     }
 
-    nsRefPtr<ReadFileTask> task = new ReadFileTask(mInputStream);
+    nsRefPtr<ReadFileTask> task = new ReadFileTask(mInputStream,
+                                                   mRemoteMaxPacketLength);
     rv = mReadFileThread->Dispatch(task, NS_DISPATCH_NORMAL);
     if (NS_FAILED(rv)) {
       NS_WARNING("Cannot dispatch read file task!");
       SendDisconnectRequest();
     }
   } else {
     NS_WARNING("Unhandled ObexRequestCode");
   }
@@ -910,29 +920,29 @@ BluetoothOppManager::SendPutHeaderReques
   delete [] fileName;
   delete [] req;
 }
 
 void
 BluetoothOppManager::SendPutRequest(uint8_t* aFileBody,
                                     int aFileBodyLength)
 {
-  int index = 3;
-  int packetLeftSpace = mRemoteMaxPacketLength - index - 3;
+  int packetLeftSpace = mRemoteMaxPacketLength - kPutRequestHeaderSize;
 
   if (!mConnected) return;
   if (aFileBodyLength > packetLeftSpace) {
     NS_WARNING("Not allowed such a small MaxPacketLength value");
     return;
   }
 
   // Section 3.3.3 "Put", IrOBEX 1.2
   // [opcode:1][length:2][Headers:var]
   uint8_t* req = new uint8_t[mRemoteMaxPacketLength];
 
+  int index = 3;
   index += AppendHeaderBody(&req[index], aFileBody, aFileBodyLength);
 
   SetObexPacketInfo(req, ObexRequestCode::Put, index);
   mLastCommand = ObexRequestCode::Put;
 
   UnixSocketRawData* s = new UnixSocketRawData(index);
   memcpy(s->mData, req, s->mSize);
   SendSocketData(s);
--- a/dom/indexedDB/AsyncConnectionHelper.cpp
+++ b/dom/indexedDB/AsyncConnectionHelper.cpp
@@ -3,32 +3,34 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "base/basictypes.h"
 
 #include "AsyncConnectionHelper.h"
 
+#include "mozilla/dom/quota/QuotaManager.h"
 #include "mozilla/storage.h"
 #include "nsComponentManagerUtils.h"
 #include "nsContentUtils.h"
 #include "nsProxyRelease.h"
 #include "nsThreadUtils.h"
 #include "nsWrapperCacheInlines.h"
 
 #include "IDBEvents.h"
 #include "IDBTransaction.h"
 #include "IndexedDatabaseManager.h"
 #include "TransactionThreadPool.h"
 
 #include "ipc/IndexedDBChild.h"
 #include "ipc/IndexedDBParent.h"
 
 USING_INDEXEDDB_NAMESPACE
+using mozilla::dom::quota::QuotaManager;
 
 namespace {
 
 IDBTransaction* gCurrentTransaction = nullptr;
 
 const uint32_t kProgressHandlerGranularity = 1000;
 
 class TransactionPoolEventTarget : public StackBasedEventTarget
@@ -282,17 +284,17 @@ AsyncConnectionHelper::Run()
     if (NS_SUCCEEDED(rv)) {
       setProgressHandler = true;
     }
   }
 
   if (NS_SUCCEEDED(rv)) {
     bool hasSavepoint = false;
     if (mDatabase) {
-      IndexedDatabaseManager::SetCurrentWindow(mDatabase->GetOwner());
+      QuotaManager::SetCurrentWindow(mDatabase->GetOwner());
 
       // Make the first savepoint.
       if (mTransaction) {
         if (!(hasSavepoint = mTransaction->StartSavepoint())) {
           NS_WARNING("Failed to make savepoint!");
         }
       }
     }
@@ -308,17 +310,17 @@ AsyncConnectionHelper::Run()
         }
         else {
           mTransaction->RollbackSavepoint();
         }
       }
 
       // Don't unset this until we're sure that all SQLite activity has
       // completed!
-      IndexedDatabaseManager::SetCurrentWindow(nullptr);
+      QuotaManager::SetCurrentWindow(nullptr);
     }
   }
   else {
     // NS_ERROR_NOT_AVAILABLE is our special code for "database is invalidated"
     // and we should fail with RECOVERABLE_ERR.
     if (rv == NS_ERROR_NOT_AVAILABLE) {
       mResultCode = NS_ERROR_DOM_INDEXEDDB_RECOVERABLE_ERR;
     }
--- a/dom/indexedDB/IDBDatabase.cpp
+++ b/dom/indexedDB/IDBDatabase.cpp
@@ -6,24 +6,24 @@
 
 #include "base/basictypes.h"
 
 #include "IDBDatabase.h"
 
 #include "mozilla/Mutex.h"
 #include "mozilla/storage.h"
 #include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/quota/QuotaManager.h"
 #include "nsDOMClassInfo.h"
 #include "nsDOMLists.h"
 #include "nsJSUtils.h"
 #include "nsProxyRelease.h"
 #include "nsThreadUtils.h"
 
 #include "AsyncConnectionHelper.h"
-#include "CheckQuotaHelper.h"
 #include "DatabaseInfo.h"
 #include "IDBEvents.h"
 #include "IDBFactory.h"
 #include "IDBFileHandle.h"
 #include "IDBIndex.h"
 #include "IDBObjectStore.h"
 #include "IDBTransaction.h"
 #include "IDBFactory.h"
@@ -32,16 +32,17 @@
 #include "DictionaryHelpers.h"
 #include "nsContentUtils.h"
 
 #include "ipc/IndexedDBChild.h"
 #include "ipc/IndexedDBParent.h"
 
 USING_INDEXEDDB_NAMESPACE
 using mozilla::dom::ContentParent;
+using mozilla::dom::quota::QuotaManager;
 
 namespace {
 
 class NoRequestDatabaseHelper : public AsyncConnectionHelper
 {
 public:
   NoRequestDatabaseHelper(IDBTransaction* aTransaction)
   : AsyncConnectionHelper(aTransaction, nullptr)
@@ -253,21 +254,21 @@ IDBDatabase::Invalidate()
   }
 
   mInvalidated = true;
 
   // Make sure we're closed too.
   Close();
 
   // When the IndexedDatabaseManager needs to invalidate databases, all it has
-  // is an origin, so we call back into the manager to cancel any prompts for
-  // our owner.
+  // is an origin, so we call into the quota manager here to cancel any prompts
+  // for our owner.
   nsPIDOMWindow* owner = GetOwner();
   if (owner) {
-    IndexedDatabaseManager::CancelPromptsForWindow(owner);
+    QuotaManager::CancelPromptsForWindow(owner);
   }
 
   DatabaseInfo::Remove(mDatabaseId);
 
   // And let the child process know as well.
   if (mActorParent) {
     NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
     mActorParent->Invalidate();
@@ -281,17 +282,17 @@ IDBDatabase::DisconnectFromActorParent()
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   // Make sure we're closed too.
   Close();
 
   // Kill any outstanding prompts.
   nsPIDOMWindow* owner = GetOwner();
   if (owner) {
-    IndexedDatabaseManager::CancelPromptsForWindow(owner);
+    QuotaManager::CancelPromptsForWindow(owner);
   }
 }
 
 void
 IDBDatabase::CloseInternal(bool aIsDead)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
@@ -802,23 +803,23 @@ IDBDatabase::IsStorageShuttingDown()
 {
   return IndexedDatabaseManager::IsShuttingDown();
 }
 
 void
 IDBDatabase::SetThreadLocals()
 {
   NS_ASSERTION(GetOwner(), "Should have owner!");
-  IndexedDatabaseManager::SetCurrentWindow(GetOwner());
+  QuotaManager::SetCurrentWindow(GetOwner());
 }
 
 void
 IDBDatabase::UnsetThreadLocals()
 {
-  IndexedDatabaseManager::SetCurrentWindow(nullptr);
+  QuotaManager::SetCurrentWindow(nullptr);
 }
 
 nsresult
 IDBDatabase::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
 {
   return IndexedDatabaseManager::FireWindowOnError(GetOwner(), aVisitor);
 }
 
--- a/dom/indexedDB/IDBTransaction.cpp
+++ b/dom/indexedDB/IDBTransaction.cpp
@@ -7,16 +7,17 @@
 #include "base/basictypes.h"
 
 #include "IDBTransaction.h"
 
 #include "nsIAppShell.h"
 #include "nsIScriptContext.h"
 
 #include "DOMError.h"
+#include "mozilla/dom/quota/QuotaManager.h"
 #include "mozilla/storage.h"
 #include "nsContentUtils.h"
 #include "nsDOMClassInfoID.h"
 #include "nsDOMLists.h"
 #include "nsEventDispatcher.h"
 #include "nsPIDOMWindow.h"
 #include "nsProxyRelease.h"
 #include "nsThreadUtils.h"
@@ -31,16 +32,17 @@
 #include "IndexedDatabaseManager.h"
 #include "TransactionThreadPool.h"
 
 #include "ipc/IndexedDBChild.h"
 
 #define SAVEPOINT_NAME "savepoint"
 
 USING_INDEXEDDB_NAMESPACE
+using mozilla::dom::quota::QuotaManager;
 
 namespace {
 
 NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
 
 PLDHashOperator
 DoomCachedStatements(const nsACString& aQuery,
                      nsCOMPtr<mozIStorageStatement>& aStatement,
@@ -888,17 +890,17 @@ CommitHelper::Run()
   }
 
   IDBDatabase* database = mTransaction->Database();
   if (database->IsInvalidated()) {
     mAbortCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
   if (mConnection) {
-    IndexedDatabaseManager::SetCurrentWindow(database->GetOwner());
+    QuotaManager::SetCurrentWindow(database->GetOwner());
 
     if (NS_SUCCEEDED(mAbortCode) && mUpdateFileRefcountFunction &&
         NS_FAILED(mUpdateFileRefcountFunction->WillCommit(mConnection))) {
       mAbortCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
 
     if (NS_SUCCEEDED(mAbortCode) && NS_FAILED(WriteAutoIncrementCounts())) {
       mAbortCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
@@ -944,17 +946,17 @@ CommitHelper::Run()
       if (NS_FAILED(rv)) {
         NS_WARNING("Failed to remove function!");
       }
     }
 
     mConnection->Close();
     mConnection = nullptr;
 
-    IndexedDatabaseManager::SetCurrentWindow(nullptr);
+    QuotaManager::SetCurrentWindow(nullptr);
   }
 
   return NS_OK;
 }
 
 nsresult
 CommitHelper::WriteAutoIncrementCounts()
 {
--- a/dom/indexedDB/IndexedDatabaseManager.cpp
+++ b/dom/indexedDB/IndexedDatabaseManager.cpp
@@ -32,20 +32,18 @@
 #include "nsContentUtils.h"
 #include "nsCRTGlue.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsEventDispatcher.h"
 #include "nsScriptSecurityManager.h"
 #include "nsThreadUtils.h"
 #include "nsXPCOM.h"
 #include "nsXPCOMPrivate.h"
-#include "xpcpublic.h"
 
 #include "AsyncConnectionHelper.h"
-#include "CheckQuotaHelper.h"
 #include "IDBDatabase.h"
 #include "IDBEvents.h"
 #include "IDBFactory.h"
 #include "IDBKeyRange.h"
 #include "OpenDatabaseHelper.h"
 #include "TransactionThreadPool.h"
 
 #include "IndexedDatabaseInlines.h"
@@ -341,19 +339,17 @@ GetASCIIOriginFromPrincipal(nsIPrincipal
 
   aOrigin.Assign(origin);
   return NS_OK;
 }
 
 } // anonymous namespace
 
 IndexedDatabaseManager::IndexedDatabaseManager()
-: mCurrentWindowIndex(BAD_TLS_INDEX),
-  mQuotaHelperMutex("IndexedDatabaseManager.mQuotaHelperMutex"),
-  mFileMutex("IndexedDatabaseManager.mFileMutex")
+: mFileMutex("IndexedDatabaseManager.mFileMutex")
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(!gInstance, "More than one instance!");
 }
 
 IndexedDatabaseManager::~IndexedDatabaseManager()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
@@ -377,29 +373,18 @@ IndexedDatabaseManager::GetOrCreate()
   nsRefPtr<IndexedDatabaseManager> instance(gInstance);
 
   if (!instance) {
     sIsMainProcess = XRE_GetProcessType() == GeckoProcessType_Default;
 
     instance = new IndexedDatabaseManager();
 
     instance->mLiveDatabases.Init();
-    instance->mQuotaHelperHash.Init();
     instance->mFileManagers.Init();
 
-    // We need a thread-local to hold the current window.
-    NS_ASSERTION(instance->mCurrentWindowIndex == BAD_TLS_INDEX, "Huh?");
-
-    if (PR_NewThreadPrivateIndex(&instance->mCurrentWindowIndex, nullptr) !=
-        PR_SUCCESS) {
-      NS_ERROR("PR_NewThreadPrivateIndex failed, IndexedDB disabled");
-      instance->mCurrentWindowIndex = BAD_TLS_INDEX;
-      return nullptr;
-    }
-
     nsresult rv;
 
     if (sIsMainProcess) {
       nsCOMPtr<nsIFile> dbBaseDirectory;
       rv = NS_GetSpecialDirectory(NS_APP_INDEXEDDB_PARENT_DIR,
                                   getter_AddRefs(dbBaseDirectory));
       if (NS_FAILED(rv)) {
           rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
@@ -415,25 +400,25 @@ IndexedDatabaseManager::GetOrCreate()
 
       // Make a lazy thread for any IO we need (like clearing or enumerating the
       // contents of indexedDB database directories).
       instance->mIOThread =
         new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS,
                            NS_LITERAL_CSTRING("IndexedDB I/O"),
                            LazyIdleThread::ManualShutdown);
 
-      // Make sure that the quota manager is up.
-      NS_ENSURE_TRUE(QuotaManager::GetOrCreate(), nullptr);
-
       // Make a timer here to avoid potential failures later. We don't actually
       // initialize the timer until shutdown.
       instance->mShutdownTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
       NS_ENSURE_TRUE(instance->mShutdownTimer, nullptr);
     }
 
+    // Make sure that the quota manager is up.
+    NS_ENSURE_TRUE(QuotaManager::GetOrCreate(), nullptr);
+
     nsCOMPtr<nsIObserverService> obs = GetObserverService();
     NS_ENSURE_TRUE(obs, nullptr);
 
     // We need this callback to know when to shut down all our threads.
     rv = obs->AddObserver(instance, PROFILE_BEFORE_CHANGE_OBSERVER_ID, false);
     NS_ENSURE_SUCCESS(rv, nullptr);
 
     if (NS_FAILED(Preferences::AddIntVarCache(&gIndexedDBQuotaMB,
@@ -907,35 +892,16 @@ IndexedDatabaseManager::OnDatabaseClosed
         if (NS_FAILED(RunSynchronizedOp(aDatabase, op))) {
           NS_WARNING("Failed to run synchronized op!");
         }
       }
     }
   }
 }
 
-void
-IndexedDatabaseManager::SetCurrentWindowInternal(nsPIDOMWindow* aWindow)
-{
-  if (aWindow) {
-#ifdef DEBUG
-    NS_ASSERTION(!PR_GetThreadPrivate(mCurrentWindowIndex),
-                 "Somebody forgot to clear the current window!");
-#endif
-    PR_SetThreadPrivate(mCurrentWindowIndex, aWindow);
-  }
-  else {
-    // We cannot assert PR_GetThreadPrivate(mCurrentWindowIndex) here
-    // because we cannot distinguish between the thread private became
-    // null and that it was set to null on the first place, 
-    // because we didn't have a window.
-    PR_SetThreadPrivate(mCurrentWindowIndex, nullptr);
-  }
-}
-
 // static
 uint32_t
 IndexedDatabaseManager::GetIndexedDBQuotaMB()
 {
   return uint32_t(NS_MAX(gIndexedDBQuotaMB, 0));
 }
 
 nsresult
@@ -1111,81 +1077,16 @@ IndexedDatabaseManager::UninitializeOrig
 
   for (int32_t i = mInitializedOrigins.Length() - 1; i >= 0; i--) {
     if (PatternMatchesOrigin(aPattern, mInitializedOrigins[i])) {
       mInitializedOrigins.RemoveElementAt(i);
     }
   }
 }
 
-bool
-IndexedDatabaseManager::QuotaIsLiftedInternal()
-{
-  nsPIDOMWindow* window = nullptr;
-  nsRefPtr<CheckQuotaHelper> helper = nullptr;
-  bool createdHelper = false;
-
-  window =
-    static_cast<nsPIDOMWindow*>(PR_GetThreadPrivate(mCurrentWindowIndex));
-
-  // Once IDB is supported outside of Windows this should become an early
-  // return true.
-  NS_ASSERTION(window, "Why don't we have a Window here?");
-
-  // Hold the lock from here on.
-  MutexAutoLock autoLock(mQuotaHelperMutex);
-
-  mQuotaHelperHash.Get(window, getter_AddRefs(helper));
-
-  if (!helper) {
-    helper = new CheckQuotaHelper(window, mQuotaHelperMutex);
-    createdHelper = true;
-
-    mQuotaHelperHash.Put(window, helper);
-
-    // Unlock while calling out to XPCOM
-    {
-      MutexAutoUnlock autoUnlock(mQuotaHelperMutex);
-
-      nsresult rv = NS_DispatchToMainThread(helper);
-      NS_ENSURE_SUCCESS(rv, false);
-    }
-
-    // Relocked.  If any other threads hit the quota limit on the same Window,
-    // they are using the helper we created here and are now blocking in
-    // PromptAndReturnQuotaDisabled.
-  }
-
-  bool result = helper->PromptAndReturnQuotaIsDisabled();
-
-  // If this thread created the helper and added it to the hash, this thread
-  // must remove it.
-  if (createdHelper) {
-    mQuotaHelperHash.Remove(window);
-  }
-
-  return result;
-}
-
-void
-IndexedDatabaseManager::CancelPromptsForWindowInternal(nsPIDOMWindow* aWindow)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  nsRefPtr<CheckQuotaHelper> helper;
-
-  MutexAutoLock autoLock(mQuotaHelperMutex);
-
-  mQuotaHelperHash.Get(aWindow, getter_AddRefs(helper));
-
-  if (helper) {
-    helper->Cancel();
-  }
-}
-
 // static
 nsresult
 IndexedDatabaseManager::GetASCIIOriginFromWindow(nsPIDOMWindow* aWindow,
                                                  nsCString& aASCIIOrigin)
 {
   NS_ASSERTION(NS_IsMainThread(),
                "We're about to touch a window off the main thread!");
 
--- a/dom/indexedDB/IndexedDatabaseManager.h
+++ b/dom/indexedDB/IndexedDatabaseManager.h
@@ -34,17 +34,16 @@ namespace dom {
 class TabContext;
 }
 }
 
 
 BEGIN_INDEXEDDB_NAMESPACE
 
 class AsyncConnectionHelper;
-class CheckQuotaHelper;
 class FileManager;
 class IDBDatabase;
 
 class IndexedDatabaseManager MOZ_FINAL : public nsIIndexedDatabaseManager,
                                          public nsIObserver
 {
   friend class IDBDatabase;
 
@@ -110,56 +109,25 @@ public:
   // Called when a window is being purged from the bfcache or the user leaves
   // a page which isn't going into the bfcache. Forces any live database
   // objects to close themselves and aborts any running transactions.
   void AbortCloseDatabasesForWindow(nsPIDOMWindow* aWindow);
 
   // Used to check if there are running transactions in a given window.
   bool HasOpenTransactions(nsPIDOMWindow* aWindow);
 
-  // Set the Window that the current thread is doing operations for.
-  // The caller is responsible for ensuring that aWindow is held alive.
-  static inline void
-  SetCurrentWindow(nsPIDOMWindow* aWindow)
-  {
-    IndexedDatabaseManager* mgr = Get();
-    NS_ASSERTION(mgr, "Must have a manager here!");
-
-    return mgr->SetCurrentWindowInternal(aWindow);
-  }
-
   static uint32_t
   GetIndexedDBQuotaMB();
 
   nsresult EnsureOriginIsInitialized(const nsACString& aOrigin,
                                      FactoryPrivilege aPrivilege,
                                      nsIFile** aDirectory);
 
   void UninitializeOriginsByPattern(const nsACString& aPattern);
 
-  // Determine if the quota is lifted for the Window the current thread is
-  // using.
-  static inline bool
-  QuotaIsLifted()
-  {
-    IndexedDatabaseManager* mgr = Get();
-    NS_ASSERTION(mgr, "Must have a manager here!");
-
-    return mgr->QuotaIsLiftedInternal();
-  }
-
-  static inline void
-  CancelPromptsForWindow(nsPIDOMWindow* aWindow)
-  {
-    IndexedDatabaseManager* mgr = Get();
-    NS_ASSERTION(mgr, "Must have a manager here!");
-
-    mgr->CancelPromptsForWindowInternal(aWindow);
-  }
-
   static nsresult
   GetASCIIOriginFromWindow(nsPIDOMWindow* aWindow, nsCString& aASCIIOrigin);
 
   static bool
   IsMainProcess()
 #ifdef DEBUG
   ;
 #else
@@ -219,20 +187,16 @@ private:
 
   nsresult AcquireExclusiveAccess(const nsACString& aOrigin,
                                   IDBDatabase* aDatabase,
                                   AsyncConnectionHelper* aHelper,
                                   nsIRunnable* aRunnable,
                                   WaitingOnDatabasesCallback aCallback,
                                   void* aClosure);
 
-  void SetCurrentWindowInternal(nsPIDOMWindow* aWindow);
-  bool QuotaIsLiftedInternal();
-  void CancelPromptsForWindowInternal(nsPIDOMWindow* aWindow);
-
   // Called when a database is created.
   bool RegisterDatabase(IDBDatabase* aDatabase);
 
   // Called when a database is being unlinked or destroyed.
   void UnregisterDatabase(IDBDatabase* aDatabase);
 
   // Called when a database has been closed.
   void OnDatabaseClosed(IDBDatabase* aDatabase);
@@ -469,25 +433,16 @@ private:
   bool IsClearOriginPending(const nsACString& aPattern)
   {
     return !!FindSynchronizedOp(aPattern, nullptr);
   }
 
   // Maintains a list of live databases per origin.
   nsClassHashtable<nsCStringHashKey, nsTArray<IDBDatabase*> > mLiveDatabases;
 
-  // TLS storage index for the current thread's window
-  unsigned mCurrentWindowIndex;
-
-  // Lock protecting mQuotaHelperHash
-  mozilla::Mutex mQuotaHelperMutex;
-
-  // A map of Windows to the corresponding quota helper.
-  nsRefPtrHashtable<nsPtrHashKey<nsPIDOMWindow>, CheckQuotaHelper> mQuotaHelperHash;
-
   // Maintains a list of all file managers per origin. This list isn't
   // protected by any mutex but it is only ever touched on the IO thread.
   nsClassHashtable<nsCStringHashKey,
                    nsTArray<nsRefPtr<FileManager> > > mFileManagers;
 
   // Maintains a list of origins that we're currently enumerating to gather
   // usage statistics.
   nsAutoTArray<nsRefPtr<AsyncUsageRunnable>, 1> mUsageRunnables;
@@ -510,25 +465,11 @@ private:
   // and FileInfo.mSliceRefCnt
   mozilla::Mutex mFileMutex;
 
   nsString mDatabaseBasePath;
 
   static bool sIsMainProcess;
 };
 
-class AutoEnterWindow
-{
-public:
-  AutoEnterWindow(nsPIDOMWindow* aWindow)
-  {
-    IndexedDatabaseManager::SetCurrentWindow(aWindow);
-  }
-
-  ~AutoEnterWindow()
-  {
-    IndexedDatabaseManager::SetCurrentWindow(nullptr);
-  }
-};
-
 END_INDEXEDDB_NAMESPACE
 
 #endif /* mozilla_dom_indexeddb_indexeddatabasemanager_h__ */
--- a/dom/indexedDB/Makefile.in
+++ b/dom/indexedDB/Makefile.in
@@ -16,17 +16,16 @@ XPIDL_MODULE = dom_indexeddb
 LIBXUL_LIBRARY = 1
 FORCE_STATIC_LIB = 1
 
 EXPORTS_NAMESPACES = mozilla/dom/indexedDB
 
 CPPSRCS = \
   AsyncConnectionHelper.cpp \
   CheckPermissionsHelper.cpp \
-  CheckQuotaHelper.cpp \
   DatabaseInfo.cpp \
   FileInfo.cpp \
   FileManager.cpp \
   IDBCursor.cpp \
   IDBDatabase.cpp \
   IDBEvents.cpp \
   IDBFactory.cpp \
   IDBFileHandle.cpp \
--- a/dom/indexedDB/OpenDatabaseHelper.cpp
+++ b/dom/indexedDB/OpenDatabaseHelper.cpp
@@ -1506,16 +1506,32 @@ public:
       nsIBFCacheEntry* bfCacheEntry;
       if (ownerDoc && (bfCacheEntry = ownerDoc->GetBFCacheEntry())) {
         bfCacheEntry->RemoveFromBFCacheSync();
         NS_ASSERTION(database->IsClosed(),
                      "Kicking doc out of bfcache should have closed database");
         continue;
       }
 
+      // Next check if it's in the process of being bfcached.
+      nsPIDOMWindow* owner = database->GetOwner();
+      if (owner && owner->IsFrozen()) {
+        // We can't kick the document out of the bfcache because it's not yet
+        // fully in the bfcache.  Instead we'll abort everything for the window
+        // and mark it as not-bfcacheable.
+        IndexedDatabaseManager* manager = IndexedDatabaseManager::Get();
+        NS_ASSERTION(manager, "Huh?");
+        manager->AbortCloseDatabasesForWindow(owner);
+
+        NS_ASSERTION(database->IsClosed(),
+                   "AbortCloseDatabasesForWindow should have closed database");
+        ownerDoc->DisallowBFCaching();
+        continue;
+      }
+
       // Otherwise fire a versionchange event.
       nsRefPtr<nsDOMEvent> event = 
         IDBVersionChangeEvent::Create(mOldVersion, mNewVersion);
       NS_ENSURE_TRUE(event, NS_ERROR_FAILURE);
 
       bool dummy;
       database->DispatchEvent(event, &dummy);
     }
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -106,16 +106,17 @@
 #ifdef MOZ_B2G_BT
 #include "BluetoothParent.h"
 #include "BluetoothService.h"
 #endif
 
 static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
 static const char* sClipboardTextFlavors[] = { kUnicodeMime };
 
+using base::ChildPrivileges;
 using base::KillProcess;
 using namespace mozilla::dom::bluetooth;
 using namespace mozilla::dom::devicestorage;
 using namespace mozilla::dom::sms;
 using namespace mozilla::dom::indexedDB;
 using namespace mozilla::hal_sandbox;
 using namespace mozilla::ipc;
 using namespace mozilla::layers;
@@ -284,38 +285,45 @@ ContentParent::GetNewOrUsed(bool aForBro
     nsRefPtr<ContentParent> p =
         new ContentParent(/* appManifestURL = */ EmptyString(),
                           aForBrowserElement);
     p->Init();
     gNonAppContentParents->AppendElement(p);
     return p;
 }
 
-static bool
-AppNeedsInheritedOSPrivileges(mozIApplication* aApp)
+namespace {
+struct SpecialPermission {
+    const char* perm;           // an app permission
+    ChildPrivileges privs;      // the OS privilege it requires
+};
+}
+
+static ChildPrivileges
+PrivilegesForApp(mozIApplication* aApp)
 {
-    const char* const needInheritPermissions[] = {
+    const SpecialPermission specialPermissions[] = {
         // FIXME/bug 785592: implement a CameraBridge so we don't have
         // to hack around with OS permissions
-        "camera",
+        { "camera", base::PRIVILEGES_INHERIT },
         // FIXME/bug 793034: change our video architecture so that we
         // can stream video from remote processes
-        "deprecated-hwvideo",
+        { "deprecated-hwvideo", base::PRIVILEGES_VIDEO }
     };
-    for (size_t i = 0; i < ArrayLength(needInheritPermissions); ++i) {
-        const char* const permission = needInheritPermissions[i];
-        bool needsInherit = false;
-        if (NS_FAILED(aApp->HasPermission(permission, &needsInherit))) {
+    for (size_t i = 0; i < ArrayLength(specialPermissions); ++i) {
+        const char* const permission = specialPermissions[i].perm;
+        bool hasPermission = false;
+        if (NS_FAILED(aApp->HasPermission(permission, &hasPermission))) {
             NS_WARNING("Unable to check permissions.  Breakage may follow.");
-            return false;
-        } else if (needsInherit) {
-            return true;
+            break;
+        } else if (hasPermission) {
+            return specialPermissions[i].privs;
         }
     }
-    return false;
+    return base::PRIVILEGES_DEFAULT;
 }
 
 /*static*/ TabParent*
 ContentParent::CreateBrowserOrApp(const TabContext& aContext)
 {
     if (aContext.IsBrowserElement() || !aContext.HasOwnApp()) {
         if (ContentParent* cp = GetNewOrUsed(aContext.IsBrowserElement())) {
             nsRefPtr<TabParent> tp(new TabParent(aContext));
@@ -343,19 +351,20 @@ ContentParent::CreateBrowserOrApp(const 
     nsAutoString manifestURL;
     if (NS_FAILED(ownApp->GetManifestURL(manifestURL))) {
         NS_ERROR("Failed to get manifest URL");
         return nullptr;
     }
 
     nsRefPtr<ContentParent> p = gAppContentParents->Get(manifestURL);
     if (!p) {
-        if (AppNeedsInheritedOSPrivileges(ownApp)) {
+        ChildPrivileges privs = PrivilegesForApp(ownApp);
+        if (privs != base::PRIVILEGES_DEFAULT) {
             p = new ContentParent(manifestURL, /* isBrowserElement = */ false,
-                                  base::PRIVILEGES_INHERIT);
+                                  privs);
             p->Init();
         } else {
             p = MaybeTakePreallocatedAppProcess();
             if (p) {
                 p->SetManifestFromPreallocated(manifestURL);
             } else {
                 NS_WARNING("Unable to use pre-allocated app process");
                 p = new ContentParent(manifestURL, /* isBrowserElement = */ false,
--- a/dom/phonenumberutils/PhoneNumberUtils.jsm
+++ b/dom/phonenumberutils/PhoneNumberUtils.jsm
@@ -30,46 +30,58 @@ this.PhoneNumberUtils = {
   // mcc for Brasil
   _mcc: '724',
 
   _getCountryName: function() {
     let mcc;
     let countryName;
 
 #ifdef MOZ_B2G_RIL
-    // Get network mcc.
-    if (ril.voiceConnectionInfo && ril.voiceConnectionInfo.network)
+    // Get network mcc
+    if (ril.voiceConnectionInfo && ril.voiceConnectionInfo.network) {
       mcc = ril.voiceConnectionInfo.network.mcc;
+    }
+
+    // Get SIM mcc
+    if (!mcc) {
+      mcc = ril.iccInfo.mcc;
+    }
 
-    // Get SIM mcc or set it to mcc for Brasil
-    if (!mcc)
-      mcc = ril.iccInfo.mcc || this._mcc;
+    // Get previous mcc
+    if (!mcc && ril.voiceConnectionInfo && ril.voiceConnectionInfo.network) {
+      mcc = ril.voiceConnectionInfo.network.previousMcc;
+    }
+
+    // Set to default mcc
+    if (!mcc) {
+      mcc = this._mcc;
+    }
 #else
     mcc = this._mcc;
 #endif
 
     countryName = MCC_ISO3166_TABLE[mcc];
-    debug("MCC: " + mcc + "countryName: " + countryName);
+    if (DEBUG) debug("MCC: " + mcc + "countryName: " + countryName);
     return countryName;
   },
 
   parse: function(aNumber) {
-    debug("call parse: " + aNumber);
+    if (DEBUG) debug("call parse: " + aNumber);
     let result = PhoneNumber.Parse(aNumber, this._getCountryName());
     if (DEBUG) {
       if (result) {
         debug("InternationalFormat: " + result.internationalFormat);
         debug("InternationalNumber: " + result.internationalNumber);
         debug("NationalNumber: " + result.nationalNumber);
         debug("NationalFormat: " + result.nationalFormat);
       } else {
         debug("No result!\n");
       }
     }
     return result;
   },
 
   parseWithMCC: function(aNumber, aMCC) {
     let countryName = MCC_ISO3166_TABLE[aMCC];
-    debug("found country name: " + countryName);
+    if (DEBUG) debug("found country name: " + countryName);
     return PhoneNumber.Parse(aNumber, countryName);
   }
 };
--- a/dom/plugins/test/testplugin/Makefile.in
+++ b/dom/plugins/test/testplugin/Makefile.in
@@ -2,16 +2,17 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DEPTH     = @DEPTH@
 topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH     = @srcdir@
+FAIL_ON_WARNINGS = 1
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE       = nptest
 LIBRARY_NAME = nptest
 MODULE_NAME  = TestPlugin
 
 DIRS = secondplugin
--- a/dom/plugins/test/testplugin/nptest_droid.cpp
+++ b/dom/plugins/test/testplugin/nptest_droid.cpp
@@ -76,22 +76,16 @@ pluginDoSetWindow(InstanceData* instance
 }
 
 void
 pluginWidgetInit(InstanceData* instanceData, void* oldWindow)
 {
   // XXX nothing here yet since we don't support windowed plugins
 }
 
-static void
-pluginDrawWindow(InstanceData* instanceData, void* event)
-{
-  return;
-}
-
 int16_t
 pluginHandleEvent(InstanceData* instanceData, void* event)
 {
   return 0;
 }
 
 int32_t pluginGetEdge(InstanceData* instanceData, RectEdge edge)
 {
rename from dom/indexedDB/CheckQuotaHelper.cpp
rename to dom/quota/CheckQuotaHelper.cpp
--- a/dom/indexedDB/CheckQuotaHelper.cpp
+++ b/dom/quota/CheckQuotaHelper.cpp
@@ -7,33 +7,33 @@
 #include "CheckQuotaHelper.h"
 
 #include "nsIDOMWindow.h"
 #include "nsIObserverService.h"
 #include "nsIPermissionManager.h"
 #include "nsIPrincipal.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIURI.h"
-#include "nsXULAppAPI.h"
 
+#include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
+#include "mozilla/Services.h"
 #include "nsContentUtils.h"
 #include "nsNetUtil.h"
 #include "nsThreadUtils.h"
-#include "mozilla/Services.h"
-
-#include "IndexedDatabaseManager.h"
+#include "nsXULAppAPI.h"
 
 #define PERMISSION_INDEXEDDB_UNLIMITED "indexedDB-unlimited"
 
 #define TOPIC_QUOTA_PROMPT "indexedDB-quota-prompt"
 #define TOPIC_QUOTA_RESPONSE "indexedDB-quota-response"
 #define TOPIC_QUOTA_CANCEL "indexedDB-quota-cancel"
 
-USING_INDEXEDDB_NAMESPACE
+USING_QUOTA_NAMESPACE
 using namespace mozilla::services;
+using mozilla::dom::indexedDB::IndexedDatabaseManager;
 using mozilla::MutexAutoLock;
 
 namespace {
 
 inline
 uint32_t
 GetQuotaPermissions(nsIDOMWindow* aWindow)
 {
rename from dom/indexedDB/CheckQuotaHelper.h
rename to dom/quota/CheckQuotaHelper.h
--- a/dom/indexedDB/CheckQuotaHelper.h
+++ b/dom/quota/CheckQuotaHelper.h
@@ -1,31 +1,29 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef mozilla_dom_indexeddb_checkquotahelper_h__
-#define mozilla_dom_indexeddb_checkquotahelper_h__
+#ifndef mozilla_dom_quota_checkquotahelper_h__
+#define mozilla_dom_quota_checkquotahelper_h__
 
-// Only meant to be included in IndexedDB source files, not exported.
-#include "IndexedDatabase.h"
-#include "IDBDatabase.h"
+#include "QuotaCommon.h"
 
 #include "nsIInterfaceRequestor.h"
 #include "nsIObserver.h"
 #include "nsIRunnable.h"
 
 #include "mozilla/Mutex.h"
 #include "mozilla/CondVar.h"
 
 class nsPIDOMWindow;
 
-BEGIN_INDEXEDDB_NAMESPACE
+BEGIN_QUOTA_NAMESPACE
 
 class CheckQuotaHelper MOZ_FINAL : public nsIRunnable,
                                    public nsIInterfaceRequestor,
                                    public nsIObserver
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIRUNNABLE
@@ -44,11 +42,11 @@ private:
 
   mozilla::Mutex& mMutex;
   mozilla::CondVar mCondVar;
   uint32_t mPromptResult;
   bool mWaiting;
   bool mHasPrompted;
 };
 
-END_INDEXEDDB_NAMESPACE
+END_QUOTA_NAMESPACE
 
 #endif // mozilla_dom_indexeddb_checkquotahelper_h__
--- a/dom/quota/Makefile.in
+++ b/dom/quota/Makefile.in
@@ -15,16 +15,17 @@ XPIDL_MODULE     = dom_quota
 LIBXUL_LIBRARY   = 1
 FORCE_STATIC_LIB = 1
 
 include $(topsrcdir)/dom/dom-config.mk
 
 EXPORTS_NAMESPACES = mozilla/dom/quota
 
 CPPSRCS = \
+  CheckQuotaHelper.cpp \
   FileStreams.cpp \
   QuotaManager.cpp \
   $(NULL)
 
 EXPORTS_mozilla/dom/quota = \
   FileStreams.h \
   QuotaCommon.h \
   QuotaManager.h \
--- a/dom/quota/QuotaManager.cpp
+++ b/dom/quota/QuotaManager.cpp
@@ -5,18 +5,19 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "QuotaManager.h"
 
 #include "nsIFile.h"
 
 #include "mozilla/ClearOnShutdown.h"
 #include "nsComponentManagerUtils.h"
+#include "xpcpublic.h"
 
-#include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
+#include "CheckQuotaHelper.h"
 
 USING_QUOTA_NAMESPACE
 
 namespace {
 
 nsAutoPtr<QuotaManager> gInstance;
 
 PLDHashOperator
@@ -116,28 +117,45 @@ QuotaObject::MaybeAllocateMoreSpace(int6
   MutexAutoLock lock(quotaManager->mQuotaMutex);
 
   if (mSize >= end || !mOriginInfo) {
     return true;
   }
 
   int64_t newUsage = mOriginInfo->mUsage - mSize + end;
   if (newUsage > mOriginInfo->mLimit) {
-    if (!indexedDB::IndexedDatabaseManager::QuotaIsLifted()) {
+    // This will block the thread, but it will also drop the mutex while
+    // waiting. The mutex will be reacquired again when the waiting is finished.
+    if (!quotaManager->LockedQuotaIsLifted()) {
       return false;
     }
 
+    // Threads raced, the origin info removal has been done by some other
+    // thread.
+    if (!mOriginInfo) {
+      // The other thread could allocate more space.
+      if (end > mSize) {
+        mSize = end;
+      }
+
+      return true;
+    }
+
     nsCString origin = mOriginInfo->mOrigin;
 
     mOriginInfo->LockedClearOriginInfos();
     NS_ASSERTION(!mOriginInfo,
                  "Should have cleared in LockedClearOriginInfos!");
 
     quotaManager->mOriginInfos.Remove(origin);
 
+    // Some other thread could increase the size without blocking (increasing
+    // the origin usage without hitting the limit), but no more than this one.
+    NS_ASSERTION(mSize < end, "This shouldn't happen!");
+
     mSize = end;
 
     return true;
   }
 
   mOriginInfo->mUsage = newUsage;
   mSize = end;
 
@@ -166,39 +184,73 @@ OriginInfo::ClearOriginInfoCallback(cons
   NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
   NS_ASSERTION(aValue, "Null pointer!");
 
   aValue->mOriginInfo = nullptr;
 
   return PL_DHASH_NEXT;
 }
 
+QuotaManager::QuotaManager()
+: mCurrentWindowIndex(BAD_TLS_INDEX),
+  mQuotaMutex("QuotaManager.mQuotaMutex")
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+}
+
+QuotaManager::~QuotaManager()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+}
+
 // static
 QuotaManager*
 QuotaManager::GetOrCreate()
 {
   if (!gInstance) {
     NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-    gInstance = new QuotaManager();
+    nsAutoPtr<QuotaManager> instance(new QuotaManager());
+
+    NS_ENSURE_TRUE(instance->Init(), nullptr);
+
+    gInstance = instance.forget();
 
     ClearOnShutdown(&gInstance);
   }
 
   return gInstance;
 }
 
 // static
 QuotaManager*
 QuotaManager::Get()
 {
   // Does not return an owning reference.
   return gInstance;
 }
 
+bool
+QuotaManager::Init()
+{
+  // We need a thread-local to hold the current window.
+  NS_ASSERTION(mCurrentWindowIndex == BAD_TLS_INDEX, "Huh?");
+
+  if (PR_NewThreadPrivateIndex(&mCurrentWindowIndex, nullptr) != PR_SUCCESS) {
+    NS_ERROR("PR_NewThreadPrivateIndex failed, QuotaManager disabled");
+    mCurrentWindowIndex = BAD_TLS_INDEX;
+    return false;
+  }
+
+  mOriginInfos.Init();
+  mCheckQuotaHelpers.Init();
+
+  return true;
+}
+
 void
 QuotaManager::InitQuotaForOrigin(const nsACString& aOrigin,
                                  int64_t aLimit,
                                  int64_t aUsage)
 {
   OriginInfo* info = new OriginInfo(aOrigin, aLimit * 1024 * 1024, aUsage);
 
   MutexAutoLock lock(mQuotaMutex);
@@ -287,8 +339,88 @@ QuotaManager::GetQuotaObject(const nsACS
   nsCOMPtr<nsIFile> file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, nullptr);
 
   rv = file->InitWithPath(aPath);
   NS_ENSURE_SUCCESS(rv, nullptr);
 
   return GetQuotaObject(aOrigin, file);
 }
+
+void
+QuotaManager::SetCurrentWindowInternal(nsPIDOMWindow* aWindow)
+{
+  NS_ASSERTION(mCurrentWindowIndex != BAD_TLS_INDEX,
+               "Should have a valid TLS storage index!");
+
+  if (aWindow) {
+    NS_ASSERTION(!PR_GetThreadPrivate(mCurrentWindowIndex),
+                 "Somebody forgot to clear the current window!");
+    PR_SetThreadPrivate(mCurrentWindowIndex, aWindow);
+  }
+  else {
+    // We cannot assert PR_GetThreadPrivate(mCurrentWindowIndex) here because
+    // there are some cases where we did not already have a window.
+    PR_SetThreadPrivate(mCurrentWindowIndex, nullptr);
+  }
+}
+
+void
+QuotaManager::CancelPromptsForWindowInternal(nsPIDOMWindow* aWindow)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  nsRefPtr<CheckQuotaHelper> helper;
+
+  MutexAutoLock autoLock(mQuotaMutex);
+
+  if (mCheckQuotaHelpers.Get(aWindow, getter_AddRefs(helper))) {
+    helper->Cancel();
+  }
+}
+
+bool
+QuotaManager::LockedQuotaIsLifted()
+{
+  mQuotaMutex.AssertCurrentThreadOwns();
+
+  NS_ASSERTION(mCurrentWindowIndex != BAD_TLS_INDEX,
+               "Should have a valid TLS storage index!");
+
+  nsPIDOMWindow* window =
+    static_cast<nsPIDOMWindow*>(PR_GetThreadPrivate(mCurrentWindowIndex));
+
+  // Quota is not enforced in chrome contexts (e.g. for components and JSMs)
+  // so we must have a window here.
+  NS_ASSERTION(window, "Why don't we have a Window here?");
+
+  bool createdHelper = false;
+
+  nsRefPtr<CheckQuotaHelper> helper;
+  if (!mCheckQuotaHelpers.Get(window, getter_AddRefs(helper))) {
+    helper = new CheckQuotaHelper(window, mQuotaMutex);
+    createdHelper = true;
+
+    mCheckQuotaHelpers.Put(window, helper);
+
+    // Unlock while calling out to XPCOM
+    {
+      MutexAutoUnlock autoUnlock(mQuotaMutex);
+
+      nsresult rv = NS_DispatchToMainThread(helper);
+      NS_ENSURE_SUCCESS(rv, false);
+    }
+
+    // Relocked.  If any other threads hit the quota limit on the same Window,
+    // they are using the helper we created here and are now blocking in
+    // PromptAndReturnQuotaDisabled.
+  }
+
+  bool result = helper->PromptAndReturnQuotaIsDisabled();
+
+  // If this thread created the helper and added it to the hash, this thread
+  // must remove it.
+  if (createdHelper) {
+    mCheckQuotaHelpers.Remove(window);
+  }
+
+  return result;
+}
--- a/dom/quota/QuotaManager.h
+++ b/dom/quota/QuotaManager.h
@@ -9,18 +9,21 @@
 
 #include "QuotaCommon.h"
 
 #include "mozilla/Mutex.h"
 #include "nsDataHashtable.h"
 #include "nsRefPtrHashtable.h"
 #include "nsThreadUtils.h"
 
+class nsPIDOMWindow;
+
 BEGIN_QUOTA_NAMESPACE
 
+class CheckQuotaHelper;
 class OriginInfo;
 class QuotaManager;
 
 class QuotaObject
 {
   friend class OriginInfo;
   friend class QuotaManager;
 
@@ -118,30 +121,78 @@ public:
   already_AddRefed<QuotaObject>
   GetQuotaObject(const nsACString& aOrigin,
                  nsIFile* aFile);
 
   already_AddRefed<QuotaObject>
   GetQuotaObject(const nsACString& aOrigin,
                  const nsAString& aPath);
 
-private:
-  QuotaManager()
-  : mQuotaMutex("QuotaManager.mQuotaMutex")
+  // Set the Window that the current thread is doing operations for.
+  // The caller is responsible for ensuring that aWindow is held alive.
+  static void
+  SetCurrentWindow(nsPIDOMWindow* aWindow)
   {
-    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+    QuotaManager* quotaManager = Get();
+    NS_ASSERTION(quotaManager, "Must have a manager here!");
+
+    quotaManager->SetCurrentWindowInternal(aWindow);
+  }
 
-    mOriginInfos.Init();
+  static void
+  CancelPromptsForWindow(nsPIDOMWindow* aWindow)
+  {
+    NS_ASSERTION(aWindow, "Passed null window!");
+
+    QuotaManager* quotaManager = Get();
+    NS_ASSERTION(quotaManager, "Must have a manager here!");
+
+    quotaManager->CancelPromptsForWindowInternal(aWindow);
   }
 
-  virtual ~QuotaManager()
-  {
-    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  }
+private:
+  QuotaManager();
+
+  virtual ~QuotaManager();
+
+  bool
+  Init();
+
+  void
+  SetCurrentWindowInternal(nsPIDOMWindow* aWindow);
+
+  void
+  CancelPromptsForWindowInternal(nsPIDOMWindow* aWindow);
+
+  // Determine if the quota is lifted for the Window the current thread is
+  // using.
+  bool
+  LockedQuotaIsLifted();
+
+  // TLS storage index for the current thread's window.
+  unsigned int mCurrentWindowIndex;
 
   mozilla::Mutex mQuotaMutex;
 
   nsRefPtrHashtable<nsCStringHashKey, OriginInfo> mOriginInfos;
+
+  // A map of Windows to the corresponding quota helper.
+  nsRefPtrHashtable<nsPtrHashKey<nsPIDOMWindow>,
+                    CheckQuotaHelper> mCheckQuotaHelpers;
+};
+
+class AutoEnterWindow
+{
+public:
+  AutoEnterWindow(nsPIDOMWindow* aWindow)
+  {
+    QuotaManager::SetCurrentWindow(aWindow);
+  }
+
+  ~AutoEnterWindow()
+  {
+    QuotaManager::SetCurrentWindow(nullptr);
+  }
 };
 
 END_QUOTA_NAMESPACE
 
 #endif /* mozilla_dom_quota_quotamanager_h__ */
--- a/dom/sms/interfaces/nsIDOMSmsMessage.idl
+++ b/dom/sms/interfaces/nsIDOMSmsMessage.idl
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
-[scriptable, builtinclass, uuid(a82f9998-02a2-4db2-a0d1-f8353933d7b0)]
+[scriptable, builtinclass, uuid(59fc5ea8-33fe-40ba-890b-b9abaeb5ac26)]
 interface nsIDOMMozSmsMessage : nsISupports
 {
   readonly attribute long      id;
 
   /**
    * Should be "received", "sending", "sent" or "error".
    */
   readonly attribute DOMString delivery;
--- a/dom/sms/interfaces/nsISmsDatabaseService.idl
+++ b/dom/sms/interfaces/nsISmsDatabaseService.idl
@@ -9,17 +9,17 @@
 { 0x2454c2a1, 0xefdd, 0x4d96,    \
 { 0x83, 0xbd, 0x51, 0xa2, 0x9a, 0x21, 0xf5, 0xab } }
 #define SMS_DATABASE_SERVICE_CONTRACTID "@mozilla.org/sms/smsdatabaseservice;1"
 %}
 
 interface nsIDOMMozSmsFilter;
 interface nsISmsRequest;
 
-[scriptable, uuid(c2cb2af7-6b96-4915-bcc8-54ad705d6110)]
+[scriptable, uuid(b1311c6d-d863-4da1-8755-2fd59eb0f386)]
 interface nsISmsDatabaseService : nsISupports
 {
   [binaryname(GetMessageMoz)]
   void getMessage(in long messageId, in nsISmsRequest request);
   void deleteMessage(in long messageId, in nsISmsRequest request);
   void createMessageList(in nsIDOMMozSmsFilter filter, in boolean reverse, in nsISmsRequest request);
   void getNextMessageInList(in long listId, in nsISmsRequest request);
   void clearMessageList(in long listId);
--- a/dom/tests/mochitest/bugs/Makefile.in
+++ b/dom/tests/mochitest/bugs/Makefile.in
@@ -132,16 +132,17 @@ MOCHITEST_FILES	= \
 		test_bug809290.html \
 		file_bug809290_b1.html \
 		file_bug809290_b2.html \
 		file_bug809290_c.html \
 		file_empty.html \
 		test_sizetocontent_clamp.html \
 		test_protochains.html \
 		test_bug817476.html \
+		test_bug823173.html \
 		$(NULL)
 
 ifneq (Linux,$(OS_ARCH))
 MOCHITEST_FILES += \
 		test_resize_move_windows.html \
 		$(NULL)
 else
 $(filter disabled-on-linux-for-timeouts--bug-677841, test_resize_move_windows.html)
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/bugs/test_bug823173.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=823173
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 823173</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=823173">Mozilla Bug 823173</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 823173 **/
+try {
+  ok(!(navigator instanceof Window), "navigator is not an instance of Window");
+} catch (e) {
+  ok(false, "instanceof tests should not throw");
+}
+</script>
+</pre>
+</body>
+</html>
--- a/dom/tests/mochitest/localstorage/test_localStorageBasePrivateBrowsing_perwindowpb.html
+++ b/dom/tests/mochitest/localstorage/test_localStorageBasePrivateBrowsing_perwindowpb.html
@@ -29,22 +29,21 @@ function startTest() {
 
 var contentPage = "http://mochi.test:8888/chrome/dom/tests/mochitest/localstorage/page_blank.html";
 
 function testOnWindow(aIsPrivate, aCallback) {
   var win = mainWindow.OpenBrowserWindow({private: aIsPrivate});
   win.addEventListener("load", function onLoad() {
     win.removeEventListener("load", onLoad, false);
     win.addEventListener("DOMContentLoaded", function onInnerLoad() {
-      // I am at my wits' end figuring out how to stop this load from occurring. I give up.
       if (win.content.location.href == "about:privatebrowsing") {
         win.gBrowser.loadURI(contentPage);
         return;
       }
-      win.removeEventListener("DOMConten/tLoaded", onInnerLoad, true);
+      win.removeEventListener("DOMContentLoaded", onInnerLoad, true);
       SimpleTest.executeSoon(function() { aCallback(win); });
     }, true);
     win.gBrowser.loadURI(contentPage);
   }, true);
 }
 
 function doTest() {
   testOnWindow(false, function(aWin) {
--- a/dom/webidl/XMLHttpRequest.webidl
+++ b/dom/webidl/XMLHttpRequest.webidl
@@ -86,16 +86,18 @@ interface XMLHttpRequest : XMLHttpReques
   [Throws=Workers]
   readonly attribute XMLHttpRequestUpload upload;
 
   [Throws]
   void send();
   [Throws]
   void send(ArrayBuffer data);
   [Throws]
+  void send(ArrayBufferView data);
+  [Throws]
   void send(Blob data);
   [Throws]
   void send(Document data);
   [Throws]
   void send(DOMString? data);
   [Throws]
   void send(FormData data);
   [Throws]
--- a/dom/webidl/XPathEvaluator.webidl
+++ b/dom/webidl/XPathEvaluator.webidl
@@ -1,15 +1,14 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
-interface nsISupports;
 interface XPathExpression;
 interface XPathNSResolver;
 interface XPathResult;
 
 [Constructor]
 interface XPathEvaluator {
   // Based on nsIDOMXPathEvaluator
   [Creator, Throws]
--- a/dom/workers/XMLHttpRequest.cpp
+++ b/dom/workers/XMLHttpRequest.cpp
@@ -2004,17 +2004,18 @@ XMLHttpRequest::Send(JSObject* aBody, Er
   if (!mProxy) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   JSContext* cx = GetJSContext();
 
   jsval valToClone;
-  if (JS_IsArrayBufferObject(aBody) || file::GetDOMBlobFromJSObject(aBody)) {
+  if (JS_IsArrayBufferObject(aBody) || JS_IsArrayBufferViewObject(aBody) ||
+      file::GetDOMBlobFromJSObject(aBody)) {
     valToClone = OBJECT_TO_JSVAL(aBody);
   }
   else {
     JSString* bodyStr = JS_ValueToString(cx, OBJECT_TO_JSVAL(aBody));
     if (!bodyStr) {
       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
       return;
     }
--- a/dom/workers/XMLHttpRequest.h
+++ b/dom/workers/XMLHttpRequest.h
@@ -185,16 +185,21 @@ public:
   }
 
   void
   Send(ArrayBuffer& aBody, ErrorResult& aRv) {
     return Send(aBody.Obj(), aRv);
   }
 
   void
+  Send(ArrayBufferView& aBody, ErrorResult& aRv) {
+    return Send(aBody.Obj(), aRv);
+  }
+
+  void
   SendAsBinary(const nsAString& aBody, ErrorResult& aRv);
 
   void
   Abort(ErrorResult& aRv);
 
   uint16_t
   GetStatus(ErrorResult& aRv) const
   {
--- a/gfx/2d/DrawTargetD2D.cpp
+++ b/gfx/2d/DrawTargetD2D.cpp
@@ -1509,17 +1509,17 @@ DrawTargetD2D::GetBlendStateForOperator(
 }
 
 /* This function prepares the temporary RT for drawing and returns it when a
  * drawing operation other than OVER is required.
  */
 ID2D1RenderTarget*
 DrawTargetD2D::GetRTForOperation(CompositionOp aOperator, const Pattern &aPattern)
 {
-  if (aOperator == OP_OVER && !IsPatternSupportedByD2D(aPattern)) {
+  if (aOperator == OP_OVER && IsPatternSupportedByD2D(aPattern)) {
     return mRT;
   }
 
   PopAllClips();
 
   if (aOperator > OP_XOR) {
     mRT->Flush();
   }
@@ -1556,17 +1556,17 @@ DrawTargetD2D::GetRTForOperation(Composi
  * contents) to the rendertarget using the requested composition operation.
  * In order to respect clip for operations which are unbound by their mask,
  * the old content of the surface outside the clipped area may be blended back
  * to the surface.
  */
 void
 DrawTargetD2D::FinalizeRTForOperation(CompositionOp aOperator, const Pattern &aPattern, const Rect &aBounds)
 {
-  if (aOperator == OP_OVER && !IsPatternSupportedByD2D(aPattern)) {
+  if (aOperator == OP_OVER && IsPatternSupportedByD2D(aPattern)) {
     return;
   }
 
   if (!mTempRT) {
     return;
   }
 
   PopClipsFromRT(mTempRT);
@@ -1604,17 +1604,17 @@ DrawTargetD2D::FinalizeRTForOperation(Co
 
   RefPtr<ID3D10Texture2D> tmpTexture;
   RefPtr<ID3D10ShaderResourceView> mBckSRView;
 
   mDevice->RSSetViewports(1, &viewport);
   mPrivateData->mEffect->GetVariableByName("QuadDesc")->AsVector()->
     SetFloatVector(ShaderConstantRectD3D10(-1.0f, 1.0f, 2.0f, -2.0f));
 
-  if (!IsPatternSupportedByD2D(aPattern)) {
+  if (IsPatternSupportedByD2D(aPattern)) {
     mPrivateData->mEffect->GetVariableByName("TexCoords")->AsVector()->
       SetFloatVector(ShaderConstantRectD3D10(0, 0, 1.0f, 1.0f));
     mPrivateData->mEffect->GetVariableByName("tex")->AsShaderResource()->SetResource(mSRView);
 
     // Handle the case where we blend with the backdrop
     if (aOperator > OP_XOR) {
       IntSize size = mSize;
       SurfaceFormat format = mFormat;
@@ -2137,17 +2137,17 @@ DrawTargetD2D::FillGlyphsManual(ScaledFo
   SetScissorToRect(clipBounds);
   mDevice->Draw(4, 0);
   return true;
 }
 
 TemporaryRef<ID2D1Brush>
 DrawTargetD2D::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha)
 {
-  if (IsPatternSupportedByD2D(aPattern)) {
+  if (!IsPatternSupportedByD2D(aPattern)) {
     RefPtr<ID2D1SolidColorBrush> colBrush;
     mRT->CreateSolidColorBrush(D2D1::ColorF(1.0f, 1.0f, 1.0f, 1.0f), byRef(colBrush));
     return colBrush;
   }
 
   if (aPattern.GetType() == PATTERN_COLOR) {
     RefPtr<ID2D1SolidColorBrush> colBrush;
     Color color = static_cast<const ColorPattern*>(&aPattern)->mColor;
--- a/gfx/2d/HelpersD2D.h
+++ b/gfx/2d/HelpersD2D.h
@@ -133,34 +133,34 @@ static inline D2D1_ALPHA_MODE AlphaMode(
 static inline D2D1_PIXEL_FORMAT D2DPixelFormat(SurfaceFormat aFormat)
 {
   return D2D1::PixelFormat(DXGIFormat(aFormat), AlphaMode(aFormat));
 }
 
 static inline bool IsPatternSupportedByD2D(const Pattern &aPattern)
 {
   if (aPattern.GetType() != PATTERN_RADIAL_GRADIENT) {
-    return false;
+    return true;
   }
 
   const RadialGradientPattern *pat =
     static_cast<const RadialGradientPattern*>(&aPattern);
   
   if (pat->mRadius1 != 0) {
-    return true;
+    return false;
   }
 
   Point diff = pat->mCenter2 - pat->mCenter1;
 
   if (sqrt(diff.x * diff.x + diff.y * diff.y) >= pat->mRadius2) {
     // Inner point lies outside the circle.
-    return true;
+    return false;
   }
 
-  return false;
+  return true;
 }
 
 /**
  * This structure is used to pass rectangles to our shader constant. We can use
  * this for passing rectangular areas to SetVertexShaderConstant. In the format
  * of a 4 component float(x,y,width,height). Our vertex shader can then use
  * this to construct rectangular positions from the 0,0-1,1 quad that we source
  * it with.
--- a/gfx/thebes/gfxASurface.h
+++ b/gfx/thebes/gfxASurface.h
@@ -194,16 +194,24 @@ public:
      */
     void RecordMemoryUsed(int32_t aBytes);
     void RecordMemoryFreed();
 
     virtual int32_t KnownMemoryUsed() { return mBytesRecorded; }
 
     virtual size_t SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const;
     virtual size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const;
+    // gfxASurface has many sub-classes.  This method indicates if a sub-class
+    // is capable of measuring its own size accurately.  If not, the caller
+    // must fall back to a computed size.  (Note that gfxASurface can actually
+    // measure itself, but we must |return false| here because it serves as the
+    // (conservative) default for all the sub-classes.  Therefore, this
+    // function should only be called on a |gfxASurface*| that actually points
+    // to a sub-class of gfxASurface.)
+    virtual bool SizeOfIsMeasured() const { return false; }
 
     /**
      * The memory used by this surface (as reported by KnownMemoryUsed()) can
      * either live in this process's heap, in this process but outside the
      * heap, or in another process altogether.
      */
     enum MemoryLocation {
       MEMORY_IN_PROCESS_HEAP,
--- a/gfx/thebes/gfxImageSurface.cpp
+++ b/gfx/thebes/gfxImageSurface.cpp
@@ -185,16 +185,22 @@ gfxImageSurface::SizeOfExcludingThis(nsM
 }
 
 size_t
 gfxImageSurface::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const
 {
     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
 }
 
+bool
+gfxImageSurface::SizeOfIsMeasured() const
+{
+    return true;
+}
+
 // helper function for the CopyFrom methods
 static void
 CopyForStride(unsigned char* aDest, unsigned char* aSrc, const gfxIntSize& aSize, long aDestStride, long aSrcStride)
 {
     if (aDestStride == aSrcStride) {
         memcpy (aDest, aSrc, aSrcStride * aSize.height);
     } else {
         int lineSize = NS_MIN(aDestStride, aSrcStride);
--- a/gfx/thebes/gfxImageSurface.h
+++ b/gfx/thebes/gfxImageSurface.h
@@ -93,16 +93,17 @@ public:
                             const nsIntPoint& aDestTopLeft) MOZ_OVERRIDE;
 
     static long ComputeStride(const gfxIntSize&, gfxImageFormat);
 
     virtual size_t SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const
         MOZ_OVERRIDE;
     virtual size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const
         MOZ_OVERRIDE;
+    virtual bool SizeOfIsMeasured() const MOZ_OVERRIDE;
 
 protected:
     gfxImageSurface();
     void InitWithData(unsigned char *aData, const gfxIntSize& aSize,
                       long aStride, gfxImageFormat aFormat);
     void InitFromSurface(cairo_surface_t *csurf);
     long ComputeStride() const { return ComputeStride(mSize, mFormat); }
 
--- a/image/src/imgFrame.cpp
+++ b/image/src/imgFrame.cpp
@@ -1,11 +1,11 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "imgFrame.h"
 #include "DiscardTracker.h"
 
 #include <limits.h>
 
@@ -808,17 +808,16 @@ imgFrame::SizeOfExcludingThisWithCompute
   if (mPalettedImageData && aLocation == gfxASurface::MEMORY_IN_PROCESS_HEAP) {
     size_t n2 = aMallocSizeOf(mPalettedImageData);
     if (n2 == 0) {
       n2 = GetImageDataLength() + PaletteDataLength();
     }
     n += n2;
   }
 
-  // XXX: should pass aMallocSizeOf here.  See bug 723827.
 #ifdef USE_WIN_SURFACE
   if (mWinSurface && aLocation == mWinSurface->GetMemoryLocation()) {
     n += mWinSurface->KnownMemoryUsed();
   } else
 #endif
 #ifdef XP_MACOSX
   if (mQuartzSurface && aLocation == gfxASurface::MEMORY_IN_PROCESS_HEAP) {
     n += mSize.width * mSize.height * 4;
@@ -831,13 +830,22 @@ imgFrame::SizeOfExcludingThisWithCompute
     }
     if (n2 == 0) {  // non-HEAP or computed fallback for HEAP
       n2 = mImageSurface->KnownMemoryUsed();
     }
     n += n2;
   }
 
   if (mOptSurface && aLocation == mOptSurface->GetMemoryLocation()) {
-    n += mOptSurface->KnownMemoryUsed();
+    size_t n2 = 0;
+    if (aLocation == gfxASurface::MEMORY_IN_PROCESS_HEAP &&
+        mOptSurface->SizeOfIsMeasured()) {
+      // HEAP: measure (but only if the sub-class is capable of measuring)
+      n2 = mOptSurface->SizeOfIncludingThis(aMallocSizeOf);
+    }
+    if (n2 == 0) {  // non-HEAP or computed fallback for HEAP
+      n2 = mOptSurface->KnownMemoryUsed();
+    }
+    n += n2;
   }
 
   return n;
 }
--- a/image/src/imgLoader.cpp
+++ b/image/src/imgLoader.cpp
@@ -245,17 +245,22 @@ private:
                                                    imgCacheEntry *entry,
                                                    void *userArg)
   {
     if (!entry->HasNoProxies()) {
       size_t *n = static_cast<size_t*>(userArg);
       nsRefPtr<imgRequest> req = entry->GetRequest();
       Image *image = static_cast<Image*>(req->mImage.get());
       if (image) {
-        *n += image->HeapSizeOfDecodedWithComputedFallback(ImagesMallocSizeOf);
+        // Both this and EntryAllSizes measure images-content-used-uncompressed
+        // memory.  This function's measurement is secondary -- the result
+        // doesn't go in the "explicit" tree -- so we use moz_malloc_size_of
+        // instead of ImagesMallocSizeOf to prevent DMD from seeing it reported
+        // twice.
+        *n += image->HeapSizeOfDecodedWithComputedFallback(moz_malloc_size_of);
         *n += image->NonHeapSizeOfDecoded();
       }
     }
 
     return PL_DHASH_NEXT;
   }
 };
 
--- a/ipc/chromium/src/base/debug_util_posix.cc
+++ b/ipc/chromium/src/base/debug_util_posix.cc
@@ -15,16 +15,19 @@
 #include <sys/param.h>
 #include <sys/types.h>
 #include <unistd.h>
 #if MOZ_HAVE_EXECINFO_H
 #include <execinfo.h>
 #endif
 
 #if defined(OS_MACOSX) || defined(OS_BSD)
+#if defined(OS_OPENBSD)
+#include <sys/proc.h>
+#endif
 #include <sys/sysctl.h>
 #endif
 
 #if defined(OS_DRAGONFLY) || defined(OS_FREEBSD)
 #include <sys/user.h>
 #endif
 
 #include "base/basictypes.h"
--- a/ipc/chromium/src/base/process_util.h
+++ b/ipc/chromium/src/base/process_util.h
@@ -124,16 +124,18 @@ void SetAllFDsToCloseOnExec();
 // given multimap. Only call this function in a child process where you know
 // that there aren't any other threads.
 void CloseSuperfluousFds(const base::InjectiveMultimap& saved_map);
 #endif
 
 enum ChildPrivileges {
   PRIVILEGES_DEFAULT,
   PRIVILEGES_UNPRIVILEGED,
+  PRIVILEGES_CAMERA,
+  PRIVILEGES_VIDEO,
   PRIVILEGES_INHERIT
 };
 
 #if defined(OS_WIN)
 // Runs the given application name with the given command line. Normally, the
 // first command line argument should be the path to the process, and don't
 // forget to quote it.
 //
--- a/ipc/chromium/src/base/process_util_linux.cc
+++ b/ipc/chromium/src/base/process_util_linux.cc
@@ -24,16 +24,20 @@
 /*
  * AID_APP is the first application UID used by Android. We're using
  * it as our unprivilegied UID.  This ensure the UID used is not
  * shared with any other processes than our own childs.
  */
 # include <private/android_filesystem_config.h>
 # define CHILD_UNPRIVILEGED_UID AID_APP
 # define CHILD_UNPRIVILEGED_GID AID_APP
+# define CHILD_CAMERA_UID AID_SYSTEM
+# define CHILD_CAMERA_GID AID_SDCARD_RW
+# define CHILD_VIDEO_UID AID_MEDIA
+# define CHILD_VIDEO_GID AID_AUDIO
 #else
 /*
  * On platforms that are not gonk based, we fall back to an arbitrary
  * UID. This is generally the UID for user `nobody', albeit it is not
  * always the case.
  */
 # define CHILD_UNPRIVILEGED_UID 65534
 # define CHILD_UNPRIVILEGED_GID 65534
@@ -226,46 +230,54 @@ bool LaunchApp(const std::vector<std::st
       _exit(127);
 
     CloseSuperfluousFds(fd_shuffle2);
 
     for (size_t i = 0; i < argv.size(); i++)
       argv_cstr[i] = const_cast<char*>(argv[i].c_str());
     argv_cstr[argv.size()] = NULL;
 
-    if (privs == PRIVILEGES_UNPRIVILEGED) {
+    if (privs != PRIVILEGES_INHERIT) {
       gid_t gid = CHILD_UNPRIVILEGED_GID;
       uid_t uid = CHILD_UNPRIVILEGED_UID;
 #ifdef MOZ_WIDGET_GONK
-      static bool checked_pix_max, pix_max_ok;
-      if (!checked_pix_max) {
-        checked_pix_max = true;
-        int fd = open("/proc/sys/kernel/pid_max", O_CLOEXEC | O_RDONLY);
-        if (fd < 0) {
-          DLOG(ERROR) << "Failed to open pid_max";
+      if (privs == PRIVILEGES_UNPRIVILEGED) {
+        static bool checked_pix_max, pix_max_ok;
+        if (!checked_pix_max) {
+          checked_pix_max = true;
+          int fd = open("/proc/sys/kernel/pid_max", O_CLOEXEC | O_RDONLY);
+          if (fd < 0) {
+            DLOG(ERROR) << "Failed to open pid_max";
+            _exit(127);
+          }
+          char buf[PATH_MAX];
+          ssize_t len = read(fd, buf, sizeof(buf) - 1);
+          close(fd);
+          if (len < 0) {
+            DLOG(ERROR) << "Failed to read pid_max";
+            _exit(127);
+          }
+          buf[len] = '\0';
+          int pid_max = atoi(buf);
+          pix_max_ok =
+            (pid_max + CHILD_UNPRIVILEGED_UID > CHILD_UNPRIVILEGED_UID);
+        }
+        if (!pix_max_ok) {
+          DLOG(ERROR) << "Can't safely get unique uid/gid";
           _exit(127);
         }
-        char buf[PATH_MAX];
-        ssize_t len = read(fd, buf, sizeof(buf) - 1);
-        close(fd);
-        if (len < 0) {
-          DLOG(ERROR) << "Failed to read pid_max";
-          _exit(127);
-        }
-        buf[len] = '\0';
-        int pid_max = atoi(buf);
-        pix_max_ok =
-          (pid_max + CHILD_UNPRIVILEGED_UID > CHILD_UNPRIVILEGED_UID);
+        gid += getpid();
+        uid += getpid();
+      } else if (privs == PRIVILEGES_CAMERA) {
+        uid = CHILD_CAMERA_UID;
+        gid = CHILD_CAMERA_GID;
+      } else if (privs == PRIVILEGES_VIDEO) {
+        uid = CHILD_VIDEO_UID;
+        gid = CHILD_VIDEO_GID;
       }
-      if (!pix_max_ok) {
-        DLOG(ERROR) << "Can't safely get unique uid/gid";
-        _exit(127);
-      }
-      gid += getpid();
-      uid += getpid();
 #endif
       if (setgid(gid) != 0) {
         DLOG(ERROR) << "FAILED TO setgid() CHILD PROCESS, path: " << argv_cstr[0];
         _exit(127);
       }
       if (setuid(uid) != 0) {
         DLOG(ERROR) << "FAILED TO setuid() CHILD PROCESS, path: " << argv_cstr[0];
         _exit(127);
--- a/ipc/unixsocket/UnixSocket.cpp
+++ b/ipc/unixsocket/UnixSocket.cpp
@@ -19,16 +19,18 @@
 #include "mozilla/Monitor.h"
 #include "mozilla/Util.h"
 #include "mozilla/FileUtils.h"
 #include "nsString.h"
 #include "nsThreadUtils.h"
 #include "nsTArray.h"
 #include "nsXULAppAPI.h"
 
+static const size_t MAX_READ_SIZE = 1 << 16;
+
 #undef LOG
 #if defined(MOZ_WIDGET_GONK)
 #include <android/log.h>
 #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "GonkDBus", args);
 #else
 #define BTDEBUG true
 #define LOG(args...) if (BTDEBUG) printf(args);
 #endif
@@ -580,17 +582,17 @@ UnixSocketConsumer::SendSocketData(UnixS
 
 bool
 UnixSocketConsumer::SendSocketData(const nsACString& aStr)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (!mImpl) {
     return false;
   }
-  if (aStr.Length() > UnixSocketRawData::MAX_DATA_SIZE) {
+  if (aStr.Length() > MAX_READ_SIZE) {
     return false;
   }
   nsCString str(aStr);
   UnixSocketRawData* d = new UnixSocketRawData(aStr.Length());
   memcpy(d->mData, str.get(), aStr.Length());
   XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
                                    new SocketSendTask(this, mImpl, d));
   return true;
@@ -624,47 +626,45 @@ UnixSocketImpl::OnFileCanReadWithoutBloc
   //   - mIncoming is completely read
   //     If so, sConsumer->MessageReceived(mIncoming.forget())
   //
   //   - mIncoming isn't completely read, but there's no more
   //     data available on the socket
   //     If so, break;
   while (true) {
     if (!mIncoming) {
-      mIncoming = new UnixSocketRawData();
-      ssize_t ret = read(aFd, mIncoming->mData, UnixSocketRawData::MAX_DATA_SIZE);
+      uint8_t data[MAX_READ_SIZE];
+      ssize_t ret = read(aFd, data, MAX_READ_SIZE);
       if (ret <= 0) {
         if (ret == -1) {
           if (errno == EINTR) {
             continue; // retry system call when interrupted
           }
           else if (errno == EAGAIN || errno == EWOULDBLOCK) {
-            mIncoming.forget();
             return; // no data available: return and re-poll
           }
           // else fall through to error handling on other errno's
         }
 #ifdef DEBUG
         NS_WARNING("Cannot read from network");
 #endif
         // At this point, assume that we can't actually access
         // the socket anymore
-        mIncoming.forget();
         mReadWatcher.StopWatchingFileDescriptor();
         mWriteWatcher.StopWatchingFileDescriptor();
         nsRefPtr<SocketCloseTask> t = new SocketCloseTask(this);
         NS_DispatchToMainThread(t);
         return;
       }
-      mIncoming->mData[ret] = 0;
-      mIncoming->mSize = ret;
+      mIncoming = new UnixSocketRawData(ret);
+      memcpy(mIncoming->mData, data, ret);
       nsRefPtr<SocketReceiveTask> t =
         new SocketReceiveTask(this, mIncoming.forget());
       NS_DispatchToMainThread(t);
-      if (ret < ssize_t(UnixSocketRawData::MAX_DATA_SIZE)) {
+      if (ret < ssize_t(MAX_READ_SIZE)) {
         return;
       }
     }
   }
 }
 
 void
 UnixSocketImpl::OnFileCanWriteWithoutBlocking(int aFd)
--- a/ipc/unixsocket/UnixSocket.h
+++ b/ipc/unixsocket/UnixSocket.h
@@ -11,47 +11,38 @@
 #include <stdlib.h>
 #include "nsString.h"
 #include "nsAutoPtr.h"
 #include "mozilla/RefPtr.h"
 
 namespace mozilla {
 namespace ipc {
 
-struct UnixSocketRawData
+class UnixSocketRawData
 {
-  static const size_t MAX_DATA_SIZE = 1024;
-  uint8_t mData[MAX_DATA_SIZE];
+public:
+  nsAutoArrayPtr<uint8_t> mData;
 
   // Number of octets in mData.
   size_t mSize;
   size_t mCurrentWriteOffset;
 
   /**
-   * Constructor for situations where size is not known beforehand. (for
-   * example, when reading a packet)
-   *
-   */
-  UnixSocketRawData() :
-    mSize(0),
-    mCurrentWriteOffset(0)
-  {
-  }
-
-  /**
    * Constructor for situations where size is known beforehand (for example,
    * when being assigned strings)
    *
    */
   UnixSocketRawData(int aSize) :
     mSize(aSize),
     mCurrentWriteOffset(0)
   {
+    mData = new uint8_t[aSize];
   }
-
+private:
+  UnixSocketRawData() {}
 };
 
 class UnixSocketImpl;
 
 /**
  * UnixSocketConnector defines the socket creation and connection/listening
  * functions for a UnixSocketConsumer. Due to the fact that socket setup can
  * vary between protocols (unix sockets, tcp sockets, bluetooth sockets, etc),
--- a/js/public/MemoryMetrics.h
+++ b/js/public/MemoryMetrics.h
@@ -29,41 +29,55 @@ namespace js {
 // We need to define this value here, rather than in the code which actually
 // generates the memory reports, because HugeStringInfo uses this value.
 JS_FRIEND_API(size_t) MemoryReportingSundriesThreshold();
 
 } // namespace js
 
 namespace JS {
 
+// Data for tracking memory usage of things hanging off objects.
+struct ObjectsExtraSizes {
+    size_t slots;
+    size_t elements;
+    size_t argumentsData;
+    size_t regExpStatics;
+    size_t propertyIteratorData;
+    size_t ctypesData;
+    size_t private_;    // The '_' suffix is required because |private| is a keyword.
+                        // Note that this field is measured separately from the others.
+
+    ObjectsExtraSizes() { memset(this, 0, sizeof(ObjectsExtraSizes)); }
+
+    void add(ObjectsExtraSizes &sizes) {
+        this->slots                += sizes.slots;
+        this->elements             += sizes.elements;
+        this->argumentsData        += sizes.argumentsData;
+        this->regExpStatics        += sizes.regExpStatics;
+        this->propertyIteratorData += sizes.propertyIteratorData;
+        this->ctypesData           += sizes.ctypesData;
+        this->private_             += sizes.private_;
+    }
+};
+
 // Data for tracking analysis/inference memory usage.
 struct TypeInferenceSizes
 {
-    TypeInferenceSizes()
-      : typeScripts(0)
-      , typeResults(0)
-      , analysisPool(0)
-      , typePool(0)
-      , pendingArrays(0)
-      , allocationSiteTables(0)
-      , arrayTypeTables(0)
-      , objectTypeTables(0)
-      , typeObjects(0)
-    {}
-
     size_t typeScripts;
     size_t typeResults;
     size_t analysisPool;
     size_t typePool;
     size_t pendingArrays;
     size_t allocationSiteTables;
     size_t arrayTypeTables;
     size_t objectTypeTables;
     size_t typeObjects;
 
+    TypeInferenceSizes() { memset(this, 0, sizeof(TypeInferenceSizes)); }
+
     void add(TypeInferenceSizes &sizes) {
         this->typeScripts          += sizes.typeScripts;
         this->typeResults          += sizes.typeResults;
         this->analysisPool         += sizes.analysisPool;
         this->typePool             += sizes.typePool;
         this->pendingArrays        += sizes.pendingArrays;
         this->allocationSiteTables += sizes.allocationSiteTables;
         this->arrayTypeTables      += sizes.arrayTypeTables;
@@ -156,34 +170,31 @@ struct CompartmentStats
       , gcHeapShapesDict(0)
       , gcHeapShapesBase(0)
       , gcHeapScripts(0)
       , gcHeapTypeObjects(0)
       , gcHeapIonCodes(0)
 #if JS_HAS_XML_SUPPORT
       , gcHeapXML(0)
 #endif
-      , objectsExtraSlots(0)
-      , objectsExtraElements(0)
-      , objectsExtraArgumentsData(0)
-      , objectsExtraRegExpStatics(0)
-      , objectsExtraPropertyIteratorData(0)
-      , objectsExtraPrivate(0)
+      , objectsExtra()
       , stringCharsNonHuge(0)
       , shapesExtraTreeTables(0)
       , shapesExtraDictTables(0)
       , shapesExtraTreeShapeKids(0)
       , shapesCompartmentTables(0)
       , scriptData(0)
       , jaegerData(0)
       , ionData(0)
       , compartmentObject(0)
       , crossCompartmentWrappersTable(0)
       , regexpCompartment(0)
       , debuggeesSet(0)
+      , typeInference()
+      , hugeStrings()
     {}
 
     CompartmentStats(const CompartmentStats &other)
       : extra1(other.extra1)
       , extra2(other.extra2)
       , gcHeapArenaAdmin(other.gcHeapArenaAdmin)
       , gcHeapUnusedGcThings(other.gcHeapUnusedGcThings)
       , gcHeapObjectsOrdinary(other.gcHeapObjectsOrdinary)
@@ -198,35 +209,30 @@ struct CompartmentStats
       , gcHeapShapesDict(other.gcHeapShapesDict)
       , gcHeapShapesBase(other.gcHeapShapesBase)
       , gcHeapScripts(other.gcHeapScripts)
       , gcHeapTypeObjects(other.gcHeapTypeObjects)
       , gcHeapIonCodes(other.gcHeapIonCodes)
 #if JS_HAS_XML_SUPPORT
       , gcHeapXML(other.gcHeapXML)
 #endif
-      , objectsExtraSlots(other.objectsExtraSlots)
-      , objectsExtraElements(other.objectsExtraElements)
-      , objectsExtraArgumentsData(other.objectsExtraArgumentsData)
-      , objectsExtraRegExpStatics(other.objectsExtraRegExpStatics)
-      , objectsExtraPropertyIteratorData(other.objectsExtraPropertyIteratorData)
-      , objectsExtraPrivate(other.objectsExtraPrivate)
+      , objectsExtra(other.objectsExtra)
       , stringCharsNonHuge(other.stringCharsNonHuge)
       , shapesExtraTreeTables(other.shapesExtraTreeTables)
       , shapesExtraDictTables(other.shapesExtraDictTables)
       , shapesExtraTreeShapeKids(other.shapesExtraTreeShapeKids)
       , shapesCompartmentTables(other.shapesCompartmentTables)
       , scriptData(other.scriptData)
       , jaegerData(other.jaegerData)
       , ionData(other.ionData)
       , compartmentObject(other.compartmentObject)
       , crossCompartmentWrappersTable(other.crossCompartmentWrappersTable)
       , regexpCompartment(other.regexpCompartment)
       , debuggeesSet(other.debuggeesSet)
-      , typeInferenceSizes(other.typeInferenceSizes)
+      , typeInference(other.typeInference)
     {
       hugeStrings.append(other.hugeStrings);
     }
 
     // These fields can be used by embedders.
     void   *extra1;
     void   *extra2;
 
@@ -247,37 +253,32 @@ struct CompartmentStats
     size_t gcHeapShapesDict;
     size_t gcHeapShapesBase;
     size_t gcHeapScripts;
     size_t gcHeapTypeObjects;
     size_t gcHeapIonCodes;
 #if JS_HAS_XML_SUPPORT
     size_t gcHeapXML;
 #endif
+    ObjectsExtraSizes objectsExtra;
 
-    size_t objectsExtraSlots;
-    size_t objectsExtraElements;
-    size_t objectsExtraArgumentsData;
-    size_t objectsExtraRegExpStatics;
-    size_t objectsExtraPropertyIteratorData;
-    size_t objectsExtraPrivate;
     size_t stringCharsNonHuge;
     size_t shapesExtraTreeTables;
     size_t shapesExtraDictTables;
     size_t shapesExtraTreeShapeKids;
     size_t shapesCompartmentTables;
     size_t scriptData;
     size_t jaegerData;
     size_t ionData;
     size_t compartmentObject;
     size_t crossCompartmentWrappersTable;
     size_t regexpCompartment;
     size_t debuggeesSet;
 
-    TypeInferenceSizes typeInferenceSizes;
+    TypeInferenceSizes typeInference;
     js::Vector<HugeStringInfo, 0, js::SystemAllocPolicy> hugeStrings;
 
     // Add cStats's numbers to this object's numbers.
     void add(CompartmentStats &cStats)
     {
         #define ADD(x)  this->x += cStats.x
 
         ADD(gcHeapArenaAdmin);
@@ -295,39 +296,34 @@ struct CompartmentStats
         ADD(gcHeapShapesDict);
         ADD(gcHeapShapesBase);
         ADD(gcHeapScripts);
         ADD(gcHeapTypeObjects);
         ADD(gcHeapIonCodes);
     #if JS_HAS_XML_SUPPORT
         ADD(gcHeapXML);
     #endif
+        objectsExtra.add(cStats.objectsExtra);
 
-        ADD(objectsExtraSlots);
-        ADD(objectsExtraElements);
-        ADD(objectsExtraArgumentsData);
-        ADD(objectsExtraRegExpStatics);
-        ADD(objectsExtraPropertyIteratorData);
-        ADD(objectsExtraPrivate);
         ADD(stringCharsNonHuge);
         ADD(shapesExtraTreeTables);
         ADD(shapesExtraDictTables);
         ADD(shapesExtraTreeShapeKids);
         ADD(shapesCompartmentTables);
         ADD(scriptData);
         ADD(jaegerData);
         ADD(ionData);
         ADD(compartmentObject);
         ADD(crossCompartmentWrappersTable);
         ADD(regexpCompartment);
         ADD(debuggeesSet);
 
         #undef ADD
 
-        typeInferenceSizes.add(cStats.typeInferenceSizes);
+        typeInference.add(cStats.typeInference);
         hugeStrings.append(cStats.hugeStrings);
     }
 
     // The size of all the live things in the GC heap.
     size_t gcHeapThingsSize();
 };
 
 struct RuntimeStats
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -115,16 +115,17 @@ CPPSRCS		= \
 		jswrapper.cpp \
 		jsxml.cpp \
 		prmjtime.cpp \
 		sharkctl.cpp \
 		ArgumentsObject.cpp \
 		DateTime.cpp \
 		Debugger.cpp \
 		GlobalObject.cpp \
+		Object.cpp \
 		ObjectImpl.cpp \
 		ScopeObject.cpp \
 		Stack.cpp \
 		String.cpp \
 		BytecodeCompiler.cpp \
 		BytecodeEmitter.cpp \
 		FoldConstants.cpp \
 		Intl.cpp \
new file mode 100644
--- /dev/null
+++ b/js/src/builtin/Object.cpp
@@ -0,0 +1,988 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sw=4 et tw=99 ft=cpp:
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/Util.h"
+
+#include "jscntxt.h"
+#include "jsobj.h"
+
+#include "builtin/Object.h"
+#include "frontend/Parser.h"
+#include "vm/StringBuffer.h"
+
+#include "jsfuninlines.h"
+#include "jsobjinlines.h"
+
+using namespace js;
+using namespace js::types;
+
+using js::frontend::IsIdentifier;
+using mozilla::ArrayLength;
+
+
+static bool
+DefineProperties(JSContext *cx, HandleObject obj, HandleObject props)
+{
+    AutoIdVector ids(cx);
+    AutoPropDescArrayRooter descs(cx);
+    if (!ReadPropertyDescriptors(cx, props, true, &ids, &descs))
+        return false;
+
+    bool dummy;
+    for (size_t i = 0, len = ids.length(); i < len; i++) {
+        if (!DefineProperty(cx, obj, Handle<jsid>::fromMarkedLocation(&ids[i]), descs[i], true, &dummy))
+            return false;
+    }
+
+    return true;
+}
+
+
+JSBool
+js::obj_construct(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    RootedObject obj(cx, NULL);
+    if (args.length() > 0) {
+        /* If argv[0] is null or undefined, obj comes back null. */
+        if (!js_ValueToObjectOrNull(cx, args[0], &obj))
+            return false;
+    }
+    if (!obj) {
+        /* Make an object whether this was called with 'new' or not. */
+        JS_ASSERT(!args.length() || args[0].isNullOrUndefined());
+        if (!NewObjectScriptedCall(cx, &obj))
+            return false;
+    }
+
+    args.rval().setObject(*obj);
+    return true;
+}
+
+/* ES5 15.2.4.7. */
+static JSBool
+obj_propertyIsEnumerable(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    /* Step 1. */
+    RootedId id(cx);
+    if (!ValueToId(cx, args.length() ? args[0] : UndefinedValue(), id.address()))
+        return false;
+
+    /* Step 2. */
+    RootedObject obj(cx, ToObject(cx, args.thisv()));
+    if (!obj)
+        return false;
+
+    /* Steps 3. */
+    RootedObject pobj(cx);
+    RootedShape prop(cx);
+    if (!JSObject::lookupGeneric(cx, obj, id, &pobj, &prop))
+        return false;
+
+    /* Step 4. */
+    if (!prop) {
+        args.rval().setBoolean(false);
+        return true;
+    }
+
+    if (pobj != obj) {
+        vp->setBoolean(false);
+        return true;
+    }
+
+    /* Step 5. */
+    unsigned attrs;
+    if (!JSObject::getGenericAttributes(cx, pobj, id, &attrs))
+        return false;
+
+    args.rval().setBoolean((attrs & JSPROP_ENUMERATE) != 0);
+    return true;
+}
+
+#if JS_HAS_TOSOURCE
+static JSBool
+obj_toSource(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    JS_CHECK_RECURSION(cx, return false);
+
+    RootedObject obj(cx, ToObject(cx, args.thisv()));
+    if (!obj)
+        return false;
+
+    /* If outermost, we need parentheses to be an expression, not a block. */
+    bool outermost = (cx->cycleDetectorSet.count() == 0);
+
+    AutoCycleDetector detector(cx, obj);
+    if (!detector.init())
+        return false;
+    if (detector.foundCycle()) {
+        JSString *str = js_NewStringCopyZ(cx, "{}");
+        if (!str)
+            return false;
+        args.rval().setString(str);
+        return true;
+    }
+
+    StringBuffer buf(cx);
+    if (outermost && !buf.append('('))
+        return false;
+    if (!buf.append('{'))
+        return false;
+
+    Value val[2];
+    PodArrayZero(val);
+    AutoArrayRooter tvr2(cx, ArrayLength(val), val);
+
+    JSString *gsop[2];
+    SkipRoot skipGsop(cx, &gsop, 2);
+
+    AutoIdVector idv(cx);
+    if (!GetPropertyNames(cx, obj, JSITER_OWNONLY, &idv))
+        return false;
+
+    bool comma = false;
+    for (size_t i = 0; i < idv.length(); ++i) {
+        RootedId id(cx, idv[i]);
+        RootedObject obj2(cx);
+        RootedShape shape(cx);
+        if (!JSObject::lookupGeneric(cx, obj, id, &obj2, &shape))
+            return false;
+
+        /*  Decide early whether we prefer get/set or old getter/setter syntax. */
+        int valcnt = 0;
+        if (shape) {
+            bool doGet = true;
+            if (obj2->isNative()) {
+                unsigned attrs = shape->attributes();
+                if (attrs & JSPROP_GETTER) {
+                    doGet = false;
+                    val[valcnt] = shape->getterValue();
+                    gsop[valcnt] = cx->names().get;
+                    valcnt++;
+                }
+                if (attrs & JSPROP_SETTER) {
+                    doGet = false;
+                    val[valcnt] = shape->setterValue();
+                    gsop[valcnt] = cx->names().set;
+                    valcnt++;
+                }
+            }
+            if (doGet) {
+                valcnt = 1;
+                gsop[0] = NULL;
+                MutableHandleValue vp = MutableHandleValue::fromMarkedLocation(&val[0]);
+                if (!JSObject::getGeneric(cx, obj, obj, id, vp))
+                    return false;
+            }
+        }
+
+        /* Convert id to a linear string. */
+        RawString s = ToString(cx, IdToValue(id));
+        if (!s)
+            return false;
+        Rooted<JSLinearString*> idstr(cx, s->ensureLinear(cx));
+        if (!idstr)
+            return false;
+
+        /*
+         * If id is a string that's not an identifier, or if it's a negative
+         * integer, then it must be quoted.
+         */
+        if (JSID_IS_ATOM(id)
+            ? !IsIdentifier(idstr)
+            : (!JSID_IS_INT(id) || JSID_TO_INT(id) < 0))
+        {
+            s = js_QuoteString(cx, idstr, jschar('\''));
+            if (!s || !(idstr = s->ensureLinear(cx)))
+                return false;
+        }
+
+        for (int j = 0; j < valcnt; j++) {
+            /*
+             * Censor an accessor descriptor getter or setter part if it's
+             * undefined.
+             */
+            if (gsop[j] && val[j].isUndefined())
+                continue;
+
+            /* Convert val[j] to its canonical source form. */
+            RootedString valstr(cx, js_ValueToSource(cx, val[j]));
+            if (!valstr)
+                return false;
+            const jschar *vchars = valstr->getChars(cx);
+            if (!vchars)
+                return false;
+            size_t vlength = valstr->length();
+
+            /*
+             * Remove '(function ' from the beginning of valstr and ')' from the
+             * end so that we can put "get" in front of the function definition.
+             */
+            if (gsop[j] && IsFunctionObject(val[j])) {
+                const jschar *start = vchars;
+                const jschar *end = vchars + vlength;
+
+                uint8_t parenChomp = 0;
+                if (vchars[0] == '(') {
+                    vchars++;
+                    parenChomp = 1;
+                }
+
+                /* Try to jump "function" keyword. */
+                if (vchars)
+                    vchars = js_strchr_limit(vchars, ' ', end);
+
+                /*
+                 * Jump over the function's name: it can't be encoded as part
+                 * of an ECMA getter or setter.
+                 */
+                if (vchars)
+                    vchars = js_strchr_limit(vchars, '(', end);
+
+                if (vchars) {
+                    if (*vchars == ' ')
+                        vchars++;
+                    vlength = end - vchars - parenChomp;
+                } else {
+                    gsop[j] = NULL;
+                    vchars = start;
+                }
+            }
+
+            if (comma && !buf.append(", "))
+                return false;
+            comma = true;
+
+            if (gsop[j])
+                if (!buf.append(gsop[j]) || !buf.append(' '))
+                    return false;
+
+            if (!buf.append(idstr))
+                return false;
+            if (!buf.append(gsop[j] ? ' ' : ':'))
+                return false;
+
+            if (!buf.append(vchars, vlength))
+                return false;
+        }
+    }
+
+    if (!buf.append('}'))
+        return false;
+    if (outermost && !buf.append(')'))
+        return false;
+
+    RawString str = buf.finishString();
+    if (!str)
+        return false;
+    args.rval().setString(str);
+    return true;
+}
+#endif /* JS_HAS_TOSOURCE */
+
+JSString *
+js::obj_toStringHelper(JSContext *cx, JSObject *obj)
+{
+    if (obj->isProxy())
+        return Proxy::obj_toString(cx, obj);
+
+    StringBuffer sb(cx);
+    const char *className = obj->getClass()->name;
+    if (!sb.append("[object ") || !sb.appendInflated(className, strlen(className)) ||
+        !sb.append("]"))
+    {
+        return NULL;
+    }
+    return sb.finishString();
+}
+
+/* ES5 15.2.4.2.  Note steps 1 and 2 are errata. */
+static JSBool
+obj_toString(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    /* Step 1. */
+    if (args.thisv().isUndefined()) {
+        args.rval().setString(cx->names().objectUndefined);
+        return true;
+    }
+
+    /* Step 2. */
+    if (args.thisv().isNull()) {
+        args.rval().setString(cx->names().objectNull);
+        return true;
+    }
+
+    /* Step 3. */
+    JSObject *obj = ToObject(cx, args.thisv());
+    if (!obj)
+        return false;
+
+    /* Steps 4-5. */
+    JSString *str = js::obj_toStringHelper(cx, obj);
+    if (!str)
+        return false;
+    args.rval().setString(str);
+    return true;
+}
+
+/* ES5 15.2.4.3. */
+static JSBool
+obj_toLocaleString(JSContext *cx, unsigned argc, Value *vp)
+{
+    JS_CHECK_RECURSION(cx, return false);
+
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    /* Step 1. */
+    JSObject *obj = ToObject(cx, args.thisv());
+    if (!obj)
+        return false;
+
+    /* Steps 2-4. */
+    RootedId id(cx, NameToId(cx->names().toString));
+    return obj->callMethod(cx, id, 0, NULL, args.rval());
+}
+
+static JSBool
+obj_valueOf(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    JSObject *obj = ToObject(cx, args.thisv());
+    if (!obj)
+        return false;
+    args.rval().setObject(*obj);
+    return true;
+}
+
+#if OLD_GETTER_SETTER_METHODS
+
+enum DefineType { Getter, Setter };
+
+template<DefineType Type>
+static bool
+DefineAccessor(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    if (!BoxNonStrictThis(cx, args))
+        return false;
+
+    if (args.length() < 2 || !js_IsCallable(args[1])) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
+                             JSMSG_BAD_GETTER_OR_SETTER,
+                             Type == Getter ? js_getter_str : js_setter_str);
+        return false;
+    }
+
+    RootedId id(cx);
+    if (!ValueToId(cx, args[0], id.address()))
+        return false;
+
+    RootedObject descObj(cx, NewBuiltinClassInstance(cx, &ObjectClass));
+    if (!descObj)
+        return false;
+
+    JSAtomState &names = cx->names();
+    RootedValue trueVal(cx, BooleanValue(true));
+
+    /* enumerable: true */
+    if (!JSObject::defineProperty(cx, descObj, names.enumerable, trueVal))
+        return false;
+
+    /* configurable: true */
+    if (!JSObject::defineProperty(cx, descObj, names.configurable, trueVal))
+        return false;
+
+    /* enumerable: true */
+    PropertyName *acc = (Type == Getter) ? names.get : names.set;
+    RootedValue accessorVal(cx, args[1]);
+    if (!JSObject::defineProperty(cx, descObj, acc, accessorVal))
+        return false;
+
+    RootedObject thisObj(cx, &args.thisv().toObject());
+
+    JSBool dummy;
+    if (!js_DefineOwnProperty(cx, thisObj, id, ObjectValue(*descObj), &dummy))
+        return false;
+
+    args.rval().setUndefined();
+    return true;
+}
+
+JS_FRIEND_API(JSBool)
+js::obj_defineGetter(JSContext *cx, unsigned argc, Value *vp)
+{
+    return DefineAccessor<Getter>(cx, argc, vp);
+}
+
+JS_FRIEND_API(JSBool)
+js::obj_defineSetter(JSContext *cx, unsigned argc, Value *vp)
+{
+    return DefineAccessor<Setter>(cx, argc, vp);
+}
+
+static JSBool
+obj_lookupGetter(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    RootedId id(cx);
+    if (!ValueToId(cx, args.length() ? args[0] : UndefinedValue(), id.address()))
+        return JS_FALSE;
+    RootedObject obj(cx, ToObject(cx, args.thisv()));
+    if (!obj)
+        return JS_FALSE;
+    if (obj->isProxy()) {
+        // The vanilla getter lookup code below requires that the object is
+        // native. Handle proxies separately.
+        args.rval().setUndefined();
+        AutoPropertyDescriptorRooter desc(cx);
+        if (!Proxy::getPropertyDescriptor(cx, obj, id, false, &desc))
+            return JS_FALSE;
+        if (desc.obj && (desc.attrs & JSPROP_GETTER) && desc.getter)
+            args.rval().set(CastAsObjectJsval(desc.getter));
+        return JS_TRUE;
+    }
+    RootedObject pobj(cx);
+    RootedShape shape(cx);
+    if (!JSObject::lookupGeneric(cx, obj, id, &pobj, &shape))
+        return JS_FALSE;
+    args.rval().setUndefined();
+    if (shape) {
+        if (pobj->isNative()) {
+            if (shape->hasGetterValue())
+                args.rval().set(shape->getterValue());
+        }
+    }
+    return JS_TRUE;
+}
+
+static JSBool
+obj_lookupSetter(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    RootedId id(cx);
+    if (!ValueToId(cx, args.length() ? args[0] : UndefinedValue(), id.address()))
+        return JS_FALSE;
+    RootedObject obj(cx, ToObject(cx, args.thisv()));
+    if (!obj)
+        return JS_FALSE;
+    if (obj->isProxy()) {
+        // The vanilla setter lookup code below requires that the object is
+        // native. Handle proxies separately.
+        args.rval().setUndefined();
+        AutoPropertyDescriptorRooter desc(cx);
+        if (!Proxy::getPropertyDescriptor(cx, obj, id, false, &desc))
+            return JS_FALSE;
+        if (desc.obj && (desc.attrs & JSPROP_SETTER) && desc.setter)
+            args.rval().set(CastAsObjectJsval(desc.setter));
+        return JS_TRUE;
+    }
+    RootedObject pobj(cx);
+    RootedShape shape(cx);
+    if (!JSObject::lookupGeneric(cx, obj, id, &pobj, &shape))
+        return JS_FALSE;
+    args.rval().setUndefined();
+    if (shape) {
+        if (pobj->isNative()) {
+            if (shape->hasSetterValue())
+                args.rval().set(shape->setterValue());
+        }
+    }
+    return JS_TRUE;
+}
+#endif /* OLD_GETTER_SETTER_METHODS */
+
+/* ES5 15.2.3.2. */
+JSBool
+obj_getPrototypeOf(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    /* Step 1. */
+    if (args.length() == 0) {
+        js_ReportMissingArg(cx, args.calleev(), 0);
+        return false;
+    }
+
+    if (args[0].isPrimitive()) {
+        RootedValue val(cx, args[0]);
+        char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, val, NullPtr());
+        if (!bytes)
+            return false;
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
+                             JSMSG_UNEXPECTED_TYPE, bytes, "not an object");
+        js_free(bytes);
+        return false;
+    }
+
+    /* Step 2. */
+
+    /*
+     * Implement [[Prototype]]-getting -- particularly across compartment
+     * boundaries -- by calling a cached __proto__ getter function.
+     */
+    InvokeArgsGuard nested;
+    if (!cx->stack.pushInvokeArgs(cx, 0, &nested))
+        return false;
+    nested.setCallee(cx->global()->protoGetter());
+    nested.setThis(args[0]);
+    if (!Invoke(cx, nested))
+        return false;
+    args.rval().set(nested.rval());
+    return true;
+}
+
+#if JS_HAS_OBJ_WATCHPOINT
+
+static JSBool
+obj_watch_handler(JSContext *cx, JSObject *obj_, jsid id_, jsval old,
+                  jsval *nvp, void *closure)
+{
+    RootedObject obj(cx, obj_);
+    RootedId id(cx, id_);
+
+    /* Avoid recursion on (obj, id) already being watched on cx. */
+    AutoResolving resolving(cx, obj, id, AutoResolving::WATCH);
+    if (resolving.alreadyStarted())
+        return true;
+
+    JSObject *callable = (JSObject *)closure;
+    Value argv[] = { IdToValue(id), old, *nvp };
+    return Invoke(cx, ObjectValue(*obj), ObjectOrNullValue(callable), ArrayLength(argv), argv, nvp);
+}
+
+static JSBool
+obj_watch(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    if (args.length() <= 1) {
+        js_ReportMissingArg(cx, args.calleev(), 1);
+        return false;
+    }
+
+    RootedObject callable(cx, ValueToCallable(cx, &args[1]));
+    if (!callable)
+        return false;
+
+    RootedId propid(cx);
+    if (!ValueToId(cx, args[0], propid.address()))
+        return false;
+
+    RootedObject obj(cx, ToObject(cx, args.thisv()));
+    if (!obj)
+        return false;
+
+    RootedValue tmp(cx);
+    unsigned attrs;
+    if (!CheckAccess(cx, obj, propid, JSACC_WATCH, &tmp, &attrs))
+        return false;
+
+    args.rval().setUndefined();
+
+    if (obj->isDenseArray() && !JSObject::makeDenseArraySlow(cx, obj))
+        return false;
+    return JS_SetWatchPoint(cx, obj, propid, obj_watch_handler, callable);
+}
+
+static JSBool
+obj_unwatch(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    RootedObject obj(cx, ToObject(cx, args.thisv()));
+    if (!obj)
+        return false;
+    args.rval().setUndefined();
+    jsid id;
+    if (argc != 0) {
+        if (!ValueToId(cx, args[0], &id))
+            return false;
+    } else {
+        id = JSID_VOID;
+    }
+    return JS_ClearWatchPoint(cx, obj, id, NULL, NULL);
+}
+
+#endif /* JS_HAS_OBJ_WATCHPOINT */
+
+/* ECMA 15.2.4.5. */
+static JSBool
+obj_hasOwnProperty(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    /* Step 1. */
+    RootedId id(cx);
+    if (!ValueToId(cx, args.length() ? args[0] : UndefinedValue(), id.address()))
+        return false;
+
+    /* Step 2. */
+    RootedObject obj(cx, ToObject(cx, args.thisv()));
+    if (!obj)
+        return false;
+
+    /* Non-standard code for proxies. */
+    RootedObject obj2(cx);
+    RootedShape prop(cx);
+    if (obj->isProxy()) {
+        bool has;
+        if (!Proxy::hasOwn(cx, obj, id, &has))
+            return false;
+        args.rval().setBoolean(has);
+        return true;
+    }
+
+    /* Step 3. */
+    if (!js_HasOwnProperty(cx, obj->getOps()->lookupGeneric, obj, id, &obj2, &prop))
+        return false;
+    /* Step 4,5. */
+    args.rval().setBoolean(!!prop);
+    return true;
+}
+
+/* ES5 15.2.4.6. */
+static JSBool
+obj_isPrototypeOf(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    /* Step 1. */
+    if (args.length() < 1 || !args[0].isObject()) {
+        args.rval().setBoolean(false);
+        return true;
+    }
+
+    /* Step 2. */
+    RootedObject obj(cx, ToObject(cx, args.thisv()));
+    if (!obj)
+        return false;
+
+    /* Step 3. */
+    bool isDelegate;
+    if (!IsDelegate(cx, obj, args[0], &isDelegate))
+        return false;
+    args.rval().setBoolean(isDelegate);
+    return true;
+}
+
+/* ES5 15.2.3.5: Object.create(O [, Properties]) */
+static JSBool
+obj_create(JSContext *cx, unsigned argc, Value *vp)
+{
+    if (argc == 0) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
+                             "Object.create", "0", "s");
+        return false;
+    }
+
+    CallArgs args = CallArgsFromVp(argc, vp);
+    RootedValue v(cx, args[0]);
+    if (!v.isObjectOrNull()) {
+        char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NullPtr());
+        if (!bytes)
+            return false;
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNEXPECTED_TYPE,
+                             bytes, "not an object or null");
+        js_free(bytes);
+        return false;
+    }
+
+    JSObject *proto = v.toObjectOrNull();
+#if JS_HAS_XML_SUPPORT
+    if (proto && proto->isXML()) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_XML_PROTO_FORBIDDEN);
+        return false;
+    }
+#endif
+
+    /*
+     * Use the callee's global as the parent of the new object to avoid dynamic
+     * scoping (i.e., using the caller's global).
+     */
+    RootedObject obj(cx, NewObjectWithGivenProto(cx, &ObjectClass, proto, &args.callee().global()));
+    if (!obj)
+        return false;
+
+    /* Don't track types or array-ness for objects created here. */
+    MarkTypeObjectUnknownProperties(cx, obj->type());
+
+    /* 15.2.3.5 step 4. */
+    if (args.hasDefined(1)) {
+        if (args[1].isPrimitive()) {
+            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT);
+            return false;
+        }
+
+        RootedObject props(cx, &args[1].toObject());
+        if (!DefineProperties(cx, obj, props))
+            return false;
+    }
+
+    /* 5. Return obj. */
+    args.rval().setObject(*obj);
+    return true;
+}
+
+static JSBool
+obj_getOwnPropertyDescriptor(JSContext *cx, unsigned argc, Value *vp)
+{
+    RootedObject obj(cx);
+    if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.getOwnPropertyDescriptor", &obj))
+        return JS_FALSE;
+    RootedId id(cx);
+    if (!ValueToId(cx, argc >= 2 ? vp[3] : UndefinedValue(), id.address()))
+        return JS_FALSE;
+    return GetOwnPropertyDescriptor(cx, obj, id, vp);
+}
+
+static JSBool
+obj_keys(JSContext *cx, unsigned argc, Value *vp)
+{
+    RootedObject obj(cx);
+    if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.keys", &obj))
+        return false;
+
+    AutoIdVector props(cx);
+    if (!GetPropertyNames(cx, obj, JSITER_OWNONLY, &props))
+        return false;
+
+    AutoValueVector vals(cx);
+    if (!vals.reserve(props.length()))
+        return false;
+    for (size_t i = 0, len = props.length(); i < len; i++) {
+        jsid id = props[i];
+        if (JSID_IS_STRING(id)) {
+            vals.infallibleAppend(StringValue(JSID_TO_STRING(id)));
+        } else if (JSID_IS_INT(id)) {
+            JSString *str = Int32ToString(cx, JSID_TO_INT(id));
+            if (!str)
+                return false;
+            vals.infallibleAppend(StringValue(str));
+        } else {
+            JS_ASSERT(JSID_IS_OBJECT(id));
+        }
+    }
+
+    JS_ASSERT(props.length() <= UINT32_MAX);
+    JSObject *aobj = NewDenseCopiedArray(cx, uint32_t(vals.length()), vals.begin());
+    if (!aobj)
+        return false;
+    vp->setObject(*aobj);
+
+    return true;
+}
+
+static JSBool
+obj_getOwnPropertyNames(JSContext *cx, unsigned argc, Value *vp)
+{
+    RootedObject obj(cx);
+    if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.getOwnPropertyNames", &obj))
+        return false;
+
+    AutoIdVector keys(cx);
+    if (!GetPropertyNames(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN, &keys))
+        return false;
+
+    AutoValueVector vals(cx);
+    if (!vals.resize(keys.length()))
+        return false;
+
+    for (size_t i = 0, len = keys.length(); i < len; i++) {
+         jsid id = keys[i];
+         if (JSID_IS_INT(id)) {
+             JSString *str = Int32ToString(cx, JSID_TO_INT(id));
+             if (!str)
+                 return false;
+             vals[i].setString(str);
+         } else if (JSID_IS_ATOM(id)) {
+             vals[i].setString(JSID_TO_STRING(id));
+         } else {
+             vals[i].setObject(*JSID_TO_OBJECT(id));
+         }
+    }
+
+    JSObject *aobj = NewDenseCopiedArray(cx, vals.length(), vals.begin());
+    if (!aobj)
+        return false;
+
+    vp->setObject(*aobj);
+    return true;
+}
+
+/* ES5 15.2.3.6: Object.defineProperty(O, P, Attributes) */
+static JSBool
+obj_defineProperty(JSContext *cx, unsigned argc, Value *vp)
+{
+    RootedObject obj(cx);
+    if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.defineProperty", &obj))
+        return false;
+
+    RootedId id(cx);
+    if (!ValueToId(cx, argc >= 2 ? vp[3] : UndefinedValue(), id.address()))
+        return JS_FALSE;
+
+    const Value descval = argc >= 3 ? vp[4] : UndefinedValue();
+
+    JSBool junk;
+    if (!js_DefineOwnProperty(cx, obj, id, descval, &junk))
+        return false;
+
+    vp->setObject(*obj);
+    return true;
+}
+
+/* ES5 15.2.3.7: Object.defineProperties(O, Properties) */
+static JSBool
+obj_defineProperties(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    /* Steps 1 and 7. */
+    RootedObject obj(cx);
+    if (!GetFirstArgumentAsObject(cx, args.length(), vp, "Object.defineProperties", &obj))
+        return false;
+    args.rval().setObject(*obj);
+
+    /* Step 2. */
+    if (args.length() < 2) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
+                             "Object.defineProperties", "0", "s");
+        return false;
+    }
+    RootedValue val(cx, args[1]);
+    RootedObject props(cx, ToObject(cx, val));
+    if (!props)
+        return false;
+
+    /* Steps 3-6. */
+    return DefineProperties(cx, obj, props);
+}
+
+static JSBool
+obj_isExtensible(JSContext *cx, unsigned argc, Value *vp)
+{
+    RootedObject obj(cx);
+    if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.isExtensible", &obj))
+        return false;
+
+    vp->setBoolean(obj->isExtensible());
+    return true;
+}
+
+static JSBool
+obj_preventExtensions(JSContext *cx, unsigned argc, Value *vp)
+{
+    RootedObject obj(cx);
+    if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.preventExtensions", &obj))
+        return false;
+
+    vp->setObject(*obj);
+    if (!obj->isExtensible())
+        return true;
+
+    return obj->preventExtensions(cx);
+}
+
+static JSBool
+obj_freeze(JSContext *cx, unsigned argc, Value *vp)
+{
+    RootedObject obj(cx);
+    if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.freeze", &obj))
+        return false;
+
+    vp->setObject(*obj);
+
+    return JSObject::freeze(cx, obj);
+}
+
+static JSBool
+obj_isFrozen(JSContext *cx, unsigned argc, Value *vp)
+{
+    RootedObject obj(cx);
+    if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.preventExtensions", &obj))
+        return false;
+
+    bool frozen;
+    if (!JSObject::isFrozen(cx, obj, &frozen))
+        return false;
+    vp->setBoolean(frozen);
+    return true;
+}
+
+static JSBool
+obj_seal(JSContext *cx, unsigned argc, Value *vp)
+{
+    RootedObject obj(cx);
+    if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.seal", &obj))
+        return false;
+
+    vp->setObject(*obj);
+
+    return JSObject::seal(cx, obj);
+}
+
+static JSBool
+obj_isSealed(JSContext *cx, unsigned argc, Value *vp)
+{
+    RootedObject obj(cx);
+    if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.isSealed", &obj))
+        return false;
+
+    bool sealed;
+    if (!JSObject::isSealed(cx, obj, &sealed))
+        return false;
+    vp->setBoolean(sealed);
+    return true;
+}
+
+JSFunctionSpec js::object_methods[] = {
+#if JS_HAS_TOSOURCE
+    JS_FN(js_toSource_str,             obj_toSource,                0,0),
+#endif
+    JS_FN(js_toString_str,             obj_toString,                0,0),
+    JS_FN(js_toLocaleString_str,       obj_toLocaleString,          0,0),
+    JS_FN(js_valueOf_str,              obj_valueOf,                 0,0),
+#if JS_HAS_OBJ_WATCHPOINT
+    JS_FN(js_watch_str,                obj_watch,                   2,0),
+    JS_FN(js_unwatch_str,              obj_unwatch,                 1,0),
+#endif
+    JS_FN(js_hasOwnProperty_str,       obj_hasOwnProperty,          1,0),
+    JS_FN(js_isPrototypeOf_str,        obj_isPrototypeOf,           1,0),
+    JS_FN(js_propertyIsEnumerable_str, obj_propertyIsEnumerable,    1,0),
+#if OLD_GETTER_SETTER_METHODS
+    JS_FN(js_defineGetter_str,         js::obj_defineGetter,        2,0),
+    JS_FN(js_defineSetter_str,         js::obj_defineSetter,        2,0),
+    JS_FN(js_lookupGetter_str,         obj_lookupGetter,            1,0),
+    JS_FN(js_lookupSetter_str,         obj_lookupSetter,            1,0),
+#endif
+    JS_FS_END
+};
+
+JSFunctionSpec js::object_static_methods[] = {
+    JS_FN("getPrototypeOf",            obj_getPrototypeOf,          1,0),
+    JS_FN("getOwnPropertyDescriptor",  obj_getOwnPropertyDescriptor,2,0),
+    JS_FN("keys",                      obj_keys,                    1,0),
+    JS_FN("defineProperty",            obj_defineProperty,          3,0),
+    JS_FN("defineProperties",          obj_defineProperties,        2,0),
+    JS_FN("create",                    obj_create,                  2,0),
+    JS_FN("getOwnPropertyNames",       obj_getOwnPropertyNames,     1,0),
+    JS_FN("isExtensible",              obj_isExtensible,            1,0),
+    JS_FN("preventExtensions",         obj_preventExtensions,       1,0),
+    JS_FN("freeze",                    obj_freeze,                  1,0),
+    JS_FN("isFrozen",                  obj_isFrozen,                1,0),
+    JS_FN("seal",                      obj_seal,                    1,0),
+    JS_FN("isSealed",                  obj_isSealed,                1,0),
+    JS_FS_END
+};
new file mode 100644
--- /dev/null
+++ b/js/src/builtin/Object.h
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sw=4 et tw=99 ft=cpp:
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef Object_h___
+#define Object_h___
+
+#include "jsobj.h"
+
+namespace js {
+
+extern JSFunctionSpec object_methods[];
+extern JSFunctionSpec object_static_methods[];
+
+/* Object constructor native. Exposed only so the JIT can know its address. */
+extern JSBool
+obj_construct(JSContext *cx, unsigned argc, js::Value *vp);
+
+extern JSString *
+obj_toStringHelper(JSContext *cx, JSObject *obj);
+
+} /* namespace js */
+
+#endif
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -1325,16 +1325,38 @@ JS_SetCTypesCallbacks(JSRawObject ctypes
   JS_ASSERT(callbacks);
   JS_ASSERT(IsCTypesGlobal(ctypesObj));
 
   // Set the callbacks on a reserved slot.
   JS_SetReservedSlot(ctypesObj, SLOT_CALLBACKS, PRIVATE_TO_JSVAL(callbacks));
 }
 
 namespace js {
+
+JS_FRIEND_API(size_t)
+SizeOfDataIfCDataObject(JSMallocSizeOfFun mallocSizeOf, JSObject *obj)
+{
+    if (!CData::IsCData(obj))
+        return 0;
+
+    size_t n = 0;
+    jsval slot = JS_GetReservedSlot(obj, ctypes::SLOT_OWNS);
+    if (!JSVAL_IS_VOID(slot)) {
+        JSBool owns = JSVAL_TO_BOOLEAN(slot);
+        slot = JS_GetReservedSlot(obj, ctypes::SLOT_DATA);
+        if (!JSVAL_IS_VOID(slot)) {
+            char** buffer = static_cast<char**>(JSVAL_TO_PRIVATE(slot));
+            n += mallocSizeOf(buffer);
+            if (owns)
+                n += mallocSizeOf(*buffer);
+        }
+    }
+    return n;
+}
+
 namespace ctypes {
 
 /*******************************************************************************
 ** Type conversion functions
 *******************************************************************************/
 
 // Enforce some sanity checks on type widths and properties.
 // Where the architecture is 64-bit, make sure it's LP64 or LLP64. (ctypes.int
--- a/js/src/ds/SplayTree.h
+++ b/js/src/ds/SplayTree.h
@@ -131,16 +131,22 @@ class SplayTree
             swapChild->parent = swap->parent;
 
         root->item = swap->item;
         freeNode(swap);
 
         checkCoherency(root, NULL);
     }
 
+    template <class Op>
+    void forEach(Op op)
+    {
+        forEachInner(op, root);
+    }
+
   private:
 
     Node *lookup(const T &v)
     {
         JS_ASSERT(root);
         Node *node = root, *parent;
         do {
             parent = node;
@@ -229,16 +235,27 @@ class SplayTree
                 grandparent->left = node;
             else
                 grandparent->right = node;
         } else {
             root = node;
         }
     }
 
+    template <class Op>
+    void forEachInner(Op op, Node *node)
+    {
+        if (!node)
+            return;
+
+        forEachInner(op, node->left);
+        op(node->item);
+        forEachInner(op, node->right);
+    }
+
     Node *checkCoherency(Node *node, Node *minimum)
     {
 #ifdef DEBUG
         if (!node) {
             JS_ASSERT(!root);
             return NULL;
         }
         JS_ASSERT_IF(!node->parent, node == root);
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -94,16 +94,30 @@ static void MarkChildren(JSTracer *trc, 
 static void MarkChildren(JSTracer *trc, JSXML *xml);
 #endif
 
 } /* namespace gc */
 } /* namespace js */
 
 /*** Object Marking ***/
 
+#ifdef DEBUG
+template<typename T>
+static inline bool
+IsThingPoisoned(T *thing)
+{
+    const uint8_t pb = JS_FREE_PATTERN;
+    const uint32_t pw = pb | (pb << 8) | (pb << 16) | (pb << 24);
+    JS_STATIC_ASSERT(sizeof(T) >= sizeof(FreeSpan) + sizeof(uint32_t));
+    uint32_t *p =
+        reinterpret_cast<uint32_t *>(reinterpret_cast<FreeSpan *>(thing) + 1);
+    return *p == pw;
+}
+#endif
+
 template<typename T>
 static inline void
 CheckMarkedThing(JSTracer *trc, T *thing)
 {
     JS_ASSERT(trc);
     JS_ASSERT(thing);
     JS_ASSERT(thing->compartment());
     JS_ASSERT(thing->compartment()->rt == trc->runtime);
@@ -124,16 +138,18 @@ CheckMarkedThing(JSTracer *trc, T *thing
 
     JS_ASSERT_IF(rt->gcStrictCompartmentChecking,
                  thing->compartment()->isCollecting() ||
                  thing->compartment() == rt->atomsCompartment);
 
     JS_ASSERT_IF(IS_GC_MARKING_TRACER(trc) && ((GCMarker *)trc)->getMarkColor() == GRAY,
                  thing->compartment()->isGCMarkingGray() ||
                  thing->compartment() == rt->atomsCompartment);
+
+    JS_ASSERT(!IsThingPoisoned(thing));
 }
 
 static GCMarker *
 AsGCMarker(JSTracer *trc)
 {
     JS_ASSERT(IS_GC_MARKING_TRACER(trc));
     return static_cast<GCMarker *>(trc);
 }
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -754,19 +754,23 @@ js::gc::MarkRuntime(JSTracer *trc, bool 
     for (ContextIter acx(rt); !acx.done(); acx.next())
         acx->mark(trc);
 
     /* We can't use GCCompartmentsIter if we're called from TraceRuntime. */
     for (CompartmentsIter c(rt); !c.done(); c.next()) {
         if (IS_GC_MARKING_TRACER(trc) && !c->isCollecting())
             continue;
 
-        if ((c->activeAnalysis || c->isPreservingCode()) && IS_GC_MARKING_TRACER(trc)) {
-            gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_MARK_TYPES);
-            c->markTypes(trc);
+        if (IS_GC_MARKING_TRACER(trc)) {
+            if ((c->activeAnalysis || c->isPreservingCode())) {
+                gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_MARK_TYPES);
+                c->markTypes(trc);
+            } else {
+                c->gcTypesMarked = false;
+            }
         }
 
         /* During a GC, these are treated as weak pointers. */
         if (!IS_GC_MARKING_TRACER(trc)) {
             if (c->watchpointMap)
                 c->watchpointMap->markAll(trc);
         }
 
--- a/js/src/gc/Statistics.cpp
+++ b/js/src/gc/Statistics.cpp
@@ -280,16 +280,17 @@ static PhaseInfo phases[] = {
     { PHASE_MARK_DISCARD_CODE, "Mark Discard Code", PHASE_NO_PARENT },
     { PHASE_PURGE, "Purge", PHASE_NO_PARENT },
     { PHASE_MARK, "Mark", PHASE_NO_PARENT },
     { PHASE_MARK_ROOTS, "Mark Roots", PHASE_MARK },
     { PHASE_MARK_TYPES, "Mark Types", PHASE_MARK_ROOTS },
     { PHASE_MARK_DELAYED, "Mark Delayed", PHASE_MARK },
     { PHASE_SWEEP, "Sweep", PHASE_NO_PARENT },
     { PHASE_SWEEP_MARK, "Mark During Sweeping", PHASE_SWEEP },
+    { PHASE_SWEEP_MARK_TYPES, "Mark Types During Sweeping", PHASE_SWEEP_MARK },
     { PHASE_SWEEP_MARK_DELAYED, "Mark Delayed During Sweeping", PHASE_SWEEP_MARK },
     { PHASE_SWEEP_MARK_INCOMING_BLACK, "Mark Incoming Black Pointers", PHASE_SWEEP_MARK },
     { PHASE_SWEEP_MARK_WEAK, "Mark Weak", PHASE_SWEEP_MARK },
     { PHASE_SWEEP_MARK_INCOMING_GRAY, "Mark Incoming Gray Pointers", PHASE_SWEEP_MARK },
     { PHASE_SWEEP_MARK_GRAY, "Mark Gray", PHASE_SWEEP_MARK },
     { PHASE_SWEEP_MARK_GRAY_WEAK, "Mark Gray and Weak", PHASE_SWEEP_MARK },
     { PHASE_FINALIZE_START, "Finalize Start Callback", PHASE_SWEEP },
     { PHASE_SWEEP_ATOMS, "Sweep Atoms", PHASE_SWEEP },
--- a/js/src/gc/Statistics.h
+++ b/js/src/gc/Statistics.h
@@ -27,16 +27,17 @@ enum Phase {
     PHASE_MARK_DISCARD_CODE,
     PHASE_PURGE,
     PHASE_MARK,
     PHASE_MARK_ROOTS,
     PHASE_MARK_TYPES,
     PHASE_MARK_DELAYED,
     PHASE_SWEEP,
     PHASE_SWEEP_MARK,
+    PHASE_SWEEP_MARK_TYPES,
     PHASE_SWEEP_MARK_DELAYED,
     PHASE_SWEEP_MARK_INCOMING_BLACK,
     PHASE_SWEEP_MARK_WEAK,
     PHASE_SWEEP_MARK_INCOMING_GRAY,
     PHASE_SWEEP_MARK_GRAY,
     PHASE_SWEEP_MARK_GRAY_WEAK,
     PHASE_FINALIZE_START,
     PHASE_SWEEP_ATOMS,
--- a/js/src/ion/BacktrackingAllocator.cpp
+++ b/js/src/ion/BacktrackingAllocator.cpp
@@ -113,57 +113,73 @@ BacktrackingAllocator::go()
     IonSpew(IonSpew_RegAlloc, "Liveness analysis complete");
 
     if (IonSpewEnabled(IonSpew_RegAlloc))
         dumpLiveness();
 
     if (!init())
         return false;
 
-    if (!queuedIntervals.reserve(graph.numVirtualRegisters() * 3 / 2))
+    if (!allocationQueue.reserve(graph.numVirtualRegisters() * 3 / 2))
         return false;
 
     if (!groupAndQueueRegisters())
         return false;
 
     if (IonSpewEnabled(IonSpew_RegAlloc))
         dumpRegisterGroups();
 
     // Allocate, spill and split register intervals until finished.
-    while (!queuedIntervals.empty()) {
+    while (!allocationQueue.empty()) {
         if (mir->shouldCancel("Backtracking Allocation"))
             return false;
 
-        LiveInterval *interval = queuedIntervals.removeHighest().interval;
-        if (!processInterval(interval))
+        QueueItem item = allocationQueue.removeHighest();
+        if (item.interval ? !processInterval(item.interval) : !processGroup(item.group))
             return false;
     }
 
     if (IonSpewEnabled(IonSpew_RegAlloc))
         dumpAllocations();
 
     return resolveControlFlow() && reifyAllocations();
 }
 
 static bool
-LifetimesMightOverlap(BacktrackingVirtualRegister *reg0, BacktrackingVirtualRegister *reg1)
+LifetimesOverlap(BacktrackingVirtualRegister *reg0, BacktrackingVirtualRegister *reg1)
 {
-    // No fine grained testing, just see if there is a possibility of overlap.
-    CodePosition start0 = reg0->getFirstInterval()->start();
-    CodePosition start1 = reg1->getFirstInterval()->start();
-    CodePosition end0 = reg0->lastInterval()->end();
-    CodePosition end1 = reg1->lastInterval()->end();
-    return (end0 > start1) && (end1 > start0);
+    // Registers may have been eagerly split in two, see tryGroupReusedRegister.
+    // In such cases, only consider the first interval.
+    JS_ASSERT(reg0->numIntervals() <= 2 && reg1->numIntervals() <= 2);
+
+    LiveInterval *interval0 = reg0->getInterval(0), *interval1 = reg1->getInterval(0);
+
+    // Interval ranges are sorted in reverse order. The lifetimes overlap if
+    // any of their ranges overlap.
+    size_t index0 = 0, index1 = 0;
+    while (index0 < interval0->numRanges() && index1 < interval1->numRanges()) {
+        const LiveInterval::Range
+            *range0 = interval0->getRange(index0),
+            *range1 = interval1->getRange(index1);
+        if (range0->from >= range1->to)
+            index0++;
+        else if (range1->from >= range0->to)
+            index1++;
+        else
+            return true;
+    }
+
+    return false;
 }
 
 bool
 BacktrackingAllocator::canAddToGroup(VirtualRegisterGroup *group, BacktrackingVirtualRegister *reg)
 {
     for (size_t i = 0; i < group->registers.length(); i++) {
-        if (LifetimesMightOverlap(reg, &vregs[group->registers[i]]))
+        if (LifetimesOverlap(reg, &vregs[group->registers[i]]))
             return false;
     }
     return true;
 }
 
 bool
 BacktrackingAllocator::tryGroupRegisters(uint32_t vreg0, uint32_t vreg1)
 {
@@ -202,81 +218,191 @@ BacktrackingAllocator::tryGroupRegisters
         if (!canAddToGroup(group0, reg1))
             return true;
         if (!group0->registers.append(vreg1))
             return false;
         reg1->setGroup(group0);
         return true;
     }
 
-    if (LifetimesMightOverlap(reg0, reg1))
+    if (LifetimesOverlap(reg0, reg1))
         return true;
 
     VirtualRegisterGroup *group = new VirtualRegisterGroup();
     if (!group->registers.append(vreg0) || !group->registers.append(vreg1))
         return false;
 
     reg0->setGroup(group);
     reg1->setGroup(group);
     return true;
 }
 
 bool
+BacktrackingAllocator::tryGroupReusedRegister(uint32_t def, uint32_t use)
+{
+    BacktrackingVirtualRegister &reg = vregs[def], &usedReg = vregs[use];
+
+    // reg is a vreg which reuses its input usedReg for its output physical
+    // register. Try to group reg with usedReg if at all possible, as avoiding
+    // copies before reg's instruction is crucial for the quality of the
+    // generated code (MUST_REUSE_INPUT is used by all arithmetic instructions
+    // on x86/x64).
+
+    if (reg.intervalFor(inputOf(reg.ins()))) {
+        JS_ASSERT(reg.isTemp());
+        reg.setMustCopyInput();
+        return true;
+    }
+
+    if (!usedReg.intervalFor(outputOf(reg.ins()))) {
+        // The input is not live after the instruction, either in a safepoint
+        // for the instruction or in subsequent code. The input and output
+        // can thus be in the same group.
+        return tryGroupRegisters(use, def);
+    }
+
+    // The input is live afterwards, either in future instructions or in a
+    // safepoint for the reusing instruction. This is impossible to satisfy
+    // without copying the input.
+    //
+    // It may or may not be better to split the interval at the point of the
+    // definition, which may permit grouping. One case where it is definitely
+    // better to split is if the input never has any register uses after the
+    // instruction. Handle this splitting eagerly.
+
+    if (usedReg.numIntervals() != 1 ||
+        (usedReg.def()->isPreset() && !usedReg.def()->output()->isRegister())) {
+        reg.setMustCopyInput();
+        return true;
+    }
+    LiveInterval *interval = usedReg.getInterval(0);
+    LBlock *block = insData[reg.ins()].block();
+
+    // The input's lifetime must end within the same block as the definition,
+    // otherwise it could live on in phis elsewhere.
+    if (interval->end() > outputOf(block->lastId())) {
+        reg.setMustCopyInput();
+        return true;
+    }
+
+    for (UsePositionIterator iter = interval->usesBegin(); iter != interval->usesEnd(); iter++) {
+        if (iter->pos <= inputOf(reg.ins()))
+            continue;
+
+        LUse *use = iter->use;
+        if (FindReusingDefinition(insData[iter->pos].ins(), use)) {
+            reg.setMustCopyInput();
+            return true;
+        }
+        if (use->policy() != LUse::ANY && use->policy() != LUse::KEEPALIVE) {
+            reg.setMustCopyInput();
+            return true;
+        }
+    }
+
+    LiveInterval *preInterval = new LiveInterval(interval->vreg(), 0);
+    for (size_t i = 0; i < interval->numRanges(); i++) {
+        const LiveInterval::Range *range = interval->getRange(i);
+        JS_ASSERT(range->from <= inputOf(reg.ins()));
+
+        CodePosition to = (range->to <= outputOf(reg.ins())) ? range->to : outputOf(reg.ins());
+        if (!preInterval->addRange(range->from, to))
+            return false;
+    }
+
+    LiveInterval *postInterval = new LiveInterval(interval->vreg(), 0);
+    if (!postInterval->addRange(inputOf(reg.ins()), interval->end()))
+        return false;
+
+    LiveIntervalVector newIntervals;
+    if (!newIntervals.append(preInterval) || !newIntervals.append(postInterval))
+        return false;
+
+    if (!split(interval, newIntervals))
+        return false;
+
+    JS_ASSERT(usedReg.numIntervals() == 2);
+
+    usedReg.setCanonicalSpillExclude(inputOf(reg.ins()));
+
+    return tryGroupRegisters(use, def);
+}
+
+bool
 BacktrackingAllocator::groupAndQueueRegisters()
 {
+    // Try to group registers with their reused inputs.
     for (size_t i = 0; i < graph.numVirtualRegisters(); i++) {
-        if (mir->shouldCancel("Backtracking Group Registers"))
-            return false;
-
         BacktrackingVirtualRegister &reg = vregs[i];
+        if (!reg.numIntervals())
+            continue;
 
-        // Place all intervals for this register on the allocation queue.
-        for (size_t j = 0; j < reg.numIntervals(); j++) {
-            LiveInterval *interval = reg.getInterval(j);
-            if (interval->numRanges() > 0) {
-                size_t priority = computePriority(interval);
-                if (!queuedIntervals.insert(QueuedInterval(interval, priority)))
+        if (reg.def()->policy() == LDefinition::MUST_REUSE_INPUT) {
+            LUse *use = reg.ins()->getOperand(reg.def()->getReusedInput())->toUse();
+            if (!tryGroupReusedRegister(i, use->virtualRegister()))
+                return false;
+        }
+    }
+
+    // Try to group phis with their inputs.
+    for (size_t i = 0; i < graph.numBlocks(); i++) {
+        LBlock *block = graph.getBlock(i);
+        for (size_t j = 0; j < block->numPhis(); j++) {
+            LPhi *phi = block->getPhi(j);
+            uint32_t output = phi->getDef(0)->virtualRegister();
+            for (size_t k = 0; k < phi->numOperands(); k++) {
+                uint32_t input = phi->getOperand(k)->toUse()->virtualRegister();
+                if (!tryGroupRegisters(input, output))
                     return false;
             }
         }
+    }
 
+    for (size_t i = 0; i < graph.numVirtualRegisters(); i++) {
+        if (mir->shouldCancel("Backtracking Enqueue Registers"))
+            return false;
+
+        BacktrackingVirtualRegister &reg = vregs[i];
+        JS_ASSERT(reg.numIntervals() <= 2);
+        JS_ASSERT(!reg.canonicalSpill());
+
+        if (!reg.numIntervals())
+            continue;
+
+        // Eagerly set the canonical spill slot for registers which are preset
+        // for that slot, and reuse it for other registers in the group.
         LDefinition *def = reg.def();
-        if (def && def->policy() == LDefinition::MUST_REUSE_INPUT) {
-            LUse *use = reg.ins()->getOperand(def->getReusedInput())->toUse();
-            VirtualRegister &usedReg = vregs[use->virtualRegister()];
-            if (usedReg.intervalFor(outputOf(reg.ins())) || reg.intervalFor(inputOf(reg.ins()))) {
-                // This definitions reuses an input that is live afterwards
-                // (either in future instructions or a safepoint for the
-                // definition). This is impossible to satisfy without first
-                // copying the input, and rather than encoding this by
-                // splitting intervals (which may require even more copying
-                // later) mark the register as needing this copy during
-                // reification and relax the MUST_REUSE_INPUT constraint.
-                IonSpew(IonSpew_RegAlloc, "Relaxing reuse-input constraint on v%u", i);
-                reg.setMustCopyInput();
-            } else {
-                // This definition reuses an input that is not live afterwards.
-                // The input and output can use the same allocation, and it is
-                // desirable to do this to avoid unnecessary copies.
-                if (!tryGroupRegisters(use->virtualRegister(), def->virtualRegister()))
+        if (def->policy() == LDefinition::PRESET && !def->output()->isRegister()) {
+            reg.setCanonicalSpill(*def->output());
+            if (reg.group() && reg.group()->spill.isUse())
+                reg.group()->spill = *def->output();
+        }
+
+        // Place all intervals for this register on the allocation queue.
+        // During initial queueing use single queue items for groups of
+        // registers, so that they will be allocated together and reduce the
+        // risk of unnecessary conflicts. This is in keeping with the idea that
+        // register groups are effectively single registers whose value changes
+        // during execution. If any intervals in the group are evicted later
+        // then they will be reallocated individually.
+        size_t start = 0;
+        if (VirtualRegisterGroup *group = reg.group()) {
+            if (i == group->canonicalReg()) {
+                size_t priority = computePriority(group);
+                if (!allocationQueue.insert(QueueItem(group, priority)))
                     return false;
             }
+            start++;
         }
-
-        // Try to group phis with their inputs.
-        for (size_t i = 0; i < graph.numBlocks(); i++) {
-            LBlock *block = graph.getBlock(i);
-            for (size_t j = 0; j < block->numPhis(); j++) {
-                LPhi *phi = block->getPhi(j);
-                uint32_t output = phi->getDef(0)->virtualRegister();
-                for (size_t k = 0; k < phi->numOperands(); k++) {
-                    uint32_t input = phi->getOperand(k)->toUse()->virtualRegister();
-                    if (!tryGroupRegisters(input, output))
-                        return false;
-                }
+        for (; start < reg.numIntervals(); start++) {
+            LiveInterval *interval = reg.getInterval(start);
+            if (interval->numRanges() > 0) {
+                size_t priority = computePriority(interval);
+                if (!allocationQueue.insert(QueueItem(interval, priority)))
+                    return false;
             }
         }
     }
 
     return true;
 }
 
 static const size_t MAX_ATTEMPTS = 2;
@@ -379,16 +505,58 @@ BacktrackingAllocator::processInterval(L
         // be constructed so that any minimal interval is allocatable.
         JS_ASSERT(!minimalInterval(interval));
 
         return chooseIntervalSplit(interval);
     }
 }
 
 bool
+BacktrackingAllocator::processGroup(VirtualRegisterGroup *group)
+{
+    if (IonSpewEnabled(IonSpew_RegAlloc)) {
+        IonSpew(IonSpew_RegAlloc, "Allocating group v%u [priority %lu] [weight %lu]",
+                group->registers[0], computePriority(group), computeSpillWeight(group));
+    }
+
+    LiveInterval *conflict;
+    for (size_t attempt = 0;; attempt++) {
+        // Search for any available register which the group can be allocated to.
+        conflict = NULL;
+        for (size_t i = 0; i < AnyRegister::Total; i++) {
+            bool success;
+            if (!tryAllocateGroupRegister(registers[i], group, &success, &conflict))
+                return false;
+            if (success) {
+                conflict = NULL;
+                break;
+            }
+        }
+
+        if (attempt < MAX_ATTEMPTS &&
+            conflict &&
+            computeSpillWeight(conflict) < computeSpillWeight(group))
+        {
+            if (!evictInterval(conflict))
+                return false;
+            continue;
+        }
+
+        for (size_t i = 0; i < group->registers.length(); i++) {
+            VirtualRegister &reg = vregs[group->registers[i]];
+            JS_ASSERT(reg.numIntervals() <= 2);
+            if (!processInterval(reg.getInterval(0)))
+                return false;
+        }
+
+        return true;
+    }
+}
+
+bool
 BacktrackingAllocator::setIntervalRequirement(LiveInterval *interval)
 {
     // Set any requirement or hint on interval according to its definition and
     // uses. Return false if there are conflicting requirements which will
     // require the interval to be split.
     interval->setHint(Requirement());
     interval->setRequirement(Requirement());
 
@@ -436,16 +604,63 @@ BacktrackingAllocator::setIntervalRequir
                 return false;
         }
     }
 
     return true;
 }
 
 bool
+BacktrackingAllocator::tryAllocateGroupRegister(PhysicalRegister &r, VirtualRegisterGroup *group,
+                                                bool *psuccess, LiveInterval **pconflicting)
+{
+    *psuccess = false;
+
+    if (!r.allocatable)
+        return true;
+
+    if (r.reg.isFloat() != vregs[group->registers[0]].isDouble())
+        return true;
+
+    bool allocatable = true;
+    LiveInterval *conflicting = NULL;
+
+    for (size_t i = 0; i < group->registers.length(); i++) {
+        VirtualRegister &reg = vregs[group->registers[i]];
+        JS_ASSERT(reg.numIntervals() <= 2);
+        LiveInterval *interval = reg.getInterval(0);
+
+        for (size_t j = 0; j < interval->numRanges(); j++) {
+            AllocatedRange range(interval, interval->getRange(j)), existing;
+            if (r.allocations.contains(range, &existing)) {
+                if (conflicting) {
+                    if (conflicting != existing.interval)
+                        return true;
+                } else {
+                    conflicting = existing.interval;
+                }
+                allocatable = false;
+            }
+        }
+    }
+
+    if (!allocatable) {
+        JS_ASSERT(conflicting);
+        if (!*pconflicting || computeSpillWeight(conflicting) < computeSpillWeight(*pconflicting))
+            *pconflicting = conflicting;
+        return true;
+    }
+
+    *psuccess = true;
+
+    group->allocation = LAllocation(r.reg);
+    return true;
+}
+
+bool
 BacktrackingAllocator::tryAllocateRegister(PhysicalRegister &r, LiveInterval *interval,
                                            bool *success, LiveInterval **pconflicting)
 {
     *success = false;
 
     if (!r.allocatable)
         return true;
 
@@ -518,27 +733,28 @@ BacktrackingAllocator::evictInterval(Liv
     for (size_t i = 0; i < interval->numRanges(); i++) {
         AllocatedRange range(interval, interval->getRange(i));
         physical.allocations.remove(range);
     }
 
     interval->setAllocation(LAllocation());
 
     size_t priority = computePriority(interval);
-    return queuedIntervals.insert(QueuedInterval(interval, priority));
+    return allocationQueue.insert(QueueItem(interval, priority));
 }
 
 bool
-BacktrackingAllocator::splitAndRequeueInterval(LiveInterval *interval,
-                                               const LiveIntervalVector &newIntervals)
+BacktrackingAllocator::split(LiveInterval *interval,
+                             const LiveIntervalVector &newIntervals)
 {
     JS_ASSERT(newIntervals.length() >= 2);
 
     if (IonSpewEnabled(IonSpew_RegAlloc)) {
-        IonSpew(IonSpew_RegAlloc, "splitting interval %s:", IntervalString(interval));
+        IonSpew(IonSpew_RegAlloc, "splitting interval v%u %s:",
+                interval->vreg(), IntervalString(interval));
         for (size_t i = 0; i < newIntervals.length(); i++)
             IonSpew(IonSpew_RegAlloc, "    %s", IntervalString(newIntervals[i]));
     }
 
     // Find the earliest interval in the new list.
     LiveInterval *first = newIntervals[0];
     for (size_t i = 1; i < newIntervals.length(); i++) {
         if (newIntervals[i]->start() < first->start())
@@ -549,86 +765,102 @@ BacktrackingAllocator::splitAndRequeueIn
     VirtualRegister *reg = &vregs[interval->vreg()];
     reg->replaceInterval(interval, first);
     for (size_t i = 0; i < newIntervals.length(); i++) {
         if (newIntervals[i] != first && !reg->addInterval(newIntervals[i]))
             return false;
     }
 
     // Redistribute uses from the old interval to the new intervals. Intervals
-    // are permitted to overlap. In such cases, assign the use to the interval
-    // with the latest start position.
+    // are permitted to overlap. In such cases, assign the use to either any
+    // minimal interval containing it, otherwise the interval with the latest
+    // start position.
     for (UsePositionIterator iter(interval->usesBegin());
          iter != interval->usesEnd();
          iter++)
     {
         CodePosition pos = iter->pos;
-        LiveInterval *maxInterval = NULL;
+        LiveInterval *addInterval = NULL;
         for (size_t i = 0; i < newIntervals.length(); i++) {
-            if (newIntervals[i]->covers(pos)) {
-                if (!maxInterval || newIntervals[i]->start() > maxInterval->start())
-                    maxInterval = newIntervals[i];
+            LiveInterval *newInterval = newIntervals[i];
+            if (newInterval->covers(pos)) {
+                if (minimalUse(newInterval, insData[pos].ins())) {
+                    addInterval = newInterval;
+                    break;
+                }
+                if (!addInterval || newInterval->start() < addInterval->start())
+                    addInterval = newInterval;
             }
         }
-        maxInterval->addUse(new UsePosition(iter->use, iter->pos));
+        addInterval->addUse(new UsePosition(iter->use, iter->pos));
     }
 
+    return true;
+}
+
+bool BacktrackingAllocator::requeueIntervals(const LiveIntervalVector &newIntervals)
+{
     // Queue the new intervals for register assignment.
     for (size_t i = 0; i < newIntervals.length(); i++) {
         LiveInterval *newInterval = newIntervals[i];
         size_t priority = computePriority(newInterval);
-        if (!queuedIntervals.insert(QueuedInterval(newInterval, priority)))
+        if (!allocationQueue.insert(QueueItem(newInterval, priority)))
             return false;
     }
-
     return true;
 }
 
 void
 BacktrackingAllocator::spill(LiveInterval *interval)
 {
     IonSpew(IonSpew_RegAlloc, "Spilling interval");
 
     JS_ASSERT(interval->requirement()->kind() == Requirement::NONE);
 
     // We can't spill bogus intervals.
     JS_ASSERT(interval->hasVreg());
 
     BacktrackingVirtualRegister *reg = &vregs[interval->vreg()];
 
-    if (reg->canonicalSpill()) {
-        IonSpew(IonSpew_RegAlloc, "  Picked canonical spill location %u",
-                reg->canonicalSpill()->toStackSlot()->slot());
-        interval->setAllocation(*reg->canonicalSpill());
-        return;
-    }
+    bool useCanonical = !reg->hasCanonicalSpillExclude()
+        || interval->start() < reg->canonicalSpillExclude();
 
-    if (reg->group() && reg->group()->spill.isStackSlot()) {
-        IonSpew(IonSpew_RegAlloc, "  Reusing group spill location %u",
-                reg->group()->spill.toStackSlot()->slot());
-        interval->setAllocation(reg->group()->spill);
-        reg->setCanonicalSpill(reg->group()->spill);
-        return;
+    if (useCanonical) {
+        if (reg->canonicalSpill()) {
+            IonSpew(IonSpew_RegAlloc, "  Picked canonical spill location %s",
+                    reg->canonicalSpill()->toString());
+            interval->setAllocation(*reg->canonicalSpill());
+            return;
+        }
+
+        if (reg->group() && !reg->group()->spill.isUse()) {
+            IonSpew(IonSpew_RegAlloc, "  Reusing group spill location %s",
+                    reg->group()->spill.toString());
+            interval->setAllocation(reg->group()->spill);
+            reg->setCanonicalSpill(reg->group()->spill);
+            return;
+        }
     }
 
     uint32_t stackSlot;
-    if (reg->isDouble()) {
+    if (reg->isDouble())
         stackSlot = stackSlotAllocator.allocateDoubleSlot();
-    } else {
+    else
         stackSlot = stackSlotAllocator.allocateSlot();
-    }
     JS_ASSERT(stackSlot <= stackSlotAllocator.stackHeight());
 
-    IonSpew(IonSpew_RegAlloc, "  Allocating canonical spill location %u", stackSlot);
-    interval->setAllocation(LStackSlot(stackSlot, reg->isDouble()));
-    reg->setCanonicalSpill(*interval->getAllocation());
+    LStackSlot alloc(stackSlot, reg->isDouble());
+    interval->setAllocation(alloc);
+
+    IonSpew(IonSpew_RegAlloc, "  Allocating spill location %s", alloc.toString());
 
-    if (reg->group()) {
-        JS_ASSERT(!reg->group()->spill.isStackSlot());
-        reg->group()->spill = *interval->getAllocation();
+    if (useCanonical) {
+        reg->setCanonicalSpill(alloc);
+        if (reg->group())
+            reg->group()->spill = alloc;
     }
 }
 
 // Add moves to resolve conflicting assignments between a block and its
 // predecessors. XXX try to common this with LinearScanAllocator.
 bool
 BacktrackingAllocator::resolveControlFlow()
 {
@@ -727,34 +959,16 @@ BacktrackingAllocator::resolveControlFlo
                 }
             }
         }
     }
 
     return true;
 }
 
-static LDefinition *
-FindReusingDefinition(LInstruction *ins, LAllocation *alloc)
-{
-    for (size_t i = 0; i < ins->numDefs(); i++) {
-        LDefinition *def = ins->getDef(i);
-        if (def->policy() == LDefinition::MUST_REUSE_INPUT &&
-            ins->getOperand(def->getReusedInput()) == alloc)
-            return def;
-    }
-    for (size_t i = 0; i < ins->numTemps(); i++) {
-        LDefinition *def = ins->getTemp(i);
-        if (def->policy() == LDefinition::MUST_REUSE_INPUT &&
-            ins->getOperand(def->getReusedInput()) == alloc)
-            return def;
-    }
-    return NULL;
-}
-
 bool
 BacktrackingAllocator::isReusedInput(LUse *use, LInstruction *ins, bool considerCopy)
 {
     if (LDefinition *def = FindReusingDefinition(ins, use))
         return considerCopy || !vregs[def->virtualRegister()].mustCopyInput();
     return false;
 }
 
@@ -814,29 +1028,21 @@ BacktrackingAllocator::reifyAllocations(
     return true;
 }
 
 void
 BacktrackingAllocator::dumpRegisterGroups()
 {
     printf("Register groups:\n");
     for (size_t i = 0; i < graph.numVirtualRegisters(); i++) {
-        if (VirtualRegisterGroup *group = vregs[i].group()) {
-            bool minimum = true;
-            for (size_t j = 0; j < group->registers.length(); j++) {
-                if (group->registers[j] < i) {
-                    minimum = false;
-                    break;
-                }
-            }
-            if (minimum) {
-                for (size_t j = 0; j < group->registers.length(); j++)
-                    printf(" v%u", group->registers[j]);
-                printf("\n");
-            }
+        VirtualRegisterGroup *group = vregs[i].group();
+        if (group && i == group->canonicalReg()) {
+            for (size_t j = 0; j < group->registers.length(); j++)
+                printf(" v%u", group->registers[j]);
+            printf("\n");
         }
     }
 }
 
 void
 BacktrackingAllocator::dumpLiveness()
 {
 #ifdef DEBUG
@@ -900,16 +1106,30 @@ BacktrackingAllocator::dumpLiveness()
         }
         printf("\n");
     }
 
     printf("\n");
 #endif // DEBUG
 }
 
+#ifdef DEBUG
+struct BacktrackingAllocator::PrintLiveIntervalRange
+{
+    void operator()(const AllocatedRange &item)
+    {
+        if (item.range == item.interval->getRange(0)) {
+            printf("  v%u: %s\n",
+                   item.interval->hasVreg() ? item.interval->vreg() : 0,
+                   IntervalString(item.interval));
+        }
+    }
+};
+#endif
+
 void
 BacktrackingAllocator::dumpAllocations()
 {
 #ifdef DEBUG
     printf("Allocations:\n");
 
     for (size_t i = 0; i < graph.numVirtualRegisters(); i++) {
         printf("v%lu:", i);
@@ -919,16 +1139,23 @@ BacktrackingAllocator::dumpAllocations()
                 printf(" *");
             LiveInterval *interval = vreg.getInterval(j);
             printf("%s :: %s", IntervalString(interval), interval->getAllocation()->toString());
         }
         printf("\n");
     }
 
     printf("\n");
+
+    for (size_t i = 0; i < AnyRegister::Total; i++) {
+        printf("reg %s:\n", AnyRegister::FromCode(i).name());
+        registers[i].allocations.forEach(PrintLiveIntervalRange());
+    }
+
+    printf("\n");
 #endif // DEBUG
 }
 
 bool
 BacktrackingAllocator::addLiveInterval(LiveIntervalVector &intervals, uint32_t vreg,
                                        CodePosition from, CodePosition to)
 {
     LiveInterval *interval = new LiveInterval(vreg, 0);
@@ -950,16 +1177,27 @@ BacktrackingAllocator::computePriority(c
     for (size_t i = 0; i < interval->numRanges(); i++) {
         const LiveInterval::Range *range = interval->getRange(i);
         lifetimeTotal += range->to.pos() - range->from.pos();
     }
 
     return lifetimeTotal;
 }
 
+size_t
+BacktrackingAllocator::computePriority(const VirtualRegisterGroup *group)
+{
+    size_t priority = 0;
+    for (size_t j = 0; j < group->registers.length(); j++) {
+        uint32_t vreg = group->registers[j];
+        priority += computePriority(vregs[vreg].getInterval(0));
+    }
+    return priority;
+}
+
 CodePosition
 BacktrackingAllocator::minimalDefEnd(LInstruction *ins)
 {
     // Compute the shortest interval that captures vregs defined by ins.
     // Watch for instructions that are followed by an OSI point and/or Nop.
     // If moves are introduced between the instruction and the OSI point then
     // safepoint information for the instruction may be incorrect. This is
     // pretty disgusting and should be fixed somewhere else in the compiler.
@@ -986,16 +1224,21 @@ BacktrackingAllocator::minimalUse(const 
     // Whether interval is a minimal interval capturing a use at ins.
     return (interval->start() == inputOf(ins)) &&
         (interval->end() == outputOf(ins) || interval->end() == outputOf(ins).next());
 }
 
 bool
 BacktrackingAllocator::minimalInterval(const LiveInterval *interval, bool *pfixed)
 {
+    if (!interval->hasVreg()) {
+        *pfixed = true;
+        return true;
+    }
+
     if (interval->index() == 0) {
         VirtualRegister &reg = vregs[interval->vreg()];
         if (pfixed)
             *pfixed = reg.def()->policy() == LDefinition::PRESET && reg.def()->output()->isRegister();
         return minimalDef(interval, reg.ins());
     }
 
     for (UsePositionIterator iter = interval->usesBegin(); iter != interval->usesEnd(); iter++) {
@@ -1066,16 +1309,27 @@ BacktrackingAllocator::computeSpillWeigh
         usesTotal += 2000;
 
     // Compute spill weight as a use density, lowering the weight for long
     // lived intervals with relatively few uses.
     size_t lifetimeTotal = computePriority(interval);
     return lifetimeTotal ? usesTotal / lifetimeTotal : 0;
 }
 
+size_t
+BacktrackingAllocator::computeSpillWeight(const VirtualRegisterGroup *group)
+{
+    size_t maxWeight = 0;
+    for (size_t j = 0; j < group->registers.length(); j++) {
+        uint32_t vreg = group->registers[j];
+        maxWeight = Max(maxWeight, computeSpillWeight(vregs[vreg].getInterval(0)));
+    }
+    return maxWeight;
+}
+
 bool
 BacktrackingAllocator::trySplitAcrossHotcode(LiveInterval *interval, bool *success)
 {
     // If this interval has portions that are hot and portions that are cold,
     // split it at the boundaries between hot and cold code.
 
     const LiveInterval::Range *hotRange = NULL;
 
@@ -1138,17 +1392,17 @@ BacktrackingAllocator::trySplitAcrossHot
     if (!newIntervals.append(hotInterval))
         return false;
     if (preInterval && !newIntervals.append(preInterval))
         return false;
     if (postInterval && !newIntervals.append(postInterval))
         return false;
 
     *success = true;
-    return splitAndRequeueInterval(interval, newIntervals);
+    return split(interval, newIntervals) && requeueIntervals(newIntervals);
 }
 
 bool
 BacktrackingAllocator::trySplitAfterLastRegisterUse(LiveInterval *interval, bool *success)
 {
     // If this interval's later uses do not require it to be in a register,
     // split it after the last use which does require a register.
 
@@ -1208,17 +1462,17 @@ BacktrackingAllocator::trySplitAfterLast
         }
     }
 
     LiveIntervalVector newIntervals;
     if (!newIntervals.append(preInterval) || !newIntervals.append(postInterval))
         return false;
 
     *success = true;
-    return splitAndRequeueInterval(interval, newIntervals);
+    return split(interval, newIntervals) && requeueIntervals(newIntervals);
 }
 
 bool
 BacktrackingAllocator::splitAtAllRegisterUses(LiveInterval *interval)
 {
     // Split this interval so that all its register uses become minimal
     // intervals and allow the vreg to be spilled throughout its range.
 
@@ -1283,20 +1537,27 @@ BacktrackingAllocator::splitAtAllRegiste
     for (size_t i = 0; i < registerUses.length(); i++) {
         // Watch for duplicate register use positions.
         if (i > 0 && registerUses[i].from == registerUses[i - 1].from)
             continue;
         if (!addLiveInterval(newIntervals, vreg, registerUses[i].from, registerUses[i].to))
             return false;
     }
 
-    if (!addLiveInterval(newIntervals, vreg, spillStart, interval->end()))
+    LiveInterval *spillInterval = new LiveInterval(vreg, 0);
+    for (size_t i = 0; i < interval->numRanges(); i++) {
+        const LiveInterval::Range *range = interval->getRange(i);
+        CodePosition from = range->from < spillStart ? spillStart : range->from;
+        if (!spillInterval->addRange(from, range->to))
+            return false;
+    }
+    if (!newIntervals.append(spillInterval))
         return false;
 
-    return splitAndRequeueInterval(interval, newIntervals);
+    return split(interval, newIntervals) && requeueIntervals(newIntervals);
 }
 
 bool
 BacktrackingAllocator::chooseIntervalSplit(LiveInterval *interval)
 {
     bool success = false;
 
     if (!trySplitAcrossHotcode(interval, &success))
--- a/js/src/ion/BacktrackingAllocator.h
+++ b/js/src/ion/BacktrackingAllocator.h
@@ -34,27 +34,38 @@ struct VirtualRegisterGroup : public Tem
     LAllocation allocation;
 
     // Spill location to be shared by registers in the group.
     LAllocation spill;
 
     VirtualRegisterGroup()
       : allocation(LUse(0, LUse::ANY)), spill(LUse(0, LUse::ANY))
     {}
+
+    uint32_t canonicalReg() {
+        uint32_t minimum = registers[0];
+        for (size_t i = 1; i < registers.length(); i++)
+            minimum = Min(minimum, registers[i]);
+        return minimum;
+    }
 };
 
 class BacktrackingVirtualRegister : public VirtualRegister
 {
     // If this register's definition is MUST_REUSE_INPUT, whether a copy must
     // be introduced before the definition that relaxes the policy.
     bool mustCopyInput_;
 
     // Spill location to use for this register.
     LAllocation canonicalSpill_;
 
+    // Code position above which the canonical spill cannot be used; such
+    // intervals may overlap other registers in the same group.
+    CodePosition canonicalSpillExclude_;
+
     // If this register is associated with a group of other registers,
     // information about the group. This structure is shared between all
     // registers in the group.
     VirtualRegisterGroup *group_;
 
   public:
     void setMustCopyInput() {
         mustCopyInput_ = true;
@@ -62,50 +73,64 @@ class BacktrackingVirtualRegister : publ
     bool mustCopyInput() {
         return mustCopyInput_;
     }
 
     void setCanonicalSpill(LAllocation alloc) {
         canonicalSpill_ = alloc;
     }
     const LAllocation *canonicalSpill() const {
-        return canonicalSpill_.isStackSlot() ? &canonicalSpill_ : NULL;
+        return canonicalSpill_.isUse() ? NULL : &canonicalSpill_;
+    }
+
+    void setCanonicalSpillExclude(CodePosition pos) {
+        canonicalSpillExclude_ = pos;
     }
-    unsigned canonicalSpillSlot() const {
-        return canonicalSpill_.toStackSlot()->slot();
+    bool hasCanonicalSpillExclude() const {
+        return canonicalSpillExclude_.pos() != 0;
+    }
+    CodePosition canonicalSpillExclude() const {
+        JS_ASSERT(hasCanonicalSpillExclude());
+        return canonicalSpillExclude_;
     }
 
     void setGroup(VirtualRegisterGroup *group) {
         group_ = group;
     }
     VirtualRegisterGroup *group() {
         return group_;
     }
 };
 
 class BacktrackingAllocator : public LiveRangeAllocator<BacktrackingVirtualRegister>
 {
-    // Priority queue element: an interval and its immutable priority.
-    struct QueuedInterval
+    // Priority queue element: either an interval or group of intervals and the
+    // associated priority.
+    struct QueueItem
     {
         LiveInterval *interval;
+        VirtualRegisterGroup *group;
 
-        QueuedInterval(LiveInterval *interval, size_t priority)
-          : interval(interval), priority_(priority)
+        QueueItem(LiveInterval *interval, size_t priority)
+          : interval(interval), group(NULL), priority_(priority)
         {}
 
-        static size_t priority(const QueuedInterval &v) {
+        QueueItem(VirtualRegisterGroup *group, size_t priority)
+          : interval(NULL), group(group), priority_(priority)
+        {}
+
+        static size_t priority(const QueueItem &v) {
             return v.priority_;
         }
 
       private:
         size_t priority_;
     };
 
-    PriorityQueue<QueuedInterval, QueuedInterval, 0, SystemAllocPolicy> queuedIntervals;
+    PriorityQueue<QueueItem, QueueItem, 0, SystemAllocPolicy> allocationQueue;
 
     // A subrange over which a physical register is allocated.
     struct AllocatedRange {
         LiveInterval *interval;
         const LiveInterval::Range *range;
 
         AllocatedRange()
           : interval(NULL), range(NULL)
@@ -151,47 +176,56 @@ class BacktrackingAllocator : public Liv
 
   private:
 
     typedef Vector<LiveInterval *, 4, SystemAllocPolicy> LiveIntervalVector;
 
     bool init();
     bool canAddToGroup(VirtualRegisterGroup *group, BacktrackingVirtualRegister *reg);
     bool tryGroupRegisters(uint32_t vreg0, uint32_t vreg1);
+    bool tryGroupReusedRegister(uint32_t def, uint32_t use);
     bool groupAndQueueRegisters();
     bool processInterval(LiveInterval *interval);
+    bool processGroup(VirtualRegisterGroup *group);
     bool setIntervalRequirement(LiveInterval *interval);
     bool tryAllocateRegister(PhysicalRegister &r, LiveInterval *interval,
                              bool *success, LiveInterval **pconflicting);
+    bool tryAllocateGroupRegister(PhysicalRegister &r, VirtualRegisterGroup *group,
+                                  bool *psuccess, LiveInterval **pconflicting);
     bool evictInterval(LiveInterval *interval);
-    bool splitAndRequeueInterval(LiveInterval *interval,
-                                 const LiveIntervalVector &newIntervals);
+    bool split(LiveInterval *interval, const LiveIntervalVector &newIntervals);
+    bool requeueIntervals(const LiveIntervalVector &newIntervals);
     void spill(LiveInterval *interval);
 
     bool isReusedInput(LUse *use, LInstruction *ins, bool considerCopy = false);
     bool addLiveInterval(LiveIntervalVector &intervals, uint32_t vreg,
                          CodePosition from, CodePosition to);
 
     bool resolveControlFlow();
     bool reifyAllocations();
 
     void dumpRegisterGroups();
     void dumpLiveness();
     void dumpAllocations();
 
+    struct PrintLiveIntervalRange;
+
     CodePosition minimalDefEnd(LInstruction *ins);
     bool minimalDef(const LiveInterval *interval, LInstruction *ins);
     bool minimalUse(const LiveInterval *interval, LInstruction *ins);
     bool minimalInterval(const LiveInterval *interval, bool *pfixed = NULL);
 
     // Heuristic methods.
 
     size_t computePriority(const LiveInterval *interval);
     size_t computeSpillWeight(const LiveInterval *interval);
 
+    size_t computePriority(const VirtualRegisterGroup *group);
+    size_t computeSpillWeight(const VirtualRegisterGroup *group);
+
     bool chooseIntervalSplit(LiveInterval *interval);
     bool trySplitAcrossHotcode(LiveInterval *interval, bool *success);
     bool trySplitAfterLastRegisterUse(LiveInterval *interval, bool *success);
     bool splitAtAllRegisterUses(LiveInterval *interval);
 };
 
 } // namespace ion
 } // namespace js
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -2215,16 +2215,26 @@ CodeGenerator::visitPowD(LPowD *ins)
     masm.passABIArg(power);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ecmaPow), MacroAssembler::DOUBLE);
 
     JS_ASSERT(ToFloatRegister(ins->output()) == ReturnFloatReg);
     return true;
 }
 
 bool
+CodeGenerator::visitNegD(LNegD *ins)
+{
+    FloatRegister input = ToFloatRegister(ins->input());
+    JS_ASSERT(input == ToFloatRegister(ins->output()));
+
+    masm.negateDouble(input);
+    return true;
+}
+
+bool
 CodeGenerator::visitRandom(LRandom *ins)
 {
     Register temp = ToRegister(ins->temp());
     Register temp2 = ToRegister(ins->temp2());
 
     masm.loadJSContext(temp);
 
     masm.setupUnalignedABICall(1, temp2);
--- a/js/src/ion/CodeGenerator.h
+++ b/js/src/ion/CodeGenerator.h
@@ -116,16 +116,17 @@ class CodeGenerator : public CodeGenerat
     bool visitBoundsCheckLower(LBoundsCheckLower *lir);
     bool visitLoadFixedSlotV(LLoadFixedSlotV *ins);
     bool visitLoadFixedSlotT(LLoadFixedSlotT *ins);
     bool visitStoreFixedSlotV(LStoreFixedSlotV *ins);
     bool visitStoreFixedSlotT(LStoreFixedSlotT *ins);
     bool visitAbsI(LAbsI *lir);
     bool visitPowI(LPowI *lir);
     bool visitPowD(LPowD *lir);
+    bool visitNegD(LNegD *lir);
     bool visitRandom(LRandom *lir);
     bool visitMathFunctionD(LMathFunctionD *ins);
     bool visitModD(LModD *ins);
     bool visitMinMaxI(LMinMaxI *lir);
     bool visitBinaryV(LBinaryV *lir);
     bool visitCompareS(LCompareS *lir);
     bool visitCompareV(LCompareV *lir);
     bool visitIsNullOrLikeUndefined(LIsNullOrLikeUndefined *lir);
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -3129,22 +3129,21 @@ IonBuilder::jsop_call_inline(HandleFunct
     // Note that we leave the callee on the simulated stack for the
     // duration of the call.
     MDefinitionVector argv;
     if (!argv.resizeUninitialized(argc + 1))
         return false;
     for (int32_t i = argc; i >= 0; i--)
         argv[i] = current->pop();
 
-    // Compilation information is allocated for the duration of the current tempLifoAlloc
-    // lifetime.
+    LifoAlloc *alloc = GetIonContext()->temp->lifoAlloc();
     RootedScript calleeScript(cx, callee->nonLazyScript());
-    CompileInfo *info = cx->tempLifoAlloc().new_<CompileInfo>(calleeScript.get(), callee,
-                                                              (jsbytecode *)NULL, constructing,
-                                                              SequentialExecution);
+    CompileInfo *info = alloc->new_<CompileInfo>(calleeScript.get(), callee,
+                                                 (jsbytecode *)NULL, constructing,
+                                                 SequentialExecution);
     if (!info)
         return false;
 
     MIRGraphExits saveExits;
     AutoAccumulateExits aae(graph(), saveExits);
 
     TypeInferenceOracle oracle;
     if (!oracle.init(cx, calleeScript))
@@ -4105,16 +4104,113 @@ IonBuilder::jsop_call(uint32_t argc, boo
 
     RootedFunction target(cx, NULL);
     if (numTargets == 1)
         target = targets[0]->toFunction();
 
     return makeCallBarrier(target, argc, constructing, types, barrier);
 }
 
+static bool
+TestShouldDOMCall(JSContext *cx, types::TypeSet *inTypes, HandleFunction func,
+                  JSJitInfo::OpType opType)
+{
+    if (!func->isNative() || !func->jitInfo())
+        return false;
+    // If all the DOM objects flowing through are legal with this
+    // property, we can bake in a call to the bottom half of the DOM
+    // accessor
+    DOMInstanceClassMatchesProto instanceChecker =
+        GetDOMCallbacks(cx->runtime)->instanceClassMatchesProto;
+
+    const JSJitInfo *jinfo = func->jitInfo();
+    if (jinfo->type != opType)
+        return false;
+
+    for (unsigned i = 0; i < inTypes->getObjectCount(); i++) {
+        types::TypeObject *curType = inTypes->getTypeObject(i);
+
+        if (!curType) {
+            JSObject *curObj = inTypes->getSingleObject(i);
+
+            if (!curObj)
+                continue;
+
+            curType = curObj->getType(cx);
+        }
+
+        JSObject *typeProto = curType->proto;
+        RootedObject proto(cx, typeProto);
+        if (!instanceChecker(proto, jinfo->protoID, jinfo->depth))
+            return false;
+    }
+
+    return true;
+}
+
+static bool
+TestAreKnownDOMTypes(JSContext *cx, types::TypeSet *inTypes)
+{
+    if (inTypes->unknownObject())
+        return false;
+
+    // First iterate to make sure they all are DOM objects, then freeze all of
+    // them as such if they are.
+    for (unsigned i = 0; i < inTypes->getObjectCount(); i++) {
+        types::TypeObject *curType = inTypes->getTypeObject(i);
+
+        if (!curType) {
+            JSObject *curObj = inTypes->getSingleObject(i);
+
+            // Skip holes in TypeSets.
+            if (!curObj)
+                continue;
+
+            curType = curObj->getType(cx);
+        }
+
+        if (curType->unknownProperties())
+            return false;
+
+        // Unlike TypeSet::HasObjectFlags, TypeObject::hasAnyFlags doesn't add a
+        // freeze.
+        if (curType->hasAnyFlags(types::OBJECT_FLAG_NON_DOM))
+            return false;
+    }
+
+    // If we didn't check anything, no reason to say yes.
+    if (inTypes->getObjectCount() > 0)
+        return true;
+
+    return false;
+}
+
+static void
+FreezeDOMTypes(JSContext *cx, types::StackTypeSet *inTypes)
+{
+    for (unsigned i = 0; i < inTypes->getObjectCount(); i++) {
+        types::TypeObject *curType = inTypes->getTypeObject(i);
+
+        if (!curType) {
+            JSObject *curObj = inTypes->getSingleObject(i);
+
+            // Skip holes in TypeSets.
+            if (!curObj)
+                continue;
+
+            curType = curObj->getType(cx);
+        }
+
+        // Add freeze by asking the question.
+        DebugOnly<bool> wasntDOM =
+            types::HeapTypeSet::HasObjectFlags(cx, curType, types::OBJECT_FLAG_NON_DOM);
+        JS_ASSERT(!wasntDOM);
+    }
+}
+
 MCall *
 IonBuilder::makeCallHelper(HandleFunction target, uint32_t argc, bool constructing)
 {
     // This function may be called with mutated stack.
     // Querying TI for popped types is invalid.
 
     uint32_t targetArgs = argc;
 
@@ -4166,19 +4262,30 @@ IonBuilder::makeCallHelper(HandleFunctio
         thisArg->block()->discard(thisArg);
         current->add(newThis);
         thisArg = newThis;
     }
 
     // Pass |this| and function.
     call->addArg(0, thisArg);
 
+    if (target && JSOp(*pc) == JSOP_CALL) {
+        // We know we have a single call target.  Check whether the "this" types
+        // are DOM types and our function a DOM function, and if so flag the
+        // MCall accordingly.
+        types::StackTypeSet *thisTypes = oracle->getCallArg(script(), argc, 0, pc);
+        if (thisTypes &&
+            TestAreKnownDOMTypes(cx, thisTypes) &&
+            TestShouldDOMCall(cx, thisTypes, target, JSJitInfo::Method))
+        {
+            FreezeDOMTypes(cx, thisTypes);
+            call->setDOMFunction();
+        }
+    }
     MDefinition *fun = current->pop();
-    if (fun->isDOMFunction())
-        call->setDOMFunction();
     call->initFunction(fun);
 
     current->add(call);
     return call;
 }
 
 bool
 IonBuilder::makeCallBarrier(HandleFunction target, uint32_t argc,
@@ -5946,113 +6053,16 @@ IonBuilder::TestCommonPropFunc(JSContext
     }
 
     *funcp = found->toFunction();
     *isDOM = thinkDOM;
 
     return true;
 }
 
-static bool
-TestShouldDOMCall(JSContext *cx, types::TypeSet *inTypes, HandleFunction func,
-                  JSJitInfo::OpType opType)
-{
-    if (!func->isNative() || !func->jitInfo())
-        return false;
-    // If all the DOM objects flowing through are legal with this
-    // property, we can bake in a call to the bottom half of the DOM
-    // accessor
-    DOMInstanceClassMatchesProto instanceChecker =
-        GetDOMCallbacks(cx->runtime)->instanceClassMatchesProto;
-
-    const JSJitInfo *jinfo = func->jitInfo();
-    if (jinfo->type != opType)
-        return false;
-
-    for (unsigned i = 0; i < inTypes->getObjectCount(); i++) {
-        types::TypeObject *curType = inTypes->getTypeObject(i);
-
-        if (!curType) {
-            JSObject *curObj = inTypes->getSingleObject(i);
-
-            if (!curObj)
-                continue;
-
-            curType = curObj->getType(cx);
-        }
-
-        JSObject *typeProto = curType->proto;
-        RootedObject proto(cx, typeProto);
-        if (!instanceChecker(proto, jinfo->protoID, jinfo->depth))
-            return false;
-    }
-
-    return true;
-}
-
-static bool
-TestAreKnownDOMTypes(JSContext *cx, types::TypeSet *inTypes)
-{
-    if (inTypes->unknown())
-        return false;
-
-    // First iterate to make sure they all are DOM objects, then freeze all of
-    // them as such if they are.
-    for (unsigned i = 0; i < inTypes->getObjectCount(); i++) {
-        types::TypeObject *curType = inTypes->getTypeObject(i);
-
-        if (!curType) {
-            JSObject *curObj = inTypes->getSingleObject(i);
-
-            // Skip holes in TypeSets.
-            if (!curObj)
-                continue;
-
-            curType = curObj->getType(cx);
-        }
-
-        if (curType->unknownProperties())
-            return false;
-
-        // Unlike TypeSet::HasObjectFlags, TypeObject::hasAnyFlags doesn't add a
-        // freeze.
-        if (curType->hasAnyFlags(types::OBJECT_FLAG_NON_DOM))
-            return false;
-    }
-
-    // If we didn't check anything, no reason to say yes.
-    if (inTypes->getObjectCount() > 0)
-        return true;
-
-    return false;
-}
-
-static void
-FreezeDOMTypes(JSContext *cx, types::StackTypeSet *inTypes)
-{
-    for (unsigned i = 0; i < inTypes->getObjectCount(); i++) {
-        types::TypeObject *curType = inTypes->getTypeObject(i);
-
-        if (!curType) {
-            JSObject *curObj = inTypes->getSingleObject(i);
-
-            // Skip holes in TypeSets.
-            if (!curObj)
-                continue;
-
-            curType = curObj->getType(cx);
-        }
-
-        // Add freeze by asking the question.
-        DebugOnly<bool> wasntDOM =
-            types::HeapTypeSet::HasObjectFlags(cx, curType, types::OBJECT_FLAG_NON_DOM);
-        JS_ASSERT(!wasntDOM);
-    }
-}
-
 bool
 IonBuilder::annotateGetPropertyCache(JSContext *cx, MDefinition *obj, MGetPropertyCache *getPropCache,
                                     types::StackTypeSet *objTypes, types::StackTypeSet *pushedTypes)
 {
     RootedId id(cx, NameToId(getPropCache->name()));
     if ((jsid)id != types::MakeTypeId(cx, id))
         return true;
 
@@ -6307,25 +6317,16 @@ IonBuilder::getPropTryConstant(bool *emi
     if (testObject)
         current->add(MGuardObject::New(obj));
     else if (testString)
         current->add(MGuardString::New(obj));
     else
         obj->setFoldedUnchecked();
 
     MConstant *known = MConstant::New(ObjectValue(*singleton));
-    if (singleton->isFunction()) {
-        RootedFunction singletonFunc(cx, singleton->toFunction());
-        if (TestAreKnownDOMTypes(cx, unaryTypes.inTypes) &&
-            TestShouldDOMCall(cx, unaryTypes.inTypes, singletonFunc, JSJitInfo::Method))
-        {
-            FreezeDOMTypes(cx, unaryTypes.inTypes);
-            known->setDOMFunction();
-        }
-    }
 
     current->add(known);
     current->push(known);
 
     *emitted = true;
     return true;
 }
 
--- a/js/src/ion/IonCaches.cpp
+++ b/js/src/ion/IonCaches.cpp
@@ -701,16 +701,153 @@ IonCacheGetProperty::attachCallGetter(JS
     updateLastJump(exitJump);
 
     IonSpew(IonSpew_InlineCaches, "Generated native GETPROP stub at %p %s", code->raw(),
             idempotent() ? "(idempotent)" : "(not idempotent)");
 
     return true;
 }
 
+bool
+IonCacheGetProperty::attachDenseArrayLength(JSContext *cx, IonScript *ion, JSObject *obj)
+{
+    JS_ASSERT(obj->isDenseArray());
+    JS_ASSERT(!idempotent());
+
+    Label failures;
+    MacroAssembler masm;
+
+    // Guard object is a dense array.
+    RootedObject globalObj(cx, &script->global());
+    RootedShape shape(cx, GetDenseArrayShape(cx, globalObj));
+    if (!shape)
+        return false;
+    masm.branchTestObjShape(Assembler::NotEqual, object(), shape, &failures);
+
+    // Load length.
+    Register outReg;
+    if (output().hasValue()) {
+        outReg = output().valueReg().scratchReg();
+    } else {
+        JS_ASSERT(output().type() == MIRType_Int32);
+        outReg = output().typedReg().gpr();
+    }
+
+    masm.loadPtr(Address(object(), JSObject::offsetOfElements()), outReg);
+    masm.load32(Address(outReg, ObjectElements::offsetOfLength()), outReg);
+
+    // The length is an unsigned int, but the value encodes a signed int.
+    JS_ASSERT(object() != outReg);
+    masm.branchTest32(Assembler::Signed, outReg, outReg, &failures);
+
+    if (output().hasValue())
+        masm.tagValue(JSVAL_TYPE_INT32, outReg, output().valueReg());
+
+    u.getprop.hasDenseArrayLengthStub = true;
+    incrementStubCount();
+
+    /* Success. */
+    RepatchLabel rejoin_;
+    CodeOffsetJump rejoinOffset = masm.jumpWithPatch(&rejoin_);
+    masm.bind(&rejoin_);
+
+    /* Failure. */
+    masm.bind(&failures);
+    RepatchLabel exit_;
+    CodeOffsetJump exitOffset = masm.jumpWithPatch(&exit_);
+    masm.bind(&exit_);
+
+    Linker linker(masm);
+    IonCode *code = linker.newCode(cx);
+    if (!code)
+        return false;
+
+    rejoinOffset.fixup(&masm);
+    exitOffset.fixup(&masm);
+
+    if (ion->invalidated())
+        return true;
+
+    CodeLocationJump rejoinJump(code, rejoinOffset);
+    CodeLocationJump exitJump(code, exitOffset);
+    CodeLocationJump lastJump_ = lastJump();
+    PatchJump(lastJump_, CodeLocationLabel(code));
+    PatchJump(rejoinJump, rejoinLabel());
+    PatchJump(exitJump, cacheLabel());
+    updateLastJump(exitJump);
+
+    IonSpew(IonSpew_InlineCaches, "Generated GETPROP dense array length stub at %p", code->raw());
+
+    return true;
+}
+
+bool
+IonCacheGetProperty::attachTypedArrayLength(JSContext *cx, IonScript *ion, JSObject *obj)
+{
+    JS_ASSERT(obj->isTypedArray());
+    JS_ASSERT(!idempotent());
+
+    Label failures;
+    MacroAssembler masm;
+
+    Register tmpReg;
+    if (output().hasValue()) {
+        tmpReg = output().valueReg().scratchReg();
+    } else {
+        JS_ASSERT(output().type() == MIRType_Int32);
+        tmpReg = output().typedReg().gpr();
+    }
+    JS_ASSERT(object() != tmpReg);
+
+    // Implement the negated version of JSObject::isTypedArray predicate.
+    masm.loadObjClass(object(), tmpReg);
+    masm.branchPtr(Assembler::Below, tmpReg, ImmWord(&TypedArray::classes[0]), &failures);
+    masm.branchPtr(Assembler::AboveOrEqual, tmpReg, ImmWord(&TypedArray::classes[TypedArray::TYPE_MAX]), &failures);
+
+    // Load length.
+    masm.loadTypedOrValue(Address(object(), TypedArray::lengthOffset()), output());
+
+    u.getprop.hasTypedArrayLengthStub = true;
+    incrementStubCount();
+
+    /* Success. */
+    RepatchLabel rejoin_;
+    CodeOffsetJump rejoinOffset = masm.jumpWithPatch(&rejoin_);
+    masm.bind(&rejoin_);
+
+    /* Failure. */
+    masm.bind(&failures);
+    RepatchLabel exit_;
+    CodeOffsetJump exitOffset = masm.jumpWithPatch(&exit_);
+    masm.bind(&exit_);
+
+    Linker linker(masm);
+    IonCode *code = linker.newCode(cx);
+    if (!code)
+        return false;
+
+    rejoinOffset.fixup(&masm);
+    exitOffset.fixup(&masm);
+
+    if (ion->invalidated())
+        return true;
+
+    CodeLocationJump rejoinJump(code, rejoinOffset);
+    CodeLocationJump exitJump(code, exitOffset);
+    CodeLocationJump lastJump_ = lastJump();
+    PatchJump(lastJump_, CodeLocationLabel(code));
+    PatchJump(rejoinJump, rejoinLabel());
+    PatchJump(exitJump, cacheLabel());
+    updateLastJump(exitJump);
+
+    IonSpew(IonSpew_InlineCaches, "Generated GETPROP typed array length stub at %p", code->raw());
+
+    return true;
+}
+
 static bool
 TryAttachNativeGetPropStub(JSContext *cx, IonScript *ion,
                            IonCacheGetProperty &cache, HandleObject obj,
                            HandlePropertyName name,
                            const SafepointIndex *safepointIndex,
                            void *returnAddr, bool *isCacheable)
 {
     JS_ASSERT(!*isCacheable);
@@ -820,16 +957,32 @@ js::ion::GetPropertyCache(JSContext *cx,
     bool isCacheable = false;
     if (!TryAttachNativeGetPropStub(cx, ion, cache, obj, name,
                                     safepointIndex, returnAddr,
                                     &isCacheable))
     {
         return false;
     }
 
+    if (!isCacheable && !cache.idempotent() && cx->names().length == name) {
+        if (cache.output().type() != MIRType_Value && cache.output().type() != MIRType_Int32) {
+            // The next execution should cause an invalidation because the type
+            // does not fit.
+            isCacheable = false;
+        } else if (obj->isDenseArray() && !cache.hasDenseArrayLengthStub()) {
+            isCacheable = true;
+            if (!cache.attachDenseArrayLength(cx, ion, obj))
+                return false;
+        } else if (obj->isTypedArray() && !cache.hasTypedArrayLengthStub()) {
+            isCacheable = true;
+            if (!cache.attachTypedArrayLength(cx, ion, obj))
+                return false;
+        }
+    }
+
     if (cache.idempotent() && !isCacheable) {
         // Invalidate the cache if the property was not found, or was found on
         // a non-native object. This ensures:
         // 1) The property read has no observable side-effects.
         // 2) There's no need to dynamically monitor the return type. This would
         //    be complicated since (due to GVN) there can be multiple pc's
         //    associated with a single idempotent cache.
         IonSpew(IonSpew_InlineCaches, "Invalidating from idempotent cache %s:%d",
--- a/js/src/ion/IonCaches.h
+++ b/js/src/ion/IonCaches.h
@@ -106,17 +106,19 @@ class IonCache
 #else
     static const size_t REJOIN_LABEL_OFFSET = 0;
 #endif
     union {
         struct {
             Register object;
             PropertyName *name;
             TypedOrValueRegisterSpace output;
-            bool allowGetters;
+            bool allowGetters : 1;
+            bool hasDenseArrayLengthStub : 1;
+            bool hasTypedArrayLengthStub : 1;
         } getprop;
         struct {
             Register object;
             PropertyName *name;
             ConstantOrRegisterSpace value;
             bool strict;
         } setprop;
         struct {
@@ -260,28 +262,34 @@ class IonCacheGetProperty : public IonCa
                         TypedOrValueRegister output,
                         bool allowGetters)
     {
         init(GetProperty, liveRegs, initialJump, rejoinLabel, cacheLabel);
         u.getprop.object = object;
         u.getprop.name = name;
         u.getprop.output.data() = output;
         u.getprop.allowGetters = allowGetters;
+        u.getprop.hasDenseArrayLengthStub = false;
+        u.getprop.hasTypedArrayLengthStub = false;
     }
 
     Register object() const { return u.getprop.object; }
     PropertyName *name() const { return u.getprop.name; }
     TypedOrValueRegister output() const { return u.getprop.output.data(); }
     bool allowGetters() const { return u.getprop.allowGetters; }
+    bool hasDenseArrayLengthStub() const { return u.getprop.hasDenseArrayLengthStub; }
+    bool hasTypedArrayLengthStub() const { return u.getprop.hasTypedArrayLengthStub; }
 
     bool attachReadSlot(JSContext *cx, IonScript *ion, JSObject *obj, JSObject *holder,
                         HandleShape shape);
     bool attachCallGetter(JSContext *cx, IonScript *ion, JSObject *obj, JSObject *holder,
                           HandleShape shape,
                           const SafepointIndex *safepointIndex, void *returnAddr);
+    bool attachDenseArrayLength(JSContext *cx, IonScript *ion, JSObject *obj);
+    bool attachTypedArrayLength(JSContext *cx, IonScript *ion, JSObject *obj);
 };
 
 class IonCacheSetProperty : public IonCache
 {
   public:
     IonCacheSetProperty(CodeOffsetJump initialJump,
                         CodeOffsetLabel rejoinLabel,
                         CodeOffsetLabel cacheLabel,
--- a/js/src/ion/LiveRangeAllocator.h
+++ b/js/src/ion/LiveRangeAllocator.h
@@ -150,16 +150,34 @@ DefinitionCompatibleWith(LInstruction *i
       default:
         JS_NOT_REACHED("Unknown definition policy");
     }
     return false;
 }
 
 #endif // DEBUG
 
+static inline LDefinition *
+FindReusingDefinition(LInstruction *ins, LAllocation *alloc)
+{
+    for (size_t i = 0; i < ins->numDefs(); i++) {
+        LDefinition *def = ins->getDef(i);
+        if (def->policy() == LDefinition::MUST_REUSE_INPUT &&
+            ins->getOperand(def->getReusedInput()) == alloc)
+            return def;
+    }
+    for (size_t i = 0; i < ins->numTemps(); i++) {
+        LDefinition *def = ins->getTemp(i);
+        if (def->policy() == LDefinition::MUST_REUSE_INPUT &&
+            ins->getOperand(def->getReusedInput()) == alloc)
+            return def;
+    }
+    return NULL;
+}
+
 /*
  * A live interval is a set of disjoint ranges of code positions where a
  * virtual register is live. Register allocation operates on these intervals,
  * splitting them as necessary and assigning allocations to them as it runs.
  */
 class LiveInterval
   : public InlineListNode<LiveInterval>,
     public TempObject
--- a/js/src/ion/arm/CodeGenerator-arm.cpp
+++ b/js/src/ion/arm/CodeGenerator-arm.cpp
@@ -314,25 +314,16 @@ CodeGeneratorARM::visitMinMaxD(LMinMaxD 
     masm.bind(&returnSecond);
     masm.ma_vmov(second, output);
 
     masm.bind(&done);
     return true;
 }
 
 bool
-CodeGeneratorARM::visitNegD(LNegD *ins)
-{
-    FloatRegister input = ToFloatRegister(ins->input());
-    JS_ASSERT(input == ToFloatRegister(ins->output()));
-    masm.ma_vneg(input, input);
-    return true;
-}
-
-bool
 CodeGeneratorARM::visitAbsD(LAbsD *ins)
 {
     FloatRegister input = ToFloatRegister(ins->input());
     JS_ASSERT(input == ToFloatRegister(ins->output()));
     masm.ma_vabs(input, input);
     return true;
 }
 
--- a/js/src/ion/arm/CodeGenerator-arm.h
+++ b/js/src/ion/arm/CodeGenerator-arm.h
@@ -64,17 +64,16 @@ class CodeGeneratorARM : public CodeGene
     // true, and the false block if |cond| is false.
     void emitBranch(Assembler::Condition cond, MBasicBlock *ifTrue, MBasicBlock *ifFalse);
 
     bool emitTableSwitchDispatch(MTableSwitch *mir, const Register &index, const Register &base);
 
   public:
     // Instruction visitors.
     virtual bool visitMinMaxD(LMinMaxD *ins);
-    virtual bool visitNegD(LNegD *ins);
     virtual bool visitAbsD(LAbsD *ins);
     virtual bool visitSqrtD(LSqrtD *ins);
     virtual bool visitAddI(LAddI *ins);
     virtual bool visitSubI(LSubI *ins);
     virtual bool visitBitNotI(LBitNotI *ins);
     virtual bool visitBitOpI(LBitOpI *ins);
 
     virtual bool visitMulI(LMulI *ins);
--- a/js/src/ion/arm/MacroAssembler-arm.cpp
+++ b/js/src/ion/arm/MacroAssembler-arm.cpp
@@ -64,16 +64,22 @@ MacroAssemblerARM::branchTruncateDouble(
     ma_vcvt_F64_I32(src, ScratchFloatReg);
     ma_vxfer(ScratchFloatReg, dest);
     ma_cmp(dest, Imm32(0x7fffffff));
     ma_cmp(dest, Imm32(0x80000000), Assembler::NotEqual);
     ma_b(fail, Assembler::Equal);
 }
 
 void
+MacroAssemblerARM::negateDouble(FloatRegister reg)
+{
+    ma_vneg(reg, reg);
+}
+
+void
 MacroAssemblerARM::inc64(AbsoluteAddress dest)
 {
 
     ma_strd(r0, r1, EDtrAddr(sp, EDtrOffImm(-8)), PreIndex);
 
     ma_mov(Imm32((int32_t)dest.addr), ScratchRegister);
 
     ma_ldrd(EDtrAddr(ScratchRegister, EDtrOffImm(0)), r0, r1);
--- a/js/src/ion/arm/MacroAssembler-arm.h
+++ b/js/src/ion/arm/MacroAssembler-arm.h
@@ -45,16 +45,18 @@ class MacroAssemblerARM : public Assembl
         secondScratchReg_ = reg;
     }
 
     void convertInt32ToDouble(const Register &src, const FloatRegister &dest);
     void convertUInt32ToDouble(const Register &src, const FloatRegister &dest);
     void convertDoubleToFloat(const FloatRegister &src, const FloatRegister &dest);
     void branchTruncateDouble(const FloatRegister &src, const Register &dest, Label *fail);
 
+    void negateDouble(FloatRegister reg);
+
     void inc64(AbsoluteAddress dest);
 
     // somewhat direct wrappers for the low-level assembler funcitons
     // bitops
     // attempt to encode a virtual alu instruction using
     // two real instructions.
   private:
     bool alu_dbl(Register src1, Imm32 imm, Register dest, ALUOp op,
--- a/js/src/ion/shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/ion/shared/CodeGenerator-x86-shared.cpp
@@ -416,31 +416,16 @@ CodeGeneratorX86Shared::visitMinMaxD(LMi
     masm.bind(&returnSecond);
     masm.movsd(second, output);
 
     masm.bind(&done);
     return true;
 }
 
 bool
-CodeGeneratorX86Shared::visitNegD(LNegD *ins)
-{
-    // XOR the float in a float register with -0.0.
-    FloatRegister input = ToFloatRegister(ins->input());
-    JS_ASSERT(input == ToFloatRegister(ins->output()));
-
-    // From MacroAssemblerX86Shared::maybeInlineDouble
-    masm.pcmpeqw(ScratchFloatReg, ScratchFloatReg);
-    masm.psllq(Imm32(63), ScratchFloatReg);
-
-    masm.xorpd(ScratchFloatReg, input); // s ^ 0x80000000000000
-    return true;
-}
-
-bool
 CodeGeneratorX86Shared::visitAbsD(LAbsD *ins)
 {
     FloatRegister input = ToFloatRegister(ins->input());
     JS_ASSERT(input == ToFloatRegister(ins->output()));
     masm.xorpd(ScratchFloatReg, ScratchFloatReg);
     masm.subsd(input, ScratchFloatReg); // negate the sign bit.
     masm.andpd(ScratchFloatReg, input); // s & ~s
     return true;
--- a/js/src/ion/shared/CodeGenerator-x86-shared.h
+++ b/js/src/ion/shared/CodeGenerator-x86-shared.h
@@ -79,17 +79,16 @@ class CodeGeneratorX86Shared : public Co
     bool emitTableSwitchDispatch(MTableSwitch *mir, const Register &index, const Register &base);
 
   public:
     CodeGeneratorX86Shared(MIRGenerator *gen, LIRGraph *graph);
 
   public:
     // Instruction visitors.
     virtual bool visitMinMaxD(LMinMaxD *ins);
-    virtual bool visitNegD(LNegD *ins);
     virtual bool visitAbsD(LAbsD *ins);
     virtual bool visitSqrtD(LSqrtD *ins);
     virtual bool visitPowHalfD(LPowHalfD *ins);
     virtual bool visitAddI(LAddI *ins);
     virtual bool visitSubI(LSubI *ins);
     virtual bool visitMulI(LMulI *ins);
     virtual bool visitDivI(LDivI *ins);
     virtual bool visitModI(LModI *ins);
--- a/js/src/ion/shared/MacroAssembler-x86-shared.h
+++ b/js/src/ion/shared/MacroAssembler-x86-shared.h
@@ -247,16 +247,24 @@ class MacroAssemblerX86Shared : public A
         movsd(src, Operand(dest));
     }
     void storeDouble(FloatRegister src, const BaseIndex &dest) {
         movsd(src, Operand(dest));
     }
     void zeroDouble(FloatRegister reg) {
         xorpd(reg, reg);
     }
+    void negateDouble(FloatRegister reg) {
+        // From MacroAssemblerX86Shared::maybeInlineDouble
+        pcmpeqw(ScratchFloatReg, ScratchFloatReg);
+        psllq(Imm32(63), ScratchFloatReg);
+
+        // XOR the float in a float register with -0.0.
+        xorpd(ScratchFloatReg, reg); // s ^ 0x80000000000000
+    }
     void addDouble(FloatRegister src, FloatRegister dest) {
         addsd(src, dest);
     }
     void convertDoubleToFloat(const FloatRegister &src, const FloatRegister &dest) {
         cvtsd2ss(src, dest);
     }
     void loadFloatAsDouble(const Register &src, FloatRegister dest) {
         movd(src, dest);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/test-jitinfo.js
@@ -0,0 +1,20 @@
+// Test for the shell's FakeDOMObject constructor. This test
+// ensures the fuzzers know about this object.
+function f() {
+    var res = 0;
+    var d = new FakeDOMObject();
+    assertEq(d !== new FakeDOMObject(), true);
+
+    for (var i=0; i<100; i++) {
+	var x = d.x;
+	assertEq(typeof x, "number");
+
+	d.x = 10;
+	d.x = undefined;
+
+	assertEq(d.doFoo(), 0);
+	assertEq(d.doFoo(1), 1);
+	assertEq(d.doFoo(1, 2), 2);
+    }
+}
+f();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/ArrayLengthGetPropertyIC.js
@@ -0,0 +1,54 @@
+function intLength (a, l) {
+    var res = 0;
+    for (var i = 0; i < l; i++)
+        res += a.length;
+    return res / l;
+}
+
+function valueLength (a, l) {
+    var res = 0;
+    for (var i = 0; i < l; i++)
+        res += a.length;
+    return res / l;
+}
+
+var denseArray = [0,1,2,3,4,5,6,7,8,9];
+var typedArray = new Uint8Array(10);
+var hugeArray  = new Array(4294967295);
+var fakeArray1 = { length: 10 };
+var fakeArray2 = { length: 10.5 };
+
+// Check the interpreter result and play with TI type objects.
+assertEq(intLength(denseArray, 10), 10);
+assertEq(intLength(typedArray, 10), 10);
+// assertEq(intLength(fakeArray1, 10), 10);
+
+assertEq(valueLength(denseArray, 10), 10);
+assertEq(valueLength(typedArray, 10), 10);
+assertEq(valueLength(hugeArray , 10), 4294967295);
+assertEq(valueLength(fakeArray2, 10), 10.5);
+
+// Heat up to compile (either JM / Ion)
+assertEq(intLength(denseArray, 100), 10);
+assertEq(valueLength(denseArray, 100), 10);
+
+// No bailout should occur during any of the following checks:
+
+// Check get-property length IC with dense array.
+assertEq(intLength(denseArray, 1), 10);
+assertEq(valueLength(denseArray, 1), 10);
+
+// Check get-property length IC with typed array.
+assertEq(intLength(typedArray, 1), 10);
+assertEq(valueLength(typedArray, 1), 10);
+
+// Check length which do not fit on non-double value.
+assertEq(valueLength(hugeArray, 1), 4294967295);
+
+// Check object length property.
+assertEq(intLength(fakeArray1, 1), 10);
+assertEq(valueLength(fakeArray2, 1), 10.5);
+
+// Cause invalidation of intLength by returning a double.
+assertEq(intLength(hugeArray, 1), 4294967295);
+assertEq(intLength(fakeArray2, 1), 10.5);
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -11,16 +11,17 @@
 #include "jscntxt.h"
 #include "jscompartment.h"
 #include "jsfriendapi.h"
 #include "jsinterp.h"
 #include "jsprobes.h"
 #include "jsxml.h"
 #include "jsgc.h"
 
+#include "builtin/Object.h" // For js::obj_construct
 #include "frontend/ParseMaps.h"
 #include "vm/RegExpObject.h"
 
 #include "jsgcinlines.h"
 
 namespace js {
 
 inline void
@@ -423,17 +424,17 @@ CallJSNativeConstructor(JSContext *cx, N
      * - new Iterator(x) is user-hookable; it returns x.__iterator__() which
      *   could be any object.
      *
      * - (new Object(Object)) returns the callee.
      */
     JS_ASSERT_IF(native != FunctionProxyClass.construct &&
                  native != js::CallOrConstructBoundFunction &&
                  native != js::IteratorConstructor &&
-                 (!callee->isFunction() || callee->toFunction()->native() != js_Object),
+                 (!callee->isFunction() || callee->toFunction()->native() != obj_construct),
                  !args.rval().isPrimitive() && callee != &args.rval().toObject());
 
     return true;
 }
 
 JS_ALWAYS_INLINE bool
 CallJSPropertyOp(JSContext *cx, PropertyOp op, HandleObject receiver, HandleId id, MutableHandleValue vp)
 {
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -564,16 +564,18 @@ JSCompartment::markTypes(JSTracer *trc)
             rt->gcMarker.pushArenaList(aheader);
     }
 
     for (CellIterUnderGC i(this, FINALIZE_TYPE_OBJECT); !i.done(); i.next()) {
         types::TypeObject *type = i.get<types::TypeObject>();
         MarkTypeObjectRoot(trc, &type, "mark_types_scan");
         JS_ASSERT(type == i.get<types::TypeObject>());
     }
+
+    gcTypesMarked = true;
 }
 
 void
 JSCompartment::discardJitCode(FreeOp *fop, bool discardConstraints)
 {
 #ifdef JS_METHODJIT
 
     /*
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -373,16 +373,22 @@ struct JSCompartment : private JS::shado
     JSObject                     *gcLiveArrayBuffers;
 
     /* Linked list of live weakmaps in this compartment. */
     js::WeakMapBase              *gcWeakMapList;
 
     /* This compartment's gray roots. */
     js::Vector<js::GrayRoot, 0, js::SystemAllocPolicy> gcGrayRoots;
 
+    /*
+     * Whether type objects have been marked by markTypes().  This is used to
+     * determine whether they need to be swept.
+     */
+    bool                         gcTypesMarked;
+
   private:
     /*
      * Malloc counter to measure memory pressure for GC scheduling. It runs from
      * gcMaxMallocBytes down to zero. This counter should be used only when it's
      * not possible to know the size of a free.
      */
     ptrdiff_t                    gcMallocBytes;
 
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -293,16 +293,19 @@ JS_FRIEND_API(JSGCTraceKind)
 GCThingTraceKind(void *thing);
 
 /*
  * Invoke cellCallback on every gray JS_OBJECT in the given compartment.
  */
 extern JS_FRIEND_API(void)
 IterateGrayObjects(JSCompartment *compartment, GCThingCallback cellCallback, void *data);
 
+extern JS_FRIEND_API(size_t)
+SizeOfDataIfCDataObject(JSMallocSizeOfFun mallocSizeOf, JSObject *obj);
+
 /*
  * Shadow declarations of JS internal structures, for access by inline access
  * functions below. Do not use these structures in any other way. When adding
  * new fields for access by inline methods, make sure to add static asserts to
  * the original header file to ensure that offsets are consistent.
  */
 namespace shadow {
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -3816,19 +3816,30 @@ ResetIncrementalGC(JSRuntime *rt, const 
         rt->gcIncrementalState = NO_INCREMENTAL;
 
         JS_ASSERT(!rt->gcStrictCompartmentChecking);
 
         break;
       }
 
       case SWEEP:
-        for (CompartmentsIter c(rt); !c.done(); c.next())
+        for (CompartmentsIter c(rt); !c.done(); c.next()) {
             c->scheduledForDestruction = false;
 
+            if (c->activeAnalysis && !c->gcTypesMarked) {
+                AutoCopyFreeListToArenas copy(rt);
+                gcstats::AutoPhase ap1(rt->gcStats, gcstats::PHASE_SWEEP);
+                gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_SWEEP_MARK);
+                gcstats::AutoPhase ap3(rt->gcStats, gcstats::PHASE_SWEEP_MARK_TYPES);
+                rt->gcIncrementalState = MARK_ROOTS;
+                c->markTypes(&rt->gcMarker);
+                rt->gcIncrementalState = SWEEP;
+            }
+        }
+
         /* If we had started sweeping then sweep to completion here. */
         IncrementalCollectSlice(rt, SliceBudget::Unlimited, gcreason::RESET, GC_NORMAL);
 
         {
             gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_WAIT_BACKGROUND_THREAD);
             rt->gcHelperThread.waitBackgroundSweepOrAllocEnd();
         }
         break;
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -480,16 +480,29 @@ class GCCompartmentGroupIter {
 
 /*
  * Allocates a new GC thing. After a successful allocation the caller must
  * fully initialize the thing before calling any function that can potentially
  * trigger GC. This will ensure that GC tracing never sees junk values stored
  * in the partially initialized thing.
  */
 
+template<typename T>
+static inline void
+UnpoisonThing(T *thing)
+{
+#ifdef DEBUG
+    /* Change the contents of memory slightly so that IsThingPoisoned returns false. */
+    JS_STATIC_ASSERT(sizeof(T) >= sizeof(FreeSpan) + sizeof(uint8_t));
+    uint8_t *p =
+        reinterpret_cast<uint8_t *>(reinterpret_cast<FreeSpan *>(thing) + 1);
+    *p = 0;
+#endif
+}
+
 template <typename T>
 inline T *
 NewGCThing(JSContext *cx, js::gc::AllocKind kind, size_t thingSize)
 {
     AssertCanGC();
     JS_ASSERT(thingSize == js::gc::Arena::thingSize(kind));
     JS_ASSERT_IF(cx->compartment == cx->runtime->atomsCompartment,
                  kind == FINALIZE_STRING ||
@@ -504,28 +517,31 @@ NewGCThing(JSContext *cx, js::gc::AllocK
 #ifdef JS_GC_ZEAL
     if (cx->runtime->needZealousGC())
         js::gc::RunDebugGC(cx);
 #endif
 
     MaybeCheckStackRoots(cx, /* relax = */ false);
 
     JSCompartment *comp = cx->compartment;
-    void *t = comp->arenas.allocateFromFreeList(kind, thingSize);
+    T *t = static_cast<T *>(comp->arenas.allocateFromFreeList(kind, thingSize));
     if (!t)
-        t = js::gc::ArenaLists::refillFreeList(cx, kind);
+        t = static_cast<T *>(js::gc::ArenaLists::refillFreeList(cx, kind));
 
     JS_ASSERT_IF(t && comp->wasGCStarted() && (comp->isGCMarking() || comp->isGCSweeping()),
-                 static_cast<T *>(t)->arenaHeader()->allocatedDuringIncremental);
+                 t->arenaHeader()->allocatedDuringIncremental);
 
 #if defined(JSGC_GENERATIONAL) && defined(JS_GC_ZEAL)
     if (cx->runtime->gcVerifyPostData && IsNurseryAllocable(kind) && !IsAtomsCompartment(comp))
         comp->gcNursery.insertPointer(t);
 #endif
-    return static_cast<T *>(t);
+
+    if (t)
+        UnpoisonThing(t);
+    return t;
 }
 
 /* Alternate form which allocates a GC thing if doing so cannot trigger a GC. */
 template <typename T>
 inline T *
 TryNewGCThing(JSContext *cx, js::gc::AllocKind kind, size_t thingSize)
 {
     AutoAssertNoGC nogc;
@@ -536,26 +552,29 @@ TryNewGCThing(JSContext *cx, js::gc::All
     JS_ASSERT(!cx->runtime->noGCOrAllocationCheck);
 
 #ifdef JS_GC_ZEAL
     if (cx->runtime->needZealousGC())
         return NULL;
 #endif
 
     JSCompartment *comp = cx->compartment;
-    void *t = comp->arenas.allocateFromFreeList(kind, thingSize);
+    T *t = static_cast<T *>(comp->arenas.allocateFromFreeList(kind, thingSize));
     JS_ASSERT_IF(t && comp->wasGCStarted() && (comp->isGCMarking() || comp->isGCSweeping()),
-                 static_cast<T *>(t)->arenaHeader()->allocatedDuringIncremental);
+                 t->arenaHeader()->allocatedDuringIncremental);
 
 #if defined(JSGC_GENERATIONAL) && defined(JS_GC_ZEAL)
     JSCompartment *comp = cx->compartment;
     if (cx->runtime->gcVerifyPostData && IsNurseryAllocable(kind) && !IsAtomsCompartment(comp))
         comp->gcNursery.insertPointer(t);
 #endif
-    return static_cast<T *>(t);
+
+    if (t)
+        UnpoisonThing(t);
+    return t;
 }
 
 } /* namespace gc */
 } /* namespace js */
 
 inline JSObject *
 js_NewGCObject(JSContext *cx, js::gc::AllocKind kind)
 {
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -6542,23 +6542,23 @@ JSCompartment::sizeOfTypeInferenceData(T
             const ObjectTableEntry &value = e.front().value;
 
             /* key.ids and values.types have the same length. */
             sizes->objectTypeTables += mallocSizeOf(key.ids) + mallocSizeOf(value.types);
         }
     }
 }
 
-void
-TypeObject::sizeOfExcludingThis(TypeInferenceSizes *sizes, JSMallocSizeOfFun mallocSizeOf)
+size_t
+TypeObject::sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf)
 {
     if (singleton) {
         /*
          * Properties and associated type sets for singletons are cleared on
          * every GC. The type object is normally destroyed too, but we don't
          * charge this to 'temporary' as this is not for GC heap values.
          */
         JS_ASSERT(!newScript);
-        return;
-    }
-
-    sizes->typeObjects += mallocSizeOf(newScript);
-}
+        return 0;
+    }
+
+    return mallocSizeOf(newScript);
+}
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -17,20 +17,16 @@
 #include "ds/LifoAlloc.h"
 #include "gc/Barrier.h"
 #include "gc/Heap.h"
 #include "js/HashTable.h"
 #include "js/Vector.h"
 
 ForwardDeclareJS(Script);
 
-namespace JS {
-struct TypeInferenceSizes;
-}
-
 namespace js {
 
 class TaggedProto
 {
   public:
     TaggedProto() : proto(NULL) {}
     TaggedProto(JSObject *proto) : proto(proto) {}
 
@@ -1031,17 +1027,17 @@ struct TypeObject : gc::Cell
     void clearNewScript(JSContext *cx);
     void getFromPrototypes(JSContext *cx, jsid id, TypeSet *types, bool force = false);
 
     void print();
 
     inline void clearProperties();
     inline void sweep(FreeOp *fop);
 
-    void sizeOfExcludingThis(TypeInferenceSizes *sizes, JSMallocSizeOfFun mallocSizeOf);
+    size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf);
 
     /*
      * Type objects don't have explicit finalizers. Memory owned by a type
      * object pending deletion is released when weak references are sweeped
      * from all the compartment's type objects.
      */
     void finalize(FreeOp *fop) {}
 
--- a/js/src/jsmemorymetrics.cpp
+++ b/js/src/jsmemorymetrics.cpp
@@ -89,17 +89,17 @@ StatsCompartmentCallback(JSRuntime *rt, 
     MOZ_ALWAYS_TRUE(rtStats->compartmentStatsVector.growBy(1));
     CompartmentStats &cStats = rtStats->compartmentStatsVector.back();
     rtStats->initExtraCompartmentStats(compartment, &cStats);
     rtStats->currCompartmentStats = &cStats;
 
     // Measure the compartment object itself, and things hanging off it.
     compartment->sizeOfIncludingThis(rtStats->mallocSizeOf,
                                      &cStats.compartmentObject,
-                                     &cStats.typeInferenceSizes,
+                                     &cStats.typeInference,
                                      &cStats.shapesCompartmentTables,
                                      &cStats.crossCompartmentWrappersTable,
                                      &cStats.regexpCompartment,
                                      &cStats.debuggeesSet);
 }
 
 static void
 StatsChunkCallback(JSRuntime *rt, void *data, gc::Chunk *chunk)
@@ -146,33 +146,29 @@ StatsCellCallback(JSRuntime *rt, void *d
             cStats->gcHeapObjectsDenseArray += thingSize;
         } else if (obj->isSlowArray()) {
             cStats->gcHeapObjectsSlowArray += thingSize;
         } else if (obj->isCrossCompartmentWrapper()) {
             cStats->gcHeapObjectsCrossCompartmentWrapper += thingSize;
         } else {
             cStats->gcHeapObjectsOrdinary += thingSize;
         }
-        size_t slotsSize, elementsSize, argumentsDataSize, regExpStaticsSize,
-               propertyIteratorDataSize;
-        obj->sizeOfExcludingThis(rtStats->mallocSizeOf, &slotsSize, &elementsSize,
-                                 &argumentsDataSize, &regExpStaticsSize,
-                                 &propertyIteratorDataSize);
-        cStats->objectsExtraSlots += slotsSize;
-        cStats->objectsExtraElements += elementsSize;
-        cStats->objectsExtraArgumentsData += argumentsDataSize;
-        cStats->objectsExtraRegExpStatics += regExpStaticsSize;
-        cStats->objectsExtraPropertyIteratorData += propertyIteratorDataSize;
 
+        ObjectsExtraSizes objectsExtra;
+        obj->sizeOfExcludingThis(rtStats->mallocSizeOf, &objectsExtra);
+        cStats->objectsExtra.add(objectsExtra);
+
+        // JSObject::sizeOfExcludingThis() doesn't measure objectsExtraPrivate,
+        // so we do it here.
         if (ObjectPrivateVisitor *opv = closure->opv) {
             js::Class *clazz = js::GetObjectClass(obj);
             if (clazz->flags & JSCLASS_HAS_PRIVATE &&
                 clazz->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS)
             {
-                cStats->objectsExtraPrivate += opv->sizeOfIncludingThis(GetObjectPrivate(obj));
+                cStats->objectsExtra.private_ += opv->sizeOfIncludingThis(GetObjectPrivate(obj));
             }
         }
         break;
     }
     case JSTRACE_STRING:
     {
         JSString *str = static_cast<JSString *>(thing);
 
@@ -249,17 +245,17 @@ StatsCellCallback(JSRuntime *rt, void *d
 # endif
 #endif
         break;
     }
     case JSTRACE_TYPE_OBJECT:
     {
         types::TypeObject *obj = static_cast<types::TypeObject *>(thing);
         cStats->gcHeapTypeObjects += thingSize;
-        obj->sizeOfExcludingThis(&cStats->typeInferenceSizes, rtStats->mallocSizeOf);
+        cStats->typeInference.typeObjects += obj->sizeOfExcludingThis(rtStats->mallocSizeOf);
         break;
     }
 #if JS_HAS_XML_SUPPORT
     case JSTRACE_XML:
     {
         cStats->gcHeapXML += thingSize;
         break;
     }
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -103,214 +103,16 @@ JS_ObjectToInnerObject(JSContext *cx, JS
 
 JS_FRIEND_API(JSObject *)
 JS_ObjectToOuterObject(JSContext *cx, JSObject *obj_)
 {
     Rooted<JSObject*> obj(cx, obj_);
     return GetOuterObject(cx, obj);
 }
 
-#if JS_HAS_TOSOURCE
-static JSBool
-obj_toSource(JSContext *cx, unsigned argc, Value *vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    JS_CHECK_RECURSION(cx, return JS_FALSE);
-
-    RootedObject obj(cx, ToObject(cx, args.thisv()));
-    if (!obj)
-        return false;
-
-    /* If outermost, we need parentheses to be an expression, not a block. */
-    bool outermost = (cx->cycleDetectorSet.count() == 0);
-
-    AutoCycleDetector detector(cx, obj);
-    if (!detector.init())
-        return false;
-    if (detector.foundCycle()) {
-        JSString *str = js_NewStringCopyZ(cx, "{}");
-        if (!str)
-            return false;
-        args.rval().setString(str);
-        return true;
-    }
-
-    StringBuffer buf(cx);
-    if (outermost && !buf.append('('))
-        return false;
-    if (!buf.append('{'))
-        return false;
-
-    Value val[2];
-    PodArrayZero(val);
-    AutoArrayRooter tvr2(cx, ArrayLength(val), val);
-
-    JSString *gsop[2];
-    SkipRoot skipGsop(cx, &gsop, 2);
-
-    AutoIdVector idv(cx);
-    if (!GetPropertyNames(cx, obj, JSITER_OWNONLY, &idv))
-        return false;
-
-    bool comma = false;
-    for (size_t i = 0; i < idv.length(); ++i) {
-        RootedId id(cx, idv[i]);
-        RootedObject obj2(cx);
-        RootedShape shape(cx);
-        if (!JSObject::lookupGeneric(cx, obj, id, &obj2, &shape))
-            return false;
-
-        /*  Decide early whether we prefer get/set or old getter/setter syntax. */
-        int valcnt = 0;
-        if (shape) {
-            bool doGet = true;
-            if (obj2->isNative()) {
-                unsigned attrs = shape->attributes();
-                if (attrs & JSPROP_GETTER) {
-                    doGet = false;
-                    val[valcnt] = shape->getterValue();
-                    gsop[valcnt] = cx->names().get;
-                    valcnt++;
-                }
-                if (attrs & JSPROP_SETTER) {
-                    doGet = false;
-                    val[valcnt] = shape->setterValue();
-                    gsop[valcnt] = cx->names().set;
-                    valcnt++;
-                }
-            }
-            if (doGet) {
-                valcnt = 1;
-                gsop[0] = NULL;
-                MutableHandleValue vp = MutableHandleValue::fromMarkedLocation(&val[0]);
-                if (!JSObject::getGeneric(cx, obj, obj, id, vp))
-                    return false;
-            }
-        }
-
-        /* Convert id to a linear string. */
-        RawString s = ToString(cx, IdToValue(id));
-        if (!s)
-            return false;
-        Rooted<JSLinearString*> idstr(cx, s->ensureLinear(cx));
-        if (!idstr)
-            return false;
-
-        /*
-         * If id is a string that's not an identifier, or if it's a negative
-         * integer, then it must be quoted.
-         */
-        if (JSID_IS_ATOM(id)
-            ? !IsIdentifier(idstr)
-            : (!JSID_IS_INT(id) || JSID_TO_INT(id) < 0))
-        {
-            s = js_QuoteString(cx, idstr, jschar('\''));
-            if (!s || !(idstr = s->ensureLinear(cx)))
-                return false;
-        }
-
-        for (int j = 0; j < valcnt; j++) {
-            /*
-             * Censor an accessor descriptor getter or setter part if it's
-             * undefined.
-             */
-            if (gsop[j] && val[j].isUndefined())
-                continue;
-
-            /* Convert val[j] to its canonical source form. */
-            RootedString valstr(cx, js_ValueToSource(cx, val[j]));
-            if (!valstr)
-                return false;
-            const jschar *vchars = valstr->getChars(cx);
-            if (!vchars)
-                return false;
-            size_t vlength = valstr->length();
-
-            /*
-             * Remove '(function ' from the beginning of valstr and ')' from the
-             * end so that we can put "get" in front of the function definition.
-             */
-            if (gsop[j] && IsFunctionObject(val[j])) {
-                const jschar *start = vchars;
-                const jschar *end = vchars + vlength;
-
-                uint8_t parenChomp = 0;
-                if (vchars[0] == '(') {
-                    vchars++;
-                    parenChomp = 1;
-                }
-
-                /* Try to jump "function" keyword. */
-                if (vchars)
-                    vchars = js_strchr_limit(vchars, ' ', end);
-
-                /*
-                 * Jump over the function's name: it can't be encoded as part
-                 * of an ECMA getter or setter.
-                 */
-                if (vchars)
-                    vchars = js_strchr_limit(vchars, '(', end);
-
-                if (vchars) {
-                    if (*vchars == ' ')
-                        vchars++;
-                    vlength = end - vchars - parenChomp;
-                } else {
-                    gsop[j] = NULL;
-                    vchars = start;
-                }
-            }
-
-            if (comma && !buf.append(", "))
-                return false;
-            comma = true;
-
-            if (gsop[j])
-                if (!buf.append(gsop[j]) || !buf.append(' '))
-                    return false;
-
-            if (!buf.append(idstr))
-                return false;
-            if (!buf.append(gsop[j] ? ' ' : ':'))
-                return false;
-
-            if (!buf.append(vchars, vlength))
-                return false;
-        }
-    }
-
-    if (!buf.append('}'))
-        return false;
-    if (outermost && !buf.append(')'))
-        return false;
-
-    RawString str = buf.finishString();
-    if (!str)
-        return false;
-    args.rval().setString(str);
-    return true;
-}
-#endif /* JS_HAS_TOSOURCE */
-
-JSString *
-js::obj_toStringHelper(JSContext *cx, JSObject *obj)
-{
-    if (obj->isProxy())
-        return Proxy::obj_toString(cx, obj);
-
-    StringBuffer sb(cx);
-    const char *className = obj->getClass()->name;
-    if (!sb.append("[object ") || !sb.appendInflated(className, strlen(className)) ||
-        !sb.append("]"))
-    {
-        return NULL;
-    }
-    return sb.finishString();
-}
-
 JSObject *
 js::NonNullObject(JSContext *cx, const Value &v)
 {
     if (v.isPrimitive()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT);
         return NULL;
     }
     return &v.toObject();
@@ -329,196 +131,16 @@ js::InformalValueTypeName(const Value &v
         return "boolean";
     if (v.isNull())
         return "null";
     if (v.isUndefined())
         return "undefined";
     return "value";
 }
 
-/* ES5 15.2.4.2.  Note steps 1 and 2 are errata. */
-static JSBool
-obj_toString(JSContext *cx, unsigned argc, Value *vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-
-    /* Step 1. */
-    if (args.thisv().isUndefined()) {
-        args.rval().setString(cx->names().objectUndefined);
-        return true;
-    }
-
-    /* Step 2. */
-    if (args.thisv().isNull()) {
-        args.rval().setString(cx->names().objectNull);
-        return true;
-    }
-
-    /* Step 3. */
-    JSObject *obj = ToObject(cx, args.thisv());
-    if (!obj)
-        return false;
-
-    /* Steps 4-5. */
-    JSString *str = js::obj_toStringHelper(cx, obj);
-    if (!str)
-        return false;
-    args.rval().setString(str);
-    return true;
-}
-
-/* ES5 15.2.4.3. */
-static JSBool
-obj_toLocaleString(JSContext *cx, unsigned argc, Value *vp)
-{
-    JS_CHECK_RECURSION(cx, return false);
-
-    CallArgs args = CallArgsFromVp(argc, vp);
-
-    /* Step 1. */
-    JSObject *obj = ToObject(cx, args.thisv());
-    if (!obj)
-        return false;
-
-    /* Steps 2-4. */
-    RootedId id(cx, NameToId(cx->names().toString));
-    return obj->callMethod(cx, id, 0, NULL, args.rval());
-}
-
-static JSBool
-obj_valueOf(JSContext *cx, unsigned argc, Value *vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    JSObject *obj = ToObject(cx, args.thisv());
-    if (!obj)
-        return false;
-    args.rval().setObject(*obj);
-    return true;
-}
-
-#if JS_HAS_OBJ_WATCHPOINT
-
-static JSBool
-obj_watch_handler(JSContext *cx, JSObject *obj_, jsid id_, jsval old,
-                  jsval *nvp, void *closure)
-{
-    RootedObject obj(cx, obj_);
-    RootedId id(cx, id_);
-
-    /* Avoid recursion on (obj, id) already being watched on cx. */
-    AutoResolving resolving(cx, obj, id, AutoResolving::WATCH);
-    if (resolving.alreadyStarted())
-        return true;
-
-    JSObject *callable = (JSObject *)closure;
-    Value argv[] = { IdToValue(id), old, *nvp };
-    return Invoke(cx, ObjectValue(*obj), ObjectOrNullValue(callable), ArrayLength(argv), argv, nvp);
-}
-
-static JSBool
-obj_watch(JSContext *cx, unsigned argc, Value *vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-
-    if (argc <= 1) {
-        js_ReportMissingArg(cx, args.calleev(), 1);
-        return false;
-    }
-
-    RootedObject callable(cx, ValueToCallable(cx, &args[1]));
-    if (!callable)
-        return false;
-
-    RootedId propid(cx);
-    if (!ValueToId(cx, args[0], propid.address()))
-        return false;
-
-    RootedObject obj(cx, ToObject(cx, args.thisv()));
-    if (!obj)
-        return false;
-
-    RootedValue tmp(cx);
-    unsigned attrs;
-    if (!CheckAccess(cx, obj, propid, JSACC_WATCH, &tmp, &attrs))
-        return false;
-
-    args.rval().setUndefined();
-
-    if (obj->isDenseArray() && !JSObject::makeDenseArraySlow(cx, obj))
-        return false;
-    return JS_SetWatchPoint(cx, obj, propid, obj_watch_handler, callable);
-}
-
-static JSBool
-obj_unwatch(JSContext *cx, unsigned argc, Value *vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-
-    RootedObject obj(cx, ToObject(cx, args.thisv()));
-    if (!obj)
-        return false;
-    args.rval().setUndefined();
-    jsid id;
-    if (argc != 0) {
-        if (!ValueToId(cx, args[0], &id))
-            return false;
-    } else {
-        id = JSID_VOID;
-    }
-    return JS_ClearWatchPoint(cx, obj, id, NULL, NULL);
-}
-
-#endif /* JS_HAS_OBJ_WATCHPOINT */
-
-/*
- * Prototype and property query methods, to complement the 'in' and
- * 'instanceof' operators.
- */
-
-/* ECMA 15.2.4.5. */
-static JSBool
-obj_hasOwnProperty(JSContext *cx, unsigned argc, Value *vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-
-    /* Step 1. */
-    RootedId id(cx);
-    if (!ValueToId(cx, args.length() ? args[0] : UndefinedValue(), id.address()))
-        return false;
-
-    /* Step 2. */
-    RootedObject obj(cx, ToObject(cx, args.thisv()));
-    if (!obj)
-        return false;
-    return js_HasOwnPropertyHelper(cx, obj->getOps()->lookupGeneric, obj, id, args.rval());
-}
-
-JSBool
-js_HasOwnPropertyHelper(JSContext *cx, LookupGenericOp lookup, HandleObject obj,
-                        HandleId id, MutableHandleValue rval)
-{
-    /* Non-standard code for proxies. */
-    RootedObject obj2(cx);
-    RootedShape prop(cx);
-    if (obj->isProxy()) {
-        bool has;
-        if (!Proxy::hasOwn(cx, obj, id, &has))
-            return false;
-        rval.setBoolean(has);
-        return true;
-    }
-
-    /* Step 3. */
-    if (!js_HasOwnProperty(cx, lookup, obj, id, &obj2, &prop))
-        return false;
-    /* Step 4,5. */
-    rval.setBoolean(!!prop);
-    return true;
-}
-
 JSBool
 js_HasOwnProperty(JSContext *cx, LookupGenericOp lookup, HandleObject obj, HandleId id,
                   MutableHandleObject objp, MutableHandleShape propp)
 {
     JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED);
     if (lookup) {
         if (!lookup(cx, obj, id, objp, propp))
             return false;
@@ -540,270 +162,16 @@ js_HasOwnProperty(JSContext *cx, LookupG
             return false;
     }
 
     if (outer != objp)
         propp.set(NULL);
     return true;
 }
 
-/* ES5 15.2.4.6. */
-static JSBool
-obj_isPrototypeOf(JSContext *cx, unsigned argc, Value *vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-
-    /* Step 1. */
-    if (args.length() < 1 || !args[0].isObject()) {
-        args.rval().setBoolean(false);
-        return true;
-    }
-
-    /* Step 2. */
-    RootedObject obj(cx, ToObject(cx, args.thisv()));
-    if (!obj)
-        return false;
-
-    /* Step 3. */
-    bool isDelegate;
-    if (!IsDelegate(cx, obj, args[0], &isDelegate))
-        return false;
-    args.rval().setBoolean(isDelegate);
-    return true;
-}
-
-/* ES5 15.2.4.7. */
-static JSBool
-obj_propertyIsEnumerable(JSContext *cx, unsigned argc, Value *vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-
-    /* Step 1. */
-    RootedId id(cx);
-    if (!ValueToId(cx, args.length() ? args[0] : UndefinedValue(), id.address()))
-        return false;
-
-    /* Step 2. */
-    RootedObject obj(cx, ToObject(cx, args.thisv()));
-    if (!obj)
-        return false;
-
-    /* Steps 3-5. */
-    return js_PropertyIsEnumerable(cx, obj, id, args.rval().address());
-}
-
-JSBool
-js_PropertyIsEnumerable(JSContext *cx, HandleObject obj, HandleId id, Value *vp)
-{
-    RootedObject pobj(cx);
-    RootedShape prop(cx);
-    if (!JSObject::lookupGeneric(cx, obj, id, &pobj, &prop))
-        return false;
-
-    if (!prop) {
-        vp->setBoolean(false);
-        return true;
-    }
-
-    /*
-     * ECMA spec botch: return false unless hasOwnProperty. Leaving "own" out
-     * of propertyIsEnumerable's name was a mistake.
-     */
-    if (pobj != obj) {
-        vp->setBoolean(false);
-        return true;
-    }
-
-    unsigned attrs;
-    if (!JSObject::getGenericAttributes(cx, pobj, id, &attrs))
-        return false;
-
-    vp->setBoolean((attrs & JSPROP_ENUMERATE) != 0);
-    return true;
-}
-
-#if OLD_GETTER_SETTER_METHODS
-
-enum DefineType { Getter, Setter };
-
-template<DefineType Type>
-static bool
-DefineAccessor(JSContext *cx, unsigned argc, Value *vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    if (!BoxNonStrictThis(cx, args))
-        return false;
-
-    if (args.length() < 2 || !js_IsCallable(args[1])) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
-                             JSMSG_BAD_GETTER_OR_SETTER,
-                             Type == Getter ? js_getter_str : js_setter_str);
-        return false;
-    }
-
-    RootedId id(cx);
-    if (!ValueToId(cx, args[0], id.address()))
-        return false;
-
-    RootedObject descObj(cx, NewBuiltinClassInstance(cx, &ObjectClass));
-    if (!descObj)
-        return false;
-
-    JSAtomState &names = cx->names();
-    RootedValue trueVal(cx, BooleanValue(true));
-
-    /* enumerable: true */
-    if (!JSObject::defineProperty(cx, descObj, names.enumerable, trueVal))
-        return false;
-
-    /* configurable: true */
-    if (!JSObject::defineProperty(cx, descObj, names.configurable, trueVal))
-        return false;
-
-    /* enumerable: true */
-    PropertyName *acc = (Type == Getter) ? names.get : names.set;
-    RootedValue accessorVal(cx, args[1]);
-    if (!JSObject::defineProperty(cx, descObj, acc, accessorVal))
-        return false;
-
-    RootedObject thisObj(cx, &args.thisv().toObject());
-
-    JSBool dummy;
-    if (!js_DefineOwnProperty(cx, thisObj, id, ObjectValue(*descObj), &dummy)) {
-        return false;
-    }
-    args.rval().setUndefined();
-    return true;
-}
-
-JS_FRIEND_API(JSBool)
-js::obj_defineGetter(JSContext *cx, unsigned argc, Value *vp)
-{
-    return DefineAccessor<Getter>(cx, argc, vp);
-}
-
-JS_FRIEND_API(JSBool)
-js::obj_defineSetter(JSContext *cx, unsigned argc, Value *vp)
-{
-    return DefineAccessor<Setter>(cx, argc, vp);
-}
-
-static JSBool
-obj_lookupGetter(JSContext *cx, unsigned argc, Value *vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-
-    RootedId id(cx);
-    if (!ValueToId(cx, args.length() ? args[0] : UndefinedValue(), id.address()))
-        return JS_FALSE;
-    RootedObject obj(cx, ToObject(cx, args.thisv()));
-    if (!obj)
-        return JS_FALSE;
-    if (obj->isProxy()) {
-        // The vanilla getter lookup code below requires that the object is
-        // native. Handle proxies separately.
-        args.rval().setUndefined();
-        AutoPropertyDescriptorRooter desc(cx);
-        if (!Proxy::getPropertyDescriptor(cx, obj, id, false, &desc))
-            return JS_FALSE;
-        if (desc.obj && (desc.attrs & JSPROP_GETTER) && desc.getter)
-            args.rval().set(CastAsObjectJsval(desc.getter));
-        return JS_TRUE;
-    }
-    RootedObject pobj(cx);
-    RootedShape shape(cx);
-    if (!JSObject::lookupGeneric(cx, obj, id, &pobj, &shape))
-        return JS_FALSE;
-    args.rval().setUndefined();
-    if (shape) {
-        if (pobj->isNative()) {
-            if (shape->hasGetterValue())
-                args.rval().set(shape->getterValue());
-        }
-    }
-    return JS_TRUE;
-}
-
-static JSBool
-obj_lookupSetter(JSContext *cx, unsigned argc, Value *vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-
-    RootedId id(cx);
-    if (!ValueToId(cx, args.length() ? args[0] : UndefinedValue(), id.address()))
-        return JS_FALSE;
-    RootedObject obj(cx, ToObject(cx, args.thisv()));
-    if (!obj)
-        return JS_FALSE;
-    if (obj->isProxy()) {
-        // The vanilla setter lookup code below requires that the object is
-        // native. Handle proxies separately.
-        args.rval().setUndefined();
-        AutoPropertyDescriptorRooter desc(cx);
-        if (!Proxy::getPropertyDescriptor(cx, obj, id, false, &desc))
-            return JS_FALSE;
-        if (desc.obj && (desc.attrs & JSPROP_SETTER) && desc.setter)
-            args.rval().set(CastAsObjectJsval(desc.setter));
-        return JS_TRUE;
-    }
-    RootedObject pobj(cx);
-    RootedShape shape(cx);
-    if (!JSObject::lookupGeneric(cx, obj, id, &pobj, &shape))
-        return JS_FALSE;
-    args.rval().setUndefined();
-    if (shape) {
-        if (pobj->isNative()) {
-            if (shape->hasSetterValue())
-                args.rval().set(shape->setterValue());
-        }
-    }
-    return JS_TRUE;
-}
-#endif /* OLD_GETTER_SETTER_METHODS */
-
-/* ES5 15.2.3.2. */
-JSBool
-obj_getPrototypeOf(JSContext *cx, unsigned argc, Value *vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-
-    /* Step 1. */
-    if (args.length() == 0) {
-        js_ReportMissingArg(cx, args.calleev(), 0);
-        return false;
-    }
-
-    if (args[0].isPrimitive()) {
-        RootedValue val(cx, args[0]);
-        char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, val, NullPtr());
-        if (!bytes)
-            return false;
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
-                             JSMSG_UNEXPECTED_TYPE, bytes, "not an object");
-        js_free(bytes);
-        return false;
-    }
-
-    /* Step 2. */
-
-    /*
-     * Implement [[Prototype]]-getting -- particularly across compartment
-     * boundaries -- by calling a cached __proto__ getter function.
-     */
-    InvokeArgsGuard nested;
-    if (!cx->stack.pushInvokeArgs(cx, 0, &nested))
-        return false;
-    nested.setCallee(cx->global()->protoGetter());
-    nested.setThis(args[0]);
-    if (!Invoke(cx, nested))
-        return false;
-    args.rval().set(nested.rval());
-    return true;
-}
-
 bool
 js::NewPropertyDescriptorObject(JSContext *cx, const PropertyDescriptor *desc, Value *vp)
 {
     if (!desc->obj) {
         vp->setUndefined();
         return true;
     }
 
@@ -952,65 +320,16 @@ js::GetFirstArgumentAsObject(JSContext *
         js_free(bytes);
         return false;
     }
 
     objp.set(&v.toObject());
     return true;
 }
 
-static JSBool
-obj_getOwnPropertyDescriptor(JSContext *cx, unsigned argc, Value *vp)
-{
-    RootedObject obj(cx);
-    if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.getOwnPropertyDescriptor", &obj))
-        return JS_FALSE;
-    RootedId id(cx);
-    if (!ValueToId(cx, argc >= 2 ? vp[3] : UndefinedValue(), id.address()))
-        return JS_FALSE;
-    return GetOwnPropertyDescriptor(cx, obj, id, vp);
-}
-
-static JSBool
-obj_keys(JSContext *cx, unsigned argc, Value *vp)
-{
-    RootedObject obj(cx);
-    if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.keys", &obj))
-        return false;
-
-    AutoIdVector props(cx);
-    if (!GetPropertyNames(cx, obj, JSITER_OWNONLY, &props))
-        return false;
-
-    AutoValueVector vals(cx);
-    if (!vals.reserve(props.length()))
-        return false;
-    for (size_t i = 0, len = props.length(); i < len; i++) {
-        jsid id = props[i];
-        if (JSID_IS_STRING(id)) {
-            vals.infallibleAppend(StringValue(JSID_TO_STRING(id)));
-        } else if (JSID_IS_INT(id)) {
-            JSString *str = Int32ToString(cx, JSID_TO_INT(id));
-            if (!str)
-                return false;
-            vals.infallibleAppend(StringValue(str));
-        } else {
-            JS_ASSERT(JSID_IS_OBJECT(id));
-        }
-    }
-
-    JS_ASSERT(props.length() <= UINT32_MAX);
-    JSObject *aobj = NewDenseCopiedArray(cx, uint32_t(vals.length()), vals.begin());
-    if (!aobj)
-        return false;
-    vp->setObject(*aobj);
-
-    return true;
-}
-
 static bool
 HasProperty(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp, bool *foundp)
 {
     if (!JSObject::hasProperty(cx, obj, id, foundp, JSRESOLVE_QUALIFIED))
         return false;
     if (!*foundp) {
         vp.setUndefined();
         return true;
@@ -1629,37 +948,16 @@ js_DefineOwnProperty(JSContext *cx, Hand
 
     bool rval;
     if (!DefineProperty(cx, obj, id, *desc, true, &rval))
         return false;
     *bp = !!rval;
     return true;
 }
 
-/* ES5 15.2.3.6: Object.defineProperty(O, P, Attributes) */
-static JSBool
-obj_defineProperty(JSContext *cx, unsigned argc, Value *vp)
-{
-    RootedObject obj(cx);
-    if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.defineProperty", &obj))
-        return false;
-
-    RootedId id(cx);
-    if (!ValueToId(cx, argc >= 2 ? vp[3] : UndefinedValue(), id.address()))
-        return JS_FALSE;
-
-    const Value descval = argc >= 3 ? vp[4] : UndefinedValue();
-
-    JSBool junk;
-    if (!js_DefineOwnProperty(cx, obj, id, descval, &junk))
-        return false;
-
-    vp->setObject(*obj);
-    return true;
-}
 
 bool
 js::ReadPropertyDescriptors(JSContext *cx, HandleObject props, bool checkAccessors,
                             AutoIdVector *ids, AutoPropDescArrayRooter *descs)
 {
     if (!GetPropertyNames(cx, props, JSITER_OWNONLY, ids))
         return false;
 
@@ -1696,163 +994,16 @@ DefineProperties(JSContext *cx, HandleOb
 }
 
 extern JSBool
 js_PopulateObject(JSContext *cx, HandleObject newborn, HandleObject props)
 {
     return DefineProperties(cx, newborn, props);
 }
 
-/* ES5 15.2.3.7: Object.defineProperties(O, Properties) */
-static JSBool
-obj_defineProperties(JSContext *cx, unsigned argc, Value *vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-
-    /* Steps 1 and 7. */
-    RootedObject obj(cx);
-    if (!GetFirstArgumentAsObject(cx, args.length(), vp, "Object.defineProperties", &obj))
-        return false;
-    args.rval().setObject(*obj);
-
-    /* Step 2. */
-    if (args.length() < 2) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
-                             "Object.defineProperties", "0", "s");
-        return false;
-    }
-    RootedValue val(cx, args[1]);
-    RootedObject props(cx, ToObject(cx, val));
-    if (!props)
-        return false;
-
-    /* Steps 3-6. */
-    return DefineProperties(cx, obj, props);
-}
-
-/* ES5 15.2.3.5: Object.create(O [, Properties]) */
-static JSBool
-obj_create(JSContext *cx, unsigned argc, Value *vp)
-{
-    if (argc == 0) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
-                             "Object.create", "0", "s");
-        return false;
-    }
-
-    CallArgs args = CallArgsFromVp(argc, vp);
-    RootedValue v(cx, args[0]);
-    if (!v.isObjectOrNull()) {
-        char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NullPtr());
-        if (!bytes)
-            return false;
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNEXPECTED_TYPE,
-                             bytes, "not an object or null");
-        js_free(bytes);
-        return false;
-    }
-
-    JSObject *proto = v.toObjectOrNull();
-#if JS_HAS_XML_SUPPORT
-    if (proto && proto->isXML()) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_XML_PROTO_FORBIDDEN);
-        return false;
-    }
-#endif
-
-    /*
-     * Use the callee's global as the parent of the new object to avoid dynamic
-     * scoping (i.e., using the caller's global).
-     */
-    RootedObject obj(cx, NewObjectWithGivenProto(cx, &ObjectClass, proto, &args.callee().global()));
-    if (!obj)
-        return false;
-
-    /* Don't track types or array-ness for objects created here. */
-    MarkTypeObjectUnknownProperties(cx, obj->type());
-
-    /* 15.2.3.5 step 4. */
-    if (args.hasDefined(1)) {
-        if (args[1].isPrimitive()) {
-            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT);
-            return false;
-        }
-
-        RootedObject props(cx, &args[1].toObject());
-        if (!DefineProperties(cx, obj, props))
-            return false;
-    }
-
-    /* 5. Return obj. */
-    args.rval().setObject(*obj);
-    return true;
-}
-
-static JSBool
-obj_getOwnPropertyNames(JSContext *cx, unsigned argc, Value *vp)
-{
-    RootedObject obj(cx);
-    if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.getOwnPropertyNames", &obj))
-        return false;
-
-    AutoIdVector keys(cx);
-    if (!GetPropertyNames(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN, &keys))
-        return false;
-
-    AutoValueVector vals(cx);
-    if (!vals.resize(keys.length()))
-        return false;
-
-    for (size_t i = 0, len = keys.length(); i < len; i++) {
-         jsid id = keys[i];
-         if (JSID_IS_INT(id)) {
-             JSString *str = Int32ToString(cx, JSID_TO_INT(id));
-             if (!str)
-                 return false;
-             vals[i].setString(str);
-         } else if (JSID_IS_ATOM(id)) {
-             vals[i].setString(JSID_TO_STRING(id));
-         } else {
-             vals[i].setObject(*JSID_TO_OBJECT(id));
-         }
-    }
-
-    JSObject *aobj = NewDenseCopiedArray(cx, vals.length(), vals.begin());
-    if (!aobj)
-        return false;
-
-    vp->setObject(*aobj);
-    return true;
-}
-
-static JSBool
-obj_isExtensible(JSContext *cx, unsigned argc, Value *vp)
-{
-    RootedObject obj(cx);
-    if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.isExtensible", &obj))
-        return false;
-
-    vp->setBoolean(obj->isExtensible());
-    return true;
-}
-
-static JSBool
-obj_preventExtensions(JSContext *cx, unsigned argc, Value *vp)
-{
-    RootedObject obj(cx);
-    if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.preventExtensions", &obj))
-        return false;
-
-    vp->setObject(*obj);
-    if (!obj->isExtensible())
-        return true;
-
-    return obj->preventExtensions(cx);
-}
-
 /* static */ inline unsigned
 JSObject::getSealedOrFrozenAttributes(unsigned attrs, ImmutabilityType it)
 {
     /* Make all attributes permanent; if freezing, make data attributes read-only. */
     if (it == FREEZE && !(attrs & (JSPROP_GETTER | JSPROP_SETTER)))
         return JSPROP_PERMANENT | JSPROP_READONLY;
     return JSPROP_PERMANENT;
 }
@@ -1968,153 +1119,30 @@ JSObject::isSealedOrFrozen(JSContext *cx
         }
     }
 
     /* All properties checked out. This object is sealed/frozen. */
     *resultp = true;
     return true;
 }
 
-static JSBool
-obj_freeze(JSContext *cx, unsigned argc, Value *vp)
-{
-    RootedObject obj(cx);
-    if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.freeze", &obj))
-        return false;
-
-    vp->setObject(*obj);
-
-    return JSObject::freeze(cx, obj);
-}
-
-static JSBool
-obj_isFrozen(JSContext *cx, unsigned argc, Value *vp)
-{
-    RootedObject obj(cx);
-    if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.preventExtensions", &obj))
-        return false;
-
-    bool frozen;
-    if (!JSObject::isFrozen(cx, obj, &frozen))
-        return false;
-    vp->setBoolean(frozen);
-    return true;
-}
-
-static JSBool
-obj_seal(JSContext *cx, unsigned argc, Value *vp)
-{
-    RootedObject obj(cx);
-    if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.seal", &obj))
-        return false;
-
-    vp->setObject(*obj);
-
-    return JSObject::seal(cx, obj);
-}
-
-static JSBool
-obj_isSealed(JSContext *cx, unsigned argc, Value *vp)
-{
-    RootedObject obj(cx);
-    if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.isSealed", &obj))
-        return false;
-
-    bool sealed;
-    if (!JSObject::isSealed(cx, obj, &sealed))
-        return false;
-    vp->setBoolean(sealed);
-    return true;
-}
-
-JSFunctionSpec object_methods[] = {
-#if JS_HAS_TOSOURCE
-    JS_FN(js_toSource_str,             obj_toSource,                0,0),
-#endif
-    JS_FN(js_toString_str,             obj_toString,                0,0),
-    JS_FN(js_toLocaleString_str,       obj_toLocaleString,          0,0),
-    JS_FN(js_valueOf_str,              obj_valueOf,                 0,0),
-#if JS_HAS_OBJ_WATCHPOINT
-    JS_FN(js_watch_str,                obj_watch,                   2,0),
-    JS_FN(js_unwatch_str,              obj_unwatch,                 1,0),
-#endif
-    JS_FN(js_hasOwnProperty_str,       obj_hasOwnProperty,          1,0),
-    JS_FN(js_isPrototypeOf_str,        obj_isPrototypeOf,           1,0),
-    JS_FN(js_propertyIsEnumerable_str, obj_propertyIsEnumerable,    1,0),
-#if OLD_GETTER_SETTER_METHODS
-    JS_FN(js_defineGetter_str,         js::obj_defineGette