merge mozilla-inbound to mozilla-central
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 21 May 2014 13:45:46 +0200
changeset 184081 50fb8c4db2fd08f54a2a769713a914e5b45f93e3
parent 183994 a6f36c31aa8f0832ab85482ca85c53ee53153738 (current diff)
parent 184080 c3f1ab75ea51699a8734ff2435183548da7b9117 (diff)
child 184121 97943ef127f484e0171fe735cc5e2628d45ed172
push id26810
push usercbook@mozilla.com
push dateWed, 21 May 2014 11:46:36 +0000
treeherdermozilla-central@50fb8c4db2fd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone32.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central
dom/base/test/test_nondomexception.html
js/src/jsgc.cpp
--- a/accessible/src/jsat/AccessFu.jsm
+++ b/accessible/src/jsat/AccessFu.jsm
@@ -913,20 +913,28 @@ var Input = {
       return;
     }
 
     aEvent.preventDefault();
     aEvent.stopPropagation();
   },
 
   moveToPoint: function moveToPoint(aRule, aX, aY) {
-    let mm = Utils.getMessageManager(Utils.CurrentBrowser);
-    mm.sendAsyncMessage('AccessFu:MoveToPoint', {rule: aRule,
-                                                 x: aX, y: aY,
-                                                 origin: 'top'});
+    // XXX: Bug 1013408 - There is no alignment between the chrome window's
+    // viewport size and the content viewport size in Android. This makes
+    // sending mouse events beyond its bounds impossible.
+    if (Utils.MozBuildApp === 'mobile/android') {
+      let mm = Utils.getMessageManager(Utils.CurrentBrowser);
+      mm.sendAsyncMessage('AccessFu:MoveToPoint',
+        {rule: aRule, x: aX, y: aY, origin: 'top'});
+    } else {
+      let win = Utils.win;
+      Utils.winUtils.sendMouseEvent('mousemove',
+        aX - win.mozInnerScreenX, aY - win.mozInnerScreenY, 0, 0, 0);
+    }
   },
 
   moveCursor: function moveCursor(aAction, aRule, aInputType) {
     let mm = Utils.getMessageManager(Utils.CurrentBrowser);
     mm.sendAsyncMessage('AccessFu:MoveCursor',
                         {action: aAction, rule: aRule,
                          origin: 'top', inputType: aInputType});
   },
@@ -994,21 +1002,19 @@ var Input = {
     mm.sendAsyncMessage('AccessFu:Scroll', {page: aPage, horizontal: aHorizontal, origin: 'top'});
   },
 
   doScroll: function doScroll(aDetails) {
     let horizontal = aDetails.horizontal;
     let page = aDetails.page;
     let p = AccessFu.adjustContentBounds(aDetails.bounds, Utils.CurrentBrowser,
                                          true, true).center();
-    let wu = Utils.win.QueryInterface(Ci.nsIInterfaceRequestor).
-      getInterface(Ci.nsIDOMWindowUtils);
-    wu.sendWheelEvent(p.x, p.y,
-                      horizontal ? page : 0, horizontal ? 0 : page, 0,
-                      Utils.win.WheelEvent.DOM_DELTA_PAGE, 0, 0, 0, 0);
+    Utils.winUtils.sendWheelEvent(p.x, p.y,
+      horizontal ? page : 0, horizontal ? 0 : page, 0,
+      Utils.win.WheelEvent.DOM_DELTA_PAGE, 0, 0, 0, 0);
   },
 
   get keyMap() {
     delete this.keyMap;
     this.keyMap = {
       a: ['moveNext', 'Anchor'],
       A: ['movePrevious', 'Anchor'],
       b: ['moveNext', 'Button'],
--- a/accessible/src/jsat/ContentControl.jsm
+++ b/accessible/src/jsat/ContentControl.jsm
@@ -40,23 +40,25 @@ this.ContentControl.prototype = {
                        'AccessFu:MoveCaret',
                        'AccessFu:MoveByGranularity'],
 
   start: function cc_start() {
     let cs = this._contentScope.get();
     for (let message of this.messagesOfInterest) {
       cs.addMessageListener(message, this);
     }
+    cs.addEventListener('mousemove', this);
   },
 
   stop: function cc_stop() {
     let cs = this._contentScope.get();
     for (let message of this.messagesOfInterest) {
       cs.removeMessageListener(message, this);
     }
+    cs.removeEventListener('mousemove', this);
   },
 
   get document() {
     return this._contentScope.get().content.document;
   },
 
   get window() {
     return this._contentScope.get().content;
@@ -119,28 +121,32 @@ this.ContentControl.prototype = {
       }
     } else if (!this._childMessageSenders.has(aMessage.target)) {
       // We failed to move, and the message is not from a child, so forward
       // to parent.
       this.sendToParent(aMessage);
     }
   },
 
+  handleEvent: function cc_handleEvent(aEvent) {
+    if (aEvent.type === 'mousemove') {
+      this.handleMoveToPoint(
+        { json: { x: aEvent.screenX, y: aEvent.screenY, rule: 'Simple' } });
+    }
+    if (!Utils.getMessageManager(aEvent.target)) {
+      aEvent.preventDefault();
+    }
+  },
+
   handleMoveToPoint: function cc_handleMoveToPoint(aMessage) {
     let [x, y] = [aMessage.json.x, aMessage.json.y];
     let rule = TraversalRules[aMessage.json.rule];
-    let vc = this.vc;
-    let win = this.window;
 
-    let dpr = win.devicePixelRatio;
+    let dpr = this.window.devicePixelRatio;
     this.vc.moveToPoint(rule, x * dpr, y * dpr, true);
-
-    let delta = Utils.isContentProcess ?
-      { x: x - win.mozInnerScreenX, y: y - win.mozInnerScreenY } : {};
-    this.sendToChild(vc, aMessage, delta);
   },
 
   handleClearCursor: function cc_handleClearCursor(aMessage) {
     let forwarded = this.sendToChild(this.vc, aMessage);
     this.vc.position = null;
     if (!forwarded) {
       this._contentScope.get().sendAsyncMessage('AccessFu:CursorCleared');
     }
--- a/accessible/src/jsat/PointerAdapter.jsm
+++ b/accessible/src/jsat/PointerAdapter.jsm
@@ -105,46 +105,24 @@ let PointerRelay = { // jshint ignore:li
     Logger.debug('PointerRelay.stop');
     delete this.lastPointerMove;
     delete this.onPointerEvent;
     for (let eventType in this._eventsOfInterest) {
       Utils.win.removeEventListener(eventType, this, true, true);
     }
   },
 
-  _suppressPointerMove: function PointerRelay__suppressPointerMove(aChangedTouches) {
-    if (!this.lastPointerMove) {
-      return false;
-    }
-    for (let i = 0; i < aChangedTouches.length; ++i) {
-      let touch = aChangedTouches[i];
-      let lastTouch;
-      try {
-        lastTouch = this.lastPointerMove.identifiedTouch ?
-          this.lastPointerMove.identifiedTouch(touch.identifier) :
-          this.lastPointerMove[i];
-      } catch (x) {
-        // Sometimes touch object can't be accessed after page navigation.
-      }
-      if (!lastTouch || lastTouch.target !== touch.target ||
-        Math.hypot(touch.screenX - lastTouch.screenX, touch.screenY -
-          lastTouch.screenY) / Utils.dpi >= GestureSettings.travelThreshold) {
-        return false;
-      }
-    }
-    return true;
-  },
-
   handleEvent: function PointerRelay_handleEvent(aEvent) {
     // Don't bother with chrome mouse events.
     if (Utils.MozBuildApp === 'browser' &&
       aEvent.view.top instanceof Ci.nsIDOMChromeWindow) {
       return;
     }
-    if (aEvent.mozInputSource === Ci.nsIDOMMouseEvent.MOZ_SOURCE_UNKNOWN) {
+    if (aEvent.mozInputSource === Ci.nsIDOMMouseEvent.MOZ_SOURCE_UNKNOWN ||
+        aEvent.isSynthesized) {
       // Ignore events that are scripted or clicks from the a11y API.
       return;
     }
 
     let changedTouches = aEvent.changedTouches || [{
       identifier: MOUSE_ID,
       screenX: aEvent.screenX,
       screenY: aEvent.screenY,
@@ -159,23 +137,16 @@ let PointerRelay = { // jshint ignore:li
     aEvent.preventDefault();
     aEvent.stopImmediatePropagation();
 
     let type = aEvent.type;
     if (!this._eventsOfInterest[type]) {
       return;
     }
     let pointerType = this._eventMap[type];
-    if (pointerType === 'pointermove') {
-      if (this._suppressPointerMove(changedTouches)) {
-        // Do not fire pointermove more than every POINTERMOVE_THROTTLE.
-        return;
-      }
-      this.lastPointerMove = changedTouches;
-    }
     this.onPointerEvent({
       type: pointerType,
       points: Array.prototype.map.call(changedTouches,
         function mapTouch(aTouch) {
           return {
             identifier: aTouch.identifier,
             x: aTouch.screenX,
             y: aTouch.screenY
--- a/accessible/src/jsat/TraversalRules.jsm
+++ b/accessible/src/jsat/TraversalRules.jsm
@@ -108,16 +108,21 @@ var gSimpleMatchFunc = function gSimpleM
         return false;
       }
     }
     return true;
   }
 
   function isFlatSubtree(acc) {
     for (let child = acc.firstChild; child; child = child.nextSibling) {
+      // text leafs inherit the actionCount of any ancestor that has a click
+      // listener.
+      if ([Roles.TEXT_LEAF, Roles.STATICTEXT].indexOf(child.role) >= 0) {
+        continue;
+      }
       if (child.childCount > 0 || child.actionCount > 0) {
         return false;
       }
     }
     return true;
   }
 
   switch (aAccessible.role) {
--- a/accessible/src/jsat/Utils.jsm
+++ b/accessible/src/jsat/Utils.jsm
@@ -49,16 +49,25 @@ this.Utils = {
 
   get win() {
     if (!this._win) {
       return null;
     }
     return this._win.get();
   },
 
+  get winUtils() {
+    let win = this.win;
+    if (!win) {
+      return null;
+    }
+    return win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(
+      Ci.nsIDOMWindowUtils);
+  },
+
   get AccRetrieval() {
     if (!this._AccRetrieval) {
       this._AccRetrieval = Cc['@mozilla.org/accessibleRetrieval;1'].
         getService(Ci.nsIAccessibleRetrieval);
     }
 
     return this._AccRetrieval;
   },
@@ -176,17 +185,16 @@ this.Utils = {
     return this.stringBundle;
   },
 
   getMessageManager: function getMessageManager(aBrowser) {
     try {
       return aBrowser.QueryInterface(Ci.nsIFrameLoaderOwner).
          frameLoader.messageManager;
     } catch (x) {
-      Logger.logException(x);
       return null;
     }
   },
 
   getViewport: function getViewport(aWindow) {
     switch (this.MozBuildApp) {
       case 'mobile/android':
         return aWindow.BrowserApp.selectedTab.getViewport();
@@ -247,18 +255,17 @@ this.Utils = {
       return new Rect(objX.value, objY.value, objW.value, objH.value);
   },
 
   /**
    * Get current display DPI.
    */
   get dpi() {
     delete this.dpi;
-    this.dpi = this.win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(
-      Ci.nsIDOMWindowUtils).displayDPI;
+    this.dpi = this.winUtils.displayDPI;
     return this.dpi;
   },
 
   isInSubtree: function isInSubtree(aAccessible, aSubTreeRoot) {
     let acc = aAccessible;
     while (acc) {
       if (acc == aSubTreeRoot) {
         return true;
--- a/accessible/tests/mochitest/jsat/doc_traversal.html
+++ b/accessible/tests/mochitest/jsat/doc_traversal.html
@@ -55,17 +55,17 @@
   <div id="button-2-3" tabindex="0" role="button" aria-pressed="false">My little togglebutton</div>
   <div id="button-2-4" tabindex="0" role="spinbutton">ARIA fun</div>
   <h1 id="heading-4" style="display:none">Invisible header</h1>
   <dl id="list-1">
     <dt id="listitem-1-1">Programming Language</dt>
     <dd>A esoteric weapon wielded by only the most formidable warriors,
     for its unrelenting strict power is unfathomable.</dd>
   </dl>
-  <ul id="list-2">
+  <ul id="list-2" onclick="alert('hi');">
     <li id="listitem-2-1">Lists of Programming Languages</li>
     <li id="listitem-2-2">Lisp
       <ol id="list-3">
         <li id="listitem-3-1">Scheme</li>
         <li id="listitem-3-2">Racket</li>
         <li id="listitem-3-3">Clojure</li>
         <li id="listitem-3-4"><strong>Standard</strong> Lisp</li>
         <li id="listitem-3-5"><a id="link-0" href="#">Common</a> Lisp</li>
--- a/b2g/config/tooltool-manifests/linux32/releng.manifest
+++ b/b2g/config/tooltool-manifests/linux32/releng.manifest
@@ -1,14 +1,14 @@
 [
 {
 "size": 51,
 "digest": "c8e40edb314eeabfb92c77cf5ff9a7857033f15dd65a00349bcf9e3e5b75624afc71f733b2ff7e029c20a78313038409c2bd022bf7e5a7e0c487fc2c2d640986",
 "algorithm": "sha512",
 "filename": "setup.sh"
 },
 {
-"size": 165464,
-"digest": "ee87db68eb63e417a366333e34a32d9220232bfb9c3e91b9941513acf0551f2603a836537ffef41a240430200970d83fa9271f55b8b5168d6967be595cdb50db",
+"size": 165226,
+"digest": "79280f7595bc9e1613e05f8b2f0db3798ac739b96191e0f133e8ccd8ad149fedc84a1046e59863574189db28363a01712ae7b368ad1714e30ff88e7ebd5dad23",
 "algorithm": "sha512",
 "filename": "sccache.tar.bz2"
 }
 ]
--- a/b2g/config/tooltool-manifests/linux64/releng.manifest
+++ b/b2g/config/tooltool-manifests/linux64/releng.manifest
@@ -1,14 +1,14 @@
 [
 {
 "size": 51,
 "digest": "c8e40edb314eeabfb92c77cf5ff9a7857033f15dd65a00349bcf9e3e5b75624afc71f733b2ff7e029c20a78313038409c2bd022bf7e5a7e0c487fc2c2d640986",
 "algorithm": "sha512",
 "filename": "setup.sh"
 },
 {
-"size": 165464,
-"digest": "ee87db68eb63e417a366333e34a32d9220232bfb9c3e91b9941513acf0551f2603a836537ffef41a240430200970d83fa9271f55b8b5168d6967be595cdb50db",
+"size": 165226,
+"digest": "79280f7595bc9e1613e05f8b2f0db3798ac739b96191e0f133e8ccd8ad149fedc84a1046e59863574189db28363a01712ae7b368ad1714e30ff88e7ebd5dad23",
 "algorithm": "sha512",
 "filename": "sccache.tar.bz2"
 }
 ]
--- a/b2g/config/tooltool-manifests/macosx64/releng.manifest
+++ b/b2g/config/tooltool-manifests/macosx64/releng.manifest
@@ -10,14 +10,14 @@
 },
 {
 "size": 56126352,
 "digest": "e156e2a39abd5bf272ee30748a6825f22ddd27565b097c66662a2a6f2e9892bc5b4bf30a3552dffbe867dbfc39e7ee086e0b2cd7935f6ea216c0cf936178a88f",
 "algorithm": "sha512",
 "filename": "clang.tar.bz2"
 },
 {
-"size": 165464,
-"digest": "ee87db68eb63e417a366333e34a32d9220232bfb9c3e91b9941513acf0551f2603a836537ffef41a240430200970d83fa9271f55b8b5168d6967be595cdb50db",
+"size": 165226,
+"digest": "79280f7595bc9e1613e05f8b2f0db3798ac739b96191e0f133e8ccd8ad149fedc84a1046e59863574189db28363a01712ae7b368ad1714e30ff88e7ebd5dad23",
 "algorithm": "sha512",
 "filename": "sccache.tar.bz2"
 }
 ]
--- a/browser/base/content/aboutneterror/netError.css
+++ b/browser/base/content/aboutneterror/netError.css
@@ -27,23 +27,24 @@ ul {
   list-style: disc;
 }
 
 #errorPageContainer {
   min-width: 320px;
   max-width: 512px;
 }
 
-#errorTitle {
+#errorTitleText {
   background: url("info.svg") left 0 no-repeat;
-  -moz-margin-start: -5em;
-  -moz-padding-start: 5em;
+  background-size: 1.2em;
+  -moz-margin-start: -2em;
+  -moz-padding-start: 2em;
 }
 
-#errorTitle:-moz-locale-dir(rtl) {
+#errorTitleText:-moz-locale-dir(rtl) {
   background-position: right 0;
 }
 
 #errorTryAgain {
   margin-top: 1.2em;
 }
 
 #errorContainer {
--- a/browser/config/tooltool-manifests/linux32/releng.manifest
+++ b/browser/config/tooltool-manifests/linux32/releng.manifest
@@ -7,14 +7,14 @@
 }, 
 {
 "size": 80458572,
 "digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
 "algorithm": "sha512", 
 "filename": "gcc.tar.xz"
 },
 {
-"size": 165464,
-"digest": "ee87db68eb63e417a366333e34a32d9220232bfb9c3e91b9941513acf0551f2603a836537ffef41a240430200970d83fa9271f55b8b5168d6967be595cdb50db",
+"size": 165226,
+"digest": "79280f7595bc9e1613e05f8b2f0db3798ac739b96191e0f133e8ccd8ad149fedc84a1046e59863574189db28363a01712ae7b368ad1714e30ff88e7ebd5dad23",
 "algorithm": "sha512",
 "filename": "sccache.tar.bz2"
 }
 ]
--- a/browser/config/tooltool-manifests/linux64/releng.manifest
+++ b/browser/config/tooltool-manifests/linux64/releng.manifest
@@ -7,14 +7,14 @@
 }, 
 {
 "size": 80458572,
 "digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
 "algorithm": "sha512", 
 "filename": "gcc.tar.xz"
 },
 {
-"size": 165464,
-"digest": "ee87db68eb63e417a366333e34a32d9220232bfb9c3e91b9941513acf0551f2603a836537ffef41a240430200970d83fa9271f55b8b5168d6967be595cdb50db",
+"size": 165226,
+"digest": "79280f7595bc9e1613e05f8b2f0db3798ac739b96191e0f133e8ccd8ad149fedc84a1046e59863574189db28363a01712ae7b368ad1714e30ff88e7ebd5dad23",
 "algorithm": "sha512",
 "filename": "sccache.tar.bz2"
 }
 ]
--- a/browser/config/tooltool-manifests/macosx64/releng.manifest
+++ b/browser/config/tooltool-manifests/macosx64/releng.manifest
@@ -10,14 +10,14 @@
 }, 
 {
 "size": 59602619, 
 "digest": "86662ebc0ef650490559005948c4f0cb015dad72c7cac43732c2bf2995247081e30c139cf8008d19670a0009fc302c4eee2676981ee3f9ff4a15c01af22b783b", 
 "algorithm": "sha512", 
 "filename": "clang.tar.bz2"
 },
 {
-"size": 165464,
-"digest": "ee87db68eb63e417a366333e34a32d9220232bfb9c3e91b9941513acf0551f2603a836537ffef41a240430200970d83fa9271f55b8b5168d6967be595cdb50db",
+"size": 165226,
+"digest": "79280f7595bc9e1613e05f8b2f0db3798ac739b96191e0f133e8ccd8ad149fedc84a1046e59863574189db28363a01712ae7b368ad1714e30ff88e7ebd5dad23",
 "algorithm": "sha512",
 "filename": "sccache.tar.bz2"
 }
 ]
--- a/browser/config/tooltool-manifests/win32/releng.manifest
+++ b/browser/config/tooltool-manifests/win32/releng.manifest
@@ -7,14 +7,14 @@
 },
 {
 "size": 51,
 "digest": "c8e40edb314eeabfb92c77cf5ff9a7857033f15dd65a00349bcf9e3e5b75624afc71f733b2ff7e029c20a78313038409c2bd022bf7e5a7e0c487fc2c2d640986",
 "algorithm": "sha512",
 "filename": "setup.sh"
 },
 {
-"size": 165464,
-"digest": "ee87db68eb63e417a366333e34a32d9220232bfb9c3e91b9941513acf0551f2603a836537ffef41a240430200970d83fa9271f55b8b5168d6967be595cdb50db",
+"size": 165226,
+"digest": "79280f7595bc9e1613e05f8b2f0db3798ac739b96191e0f133e8ccd8ad149fedc84a1046e59863574189db28363a01712ae7b368ad1714e30ff88e7ebd5dad23",
 "algorithm": "sha512",
 "filename": "sccache.tar.bz2"
 }
 ]
--- a/browser/devtools/debugger/debugger-controller.js
+++ b/browser/devtools/debugger/debugger-controller.js
@@ -457,16 +457,23 @@ ThreadState.prototype = {
     dumpn("Handling tab navigation in the ThreadState");
     this._update();
   },
 
   /**
    * Update the UI after a thread state change.
    */
   _update: function(aEvent) {
+    // Ignore "interrupted" events, which are generated by the slow script
+    // dialog and internal events such as setting breakpoints, to avoid UI
+    // flicker.
+    if (aEvent == "interrupted") {
+      return;
+    }
+
     DebuggerView.Toolbar.toggleResumeButtonState(this.activeThread.state);
 
     if (gTarget && (aEvent == "paused" || aEvent == "resumed")) {
       gTarget.emit("thread-" + aEvent);
     }
   }
 };
 
@@ -563,16 +570,21 @@ StackFrames.prototype = {
         if (!aPacket.why.frameFinished) {
           break;
         } else if (aPacket.why.frameFinished.throw) {
           this._currentException = aPacket.why.frameFinished.throw;
         } else if (aPacket.why.frameFinished.return) {
           this._currentReturnedValue = aPacket.why.frameFinished.return;
         }
         break;
+      // If paused by an explicit interrupt, which are generated by the slow
+      // script dialog and internal events such as setting breakpoints, ignore
+      // the event to avoid UI flicker.
+      case "interrupted":
+        return;
     }
 
     this.activeThread.fillFrames(CALL_STACK_PAGE_SIZE);
     DebuggerView.editor.focus();
   },
 
   /**
    * Handler for the thread client's resumed notification.
--- a/browser/devtools/debugger/test/browser.ini
+++ b/browser/devtools/debugger/test/browser.ini
@@ -73,16 +73,17 @@ support-files =
   doc_recursion-stack.html
   doc_scope-variable.html
   doc_scope-variable-2.html
   doc_scope-variable-3.html
   doc_scope-variable-4.html
   doc_script-switching-01.html
   doc_script-switching-02.html
   doc_step-out.html
+  doc_terminate-on-tab-close.html
   doc_tracing-01.html
   doc_watch-expressions.html
   doc_watch-expression-button.html
   doc_with-frame.html
   head.js
   sjs_random-javascript.sjs
   testactors.js
 
@@ -232,16 +233,17 @@ skip-if = true # Bug 933950 (leaky test)
 [browser_dbg_stack-03.js]
 [browser_dbg_stack-04.js]
 [browser_dbg_stack-05.js]
 [browser_dbg_stack-06.js]
 [browser_dbg_stack-07.js]
 [browser_dbg_step-out.js]
 [browser_dbg_tabactor-01.js]
 [browser_dbg_tabactor-02.js]
+[browser_dbg_terminate-on-tab-close.js]
 [browser_dbg_tracing-01.js]
 [browser_dbg_tracing-02.js]
 [browser_dbg_tracing-03.js]
 [browser_dbg_tracing-04.js]
 [browser_dbg_tracing-05.js]
 [browser_dbg_tracing-06.js]
 [browser_dbg_variables-view-01.js]
 [browser_dbg_variables-view-02.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_terminate-on-tab-close.js
@@ -0,0 +1,38 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests that debuggee scripts are terminated on tab closure.
+ */
+
+const TAB_URL = EXAMPLE_URL + "doc_terminate-on-tab-close.html";
+
+let gTab, gDebuggee, gDebugger, gPanel;
+
+function test() {
+  initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+    gTab = aTab;
+    gDebuggee = aDebuggee;
+    gPanel = aPanel;
+    gDebugger = gPanel.panelWin;
+
+    testTerminate();
+  });
+}
+
+function testTerminate() {
+  gDebugger.gThreadClient.addOneTimeListener("paused", () => {
+    resumeDebuggerThenCloseAndFinish(gPanel).then(function () {
+      ok(true, "should not throw after this point");
+    });
+  });
+
+  gDebuggee.debuggerThenThrow();
+}
+
+registerCleanupFunction(function() {
+  gTab = null;
+  gDebuggee = null;
+  gPanel = null;
+  gDebugger = null;
+});
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/doc_terminate-on-tab-close.html
@@ -0,0 +1,20 @@
+<!-- Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!doctype html>
+
+<html>
+  <head>
+    <meta charset="utf-8"/>
+    <title>Debugger test page</title>
+  </head>
+
+  <body>
+    <script type="text/javascript">
+      function debuggerThenThrow() {
+        debugger;
+        throw "unreachable";
+      }
+    </script>
+  </body>
+
+</html>
--- a/browser/devtools/framework/gDevTools.jsm
+++ b/browser/devtools/framework/gDevTools.jsm
@@ -575,16 +575,89 @@ let gDevToolsBrowser = {
       devtoolsKeyset = doc.createElement("keyset");
       devtoolsKeyset.setAttribute("id", "devtoolsKeyset");
     }
     devtoolsKeyset.appendChild(keys);
     let mainKeyset = doc.getElementById("mainKeyset");
     mainKeyset.parentNode.insertBefore(devtoolsKeyset, mainKeyset);
   },
 
+  /**
+   * Hook the JS debugger tool to the "Debug Script" button of the slow script
+   * dialog.
+   */
+  setSlowScriptDebugHandler: function DT_setSlowScriptDebugHandler() {
+    let debugService = Cc["@mozilla.org/dom/slow-script-debug;1"]
+                         .getService(Ci.nsISlowScriptDebug);
+    let tm = Cc["@mozilla.org/thread-manager;1"].getService(Ci.nsIThreadManager);
+
+    debugService.activationHandler = function(aWindow) {
+      let chromeWindow = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                                .getInterface(Ci.nsIWebNavigation)
+                                .QueryInterface(Ci.nsIDocShellTreeItem)
+                                .rootTreeItem
+                                .QueryInterface(Ci.nsIInterfaceRequestor)
+                                .getInterface(Ci.nsIDOMWindow)
+                                .QueryInterface(Ci.nsIDOMChromeWindow);
+      let target = devtools.TargetFactory.forTab(chromeWindow.gBrowser.selectedTab);
+
+      let setupFinished = false;
+      gDevTools.showToolbox(target, "jsdebugger").then(toolbox => {
+        let threadClient = toolbox.getCurrentPanel().panelWin.gThreadClient;
+
+        // Break in place, which means resuming the debuggee thread and pausing
+        // right before the next step happens.
+        switch (threadClient.state) {
+          case "paused":
+            // When the debugger is already paused.
+            threadClient.breakOnNext();
+            setupFinished = true;
+            break;
+          case "attached":
+            // When the debugger is already open.
+            threadClient.interrupt(() => {
+              threadClient.breakOnNext();
+              setupFinished = true;
+            });
+            break;
+          case "resuming":
+            // The debugger is newly opened.
+            threadClient.addOneTimeListener("resumed", () => {
+              threadClient.interrupt(() => {
+                threadClient.breakOnNext();
+                setupFinished = true;
+              });
+            });
+            break;
+          default:
+            throw Error("invalid thread client state in slow script debug handler: " +
+                        threadClient.state);
+          }
+      });
+
+      // Don't return from the interrupt handler until the debugger is brought
+      // up; no reason to continue executing the slow script.
+      let utils = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                         .getInterface(Ci.nsIDOMWindowUtils);
+      utils.enterModalState();
+      while (!setupFinished) {
+        tm.currentThread.processNextEvent(true);
+      }
+      utils.leaveModalState();
+    };
+  },
+
+  /**
+   * Unset the slow script debug handler.
+   */
+  unsetSlowScriptDebugHandler: function DT_unsetSlowScriptDebugHandler() {
+    let debugService = Cc["@mozilla.org/dom/slow-script-debug;1"]
+                         .getService(Ci.nsISlowScriptDebug);
+    debugService.activationHandler = undefined;
+  },
 
   /**
    * Detect the presence of a Firebug.
    *
    * @return promise
    */
   _isFirebugInstalled: function DT_isFirebugInstalled() {
     let bootstrappedAddons = Services.prefs.getCharPref("extensions.bootstrappedAddons");
@@ -664,16 +737,20 @@ let gDevToolsBrowser = {
           ref = doc.getElementById("menu_devtools_separator");
         }
 
         if (ref) {
           mp.insertBefore(elements.menuitem, ref);
         }
       }
     }
+
+    if (toolDefinition.id === "jsdebugger") {
+      gDevToolsBrowser.setSlowScriptDebugHandler();
+    }
   },
 
   /**
    * Add all tools to the developer tools menu of a window.
    *
    * @param {XULDocument} doc
    *        The document to which the tool items are to be added.
    */
@@ -839,16 +916,20 @@ let gDevToolsBrowser = {
    *
    * @param {string} toolId
    *        id of the tool to remove
    */
   _removeToolFromWindows: function DT_removeToolFromWindows(toolId) {
     for (let win of gDevToolsBrowser._trackedBrowserWindows) {
       gDevToolsBrowser._removeToolFromMenu(toolId, win.document);
     }
+
+    if (toolId === "jsdebugger") {
+      gDevToolsBrowser.unsetSlowScriptDebugHandler();
+    }
   },
 
   /**
    * Remove a tool's menuitem from a window
    *
    * @param {string} toolId
    *        Id of the tool to add a menu entry for
    * @param {XULDocument} doc
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -526,16 +526,19 @@
 @BINPATH@/components/NotificationStorage.js
 @BINPATH@/components/NotificationStorage.manifest
 @BINPATH@/components/AlarmsManager.js
 @BINPATH@/components/AlarmsManager.manifest
 @BINPATH@/components/Push.js
 @BINPATH@/components/Push.manifest
 @BINPATH@/components/PushServiceLauncher.js
 
+@BINPATH@/components/SlowScriptDebug.manifest
+@BINPATH@/components/SlowScriptDebug.js
+
 #ifndef RELEASE_BUILD
 @BINPATH@/components/InterAppComm.manifest
 @BINPATH@/components/InterAppCommService.js
 @BINPATH@/components/InterAppConnection.js
 @BINPATH@/components/InterAppMessagePort.js
 #endif
 
 @BINPATH@/components/TCPSocket.js
--- a/build/unix/elfhack/inject/moz.build
+++ b/build/unix/elfhack/inject/moz.build
@@ -15,8 +15,10 @@ else:
 
 GENERATED_SOURCES += [
     "%s.c" % cpu,
 ]
 
 DEFINES['ELFHACK_BUILD'] = True
 
 NO_PGO = True
+
+NO_VISIBILITY_FLAGS = True
copy from config/gcc_hidden.h
copy to config/gcc_hidden_dso_handle.h
--- a/config/gcc_hidden.h
+++ b/config/gcc_hidden_dso_handle.h
@@ -1,6 +1,11 @@
 /* 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/. */
 
+#ifdef __cplusplus
+extern "C"
+#endif
+void *__dso_handle;
+
 /* Begin all files as hidden visibility */
 #pragma GCC visibility push(hidden)
--- a/config/system-headers
+++ b/config/system-headers
@@ -402,19 +402,16 @@ dl.h
 docobj.h
 dos/dosextens.h
 dos.h
 Drag.h
 DriverServices.h
 DriverSynchronization.h
 DropInPanel.h
 dvidef.h
-#ifdef ANDROID
-EffectApi.h
-#endif
 elf.h
 endian.h
 Entry.h
 errno.h
 Errors.h
 Events.h
 exdisp.h
 ExDisp.h
@@ -511,16 +508,18 @@ gssapi/gssapi_generic.h
 gssapi/gssapi.h
 gssapi.h
 gtk/gtk.h
 gtk/gtkx.h
 gtk/gtkprinter.h
 gtk/gtkprintjob.h
 gtk/gtkprintunixdialog.h
 #ifdef ANDROID
+gui/BufferQueue.h
+gui/ConsumerBase.h
 gui/GraphicBufferAlloc.h
 gui/IConsumerListener.h
 gui/IGraphicBufferAlloc.h
 gui/IGraphicBufferProducer.h
 gui/ISurfaceComposer.h
 gui/ISurfaceComposerClient.h
 gui/ISurfaceTexture.h
 gui/Surface.h
@@ -537,24 +536,16 @@ hardware_legacy/uevent.h
 hardware_legacy/vibrator.h
 #endif
 HIToolbox/HIToolbox.h
 hlink.h
 #ifdef ANDROID
 HTTPBase.h
 #endif
 ia64/sys/inline.h
-#ifdef ANDROID
-IAudioFlingerClient.h
-IAudioFlinger.h
-IAudioRecord.h
-IAudioTrack.h
-IEffect.h
-IEffectClient.h
-#endif
 Icons.h
 iconv.h
 ieeefp.h
 ifaddrs.h
 image.h
 imagehlp.h
 imm.h
 initguid.h
@@ -906,16 +897,17 @@ rld_interface.h
 rmsdef.h
 Roster.h
 rpc.h
 rpcproxy.h
 rpc/types.h
 sane/sane.h
 sane/sanei.h
 sane/saneopts.h
+sched.h
 Scrap.h
 Screen.h
 Script.h
 ScrollBar.h
 sec.h
 secrng.h
 security.h
 secutil.h
--- a/configure.in
+++ b/configure.in
@@ -2545,27 +2545,29 @@ MOZ_CXX11
 AC_LANG_C
 
 dnl Check for .hidden assembler directive and visibility attribute.
 dnl Borrowed from glibc configure.in
 dnl ===============================================================
 if test "$GNU_CC" -a "$OS_TARGET" != WINNT; then
   AC_DEFINE(HAVE_VISIBILITY_HIDDEN_ATTRIBUTE)
   AC_DEFINE(HAVE_VISIBILITY_ATTRIBUTE)
-  if test -n "$gonkdir"; then
-    visibility_target=Gonk
-  else
-    visibility_target=$OS_TARGET
-  fi
-  case "$visibility_target" in
-  Darwin|Gonk)
+  case "$OS_TARGET" in
+  Darwin)
     VISIBILITY_FLAGS='-fvisibility=hidden'
     ;;
   *)
-    VISIBILITY_FLAGS='-I$(DIST)/system_wrappers -include $(topsrcdir)/config/gcc_hidden.h'
+    case $GCC_VERSION in
+    4.4*)
+      VISIBILITY_FLAGS='-I$(DIST)/system_wrappers -include $(topsrcdir)/config/gcc_hidden_dso_handle.h'
+      ;;
+    *)
+      VISIBILITY_FLAGS='-I$(DIST)/system_wrappers -include $(topsrcdir)/config/gcc_hidden.h'
+      ;;
+    esac
     WRAP_SYSTEM_INCLUDES=1
     ;;
   esac
 fi         # GNU_CC
 
 # visibility hidden flag for Sun Studio on Solaris
 if test "$SOLARIS_SUNPRO_CC"; then
 VISIBILITY_FLAGS='-xldscope=hidden'
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -1330,16 +1330,22 @@ public:
   }
 
   /**
    * Gets the system principal from the security manager.
    */
   static nsIPrincipal* GetSystemPrincipal();
 
   /**
+   * Gets the null subject principal singleton. This is only useful for
+   * assertions.
+   */
+  static nsIPrincipal* GetNullSubjectPrincipal() { return sNullSubjectPrincipal; }
+
+  /**
    * *aResourcePrincipal is a principal describing who may access the contents
    * of a resource. The resource can only be consumed by a principal that
    * subsumes *aResourcePrincipal. MAKE SURE THAT NOTHING EVER ACTS WITH THE
    * AUTHORITY OF *aResourcePrincipal.
    * It may be null to indicate that the resource has no data from any origin
    * in it yet and anything may access the resource.
    * Additional data is being mixed into the resource from aExtraPrincipal
    * (which may be null; if null, no data is being mixed in and this function
@@ -2164,16 +2170,17 @@ private:
   static void DestroyClassNameArray(void* aData);
   static void* AllocClassMatchingInfo(nsINode* aRootNode,
                                       const nsString* aClasses);
 
   static nsIXPConnect *sXPConnect;
 
   static nsIScriptSecurityManager *sSecurityManager;
   static nsIPrincipal *sSystemPrincipal;
+  static nsIPrincipal *sNullSubjectPrincipal;
 
   static nsIParserService *sParserService;
 
   static nsNameSpaceManager *sNameSpaceManager;
 
   static nsIIOService *sIOService;
 
   static bool sImgLoaderInitialized;
--- a/content/base/src/nsCSPContext.cpp
+++ b/content/base/src/nsCSPContext.cpp
@@ -13,24 +13,26 @@
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsIChannelPolicy.h"
 #include "nsIClassInfoImpl.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIDOMHTMLDocument.h"
 #include "nsIDOMHTMLElement.h"
 #include "nsIHttpChannel.h"
+#include "nsIInterfaceRequestor.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
 #include "nsIObserver.h"
 #include "nsIObserverService.h"
 #include "nsIPrincipal.h"
 #include "nsIPropertyBag2.h"
 #include "nsIScriptError.h"
+#include "nsIWebNavigation.h"
 #include "nsIWritablePropertyBag2.h"
 #include "nsString.h"
 #include "prlog.h"
 
 using namespace mozilla;
 
 #if defined(PR_LOGGING)
 static PRLogModuleInfo *
@@ -40,16 +42,57 @@ GetCspContextLog()
   if (!gCspContextPRLog)
     gCspContextPRLog = PR_NewLogModule("CSPContext");
   return gCspContextPRLog;
 }
 #endif
 
 #define CSPCONTEXTLOG(args) PR_LOG(GetCspContextLog(), 4, args)
 
+static const uint32_t CSP_CACHE_URI_CUTOFF_SIZE = 512;
+
+/**
+ * Creates a key for use in the ShouldLoad cache.
+ * Looks like: <uri>!<nsIContentPolicy::LOAD_TYPE>
+ */
+nsresult
+CreateCacheKey_Internal(nsIURI* aContentLocation,
+                        nsContentPolicyType aContentType,
+                        nsACString& outCacheKey)
+{
+  if (!aContentLocation) {
+    return NS_ERROR_FAILURE;
+  }
+
+  bool isDataScheme = false;
+  nsresult rv = aContentLocation->SchemeIs("data", &isDataScheme);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  outCacheKey.Truncate();
+  if (aContentType != nsIContentPolicy::TYPE_SCRIPT && isDataScheme) {
+    // For non-script data: URI, use ("data:", aContentType) as the cache key.
+    outCacheKey.Append(NS_LITERAL_CSTRING("data:"));
+    outCacheKey.AppendInt(aContentType);
+    return NS_OK;
+  }
+
+  nsAutoCString spec;
+  rv = aContentLocation->GetSpec(spec);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Don't cache for a URI longer than the cutoff size.
+  if (spec.Length() <= CSP_CACHE_URI_CUTOFF_SIZE) {
+    outCacheKey.Append(spec);
+    outCacheKey.Append(NS_LITERAL_CSTRING("!"));
+    outCacheKey.AppendInt(aContentType);
+  }
+
+  return NS_OK;
+}
+
 /* =====  nsIContentSecurityPolicy impl ====== */
 
 NS_IMETHODIMP
 nsCSPContext::ShouldLoad(nsContentPolicyType aContentType,
                          nsIURI*             aContentLocation,
                          nsIURI*             aRequestOrigin,
                          nsISupports*        aRequestContext,
                          const nsACString&   aMimeTypeGuess,
@@ -69,16 +112,26 @@ nsCSPContext::ShouldLoad(nsContentPolicy
   // This ShouldLoad function is called from nsCSPService::ShouldLoad,
   // which already checked a number of things, including:
   // * aContentLocation is not null; we can consume this without further checks
   // * scheme is not a whitelisted scheme (about: chrome:, etc).
   // * CSP is enabled
   // * Content Type is not whitelisted (CSP Reports, TYPE_DOCUMENT, etc).
   // * Fast Path for Apps
 
+  nsAutoCString cacheKey;
+  rv = CreateCacheKey_Internal(aContentLocation, aContentType, cacheKey);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  bool isCached = mShouldLoadCache.Get(cacheKey, outDecision);
+  if (isCached && cacheKey.Length() > 0) {
+    // this is cached, use the cached value.
+    return NS_OK;
+  }
+
   // Default decision, CSP can revise it if there's a policy to enforce
   *outDecision = nsIContentPolicy::ACCEPT;
 
   // This may be a load or a preload. If it is a preload, the document will
   // not have been fully parsed yet, and aRequestContext will be an
   // nsIDOMHTMLDocument rather than the nsIDOMHTMLElement associated with the
   // resource. As a result, we cannot extract the element's corresponding
   // nonce attribute, and so we cannot correctly check the nonce on a preload.
@@ -132,16 +185,21 @@ nsCSPContext::ShouldLoad(nsContentPolicy
                                          violatedDirective.get());
       }
 
       // TODO: future patches fix:
       // * AsyncReportViolation, bug 994322
       // * Console error reporting, bug 994322
     }
   }
+  // Done looping, cache any relevant result
+  if (cacheKey.Length() > 0 && !isPreload) {
+    mShouldLoadCache.Put(cacheKey, *outDecision);
+  }
+
 #ifdef PR_LOGGING
   {
   nsAutoCString spec;
   aContentLocation->GetSpec(spec);
   CSPCONTEXTLOG(("nsCSPContext::ShouldLoad, decision: %s, aContentLocation: %s", *outDecision ? "load" : "deny", spec.get()));
   }
 #endif
   return NS_OK;
@@ -178,16 +236,17 @@ nsCSPContext::nsCSPContext()
 }
 
 nsCSPContext::~nsCSPContext()
 {
   CSPCONTEXTLOG(("nsCSPContext::~nsCSPContext"));
   for (uint32_t i = 0; i < mPolicies.Length(); i++) {
     delete mPolicies[i];
   }
+  mShouldLoadCache.Clear();
 }
 
 NS_IMETHODIMP
 nsCSPContext::GetIsInitialized(bool *outIsInitialized)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
@@ -210,16 +269,18 @@ nsCSPContext::GetPolicyCount(uint32_t *o
 
 NS_IMETHODIMP
 nsCSPContext::RemovePolicy(uint32_t aIndex)
 {
   if (aIndex >= mPolicies.Length()) {
     return NS_ERROR_ILLEGAL_VALUE;
   }
   mPolicies.RemoveElementAt(aIndex);
+  // reset cache since effective policy changes
+  mShouldLoadCache.Clear();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCSPContext::AppendPolicy(const nsAString& aPolicyString,
                            nsIURI* aSelfURI,
                            bool aReportOnly,
                            bool aSpecCompliant)
@@ -232,16 +293,18 @@ nsCSPContext::AppendPolicy(const nsAStri
     NS_WARNING("aSelfURI should be a nullptr in AppendPolicy and removed in bug 991474");
   }
 
   // Use the mSelfURI from setRequestContext, see bug 991474
   NS_ASSERTION(mSelfURI, "mSelfURI required for AppendPolicy, but not set");
   nsCSPPolicy* policy = nsCSPParser::parseContentSecurityPolicy(aPolicyString, mSelfURI, aReportOnly, 0);
   if (policy) {
     mPolicies.AppendElement(policy);
+    // reset cache since effective policy changes
+    mShouldLoadCache.Clear();
   }
   return NS_OK;
 }
 
 // aNonceOrContent either holds the nonce-value or otherwise the content
 // of the element to be hashed.
 NS_IMETHODIMP
 nsCSPContext::getAllowsInternal(nsContentPolicyType aContentType,
@@ -373,21 +436,128 @@ nsCSPContext::SetRequestContext(nsIURI* 
   NS_ASSERTION(mSelfURI, "No mSelfURI in SetRequestContext, can not translate 'self' into actual URI");
 
   // aDocumentPrincipal will be removed in bug 994872
   // aReferrer will be used in the patch for bug 994322
 
   return NS_OK;
 }
 
+/**
+ * Based on the given docshell, determines if this CSP context allows the
+ * ancestry.
+ *
+ * In order to determine the URI of the parent document (one causing the load
+ * of this protected document), this function obtains the docShellTreeItem,
+ * then walks up the hierarchy until it finds a privileged (chrome) tree item.
+ * Getting the parent's URI looks like this in pseudocode:
+ *
+ * nsIDocShell->QI(nsIInterfaceRequestor)
+ *            ->GI(nsIDocShellTreeItem)
+ *            ->QI(nsIInterfaceRequestor)
+ *            ->GI(nsIWebNavigation)
+ *            ->GetCurrentURI();
+ *
+ * aDocShell is the docShell for the protected document.
+ */
 NS_IMETHODIMP
 nsCSPContext::PermitsAncestry(nsIDocShell* aDocShell, bool* outPermitsAncestry)
 {
-  // For now, we allows permitsAncestry, this will be fixed in Bug 994320
+  nsresult rv;
+
+  // Can't check ancestry without a docShell.
+  if (aDocShell == nullptr) {
+    return NS_ERROR_FAILURE;
+  }
+
   *outPermitsAncestry = true;
+
+  // extract the ancestry as an array
+  nsCOMArray<nsIURI> ancestorsArray;
+
+  nsCOMPtr<nsIInterfaceRequestor> ir(do_QueryInterface(aDocShell));
+  nsCOMPtr<nsIDocShellTreeItem> treeItem(do_GetInterface(ir));
+  nsCOMPtr<nsIDocShellTreeItem> parentTreeItem;
+  nsCOMPtr<nsIWebNavigation> webNav;
+  nsCOMPtr<nsIURI> currentURI;
+  nsCOMPtr<nsIURI> uriClone;
+
+  // iterate through each docShell parent item
+  while (NS_SUCCEEDED(treeItem->GetParent(getter_AddRefs(parentTreeItem))) &&
+         parentTreeItem != nullptr) {
+    ir     = do_QueryInterface(parentTreeItem);
+    NS_ASSERTION(ir, "Could not QI docShellTreeItem to nsIInterfaceRequestor");
+
+    webNav = do_GetInterface(ir);
+    NS_ENSURE_TRUE(webNav, NS_ERROR_FAILURE);
+
+    rv = webNav->GetCurrentURI(getter_AddRefs(currentURI));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (currentURI) {
+      // stop when reaching chrome
+      bool isChrome = false;
+      rv = currentURI->SchemeIs("chrome", &isChrome);
+      NS_ENSURE_SUCCESS(rv, rv);
+      if (isChrome) { break; }
+
+      // delete the userpass from the URI.
+      rv = currentURI->CloneIgnoringRef(getter_AddRefs(uriClone));
+      NS_ENSURE_SUCCESS(rv, rv);
+      rv = uriClone->SetUserPass(EmptyCString());
+      NS_ENSURE_SUCCESS(rv, rv);
+#ifdef PR_LOGGING
+      {
+      nsAutoCString spec;
+      uriClone->GetSpec(spec);
+      CSPCONTEXTLOG(("nsCSPContext::PermitsAncestry, found ancestor: %s", spec.get()));
+      }
+#endif
+      ancestorsArray.AppendElement(uriClone);
+    }
+
+    // next ancestor
+    treeItem = parentTreeItem;
+  }
+
+  nsAutoString violatedDirective;
+
+  // Now that we've got the ancestry chain in ancestorsArray, time to check
+  // them against any CSP.
+  for (uint32_t i = 0; i < mPolicies.Length(); i++) {
+    for (uint32_t a = 0; a < ancestorsArray.Length(); a++) {
+      // TODO(sid) the mapping from frame-ancestors context to TYPE_DOCUMENT is
+      // forced. while this works for now, we will implement something in
+      // bug 999656.
+#ifdef PR_LOGGING
+      {
+      nsAutoCString spec;
+      ancestorsArray[a]->GetSpec(spec);
+      CSPCONTEXTLOG(("nsCSPContext::PermitsAncestry, checking ancestor: %s", spec.get()));
+      }
+#endif
+      if (!mPolicies[i]->permits(nsIContentPolicy::TYPE_DOCUMENT,
+                                 ancestorsArray[a],
+                                 EmptyString(), // no nonce
+                                 violatedDirective)) {
+        // Policy is violated
+        nsCOMPtr<nsIObserverService> observerService =
+          mozilla::services::GetObserverService();
+        NS_ENSURE_TRUE(observerService, NS_ERROR_NOT_AVAILABLE);
+
+        observerService->NotifyObservers(ancestorsArray[a],
+                                         CSP_VIOLATION_TOPIC,
+                                         violatedDirective.get());
+        // TODO(sid) generate violation reports and remove NotifyObservers
+        //           call. (in bug 994322)
+        // TODO(sid) implement logic for report-only (in bug 994322)
+        *outPermitsAncestry = false;
+      }
+    }
+  }
   return NS_OK;
 }
 
 /* ===== nsISerializable implementation ====== */
 
 NS_IMETHODIMP
 nsCSPContext::Read(nsIObjectInputStream* aStream)
 {
--- a/content/base/src/nsCSPContext.h
+++ b/content/base/src/nsCSPContext.h
@@ -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/. */
 
 #ifndef nsCSPContext_h___
 #define nsCSPContext_h___
 
 #include "nsCSPUtils.h"
+#include "nsDataHashtable.h"
 #include "nsIChannel.h"
 #include "nsIClassInfo.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsISerializable.h"
 #include "nsXPCOM.h"
 
 #define NS_CSPCONTEXT_CONTRACTID "@mozilla.org/cspcontext;1"
  // 09d9ed1a-e5d4-4004-bfe0-27ceb923d9ac
@@ -31,13 +32,14 @@ class nsCSPContext : public nsIContentSe
 
   private:
     NS_IMETHODIMP getAllowsInternal(nsContentPolicyType aContentType,
                                     enum CSPKeyword aKeyword,
                                     const nsAString& aNonceOrContent,
                                     bool* outShouldReportViolations,
                                     bool* outIsAllowed) const;
 
-    nsTArray<nsCSPPolicy*> mPolicies;
-    nsCOMPtr<nsIURI>       mSelfURI;
+    nsTArray<nsCSPPolicy*>                     mPolicies;
+    nsCOMPtr<nsIURI>                           mSelfURI;
+    nsDataHashtable<nsCStringHashKey, int16_t> mShouldLoadCache;
 };
 
 #endif /* nsCSPContext_h___ */
--- a/content/base/src/nsCSPUtils.cpp
+++ b/content/base/src/nsCSPUtils.cpp
@@ -631,16 +631,18 @@ CSP_DirectiveToContentType(enum CSPDirec
   switch (aDir) {
     case CSP_IMG_SRC:    return nsIContentPolicy::TYPE_IMAGE;
     case CSP_SCRIPT_SRC: return nsIContentPolicy::TYPE_SCRIPT;
     case CSP_STYLE_SRC:  return nsIContentPolicy::TYPE_STYLESHEET;
     case CSP_FONT_SRC:   return nsIContentPolicy::TYPE_FONT;
     case CSP_MEDIA_SRC:  return nsIContentPolicy::TYPE_MEDIA;
     case CSP_OBJECT_SRC: return nsIContentPolicy::TYPE_OBJECT;
     case CSP_FRAME_SRC:  return nsIContentPolicy::TYPE_SUBDOCUMENT;
+    // TODO(sid): fix this mapping to be more precise (bug 999656)
+    case CSP_FRAME_ANCESTORS: return nsIContentPolicy::TYPE_DOCUMENT;
 
     // Fall through to error for the following Directives:
     case CSP_DEFAULT_SRC:
     case CSP_CONNECT_SRC:
     case CSP_REPORT_URI:
     case CSP_LAST_DIRECTIVE_VALUE:
     default:
       NS_ASSERTION(false, "Can not convert CSPDirective into nsContentPolicyType");
--- a/content/base/src/nsCSPUtils.h
+++ b/content/base/src/nsCSPUtils.h
@@ -44,32 +44,34 @@ enum CSPDirective {
   CSP_OBJECT_SRC,
   CSP_STYLE_SRC,
   CSP_IMG_SRC,
   CSP_MEDIA_SRC,
   CSP_FRAME_SRC,
   CSP_FONT_SRC,
   CSP_CONNECT_SRC,
   CSP_REPORT_URI,
+  CSP_FRAME_ANCESTORS,
   // CSP_LAST_DIRECTIVE_VALUE always needs to be the last element in the enum
   // because we use it to calculate the size for the char* array.
   CSP_LAST_DIRECTIVE_VALUE
 };
 
 static const char* CSPStrDirectives[] = {
-  "default-src", // CSP_DEFAULT_SRC = 0
-  "script-src",  // CSP_SCRIPT_SRC
-  "object-src",  // CSP_OBJECT_SRC
-  "style-src",   // CSP_STYLE_SRC
-  "img-src",     // CSP_IMG_SRC
-  "media-src",   // CSP_MEDIA_SRC
-  "frame-src",   // CSP_FRAME_SRC
-  "font-src",    // CSP_FONT_SRC
-  "connect-src", // CSP_CONNECT_SRC
-  "report-uri",  // CSP_REPORT_URI
+  "default-src",    // CSP_DEFAULT_SRC = 0
+  "script-src",     // CSP_SCRIPT_SRC
+  "object-src",     // CSP_OBJECT_SRC
+  "style-src",      // CSP_STYLE_SRC
+  "img-src",        // CSP_IMG_SRC
+  "media-src",      // CSP_MEDIA_SRC
+  "frame-src",      // CSP_FRAME_SRC
+  "font-src",       // CSP_FONT_SRC
+  "connect-src",    // CSP_CONNECT_SRC
+  "report-uri",     // CSP_REPORT_URI
+  "frame-ancestors" // CSP_FRAME_ANCESTORS
 };
 
 inline const char* CSP_EnumToDirective(enum CSPDirective aDir)
 {
   // Make sure all elements in enum CSPDirective got added to CSPStrDirectives.
   static_assert((sizeof(CSPStrDirectives) / sizeof(CSPStrDirectives[0]) ==
                 static_cast<uint32_t>(CSP_LAST_DIRECTIVE_VALUE)),
                 "CSP_LAST_DIRECTIVE_VALUE does not match length of CSPStrDirectives");
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -191,16 +191,17 @@ using namespace mozilla::layers;
 using namespace mozilla::widget;
 using namespace mozilla;
 
 const char kLoadAsData[] = "loadAsData";
 
 nsIXPConnect *nsContentUtils::sXPConnect;
 nsIScriptSecurityManager *nsContentUtils::sSecurityManager;
 nsIPrincipal *nsContentUtils::sSystemPrincipal;
+nsIPrincipal *nsContentUtils::sNullSubjectPrincipal;
 nsIParserService *nsContentUtils::sParserService = nullptr;
 nsNameSpaceManager *nsContentUtils::sNameSpaceManager;
 nsIIOService *nsContentUtils::sIOService;
 nsIConsoleService *nsContentUtils::sConsoleService;
 nsDataHashtable<nsISupportsHashKey, EventNameMapping>* nsContentUtils::sAtomEventTable = nullptr;
 nsDataHashtable<nsStringHashKey, EventNameMapping>* nsContentUtils::sStringEventTable = nullptr;
 nsCOMArray<nsIAtom>* nsContentUtils::sUserDefinedEvents = nullptr;
 nsIStringBundleService *nsContentUtils::sStringBundleService;
@@ -374,16 +375,17 @@ nsContentUtils::Init()
 
   sSecurityManager = nsScriptSecurityManager::GetScriptSecurityManager();
   if(!sSecurityManager)
     return NS_ERROR_FAILURE;
   NS_ADDREF(sSecurityManager);
 
   sSecurityManager->GetSystemPrincipal(&sSystemPrincipal);
   MOZ_ASSERT(sSystemPrincipal);
+  NS_ADDREF(sNullSubjectPrincipal = new nsNullPrincipal());
 
   nsresult rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService);
   if (NS_FAILED(rv)) {
     // This makes life easier, but we can live without it.
 
     sIOService = nullptr;
   }
 
@@ -1431,16 +1433,17 @@ nsContentUtils::Shutdown()
   for (i = 0; i < PropertiesFile_COUNT; ++i)
     NS_IF_RELEASE(sStringBundles[i]);
 
   NS_IF_RELEASE(sStringBundleService);
   NS_IF_RELEASE(sConsoleService);
   sXPConnect = nullptr;
   NS_IF_RELEASE(sSecurityManager);
   NS_IF_RELEASE(sSystemPrincipal);
+  NS_IF_RELEASE(sNullSubjectPrincipal);
   NS_IF_RELEASE(sParserService);
   NS_IF_RELEASE(sIOService);
   NS_IF_RELEASE(sLineBreaker);
   NS_IF_RELEASE(sWordBreaker);
   NS_IF_RELEASE(sBidiKeyboard);
 
   delete sAtomEventTable;
   sAtomEventTable = nullptr;
@@ -2317,19 +2320,43 @@ nsContentUtils::GenerateStateKey(nsICont
 nsIPrincipal*
 nsContentUtils::SubjectPrincipal()
 {
   JSContext* cx = GetCurrentJSContext();
   if (!cx) {
     return GetSystemPrincipal();
   }
 
-  JSCompartment* compartment = js::GetContextCompartment(cx);
-  MOZ_ASSERT(compartment);
-  JSPrincipals* principals = JS_GetCompartmentPrincipals(compartment);
+  JSCompartment *compartment = js::GetContextCompartment(cx);
+
+  // When an AutoJSAPI is instantiated, we are in a null compartment until the
+  // first JSAutoCompartment, which is kind of a purgatory as far as permissions
+  // go. It would be nice to just hard-abort if somebody does a security check
+  // in this purgatory zone, but that would be too fragile, since it could be
+  // triggered by random IsCallerChrome() checks 20-levels deep.
+  //
+  // So we want to return _something_ here - and definitely not the System
+  // Principal, since that would make an AutoJSAPI a very dangerous thing to
+  // instantiate.
+  //
+  // The natural thing to return is a null principal. Ideally, we'd return a
+  // different null principal each time, to avoid any unexpected interactions
+  // when the principal accidentally gets inherited somewhere. But
+  // GetSubjectPrincipal doesn't return strong references, so there's no way to
+  // sanely manage the lifetime of multiple null principals.
+  //
+  // So we use a singleton null principal. To avoid it being accidentally
+  // inherited and becoming a "real" subject or object principal, we do a
+  // release-mode assert during compartment creation against using this
+  // principal on an actual global.
+  if (!compartment) {
+    return sNullSubjectPrincipal;
+  }
+
+  JSPrincipals *principals = JS_GetCompartmentPrincipals(compartment);
   return nsJSPrincipals::get(principals);
 }
 
 // static
 nsIPrincipal*
 nsContentUtils::ObjectPrincipal(JSObject* aObj)
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/content/base/test/TestCSPParser.cpp
+++ b/content/base/test/TestCSPParser.cpp
@@ -662,16 +662,38 @@ nsresult TestGoodGeneratedPolicies() {
     { "default-src 'self' HTTP://foo.com",
       "default-src http://www.selfuri.com http://foo.com" },
     { "default-src 'NONE'",
       "default-src 'none'" },
     { "script-src policy-uri ",
       "script-src http://policy-uri" },
     { "img-src 'self'; ",
       "img-src http://www.selfuri.com" },
+    { "frame-ancestors foo-bar.com",
+      "frame-ancestors http://foo-bar.com" },
+    { "frame-ancestors http://a.com",
+      "frame-ancestors http://a.com" },
+    { "frame-ancestors 'self'",
+      "frame-ancestors http://www.selfuri.com" },
+    { "frame-ancestors http://self.com:88",
+      "frame-ancestors http://self.com:88" },
+    { "frame-ancestors http://a.b.c.d.e.f.g.h.i.j.k.l.x.com",
+      "frame-ancestors http://a.b.c.d.e.f.g.h.i.j.k.l.x.com" },
+    { "frame-ancestors https://self.com:34",
+      "frame-ancestors https://self.com:34" },
+    { "default-src 'none'; frame-ancestors 'self'",
+      "default-src 'none'; frame-ancestors http://www.selfuri.com" },
+    { "frame-ancestors http://self:80",
+      "frame-ancestors http://self:80" },
+    { "frame-ancestors http://self.com/bar",
+      "frame-ancestors http://self.com" },
+    { "default-src 'self'; frame-ancestors 'self'",
+      "default-src http://www.selfuri.com; frame-ancestors http://www.selfuri.com" },
+    { "frame-ancestors http://bar.com/foo.png",
+      "frame-ancestors http://bar.com" },
   };
 
   uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
   return runTestSuite(policies, policyCount, 1);
 }
 
 // ============================= TestBadGeneratedPolicies ========================
 
--- a/content/canvas/test/reftest/reftest.list
+++ b/content/canvas/test/reftest/reftest.list
@@ -149,25 +149,16 @@ fuzzy(1,65536) fuzzy-if(B2G,256,83) fuzz
 
 # Test premult:
 # random-if(B2G) from bug 983650
 fuzzy(1,65536) random-if(gtk2Widget) random-if(B2G) fuzzy-if(Android,9,65536)                                random-if(Android&&AndroidVersion<15)  == webgl-color-alpha-test.html?colorVal=1.0&alphaVal=0.5&alpha          wrapper.html?colors-half-alpha.png
 fuzzy(1,65536) random-if(gtk2Widget) fails-if(B2G) fuzzy-if(Android,9,65536) fails-if(cocoaWidget||Android) random-if(Android&&AndroidVersion<15)  == webgl-color-alpha-test.html?colorVal=0.5&alphaVal=0.5&alpha          wrapper.html?half-colors-half-alpha.png
 # random-if(B2G) from bug 983650
 fuzzy(1,65536) random-if(B2G) fuzzy-if(Android,9,65536)                                random-if(Android&&AndroidVersion<15)  == webgl-color-alpha-test.html?colorVal=0.5&alphaVal=0.5&alpha&premult  wrapper.html?colors-half-alpha.png
 
-# Test over-bright premult:
-# random-if(B2G) from bug 983650
-# This test assumes that the compositor is able to correctly handle superluminant
-# pixels in a WebGL canvas with premultiplied alpha. This is not handled by
-# Skia's software blending code. Consequently, this test is random on
-# basic-layers when using skia. See bug 1004483.
-fuzzy(1,65536) random-if(B2G) fuzzy-if(Android,9,65536) random-if(Android&&AndroidVersion<15) random-if(skiaContent&&!layersGPUAccelerated) == webgl-color-alpha-test.html?colorVal=1.0&alphaVal=0.5&alpha&premult  wrapper.html?colors-half-alpha.png
-
-
 # Check for hanging framebuffer bindings:
                                        random-if(Android&&AndroidVersion<15)  == webgl-hanging-fb-test.html?__&________  wrapper.html?green.png
                                        random-if(Android&&AndroidVersion<15)  == webgl-hanging-fb-test.html?aa&________  wrapper.html?green.png
 pref(webgl.force-layers-readback,true) random-if(Android&&AndroidVersion<15)  == webgl-hanging-fb-test.html?__&readback  wrapper.html?green.png
 pref(webgl.force-layers-readback,true) random-if(Android&&AndroidVersion<15)  == webgl-hanging-fb-test.html?aa&readback  wrapper.html?green.png
 
                                        random-if(Android&&AndroidVersion<15)  == webgl-hanging-scissor-test.html?__&________  wrapper.html?green.png
                                        random-if(Android&&AndroidVersion<15)  == webgl-hanging-scissor-test.html?aa&________  wrapper.html?green.png
--- a/content/html/document/src/HTMLAllCollection.cpp
+++ b/content/html/document/src/HTMLAllCollection.cpp
@@ -1,131 +1,66 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=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 "mozilla/dom/HTMLAllCollection.h"
 
-#include "jsapi.h"
-#include "mozilla/HoldDropJSObjects.h"
-#include "nsContentUtils.h"
-#include "nsDOMClassInfo.h"
+#include "mozilla/dom/HTMLAllCollectionBinding.h"
+#include "mozilla/dom/Nullable.h"
+#include "mozilla/dom/UnionTypes.h"
 #include "nsHTMLDocument.h"
-#include "nsJSUtils.h"
-#include "nsWrapperCacheInlines.h"
-#include "xpcpublic.h"
-
-using namespace mozilla;
-using namespace mozilla::dom;
-
-class nsHTMLDocumentSH
-{
-public:
-  static bool DocumentAllGetProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
-                                     JS::MutableHandle<JS::Value> vp);
-  static bool DocumentAllNewResolve(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
-                                    JS::MutableHandle<JSObject*> objp);
-  static void ReleaseDocument(JSFreeOp *fop, JSObject *obj);
-  static bool CallToGetPropMapper(JSContext *cx, unsigned argc, JS::Value *vp);
-};
-
-const JSClass sHTMLDocumentAllClass = {
-  "HTML document.all class",
-  JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS | JSCLASS_NEW_RESOLVE |
-  JSCLASS_EMULATES_UNDEFINED,
-  JS_PropertyStub,                                         /* addProperty */
-  JS_DeletePropertyStub,                                   /* delProperty */
-  nsHTMLDocumentSH::DocumentAllGetProperty,                /* getProperty */
-  JS_StrictPropertyStub,                                   /* setProperty */
-  JS_EnumerateStub,
-  (JSResolveOp)nsHTMLDocumentSH::DocumentAllNewResolve,
-  JS_ConvertStub,
-  nsHTMLDocumentSH::ReleaseDocument,
-  nsHTMLDocumentSH::CallToGetPropMapper
-};
 
 namespace mozilla {
 namespace dom {
 
 HTMLAllCollection::HTMLAllCollection(nsHTMLDocument* aDocument)
   : mDocument(aDocument)
 {
   MOZ_ASSERT(mDocument);
-  mozilla::HoldJSObjects(this);
+  SetIsDOMBinding();
 }
 
 HTMLAllCollection::~HTMLAllCollection()
 {
-  mObject = nullptr;
-  mozilla::DropJSObjects(this);
 }
 
-NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(HTMLAllCollection, AddRef)
-NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(HTMLAllCollection, Release)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(HTMLAllCollection,
+                                      mDocument,
+                                      mCollection,
+                                      mNamedMap)
 
-NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLAllCollection)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(HTMLAllCollection)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(HTMLAllCollection)
 
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(HTMLAllCollection)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCollection)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNamedMap)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(HTMLAllCollection)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(HTMLAllCollection)
-  tmp->mObject = nullptr;
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCollection)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mNamedMap)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(HTMLAllCollection)
-  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mObject)
-NS_IMPL_CYCLE_COLLECTION_TRACE_END
+nsINode*
+HTMLAllCollection::GetParentObject() const
+{
+  return mDocument;
+}
 
 uint32_t
 HTMLAllCollection::Length()
 {
   return Collection()->Length(true);
 }
 
 nsIContent*
 HTMLAllCollection::Item(uint32_t aIndex)
 {
   return Collection()->Item(aIndex);
 }
 
-JSObject*
-HTMLAllCollection::GetObject(JSContext* aCx, ErrorResult& aRv)
-{
-  MOZ_ASSERT(aCx);
-
-  if (!mObject) {
-    JS::Rooted<JSObject*> wrapper(aCx, mDocument->GetWrapper());
-    MOZ_ASSERT(wrapper);
-
-    JSAutoCompartment ac(aCx, wrapper);
-    JS::Rooted<JSObject*> global(aCx, JS_GetGlobalForObject(aCx, wrapper));
-    mObject = JS_NewObject(aCx, &sHTMLDocumentAllClass, JS::NullPtr(), global);
-    if (!mObject) {
-      aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
-      return nullptr;
-    }
-
-    // Make the JSObject hold a reference to the document.
-    JS_SetPrivate(mObject, ToSupports(mDocument));
-    NS_ADDREF(mDocument);
-  }
-
-  JS::ExposeObjectToActiveJS(mObject);
-  return mObject;
-}
-
 nsContentList*
 HTMLAllCollection::Collection()
 {
   if (!mCollection) {
     nsIDocument* document = mDocument;
     mCollection = document->GetElementsByTagName(NS_LITERAL_STRING("*"));
     MOZ_ASSERT(mCollection);
   }
@@ -181,202 +116,48 @@ HTMLAllCollection::GetDocumentAllList(co
 
   nsCOMPtr<nsIAtom> id = do_GetAtom(aID);
   nsRefPtr<nsContentList> docAllList =
     new nsContentList(root, DocAllResultMatch, nullptr, nullptr, true, id);
   mNamedMap.Put(aID, docAllList);
   return docAllList;
 }
 
-nsISupports*
-HTMLAllCollection::GetNamedItem(const nsAString& aID,
-                                nsWrapperCache** aCache)
+void
+HTMLAllCollection::NamedGetter(const nsAString& aID,
+                               bool& aFound,
+                               Nullable<OwningNodeOrHTMLCollection>& aResult)
 {
   nsContentList* docAllList = GetDocumentAllList(aID);
   if (!docAllList) {
-    return nullptr;
+    aFound = false;
+    aResult.SetNull();
+    return;
   }
 
   // Check if there are more than 1 entries. Do this by getting the second one
   // rather than the length since getting the length always requires walking
   // the entire document.
-
-  nsIContent* cont = docAllList->Item(1, true);
-  if (cont) {
-    *aCache = docAllList;
-    return static_cast<nsINodeList*>(docAllList);
+  if (docAllList->Item(1, true)) {
+    aFound = true;
+    aResult.SetValue().SetAsHTMLCollection() = docAllList;
+    return;
   }
 
   // There's only 0 or 1 items. Return the first one or null.
-  *aCache = cont = docAllList->Item(0, true);
-  return cont;
+  if (nsIContent* node = docAllList->Item(0, true)) {
+    aFound = true;
+    aResult.SetValue().SetAsNode() = node;
+    return;
+  }
+
+  aFound = false;
+  aResult.SetNull();
+}
+
+JSObject*
+HTMLAllCollection::WrapObject(JSContext* aCx)
+{
+  return HTMLAllCollectionBinding::Wrap(aCx, this);
 }
 
 } // namespace dom
 } // namespace mozilla
-
-static nsHTMLDocument*
-GetDocument(JSObject *obj)
-{
-  MOZ_ASSERT(js::GetObjectJSClass(obj) == &sHTMLDocumentAllClass);
-  return static_cast<nsHTMLDocument*>(
-    static_cast<nsINode*>(JS_GetPrivate(obj)));
-}
-
-bool
-nsHTMLDocumentSH::DocumentAllGetProperty(JSContext *cx, JS::Handle<JSObject*> obj_,
-                                         JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp)
-{
-  JS::Rooted<JSObject*> obj(cx, obj_);
-
-  // document.all.item and .namedItem get their value in the
-  // newResolve hook, so nothing to do for those properties here. And
-  // we need to return early to prevent <div id="item"> from shadowing
-  // document.all.item(), etc.
-  if (nsDOMClassInfo::sItem_id == id || nsDOMClassInfo::sNamedItem_id == id) {
-    return true;
-  }
-
-  JS::Rooted<JSObject*> proto(cx);
-  while (js::GetObjectJSClass(obj) != &sHTMLDocumentAllClass) {
-    if (!js::GetObjectProto(cx, obj, &proto)) {
-      return false;
-    }
-
-    if (!proto) {
-      NS_ERROR("The JS engine lies!");
-      return true;
-    }
-
-    obj = proto;
-  }
-
-  HTMLAllCollection* allCollection = GetDocument(obj)->All();
-  nsISupports *result;
-  nsWrapperCache *cache;
-
-  if (JSID_IS_STRING(id)) {
-    if (nsDOMClassInfo::sLength_id == id) {
-      // Make sure <div id="length"> doesn't shadow document.all.length.
-      vp.setNumber(allCollection->Length());
-      return true;
-    }
-
-    // For all other strings, look for an element by id or name.
-    nsDependentJSString str(id);
-    result = allCollection->GetNamedItem(str, &cache);
-  } else if (JSID_IS_INT(id) && JSID_TO_INT(id) >= 0) {
-    // Map document.all[n] (where n is a number) to the n:th item in
-    // the document.all node list.
-
-    nsIContent* node = allCollection->Item(SafeCast<uint32_t>(JSID_TO_INT(id)));
-
-    result = node;
-    cache = node;
-  } else {
-    result = nullptr;
-  }
-
-  if (result) {
-    nsresult rv = nsContentUtils::WrapNative(cx, result, cache, vp);
-    if (NS_FAILED(rv)) {
-      xpc::Throw(cx, rv);
-
-      return false;
-    }
-  } else {
-    vp.setUndefined();
-  }
-
-  return true;
-}
-
-bool
-nsHTMLDocumentSH::DocumentAllNewResolve(JSContext *cx, JS::Handle<JSObject*> obj,
-                                        JS::Handle<jsid> id,
-                                        JS::MutableHandle<JSObject*> objp)
-{
-  JS::Rooted<JS::Value> v(cx);
-
-  if (nsDOMClassInfo::sItem_id == id || nsDOMClassInfo::sNamedItem_id == id) {
-    // Define the item() or namedItem() method.
-
-    JSFunction *fnc = ::JS_DefineFunctionById(cx, obj, id, CallToGetPropMapper,
-                                              0, JSPROP_ENUMERATE);
-    objp.set(obj);
-
-    return fnc != nullptr;
-  }
-
-  if (nsDOMClassInfo::sLength_id == id) {
-    // document.all.length. Any jsval other than undefined would do
-    // here, all we need is to get into the code below that defines
-    // this propery on obj, the rest happens in
-    // DocumentAllGetProperty().
-
-    v = JSVAL_ONE;
-  } else {
-    if (!DocumentAllGetProperty(cx, obj, id, &v)) {
-      return false;
-    }
-  }
-
-  bool ok = true;
-
-  if (v.get() != JSVAL_VOID) {
-    ok = ::JS_DefinePropertyById(cx, obj, id, v, 0);
-    objp.set(obj);
-  }
-
-  return ok;
-}
-
-void
-nsHTMLDocumentSH::ReleaseDocument(JSFreeOp *fop, JSObject *obj)
-{
-  nsIHTMLDocument* doc = GetDocument(obj);
-  if (doc) {
-    nsContentUtils::DeferredFinalize(doc);
-  }
-}
-
-bool
-nsHTMLDocumentSH::CallToGetPropMapper(JSContext *cx, unsigned argc, jsval *vp)
-{
-  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
-  // Handle document.all("foo") style access to document.all.
-
-  if (args.length() != 1) {
-    // XXX: Should throw NS_ERROR_XPC_NOT_ENOUGH_ARGS for argc < 1,
-    // and create a new NS_ERROR_XPC_TOO_MANY_ARGS for argc > 1? IE
-    // accepts nothing other than one arg.
-    xpc::Throw(cx, NS_ERROR_INVALID_ARG);
-
-    return false;
-  }
-
-  // Convert all types to string.
-  JS::Rooted<JSString*> str(cx, JS::ToString(cx, args[0]));
-  if (!str) {
-    return false;
-  }
-
-  // If we are called via document.all(id) instead of document.all.item(i) or
-  // another method, use the document.all callee object as self.
-  JS::Rooted<JSObject*> self(cx);
-  if (args.calleev().isObject() &&
-      JS_GetClass(&args.calleev().toObject()) == &sHTMLDocumentAllClass) {
-    self = &args.calleev().toObject();
-  } else {
-    self = JS_THIS_OBJECT(cx, vp);
-    if (!self)
-      return false;
-  }
-
-  size_t length;
-  JS::Anchor<JSString *> anchor(str);
-  const jschar *chars = ::JS_GetStringCharsAndLength(cx, str, &length);
-  if (!chars) {
-    return false;
-  }
-
-  return ::JS_GetUCProperty(cx, self, chars, length, args.rval());
-}
--- a/content/html/document/src/HTMLAllCollection.h
+++ b/content/html/document/src/HTMLAllCollection.h
@@ -2,59 +2,94 @@
 /* vim: set ts=8 sts=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_HTMLAllCollection_h
 #define mozilla_dom_HTMLAllCollection_h
 
-#include "js/RootingAPI.h"
 #include "nsAutoPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsISupportsImpl.h"
 #include "nsRefPtrHashtable.h"
+#include "nsWrapperCache.h"
 
 #include <stdint.h>
 
 class nsContentList;
 class nsHTMLDocument;
 class nsIContent;
-class nsWrapperCache;
+class nsINode;
 
 namespace mozilla {
+
 class ErrorResult;
 
 namespace dom {
 
-class HTMLAllCollection
+class OwningNodeOrHTMLCollection;
+template<typename> class Nullable;
+
+class HTMLAllCollection MOZ_FINAL : public nsISupports
+                                  , public nsWrapperCache
 {
 public:
   HTMLAllCollection(nsHTMLDocument* aDocument);
   ~HTMLAllCollection();
 
-  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(HTMLAllCollection)
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(HTMLAllCollection)
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(HTMLAllCollection)
+
+  virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
+  nsINode* GetParentObject() const;
 
   uint32_t Length();
   nsIContent* Item(uint32_t aIndex);
+  void Item(const nsAString& aName, Nullable<OwningNodeOrHTMLCollection>& aResult)
+  {
+    NamedItem(aName, aResult);
+  }
+  nsIContent* IndexedGetter(uint32_t aIndex, bool& aFound)
+  {
+    nsIContent* result = Item(aIndex);
+    aFound = !!result;
+    return result;
+  }
 
-  JSObject* GetObject(JSContext* aCx, ErrorResult& aRv);
-
-  nsISupports* GetNamedItem(const nsAString& aID, nsWrapperCache** aCache);
+  void NamedItem(const nsAString& aName,
+                 Nullable<OwningNodeOrHTMLCollection>& aResult)
+  {
+    bool found = false;
+    NamedGetter(aName, found, aResult);
+  }
+  void NamedGetter(const nsAString& aName,
+                   bool& aFound,
+                   Nullable<OwningNodeOrHTMLCollection>& aResult);
+  void GetSupportedNames(unsigned flags, nsTArray<nsString>& aNames)
+  {
+  }
+  bool NameIsEnumerable(const nsAString& aName)
+  {
+    return false;
+  }
+  void LegacyCall(JS::Handle<JS::Value>, const nsAString& aName,
+                  Nullable<OwningNodeOrHTMLCollection>& aResult)
+  {
+    NamedItem(aName, aResult);
+  }
 
 private:
   nsContentList* Collection();
 
   /**
-   * Returns the NodeList for document.all[aID], or null if there isn't one.
+   * Returns the HTMLCollection for document.all[aID], or null if there isn't one.
    */
   nsContentList* GetDocumentAllList(const nsAString& aID);
 
-  JS::Heap<JSObject*> mObject;
   nsRefPtr<nsHTMLDocument> mDocument;
   nsRefPtr<nsContentList> mCollection;
   nsRefPtrHashtable<nsStringHashKey, nsContentList> mNamedMap;
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -2559,22 +2559,16 @@ HTMLAllCollection*
 nsHTMLDocument::All()
 {
   if (!mAll) {
     mAll = new HTMLAllCollection(this);
   }
   return mAll;
 }
 
-JSObject*
-nsHTMLDocument::GetAll(JSContext* aCx, ErrorResult& aRv)
-{
-  return All()->GetObject(aCx, aRv);
-}
-
 static void
 NotifyEditableStateChange(nsINode *aNode, nsIDocument *aDocument)
 {
   for (nsIContent* child = aNode->GetFirstChild();
        child;
        child = child->GetNextSibling()) {
     if (child->IsElement()) {
       child->AsElement()->UpdateState(true);
--- a/content/html/document/src/nsHTMLDocument.h
+++ b/content/html/document/src/nsHTMLDocument.h
@@ -99,17 +99,16 @@ public:
 
   // nsIDOMNode interface
   NS_FORWARD_NSIDOMNODE_TO_NSINODE
 
   // nsIDOMHTMLDocument interface
   NS_DECL_NSIDOMHTMLDOCUMENT
 
   mozilla::dom::HTMLAllCollection* All();
-  JSObject* GetAll(JSContext* aCx, mozilla::ErrorResult& aRv);
 
   nsISupports* ResolveName(const nsAString& aName, nsWrapperCache **aCache);
 
   virtual void AddedForm() MOZ_OVERRIDE;
   virtual void RemovedForm() MOZ_OVERRIDE;
   virtual int32_t GetNumFormsSynchronous() MOZ_OVERRIDE;
   virtual void TearingDownEditor(nsIEditor *aEditor) MOZ_OVERRIDE;
   virtual void SetIsXHTML(bool aXHTML) MOZ_OVERRIDE { mIsRegularHTML = !aXHTML; }
--- a/content/media/MediaDecoderReader.h
+++ b/content/media/MediaDecoderReader.h
@@ -73,26 +73,27 @@ public:
   // Moves the decode head to aTime microseconds. aStartTime and aEndTime
   // denote the start and end times of the media in usecs, and aCurrentTime
   // is the current playback position in microseconds.
   virtual nsresult Seek(int64_t aTime,
                         int64_t aStartTime,
                         int64_t aEndTime,
                         int64_t aCurrentTime) = 0;
 
-  // Called to move the reader into idle/active state. When the reader is
+  // Called to move the reader into idle state. When the reader is
   // created it is assumed to be active (i.e. not idle). When the media
   // element is paused and we don't need to decode any more data, the state
   // machine calls SetIdle() to inform the reader that its decoder won't be
-  // needed for a while. When we need to decode data again, the state machine
-  // calls SetActive() to activate the decoder. The reader can use these
-  // notifications to enter/exit a low power state when the decoder isn't
-  // needed, if desired. This is most useful on mobile.
+  // needed for a while. The reader can use these notifications to enter
+  // a low power state when the decoder isn't needed, if desired.
+  // This is most useful on mobile.
+  // Note: DecodeVideoFrame, DecodeAudioData, ReadMetadata and Seek should
+  // activate the decoder if necessary. The state machine only needs to know
+  // when to call SetIdle().
   virtual void SetIdle() { }
-  virtual void SetActive() { }
 
   // Tell the reader that the data decoded are not for direct playback, so it
   // can accept more files, in particular those which have more channels than
   // available in the audio output.
   void SetIgnoreAudioOutputFormat()
   {
     mIgnoreAudioOutputFormat = true;
   }
--- a/content/media/MediaDecoderStateMachine.cpp
+++ b/content/media/MediaDecoderStateMachine.cpp
@@ -189,17 +189,16 @@ MediaDecoderStateMachine::MediaDecoderSt
   mPlaybackRate(1.0),
   mPreservesPitch(true),
   mBasePosition(0),
   mAmpleVideoFrames(2),
   mLowAudioThresholdUsecs(LOW_AUDIO_USECS),
   mAmpleAudioThresholdUsecs(AMPLE_AUDIO_USECS),
   mDispatchedAudioDecodeTask(false),
   mDispatchedVideoDecodeTask(false),
-  mIsReaderIdle(false),
   mAudioCaptured(false),
   mTransportSeekable(true),
   mMediaSeekable(true),
   mPositionChangeQueued(false),
   mAudioCompleted(false),
   mGotDurationFromMetaData(false),
   mDispatchedEventToDecode(false),
   mStopAudioThread(true),
@@ -568,17 +567,16 @@ MediaDecoderStateMachine::DecodeVideo()
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
 
   if (mState != DECODER_STATE_DECODING && mState != DECODER_STATE_BUFFERING) {
     mDispatchedVideoDecodeTask = false;
     return;
   }
-  EnsureActive();
 
   // We don't want to consider skipping to the next keyframe if we've
   // only just started up the decode loop, so wait until we've decoded
   // some frames before enabling the keyframe skip logic on video.
   if (mIsVideoPrerolling &&
       (static_cast<uint32_t>(VideoQueue().GetSize())
         >= mVideoPrerollFrames * mPlaybackRate))
   {
@@ -662,17 +660,16 @@ MediaDecoderStateMachine::DecodeAudio()
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
 
   if (mState != DECODER_STATE_DECODING && mState != DECODER_STATE_BUFFERING) {
     mDispatchedAudioDecodeTask = false;
     return;
   }
-  EnsureActive();
 
   // We don't want to consider skipping to the next keyframe if we've
   // only just started up the decode loop, so wait until we've decoded
   // some audio data before enabling the keyframe skip logic on audio.
   if (mIsAudioPrerolling &&
       GetDecodedAudioDuration() >= mAudioPrerollUsecs * mPlaybackRate) {
     mIsAudioPrerolling = false;
   }
@@ -1495,54 +1492,31 @@ MediaDecoderStateMachine::EnqueueDecodeM
     NS_WARNING("Dispatch ReadMetadata task failed.");
     return rv;
   }
 
   return NS_OK;
 }
 
 void
-MediaDecoderStateMachine::EnsureActive()
-{
-  AssertCurrentThreadInMonitor();
-  MOZ_ASSERT(OnDecodeThread());
-  if (!mIsReaderIdle) {
-    return;
-  }
-  mIsReaderIdle = false;
-  {
-    ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
-    SetReaderActive();
-  }
-}
-
-void
 MediaDecoderStateMachine::SetReaderIdle()
 {
 #ifdef PR_LOGGING
   {
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     DECODER_LOG(PR_LOG_DEBUG, "SetReaderIdle() audioQueue=%lld videoQueue=%lld",
                 GetDecodedAudioDuration(),
                 VideoQueue().Duration());
   }
 #endif
   MOZ_ASSERT(OnDecodeThread());
   mReader->SetIdle();
 }
 
 void
-MediaDecoderStateMachine::SetReaderActive()
-{
-  DECODER_LOG(PR_LOG_DEBUG, "SetReaderActive()");
-  MOZ_ASSERT(OnDecodeThread());
-  mReader->SetActive();
-}
-
-void
 MediaDecoderStateMachine::DispatchDecodeTasksIfNeeded()
 {
   AssertCurrentThreadInMonitor();
 
   // NeedToDecodeAudio() can go from false to true while we hold the
   // monitor, but it can't go from true to false. This can happen because
   // NeedToDecodeAudio() takes into account the amount of decoded audio
   // that's been written to the AudioStream but not played yet. So if we
@@ -1570,29 +1544,23 @@ MediaDecoderStateMachine::DispatchDecode
 
   if (needToDecodeAudio) {
     EnsureAudioDecodeTaskQueued();
   }
   if (needToDecodeVideo) {
     EnsureVideoDecodeTaskQueued();
   }
 
-  if (mIsReaderIdle == needIdle) {
-    return;
-  }
-  mIsReaderIdle = needIdle;
-  RefPtr<nsIRunnable> event;
-  if (mIsReaderIdle) {
-    event = NS_NewRunnableMethod(this, &MediaDecoderStateMachine::SetReaderIdle);
-  } else {
-    event = NS_NewRunnableMethod(this, &MediaDecoderStateMachine::SetReaderActive);
-  }
-  if (NS_FAILED(mDecodeTaskQueue->Dispatch(event.forget())) &&
-      mState != DECODER_STATE_SHUTDOWN) {
-    NS_WARNING("Failed to dispatch event to set decoder idle state");
+  if (needIdle) {
+    RefPtr<nsIRunnable> event = NS_NewRunnableMethod(
+        this, &MediaDecoderStateMachine::SetReaderIdle);
+    nsresult rv = mDecodeTaskQueue->Dispatch(event.forget());
+    if (NS_FAILED(rv) && mState != DECODER_STATE_SHUTDOWN) {
+      NS_WARNING("Failed to dispatch event to set decoder idle state");
+    }
   }
 }
 
 nsresult
 MediaDecoderStateMachine::EnqueueDecodeSeekTask()
 {
   NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
                "Should be on state machine or decode thread.");
@@ -1823,17 +1791,16 @@ MediaDecoderStateMachine::CallDecodeMeta
 nsresult MediaDecoderStateMachine::DecodeMetadata()
 {
   AssertCurrentThreadInMonitor();
   NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
   DECODER_LOG(PR_LOG_DEBUG, "Decoding Media Headers");
   if (mState != DECODER_STATE_DECODING_METADATA) {
     return NS_ERROR_FAILURE;
   }
-  EnsureActive();
 
   nsresult res;
   MediaInfo info;
   MetadataTags* tags;
   {
     ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
     res = mReader->ReadMetadata(&info, &tags);
   }
@@ -1925,17 +1892,16 @@ nsresult MediaDecoderStateMachine::Decod
 void MediaDecoderStateMachine::DecodeSeek()
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
   AutoSetOnScopeExit<bool> unsetOnExit(mDispatchedDecodeSeekTask, false);
   if (mState != DECODER_STATE_SEEKING) {
     return;
   }
-  EnsureActive();
 
   // During the seek, don't have a lock on the decoder state,
   // otherwise long seek operations can block the main thread.
   // The events dispatched to the main thread are SYNC calls.
   // These calls are made outside of the decode monitor lock so
   // it is safe for the main thread to makes calls that acquire
   // the lock since it won't deadlock. We check the state when
   // acquiring the lock again in case shutdown has occurred
--- a/content/media/MediaDecoderStateMachine.h
+++ b/content/media/MediaDecoderStateMachine.h
@@ -560,33 +560,26 @@ protected:
   // otherwise this dispatches a task to do the decode.
   // The decoder monitor must be held.
   nsresult EnsureVideoDecodeTaskQueued();
 
   // Dispatches a task to the decode task queue to seek the decoder.
   // The decoder monitor must be held.
   nsresult EnqueueDecodeSeekTask();
 
-  // Calls the reader's SetIdle(), with aIsIdle as parameter. This is only
-  // called in a task dispatched to the decode task queue, don't call it
-  // directly.
+  // Calls the reader's SetIdle(). This is only called in a task dispatched to
+  // the decode task queue, don't call it directly.
   void SetReaderIdle();
-  void SetReaderActive();
 
   // Re-evaluates the state and determines whether we need to dispatch
   // events to run the decode, or if not whether we should set the reader
   // to idle mode. This is threadsafe, and can be called from any thread.
   // The decoder monitor must be held.
   void DispatchDecodeTasksIfNeeded();
 
-  // Called before we do anything on the decode task queue to set the reader
-  // as not idle if it was idle. This is called before we decode, seek, or
-  // decode metadata (in case we were dormant or awaiting resources).
-  void EnsureActive();
-
   // Queries our state to see whether the decode has finished for all streams.
   // If so, we move into DECODER_STATE_COMPLETED and schedule the state machine
   // to run.
   // The decoder monitor must be held.
   void CheckIfDecodeComplete();
 
   // Returns the "media time". This is the absolute time which the media
   // playback has reached. i.e. this returns values in the range
@@ -846,22 +839,16 @@ protected:
   // True when we have dispatched a task to the decode task queue to run
   // the audio decode.
   bool mDispatchedAudioDecodeTask;
 
   // True when we have dispatched a task to the decode task queue to run
   // the video decode.
   bool mDispatchedVideoDecodeTask;
 
-  // True when the reader is initialized, but has been ordered "idle" by the
-  // state machine. This happens when the MediaQueue's of decoded data are
-  // "full" and playback is paused. The reader may choose to use the idle
-  // notification to enter a low power state.
-  bool mIsReaderIdle;
-
   // If the video decode is falling behind the audio, we'll start dropping the
   // inter-frames up until the next keyframe which is at or before the current
   // playback position. skipToNextKeyframe is true if we're currently
   // skipping up to the next keyframe.
   bool mSkipToNextKeyFrame;
 
   // True if we shouldn't play our audio (but still write it to any capturing
   // streams). When this is true, mStopAudioThread is always true and
--- a/content/media/mediasource/MediaSourceDecoder.cpp
+++ b/content/media/mediasource/MediaSourceDecoder.cpp
@@ -142,17 +142,16 @@ private:
         continue;
       }
       if (aTimeThreshold >= mDecoders[i]->GetMediaStartTime()) {
         GetVideoReader()->SetIdle();
 
         mActiveVideoDecoder = i;
         MSE_DEBUG("%p MSR::DecodeVF switching to %d", this, mActiveVideoDecoder);
 
-        GetVideoReader()->SetActive();
         return true;
       }
     }
 
     return false;
   }
 
   MediaDecoderReader* GetAudioReader() {
@@ -321,17 +320,16 @@ void
 MediaSourceReader::CallDecoderInitialization()
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   for (uint32_t i = 0; i < mPendingDecoders.Length(); ++i) {
     nsRefPtr<SubBufferDecoder> decoder = mPendingDecoders[i];
     MediaDecoderReader* reader = decoder->GetReader();
     MSE_DEBUG("%p: Initializating subdecoder %p reader %p", this, decoder.get(), reader);
 
-    reader->SetActive();
     MediaInfo mi;
     nsAutoPtr<MetadataTags> tags; // TODO: Handle metadata.
     nsresult rv;
     int64_t startTime = 0;
     {
       ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
       rv = reader->ReadMetadata(&mi, getter_Transfers(tags));
       if (NS_SUCCEEDED(rv)) {
@@ -451,18 +449,16 @@ MediaSourceReader::ReadMetadata(MediaInf
   // Shorter term, make this block until we've got at least one video track
   // and lie about having an audio track, then resample/remix as necessary
   // to match any audio track added later to fit the format we lied about
   // now.  For now we just configure what we've got and cross our fingers.
   int64_t maxDuration = -1;
   for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
     MediaDecoderReader* reader = mDecoders[i]->GetReader();
 
-    reader->SetActive(); // XXX check where this should be called
-
     MediaInfo mi = reader->GetMediaInfo();
 
     if (mi.HasVideo() && !mInfo.HasVideo()) {
       MOZ_ASSERT(mActiveVideoDecoder == -1);
       mActiveVideoDecoder = i;
       mInfo.mVideo = mi.mVideo;
       maxDuration = std::max(maxDuration, mDecoders[i]->GetMediaDuration());
       MSE_DEBUG("%p: MSR::ReadMetadata video decoder=%u maxDuration=%lld", this, i, maxDuration);
--- a/content/media/omx/MediaOmxReader.cpp
+++ b/content/media/omx/MediaOmxReader.cpp
@@ -42,19 +42,16 @@ extern PRLogModuleInfo* gMediaDecoderLog
 
 MediaOmxReader::MediaOmxReader(AbstractMediaDecoder *aDecoder)
   : MediaDecoderReader(aDecoder)
   , mHasVideo(false)
   , mHasAudio(false)
   , mVideoSeekTimeUs(-1)
   , mAudioSeekTimeUs(-1)
   , mSkipCount(0)
-#ifdef DEBUG
-  , mIsActive(true)
-#endif
 {
 #ifdef PR_LOGGING
   if (!gMediaDecoderLog) {
     gMediaDecoderLog = PR_NewLogModule("MediaDecoder");
   }
 #endif
 
   mAudioChannel = dom::AudioChannelService::GetDefaultAudioChannel();
@@ -130,17 +127,17 @@ nsresult MediaOmxReader::InitOmxDecoder(
   }
   return NS_OK;
 }
 
 nsresult MediaOmxReader::ReadMetadata(MediaInfo* aInfo,
                                       MetadataTags** aTags)
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
-  MOZ_ASSERT(mIsActive);
+  EnsureActive();
 
   *aTags = nullptr;
 
   // Initialize the internal OMX Decoder.
   nsresult rv = InitOmxDecoder();
   if (NS_FAILED(rv)) {
     return rv;
   }
@@ -206,17 +203,18 @@ nsresult MediaOmxReader::ReadMetadata(Me
  *aInfo = mInfo;
 
   return NS_OK;
 }
 
 bool MediaOmxReader::DecodeVideoFrame(bool &aKeyframeSkip,
                                       int64_t aTimeThreshold)
 {
-  MOZ_ASSERT(mIsActive);
+  NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
+  EnsureActive();
 
   // Record number of frames decoded and parsed. Automatically update the
   // stats counters using the AutoNotifyDecoded stack-based class.
   uint32_t parsed = 0, decoded = 0;
   AbstractMediaDecoder::AutoNotifyDecoded autoNotify(mDecoder, parsed, decoded);
 
   bool doSeek = mVideoSeekTimeUs != -1;
   if (doSeek) {
@@ -336,17 +334,17 @@ void MediaOmxReader::NotifyDataArrived(c
   if (omxDecoder) {
     omxDecoder->NotifyDataArrived(aBuffer, aLength, aOffset);
   }
 }
 
 bool MediaOmxReader::DecodeAudioData()
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
-  MOZ_ASSERT(mIsActive);
+  EnsureActive();
 
   // This is the approximate byte position in the stream.
   int64_t pos = mDecoder->GetResource()->Tell();
 
   // Read next frame
   MPAPI::AudioFrame source;
   if (!mOmxDecoder->ReadAudio(&source, mAudioSeekTimeUs)) {
     return false;
@@ -370,17 +368,17 @@ bool MediaOmxReader::DecodeAudioData()
                               OmxCopy(static_cast<uint8_t *>(source.mData),
                                       source.mSize,
                                       source.mAudioChannels));
 }
 
 nsresult MediaOmxReader::Seek(int64_t aTarget, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime)
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
-  MOZ_ASSERT(mIsActive);
+  EnsureActive();
 
   ResetDecode();
   VideoFrameContainer* container = mDecoder->GetVideoFrameContainer();
   if (container && container->GetImageContainer()) {
     container->GetImageContainer()->ClearAllImagesExceptFront();
   }
 
   if (mHasAudio && mHasVideo) {
@@ -405,29 +403,23 @@ nsresult MediaOmxReader::Seek(int64_t aT
 static uint64_t BytesToTime(int64_t offset, uint64_t length, uint64_t durationUs) {
   double perc = double(offset) / double(length);
   if (perc > 1.0)
     perc = 1.0;
   return uint64_t(double(durationUs) * perc);
 }
 
 void MediaOmxReader::SetIdle() {
-#ifdef DEBUG
-  mIsActive = false;
-#endif
   if (!mOmxDecoder.get()) {
     return;
   }
   mOmxDecoder->Pause();
 }
 
-void MediaOmxReader::SetActive() {
-#ifdef DEBUG
-  mIsActive = true;
-#endif
+void MediaOmxReader::EnsureActive() {
   if (!mOmxDecoder.get()) {
     return;
   }
   DebugOnly<nsresult> result = mOmxDecoder->Play();
   NS_ASSERTION(result == NS_OK, "OmxDecoder should be in play state to continue decoding");
 }
 
 #ifdef MOZ_AUDIO_OFFLOAD
--- a/content/media/omx/MediaOmxReader.h
+++ b/content/media/omx/MediaOmxReader.h
@@ -44,16 +44,20 @@ protected:
   android::sp<android::MediaExtractor> mExtractor;
 
   // Called by ReadMetadata() during MediaDecoderStateMachine::DecodeMetadata()
   // on decode thread. It create and initialize the OMX decoder including
   // setting up custom extractor. The extractor provide the essential
   // information used for creating OMX decoder such as video/audio codec.
   virtual nsresult InitOmxDecoder();
 
+  // Called inside DecodeVideoFrame, DecodeAudioData, ReadMetadata and Seek
+  // to activate the decoder automatically.
+  virtual void EnsureActive();
+
 public:
   MediaOmxReader(AbstractMediaDecoder* aDecoder);
   ~MediaOmxReader();
 
   virtual nsresult Init(MediaDecoderReader* aCloneDonor);
 
   virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset);
 
@@ -78,35 +82,28 @@ public:
 
   virtual void ReleaseDecoder() MOZ_OVERRIDE;
 
   virtual nsresult ReadMetadata(MediaInfo* aInfo,
                                 MetadataTags** aTags);
   virtual nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime);
 
   virtual void SetIdle() MOZ_OVERRIDE;
-  virtual void SetActive() MOZ_OVERRIDE;
 
   void SetAudioChannel(dom::AudioChannel aAudioChannel) {
     mAudioChannel = aAudioChannel;
   }
 
   android::sp<android::MediaSource> GetAudioOffloadTrack() {
     return mAudioOffloadTrack;
   }
 
 #ifdef MOZ_AUDIO_OFFLOAD
   // Check whether it is possible to offload current audio track. This access
   // canOffloadStream() from libStageFright Utils.cpp, which is not there in
   // ANDROID_VERSION < 19
   void CheckAudioOffload();
 #endif
-
-private:
-  // This flag is true when SetActive() has been called without a matching
-  // SetIdle(). This is used to sanity check the SetIdle/SetActive calls, to
-  // ensure SetActive has been called before a decode call.
-  DebugOnly<bool> mIsActive;
 };
 
 } // namespace mozilla
 
 #endif
--- a/content/media/omx/RtspOmxReader.cpp
+++ b/content/media/omx/RtspOmxReader.cpp
@@ -294,51 +294,39 @@ nsresult RtspOmxReader::Seek(int64_t aTi
 
   // Call |MediaOmxReader::Seek| to notify the OMX decoder we are performing a
   // seek operation. The function will clear the |mVideoQueue| and |mAudioQueue|
   // that store the decoded data and also call the |DecodeToTarget| to pass
   // the seek time to OMX a/v decoders.
   return MediaOmxReader::Seek(aTime, aStartTime, aEndTime, aCurrentTime);
 }
 
-nsresult
-RtspOmxReader::ReadMetadata(MediaInfo* aInfo,
-                            MetadataTags** aTags)
-{
-  SetActive();
-
-  nsresult rv = MediaOmxReader::ReadMetadata(aInfo, aTags);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
 void RtspOmxReader::SetIdle() {
   // Call parent class to set OMXCodec idle.
   MediaOmxReader::SetIdle();
 
   // Need to pause RTSP streaming OMXCodec decoding.
   if (mRtspResource) {
     nsIStreamingProtocolController* controller =
         mRtspResource->GetMediaStreamController();
     if (controller) {
       controller->Pause();
     }
     mRtspResource->SetSuspend(true);
   }
 }
 
-void RtspOmxReader::SetActive() {
+void RtspOmxReader::EnsureActive() {
   // Need to start RTSP streaming OMXCodec decoding.
   if (mRtspResource) {
     nsIStreamingProtocolController* controller =
         mRtspResource->GetMediaStreamController();
     if (controller) {
       controller->Play();
     }
     mRtspResource->SetSuspend(false);
   }
 
   // Call parent class to set OMXCodec active.
-  MediaOmxReader::SetActive();
+  MediaOmxReader::EnsureActive();
 }
 
 } // namespace mozilla
--- a/content/media/omx/RtspOmxReader.h
+++ b/content/media/omx/RtspOmxReader.h
@@ -23,35 +23,33 @@ class RtspMediaResource;
  * The major reason that RtspOmxReader inherit from MediaOmxReader is the
  * same video/audio decoding logic we can reuse.
  */
 class RtspOmxReader : public MediaOmxReader
 {
 protected:
   // Provide a Rtsp extractor.
   nsresult InitOmxDecoder() MOZ_FINAL MOZ_OVERRIDE;
+  virtual void EnsureActive() MOZ_OVERRIDE;
 
 public:
   RtspOmxReader(AbstractMediaDecoder* aDecoder)
     : MediaOmxReader(aDecoder) {
     MOZ_COUNT_CTOR(RtspOmxReader);
     NS_ASSERTION(mDecoder, "RtspOmxReader mDecoder is null.");
     NS_ASSERTION(mDecoder->GetResource(),
                  "RtspOmxReader mDecoder->GetResource() is null.");
     mRtspResource = mDecoder->GetResource()->GetRtspPointer();
     MOZ_ASSERT(mRtspResource);
   }
 
   virtual ~RtspOmxReader() MOZ_OVERRIDE {
     MOZ_COUNT_DTOR(RtspOmxReader);
   }
 
-  virtual nsresult ReadMetadata(MediaInfo* aInfo,
-                                MetadataTags** aTags) MOZ_OVERRIDE;
-
   // Implement a time-based seek instead of byte-based..
   virtual nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime,
                         int64_t aCurrentTime) MOZ_FINAL MOZ_OVERRIDE;
 
   // Override GetBuffered() to do nothing for below reasons:
   // 1. Because the Rtsp stream is a/v separated. The buffered data in a/v
   // tracks are not consistent with time stamp.
   // For example: audio buffer: 1~2s, video buffer: 1.5~2.5s
@@ -61,17 +59,16 @@ public:
   // ChannelMediaResource, it has a "cache" that can store the whole streaming
   // data so the |GetBuffered| function can retrieve useful time ranges.
   virtual nsresult GetBuffered(mozilla::dom::TimeRanges* aBuffered,
                                int64_t aStartTime) MOZ_FINAL MOZ_OVERRIDE {
     return NS_OK;
   }
 
   virtual void SetIdle() MOZ_OVERRIDE;
-  virtual void SetActive() MOZ_OVERRIDE;
 
 private:
   // A pointer to RtspMediaResource for calling the Rtsp specific function.
   // The lifetime of mRtspResource is controlled by MediaDecoder. MediaDecoder
   // holds the MediaDecoderStateMachine and RtspMediaResource.
   // And MediaDecoderStateMachine holds this RtspOmxReader.
   RtspMediaResource* mRtspResource;
 };
--- a/content/svg/content/src/SVGTransformableElement.cpp
+++ b/content/svg/content/src/SVGTransformableElement.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "gfx2DGlue.h"
 #include "mozilla/dom/SVGAnimatedTransformList.h"
+#include "mozilla/dom/SVGGraphicsElementBinding.h"
 #include "mozilla/dom/SVGTransformableElement.h"
 #include "mozilla/dom/SVGMatrix.h"
 #include "mozilla/dom/SVGSVGElement.h"
 #include "nsContentUtils.h"
 #include "nsIDOMMutationEvent.h"
 #include "nsIFrame.h"
 #include "nsISVGChildFrame.h"
 #include "mozilla/dom/SVGRect.h"
@@ -177,32 +178,56 @@ SVGTransformableElement::GetNearestViewp
 
 nsSVGElement*
 SVGTransformableElement::GetFarthestViewportElement()
 {
   return SVGContentUtils::GetOuterSVGElement(this);
 }
 
 already_AddRefed<SVGIRect>
-SVGTransformableElement::GetBBox(ErrorResult& rv)
+SVGTransformableElement::GetBBox(const SVGBoundingBoxOptions& aOptions, 
+                                 ErrorResult& rv)
 {
   nsIFrame* frame = GetPrimaryFrame(Flush_Layout);
 
   if (!frame || (frame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
     rv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
-
   nsISVGChildFrame* svgframe = do_QueryFrame(frame);
   if (!svgframe) {
     rv.Throw(NS_ERROR_NOT_IMPLEMENTED); // XXX: outer svg
     return nullptr;
   }
 
-  return NS_NewSVGRect(this, ToRect(nsSVGUtils::GetBBox(frame)));
+  if (!NS_SVGNewGetBBoxEnabled()) {
+    return NS_NewSVGRect(this, ToRect(nsSVGUtils::GetBBox(frame)));
+  } else {
+    uint32_t aFlags = 0;
+    if (aOptions.mFill) {
+      aFlags |= nsSVGUtils::eBBoxIncludeFill;
+    }
+    if (aOptions.mStroke) {
+      aFlags |= nsSVGUtils::eBBoxIncludeStroke;
+    }
+    if (aOptions.mMarkers) {
+      aFlags |= nsSVGUtils::eBBoxIncludeMarkers;
+    }
+    if (aOptions.mClipped) {
+      aFlags |= nsSVGUtils::eBBoxIncludeClipped;
+    }
+    if (aFlags == 0) {
+      return NS_NewSVGRect(this,0,0,0,0);
+    }
+    if (aFlags == nsSVGUtils::eBBoxIncludeMarkers || 
+        aFlags == nsSVGUtils::eBBoxIncludeClipped) {
+      aFlags |= nsSVGUtils::eBBoxIncludeFill;
+    }
+    return NS_NewSVGRect(this, ToRect(nsSVGUtils::GetBBox(frame, aFlags)));
+  }
 }
 
 already_AddRefed<SVGMatrix>
 SVGTransformableElement::GetCTM()
 {
   nsIDocument* currentDoc = GetCurrentDoc();
   if (currentDoc) {
     // Flush all pending notifications so that our frames are up to date
--- a/content/svg/content/src/SVGTransformableElement.h
+++ b/content/svg/content/src/SVGTransformableElement.h
@@ -14,31 +14,33 @@
 
 namespace mozilla {
 namespace dom {
 
 class SVGAnimatedTransformList;
 class SVGGraphicsElement;
 class SVGMatrix;
 class SVGIRect;
+class SVGBoundingBoxOptions;
 
 class SVGTransformableElement : public nsSVGElement
 {
 public:
   SVGTransformableElement(already_AddRefed<nsINodeInfo>& aNodeInfo)
     : nsSVGElement(aNodeInfo) {}
   virtual ~SVGTransformableElement() {}
 
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE = 0;
 
   // WebIDL
   already_AddRefed<SVGAnimatedTransformList> Transform();
   nsSVGElement* GetNearestViewportElement();
   nsSVGElement* GetFarthestViewportElement();
-  already_AddRefed<SVGIRect> GetBBox(ErrorResult& rv);
+  already_AddRefed<SVGIRect> GetBBox(const SVGBoundingBoxOptions& aOptions, 
+                                     ErrorResult& rv);
   already_AddRefed<SVGMatrix> GetCTM();
   already_AddRefed<SVGMatrix> GetScreenCTM();
   already_AddRefed<SVGMatrix> GetTransformToElement(SVGGraphicsElement& aElement,
                                                     ErrorResult& rv);
 
   // nsIContent interface
   NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const MOZ_OVERRIDE;
 
new file mode 100644
--- /dev/null
+++ b/content/svg/content/test/getBBox-method-helper.svg
@@ -0,0 +1,299 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg"  xmlns:xlink="http://www.w3.org/1999/xlink"
+  viewBox="0 0 500 500" width="500px" height="500px">
+  <defs>
+    <clipPath id="rect01" clip-rule="evenodd" clipPathUnits="objectBoundingBox">
+      <rect x="0" y="0" width="0.5" height="1.0"/>
+    </clipPath>
+    <clipPath id="rect02" clip-rule="evenodd" clipPathUnits="objectBoundingBox">
+      <rect x="0.5" y="0" width="0.5" height="1.0"/>
+    </clipPath>
+    <clipPath id="rect03" clip-rule="evenodd" clipPathUnits="objectBoundingBox">
+      <rect x="0.5" y="0" width="0.5" height="1.0"/>
+    </clipPath>
+    <clipPath id="rect04" clip-rule="evenodd" clipPathUnits="objectBoundingBox">
+      <rect x="0" y="0" width="0.5" height="1.0"/>
+    </clipPath>
+    <clipPath id="rect05" clip-rule="evenodd">
+      <rect x="0" y="60" width="10px" height="23px"/>
+    </clipPath>
+    <clipPath id="rect06" clip-rule="evenodd">
+      <rect x="10" y="60" width="10px" height="23px"/>
+    </clipPath>
+    <clipPath id="rect4" clip-rule="evenodd">
+      <rect x="200" y="200" width="200" height="200"/>
+    </clipPath>
+    <clipPath id="rect-none" clip-rule="evenodd">
+    </clipPath>
+    <clipPath id="rect5" clip-rule="evenodd">
+      <rect x="0" y="0" width="100" height="100"/>
+    </clipPath>
+    <clipPath id="rect6" clip-rule="evenodd">
+      <rect x="150" y="0" width="100" height="100"/>
+    </clipPath>
+    <clipPath id="rect7" clip-rule="evenodd">
+      <rect x="0" y="100" width="100" height="100"/>
+    </clipPath>
+    <clipPath id="rect8" clip-rule="evenodd">
+      <rect x="10" y="10" width="180" height="180"/>
+    </clipPath>
+    <clipPath id="rect9" clip-rule="evenodd">
+      <rect x="100" y="100" width="200" height="200"/>
+    </clipPath>
+
+    <clipPath id="circle1" clip-rule="evenodd">
+      <circle cx="203" cy="203" r="150"/>
+    </clipPath>
+    <clipPath id="circle2" clip-rule="evenodd" clipPathUnits="objectBoundingBox">
+      <circle cx="0.5" cy="0.5" r="0.25"/>
+    </clipPath>
+    <clipPath id="circle3" clip-rule="evenodd">
+      <circle cx="100" cy="100" r="50"/>
+      <circle cx="300" cy="300" r="50"/>
+    </clipPath>
+
+    <clipPath id="circle4" clip-rule="evenodd">
+      <circle cx="50" cy="50" r="50"/>
+    </clipPath>
+    <clipPath id="circle5" clip-rule="evenodd">
+      <circle cx="150" cy="50" r="50"/>
+    </clipPath>
+    <clipPath id="circle6" clip-rule="evenodd">
+      <circle cx="50" cy="200" r="50"/>
+    </clipPath>
+    <clipPath id="circle7" clip-rule="evenodd" clipPathUnits="objectBoundingBox">
+      <circle cx="0.5" cy="0.5" r="0.5"/>
+    </clipPath>
+
+    <clipPath id="circle8" clip-rule="evenodd" clipPathUnits="userSpaceOnUse">
+      <circle cx="110"  cy="20"  r="90"/>
+    </clipPath>
+
+    <clipPath id="circle9" clip-rule="evenodd" clipPathUnits="userSpaceOnUse">
+      <circle cx="290"  cy="20" r="90"/>
+    </clipPath>
+
+    <clipPath id="circle10" clip-rule="evenodd" clipPathUnits="userSpaceOnUse">
+      <circle cx="110" cy="200"  r="90"/>
+    </clipPath>
+
+    <clipPath id="circle11" clip-rule="evenodd">
+      <circle cx="0" cy="0" r="150"/>
+    </clipPath>
+
+    <clipPath id="star" clip-rule="evenodd">
+      <path d="M400,25 L619,703 43,283 757,283 181,703 z" />
+    </clipPath>
+
+    <marker id="m_atr" markerUnits="strokeWidth" markerWidth="3" markerHeight="3" viewBox="0 0 10 10" refX="5" refY="5">
+        <polygon points="0,0 5,5 0,10 10,5" fill="red"/>
+    </marker>
+
+    <switch>
+      <rect id="rect-10" x="20"  y="20"  width="180" height="180" fill="blue"  stroke="cyan" stroke-width="8"/>
+      <rect id="rect-11" x="200" y="20"  width="180" height="180" fill="lightgreen" stroke="none" />
+      <rect id="rect-12" x="20"  y="200" width="180" height="180" fill="darkcyan" stroke="none" />
+    </switch>
+
+    <clipPath id="clipCircle1">
+      <circle id="c1" cx="100" cy="100" r="50"/>
+    </clipPath>
+
+    <clipPath id="clipCircle2">
+      <circle id="c2" cx="150" cy="150" r="50"/>
+    </clipPath>
+
+    <clipPath id="clipPath1">
+      <path id="p1" d="M10 10l100 0 0 100 -100 0ZM50 50l40 0 0 40 -40 0Z" clip-rule="evenodd"/>
+    </clipPath>
+
+    <!-- "If a valid 'clip-path' reference is placed on one of the children of a 'clipPath' element, 
+        then the given child element is clipped by the referenced clipping path before OR'ing the 
+      silhouette of the child element with the silhouettes of the other child elements." -->
+
+    <clipPath id="clipRects1">
+      <rect x="50" y="30" width="25" height="100"/>
+      <rect x="25" y="50" width="10" height="10" clip-path="url(#clipTwoCircles)"/>
+    </clipPath>
+
+    <!-- Test use in a clipPath -->
+    <clipPath id="clipTwoCircles">
+      <use xlink:href="#c1"/>
+      <use xlink:href="#c2"/>
+    </clipPath>
+
+    <clipPath id="clipInClip1">
+      <use xlink:href="#c2" clip-path="url(#clipCircle1)"/>
+      <use xlink:href="#p1"/>
+    </clipPath>
+
+    <clipPath id="clipOnClip1" clip-path="url(#clipCircle1)">
+      <use xlink:href="#c2"/>
+      <use xlink:href="#p1"/>
+    </clipPath>
+
+  </defs>
+
+  <!-- text -->
+  <text id="text1" font-size="20px" font-familiy="monospace" fill="red"  x="0" y="50" clip-path="url('#rect01')">99</text>
+  <text id="text2" font-size="20px" font-familiy="monospace" fill="blue" x="100" y="120" clip-path="url('#rect02')">99</text>
+  <text id="text3" font-size="20px" font-familiy="monospace" clip-path="url('#rect03')" x="0" y="120">
+    <tspan x="0" y="50" fill="red">99</tspan>
+  </text>
+  <text id="text4" font-size="20px" font-familiy="monospace" clip-path="url('#rect04')" x="0" y="120">
+    <tspan x="100" y="120" fill="blue">99</tspan>
+  </text>
+  <text id="text5" font-size="20px" font-familiy="monospace" fill="red"  x="0" y="80" clip-path="url('#rect05')">99</text>
+  <text id="text6" font-size="20px" font-familiy="monospace" fill="blue" x="0" y="80" clip-path="url('#rect06')">99</text>
+
+  <!-- image -->
+  <image id="image1" x="150" y="150" width="200" height="200" preserveApectRatio="none" clip="rect(200,300,300,200)"
+   xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
+  
+  <image id="image2" x="2"   y="2"   width="200" height="200" clip-path="url('#circle1')" preserveApectRatio="none"
+   xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
+
+  <image id="image3" x="205" y="2"   width="200" height="200" clip-path="url('#circle1')" preserveApectRatio="none"
+   xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
+
+  <image id="image4" x="2"   y="205" width="200" height="200" clip-path="url('#circle1')" preserveApectRatio="none"
+   xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
+
+  <image id="image5" x="205" y="205" width="200" height="200" clip-path="url('#circle1')" preserveApectRatio="none"
+   xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
+
+  <image id="image6" x="2"   y="2"   width="200" height="200" clip-path="url('#circle2')" preserveApectRatio="none"
+   xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
+
+  <image id="image7" x="205" y="2"   width="200" height="200" clip-path="url('#circle2')" preserveApectRatio="none"
+   xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
+
+  <image id="image8" x="2"   y="205" width="200" height="200" clip-path="url('#circle2')" preserveApectRatio="none"
+   xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
+
+  <image id="image9" x="205" y="205" width="200" height="200" clip-path="url('#circle2')" preserveApectRatio="none"
+   xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
+
+  <image id="image10" x="0" y="0" width="400" height="400" clip-path="url('#rect4')"
+   xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
+
+  <image id="image11" x="0" y="0" width="400" height="400" clip-path="url('#rect-none')"
+   xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
+
+  <image id="image12" x="25" y="43" width="768" height="768" clip-path="url('#star')" preserveApectRatio="none"
+   xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
+
+  <image id="image13" x="0" y="0" width="400" height="400" clip-path="url('#circle3')"
+   xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
+
+  <image id="image14" x="0" y="0" width="400" height="400" clip-path="url('#m_atr')"
+   xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
+
+  <!-- path -->
+  <path id="path1" d="M10,50 L25,100 H110 V50 Q60,0 10,50" stroke="black" stroke-width="8" stroke-miterlimit="1" stroke-linejoin="miter" fill="lightcyan" marker-mid="url(#m_atr)"/>
+  <path id="path2" d="M160,50 L175,100 H260 V50 Q210,0 160,50" stroke="black" stroke-width="8" stroke-linejoin="bevel" fill="lightcyan" marker-mid="url(#m_atr)"/>
+  <path id="path3" d="M10,150 L25,200 H110 V150 Q60,100 10,150" stroke="black" stroke-width="8" stroke-linejoin="round" fill="lightcyan" marker-mid="url(#m_atr)"/>
+  
+
+  <path id="path4" d="M10,50 L25,100 H110 V50 Q60,0 10,50" stroke="black" stroke-width="8" stroke-miterlimit="1" stroke-linejoin="miter" fill="lightcyan"
+    marker-mid="url(#m_atr)" clip-path="url(#circle4)"/>
+
+  <path id="path5" d="M160,50 L175,100 H260 V50 Q210,0 160,50" stroke="black" stroke-width="8" stroke-linejoin="bevel" fill="lightcyan"
+    marker-mid="url(#m_atr)" clip-path="url(#circle5)"/>
+
+  <path id="path6" d="M10,150 L25,200 H110 V150 Q60,100 10,150" stroke="black" stroke-width="8" stroke-linejoin="round" fill="lightcyan"
+    marker-mid="url(#m_atr)" clip-path="url(#circle6)"/>
+
+  <path id="path7" d="M10,50 L25,100 H110 V50 Q60,0 10,50"
+        stroke="black" stroke-width="8" stroke-miterlimit="1" stroke-linejoin="miter" fill="lightcyan"
+        clip-path="url('#rect5')" marker-mid="url(#m_atr)"/>
+
+  <path id="path8" d="M160,50 L175,100 H260 V50 Q210,0 160,50"
+        stroke="black" stroke-width="8" stroke-linejoin="bevel" fill="lightcyan"
+        clip-path="url('#rect6')" marker-mid="url(#m_atr)"/>
+
+  <path id="path9" d="M10,150 L25,200 H110 V150 Q60,100 10,150"
+        stroke="black" stroke-width="8" stroke-linejoin="round" fill="lightcyan"
+        clip-path="url('#rect7')" marker-mid="url(#m_atr)"/>
+
+  <path id="path10" d="M10,50 L25,100 H110 V50 Q60,0 10,50"
+        stroke="black" stroke-width="8" stroke-miterlimit="1" stroke-linejoin="miter" fill="lightcyan"
+        clip-path="url('#circle7')" marker-mid="url(#m_atr)"/>
+
+  <path id="path11" d="M160,50 L175,100 H260 V50 Q210,0 160,50"
+        stroke="black" stroke-width="8" stroke-linejoin="bevel" fill="lightcyan"
+        clip-path="url('#circle7')" marker-mid="url(#m_atr)"/>
+
+  <path id="path12" d="M10,150 L25,200 H110 V150 Q60,100 10,150"
+        stroke="black" stroke-width="8" stroke-linejoin="round" fill="lightcyan"
+        clip-path="url('#circle7')" marker-mid="url(#m_atr)"/>
+
+  <!-- use -->
+  <use id="use1" xlink:href="#rect-10" x="50" y="50" clip-path="url('#circle8')"/>
+  <use id="use2" xlink:href="#rect-11" x="50" y="50" clip-path="url('#circle9')"/>
+  <use id="use3" xlink:href="#rect-12" x="50" y="50" clip-path="url('#circle10')"/>
+  
+  <use id="use4" xlink:href="#rect-10" x="2"   y="2"   width="200" height="200" clip-path="url('#circle11')"/>
+  <use id="use5" xlink:href="#rect-10" x="205" y="2"   width="200" height="200" clip-path="url('#circle11')"/>
+  <use id="use6" xlink:href="#rect-10" x="2"   y="205" width="200" height="200" clip-path="url('#circle11')"/>
+  <use id="use7" xlink:href="#rect-10" x="205" y="205" width="200" height="200" clip-path="url('#circle11')"/>
+
+  <use id="use8" xlink:href="#rect-10" x="50" y="50" clip-path="url('#m_atr')"/>
+
+  <!-- foreignObject -->
+  <foreignObject id="fo1" x="2" y="2" width="200" height="200" clip-path="url('#circle1')" clip="rect(2,102,102,2)">
+    <div xmlns="http://www.w3.org/1999/xhtml" style="background-color:pink;width:100%;height:100%;">
+There are issues of dynamic loading required for tiling. Accroding to 'postpone' attribute of Resource Priorities, the dynamic loading is controlled by positional relation with bounding box of embedded contents and container's viewport. However, each bounding boxes of embedded contents should be whole earth basically when this method is used. (green part on Example) Tiling is impossible unless this situation is changed.
+    </div>
+  </foreignObject>
+  <foreignObject id="fo2" x="205" y="2" width="200" height="200" clip-path="url('#circle1')" >
+    <div xmlns="http://www.w3.org/1999/xhtml" style="background-color:pink;width:100%;height:100%;">
+There are issues of dynamic loading required for tiling. Accroding to 'postpone' attribute of Resource Priorities, the dynamic loading is controlled by positional relation with bounding box of embedded contents and container's viewport. However, each bounding boxes of embedded contents should be whole earth basically when this method is used. (green part on Example) Tiling is impossible unless this situation is changed.
+    </div>
+  </foreignObject>
+  <foreignObject id="fo3" x="2" y="205" width="200" height="200" clip-path="url('#circle1')" >
+    <div xmlns="http://www.w3.org/1999/xhtml" style="background-color:pink;width:100%;height:100%;">
+There are issues of dynamic loading required for tiling. Accroding to 'postpone' attribute of Resource Priorities, the dynamic loading is controlled by positional relation with bounding box of embedded contents and container's viewport. However, each bounding boxes of embedded contents should be whole earth basically when this method is used. (green part on Example) Tiling is impossible unless this situation is changed.
+    </div>
+  </foreignObject>
+  <foreignObject id="fo4" x="205" y="205" width="200" height="200" clip-path="url('#circle1')" clip="rect(2,102,102,2)">
+    <div xmlns="http://www.w3.org/1999/xhtml" style="background-color:pink;width:100%;height:100%;">
+There are issues of dynamic loading required for tiling. Accroding to 'postpone' attribute of Resource Priorities, the dynamic loading is controlled by positional relation with bounding box of embedded contents and container's viewport. However, each bounding boxes of embedded contents should be whole earth basically when this method is used. (green part on Example) Tiling is impossible unless this situation is changed.
+    </div>
+  </foreignObject>
+  
+  <foreignObject id="fo5" x="250" y="250" width="200" height="200" clip-path="url('#rect8')">
+    <div xmlns="http://www.w3.org/1999/xhtml" style="background-color:pink;width:100%;height:100%;">
+There are issues of dynamic loading required for tiling. Accroding to 'postpone' attribute of Resource Priorities, the dynamic loading is controlled by positional relation with bounding box of embedded contents and container's viewport. However, each bounding boxes of embedded contents should be whole earth basically when this method is used. (green part on Example) Tiling is impossible unless this situation is changed.
+    </div>
+  </foreignObject>
+
+  <foreignObject id="fo6" x="0" y="0" width="200" height="200" clip-path="url('#rect9')">
+    <div xmlns="http://www.w3.org/1999/xhtml" style="background-color:pink;width:100%;height:100%;">
+There are issues of dynamic loading required for tiling. Accroding to 'postpone' attribute of Resource Priorities, the dynamic loading is controlled by positional relation with bounding box of embedded contents and container's viewport. However, each bounding boxes of embedded contents should be whole earth basically when this method is used. (green part on Example) Tiling is impossible unless this situation is changed.
+    </div>
+  </foreignObject>
+
+  <foreignObject id="fo7" x="0" y="0" width="200" height="200" clip-path="url('#rect8')">
+    <div xmlns="http://www.w3.org/1999/xhtml" style="background-color:pink;width:100%;height:100%;">
+There are issues of dynamic loading required for tiling. Accroding to 'postpone' attribute of Resource Priorities, the dynamic loading is controlled by positional relation with bounding box of embedded contents and container's viewport. However, each bounding boxes of embedded contents should be whole earth basically when this method is used. (green part on Example) Tiling is impossible unless this situation is changed.
+    </div>
+  </foreignObject>
+
+  <foreignObject id="fo8" x="0" y="0" width="200" height="200" clip-path="url('#m_atr')">
+    <div xmlns="http://www.w3.org/1999/xhtml" style="background-color:pink;width:100%;height:100%;">
+There are issues of dynamic loading required for tiling. Accroding to 'postpone' attribute of Resource Priorities, the dynamic loading is controlled by positional relation with bounding box of embedded contents and container's viewport. However, each bounding boxes of embedded contents should be whole earth basically when this method is used. (green part on Example) Tiling is impossible unless this situation is changed.
+    </div>
+  </foreignObject>
+
+  <!-- -->
+  <rect id="rect-1" width="200" height="200" fill="blue" clip-path="url(#clipInClip1)"/>
+  <rect id="rect-2" width="200" height="200" fill="blue" clip-path="url(#clipRects1)"/>
+  <rect id="rect-3" width="300" height="300" fill="blue" clip-path="url(#clipOnClip1)"/>
+
+  <g clip-path="url(#clipCircle1)" id="g1">
+    <use xlink:href="#c2" fill="red"/>
+    <use xlink:href="#p1" fill="red" fill-rule="evenodd"/>
+  </g>
+
+</svg>
--- a/content/svg/content/test/mochitest.ini
+++ b/content/svg/content/test/mochitest.ini
@@ -4,16 +4,17 @@ support-files =
   a_href_destination.svg
   a_href_helper_01.svg
   a_href_helper_02_03.svg
   a_href_helper_04.svg
   animated-svg-image-helper.html
   animated-svg-image-helper.svg
   bbox-helper.svg
   bounds-helper.svg
+  getBBox-method-helper.svg
   dataTypes-helper.svg
   fragments-helper.svg
   getCTM-helper.svg
   getSubStringLength-helper.svg
   matrixUtils.js
   MutationEventChecker.js
   pointer-events.js
   scientific-helper.svg
@@ -29,16 +30,17 @@ support-files =
 [test_a_href_02.xhtml]
 [test_animLengthObjectIdentity.xhtml]
 [test_animLengthReadonly.xhtml]
 [test_animLengthUnits.xhtml]
 [test_bbox-with-invalid-viewBox.xhtml]
 [test_bbox.xhtml]
 [test_bounds.html]
 [test_bug872812.html]
+[test_getBBox-method.html]
 [test_dataTypes.html]
 [test_dataTypesModEvents.html]
 [test_fragments.html]
 [test_getCTM.html]
 [test_getElementById.xhtml]
 [test_getSubStringLength.xhtml]
 [test_hasFeature.xhtml]
 [test_lang.xhtml]
new file mode 100644
--- /dev/null
+++ b/content/svg/content/test/test_getBBox-method.html
@@ -0,0 +1,249 @@
+<!DOCTYPE HTML>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=999964
+-->
+<head>
+  <meta charset="utf-8"/>
+  <title>Test case for Bug 999964</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=999964">Mozilla Bug 999964</a>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+
+<iframe id="svg" src="getBBox-method-helper.svg"></iframe>
+
+<pre id="test">
+<script class="testbody" type="application/javascript">
+
+  /** Test case for Bug 999964 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function run()
+{
+  var flag = SpecialPowers.getBoolPref("svg.new-getBBox.enabled");
+  if (!flag) {
+    ok(!flag, "skip test for bug999964.");
+    SimpleTest.finish();
+    return;
+  }
+
+  var doc = $("svg").contentDocument;
+
+  function isFuzzy(a, b, error, name)
+  {
+    ok(!(Math.abs(a - b) > error), name, "got " + a + ", expected " + b + " (within " + error + ")");
+  }
+
+  function getBBox(id, opt) {
+    return doc.getElementById(id).getBBox(opt);
+  }
+
+  function checkBBox(id, opt, x, y, width, height, error) {
+    var bbox = getBBox(id, opt);
+    isFuzzy(bbox.x, x, error, id + ".getBBox().x");
+    isFuzzy(bbox.y, y, error, id + ".getBBox().y");
+    isFuzzy(bbox.width, width, error, id + ".getBBox().width");
+    isFuzzy(bbox.height, height, error, id + ".getBBox().height");
+  }
+
+  function compareBBox1(id1, id2) {
+    var bbox1 = getBBox(id1);
+    var bbox2 = getBBox(id2);
+    is(bbox1.x, bbox2.x, id1 + ".getBBox().x");
+    is(bbox1.y, bbox2.y, id1 + ".getBBox().y");
+    isFuzzy(bbox1.width, bbox2.width, 0.0002, id1 + ".getBBox().width");
+    isFuzzy(bbox1.height, bbox2.height, 0.0001, id1 + ".getBBox().height");
+  }
+
+  function compareBBox2(id1, id2) {
+    // without 'x'
+    var bbox1 = getBBox(id1);
+    var bbox2 = getBBox(id2);
+    is(bbox1.y, bbox2.y, id1 + ".getBBox().y");
+    isFuzzy(bbox1.width, bbox2.width, 0.0002, id1 + ".getBBox().width");
+    isFuzzy(bbox1.height, bbox2.height, 0.0001, id1 + ".getBBox().height");
+  }
+
+  var opt = { fill: true, stroke: true, markers: true, clipped: true };
+
+  // <text>
+  // fill
+  opt = { fill: true, stroke: false, markers: false, clipped: false };
+  compareBBox1("text1","text3");
+  compareBBox1("text2","text4");
+  compareBBox1("text5","text6");
+  // all
+  opt = { fill: true, stroke: true, markers: true, clipped: true };
+  compareBBox2("text1","text3");
+  compareBBox2("text2","text4");
+  compareBBox2("text5","text6");
+  // clipped
+  opt = { fill: false, stroke: false, markers: false, clipped: true };
+  compareBBox2("text1","text3");
+  compareBBox2("text2","text4");
+  compareBBox2("text5","text6");
+
+  // <image>
+  opt = { fill: true, stroke: true, markers: true, clipped: true };
+  checkBBox("image1", opt, 250, 250, 100, 100);
+  checkBBox("image2", opt, 53, 53, 149, 149);
+  checkBBox("image3", opt, 205, 53, 148, 149);
+  checkBBox("image4", opt, 53, 205, 149, 148);
+  checkBBox("image5", opt, 205, 205, 148, 148);
+  checkBBox("image6", opt, 52, 52, 100, 100);
+  checkBBox("image7", opt, 255, 52, 100, 100);
+  checkBBox("image8", opt, 52, 255, 100, 100);
+  checkBBox("image9", opt, 255, 255, 100, 100);
+  checkBBox("image10", opt, 200, 200, 200, 200);
+  checkBBox("image11", opt, 0, 0, 0, 0);
+  checkBBox("image12", opt, 43, 43, 714, 660);
+  checkBBox("image13", opt, 50, 50, 300, 300);
+  checkBBox("image14", opt, 0, 0, 0, 0);
+
+  opt = { fill: true, stroke: false, markers: false, clipped: false };
+  checkBBox("image1", opt, 150,150,200,200, 0);
+  checkBBox("image2", opt, 2,2,200,200, 0);
+  checkBBox("image3", opt, 205,2,200,200, 0);
+  checkBBox("image4", opt, 2,205,200,200, 0);
+  checkBBox("image5", opt, 205,205,200,200, 0);
+  checkBBox("image6", opt, 2,2,200,200, 0);
+  checkBBox("image7", opt, 205,2,200,200, 0);
+  checkBBox("image8", opt, 2,205,200,200, 0);
+  checkBBox("image9", opt, 205,205,200,200, 0);
+  checkBBox("image10", opt, 0,0,400,400, 0);
+  checkBBox("image11", opt, 0,0,400,400, 0);
+  checkBBox("image12", opt, 25,43,768,768, 0);
+  checkBBox("image13", opt, 0,0,400,400, 0);
+  
+  // <path>
+  opt = { fill: true, stroke: true, markers: true, clipped: true };
+  checkBBox("path1", opt, 2,17,120,95, 0);
+  checkBBox("path2", opt, 156,21,116,91, 0);
+  checkBBox("path3", opt, 6,121,116,91, 0);
+  checkBBox("path4", opt, 2,17,98,83, 0);
+  checkBBox("path5", opt, 156,21,44,79, 0);
+  checkBBox("path6", opt, 6,150,94,62, 0);
+  checkBBox("path7", opt, 2,17,98,83, 0);
+  checkBBox("path8", opt, 156,21,94,79, 0);
+  checkBBox("path9", opt, 6,121,94,79, 0);
+  checkBBox("path10", opt, 10,25,100,75, 0);
+  checkBBox("path11", opt, 160,25,100,75, 0);
+  checkBBox("path12", opt, 10,125,100,75, 0);
+
+  opt = { fill: true, stroke: false, markers: false, clipped: true };
+  checkBBox("path1", opt, 10,25,100,75, 0);
+  checkBBox("path2", opt, 160,25,100,75, 0);
+  checkBBox("path3", opt, 10,125,100,75, 0);
+  checkBBox("path4", opt, 10,25,90,75, 0);
+  checkBBox("path5", opt, 160,25,40,75, 0);
+  checkBBox("path6", opt, 10,150,90,50, 0);
+  checkBBox("path7", opt, 10,25,90,75, 0);
+  checkBBox("path8", opt, 160,25,90,75, 0);
+  checkBBox("path9", opt, 10,125,90,75, 0);
+  checkBBox("path10", opt, 10,25,100,75, 0);
+  checkBBox("path11", opt, 160,25,100,75, 0);
+  checkBBox("path12", opt, 10,125,100,75, 0);
+
+  opt = { fill: true, stroke: false, markers: false, clipped: false };
+  checkBBox("path1", opt, 10,25,100,75, 0);
+  checkBBox("path2", opt, 160,25,100,75, 0);
+  checkBBox("path3", opt, 10,125,100,75, 0);
+  checkBBox("path4", opt, 10,25,100,75, 0);
+  checkBBox("path5", opt, 160,25,100,75, 0);
+  checkBBox("path6", opt, 10,125,100,75, 0);
+  checkBBox("path7", opt, 10,25,100,75, 0);
+  checkBBox("path8", opt, 160,25,100,75, 0);
+  checkBBox("path9", opt, 10,125,100,75, 0);
+  checkBBox("path10", opt, 10,25,100,75, 0);
+  checkBBox("path11", opt, 160,25,100,75, 0);
+  checkBBox("path12", opt, 10,125,100,75, 0);
+
+  opt = { fill: false, stroke: true, markers: false, clipped: false };
+  checkBBox("path1", opt, 2,17,116,91, 0);
+  checkBBox("path2", opt, 156,21,108,83, 0);
+  checkBBox("path3", opt, 6,121,108,83, 0);
+  checkBBox("path4", opt, 2,17,116,91, 0);
+  checkBBox("path5", opt, 156,21,108,83, 0);
+  checkBBox("path6", opt, 6,121,108,83, 0);
+  checkBBox("path7", opt, 2,17,116,91, 0);
+  checkBBox("path8", opt, 156,21,108,83, 0);
+  checkBBox("path9", opt, 6,121,108,83, 0);
+  checkBBox("path10", opt, 2,17,116,91, 0);
+  checkBBox("path11", opt, 156,21,108,83, 0);
+  checkBBox("path12", opt, 6,121,108,83, 0);
+
+  opt = { fill: false, stroke: false, markers: true, clipped: false };
+  checkBBox("path1", opt, 10,25,112,87, 0);
+  checkBBox("path2", opt, 160,25,112,87, 0);
+  checkBBox("path3", opt, 10,125,112,87, 0);
+  checkBBox("path4", opt, 10,25,112,87, 0);
+  checkBBox("path5", opt, 160,25,112,87, 0);
+  checkBBox("path6", opt, 10,125,112,87, 0);
+  checkBBox("path7", opt, 10,25,112,87, 0);
+  checkBBox("path8", opt, 160,25,112,87, 0);
+  checkBBox("path9", opt, 10,125,112,87, 0);
+  checkBBox("path10", opt, 10,25,112,87, 0);
+  checkBBox("path11", opt, 160,25,112,87, 0);
+  checkBBox("path12", opt, 10,125,112,87, 0);
+
+  // <use>
+  opt = { fill: true, stroke: false, markers: false, clipped: false };
+  checkBBox("use1", opt, 70,70,180,180, 0);
+  checkBBox("use2", opt, 250,70,180,180, 0);
+  checkBBox("use3", opt, 70,250,180,180, 0);
+  checkBBox("use4", opt, 22,22,180,180, 0);
+  checkBBox("use5", opt, 225,22,180,180, 0);
+  checkBBox("use6", opt, 22,225,180,180, 0);
+  checkBBox("use7", opt, 225,225,180,180, 0);
+
+  opt = { fill: true, stroke: true, markers: true, clipped: true };
+  checkBBox("use1", opt, 70,66,180,94, 0);
+  checkBBox("use2", opt, 250,70,180,90, 0);
+  checkBBox("use3", opt, 70,250,180,90, 0);
+  checkBBox("use4", opt, 18,18,134,134, 0);
+  checkBBox("use5", opt, 221,18,134,134, 0);
+  checkBBox("use6", opt, 18,221,134,134, 0);
+  checkBBox("use7", opt, 221,221,134,134, 0);
+  checkBBox("use8", opt, 0,0,0,0, 0);
+
+  // <foreignObject>
+  opt = { fill: true, stroke: false, markers: false, clipped: false };
+  checkBBox("fo1", opt, 2,2,200,200, 0);
+  checkBBox("fo2", opt, 205,2,200,200, 0);
+  checkBBox("fo3", opt, 2,205,200,200, 0);
+  checkBBox("fo4", opt, 205,205,200,200, 0);
+  checkBBox("fo5", opt, 250,250,200,200, 0);
+  checkBBox("fo6", opt, 0,0,200,200, 0);
+  checkBBox("fo7", opt, 0,0,200,200, 0);
+
+  opt = { fill: true, stroke: true, markers: true, clipped: true };
+  checkBBox("fo1", opt, 53,53,51,51, 0);
+  checkBBox("fo2", opt, 205,53,148,149, 0);
+  checkBBox("fo3", opt, 53,205,149,148, 0);
+  checkBBox("fo4", opt, 207,207,100,100, 0);
+  checkBBox("fo5", opt, 0,0,0,0, 0);
+  checkBBox("fo6", opt, 100,100,100,100, 0);
+  checkBBox("fo7", opt, 10,10,180,180, 0);
+  checkBBox("fo8", opt, 0,0,0,0, 0);
+
+  // from http://www.w3.org/Graphics/SVG/Test/20110816/harness/htmlObjectApproved/masking-path-07-b.html
+  opt = { fill: true, stroke: true, markers: true, clipped: true };
+  checkBBox("rect-1", opt, 10,10,140,140, 0);
+  checkBBox("rect-2", opt, 50,30,25,100, 0);
+  checkBBox("rect-3", opt, 50,50,100,100, 0);
+  checkBBox("g1", opt, 50,50,100,100, 0);
+
+  SimpleTest.finish();
+}
+
+window.addEventListener("load", run, false);
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -93,16 +93,21 @@
 #include "nsIUploadChannel2.h"
 #include "nsFormData.h"
 #include "nsIPrivateBrowsingChannel.h"
 #include "nsIDocShell.h"
 
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
 
+#if defined(XP_LINUX)
+#include "mozilla/Hal.h"
+#endif
+#include "mozilla/dom/ContentChild.h"
+
 namespace mozilla {
 namespace dom {
 
 static bool sDoNotTrackEnabled = false;
 static bool sVibratorEnabled   = false;
 static uint32_t sMaxVibrateMS  = 0;
 static uint32_t sMaxVibrateListLen = 0;
 
@@ -1478,51 +1483,37 @@ Navigator::GetDataStores(const nsAString
 already_AddRefed<Promise>
 Navigator::GetFeature(const nsAString& aName)
 {
   nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(mWindow);
   nsRefPtr<Promise> p = new Promise(go);
 
 #if defined(XP_LINUX)
   if (aName.EqualsLiteral("hardware.memory")) {
-    static int memLevel = 1;
-    if (memLevel == 1) {
-      FILE* f = fopen("/proc/meminfo", "r");
-      if (!f) {
-        p->MaybeReject(NS_LITERAL_STRING("CannotOpenMeminfo"));
-        return p.forget();
-      }
-
-      int memTotal;
-      int n = fscanf(f, "MemTotal: %d kB\n", &memTotal);
-      fclose(f);
-
-      if (memTotal == 0 || n != 1) {
+    // with seccomp enabled, fopen() should be in a non-sandboxed process
+    if (XRE_GetProcessType() == GeckoProcessType_Default) {
+      uint32_t memLevel = mozilla::hal::GetTotalSystemMemoryLevel();
+      if (memLevel == 0) {
         p->MaybeReject(NS_LITERAL_STRING("Abnormal"));
         return p.forget();
       }
-      // From KB to MB
-      memTotal /= 1024;
-
-      // round the value up to the next power of two
-      while (memLevel <= memTotal) {
-        memLevel *= 2;
-      }
+      p->MaybeResolve((int)memLevel);
+    } else {
+      mozilla::dom::ContentChild* cc =
+        mozilla::dom::ContentChild::GetSingleton();
+      nsRefPtr<Promise> ipcRef(p);
+      cc->SendGetSystemMemory(reinterpret_cast<uint64_t>(ipcRef.forget().take()));
     }
-    p->MaybeResolve(memLevel);
+    return p.forget();
   } // hardware.memory
-  else
 #endif
-  {
-    // resolve with <undefined> because the feature name is not supported
-    p->MaybeResolve(JS::UndefinedHandleValue);
-  }
+  // resolve with <undefined> because the feature name is not supported
+  p->MaybeResolve(JS::UndefinedHandleValue);
 
   return p.forget();
-
 }
 
 
 PowerManager*
 Navigator::GetMozPower(ErrorResult& aRv)
 {
   if (!mPowerManager) {
     if (!mWindow) {
new file mode 100644
--- /dev/null
+++ b/dom/base/SlowScriptDebug.js
@@ -0,0 +1,21 @@
+/* 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";
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+function SlowScriptDebug() { }
+
+SlowScriptDebug.prototype = {
+  classID: Components.ID("{e740ddb4-18b4-4aac-8ae1-9b0f4320769d}"),
+  classDescription: "Slow script debug handler",
+  contractID: "@mozilla.org/dom/slow-script-debug;1",
+  QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsISlowScriptDebug]),
+
+  get activationHandler()   { return this._activationHandler; },
+  set activationHandler(cb) { return this._activationHandler = cb; },
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SlowScriptDebug]);
new file mode 100644
--- /dev/null
+++ b/dom/base/SlowScriptDebug.manifest
@@ -0,0 +1,2 @@
+component {e740ddb4-18b4-4aac-8ae1-9b0f4320769d} SlowScriptDebug.js
+contract @mozilla.org/dom/slow-script-debug;1 {e740ddb4-18b4-4aac-8ae1-9b0f4320769d}
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -8,16 +8,17 @@ TEST_DIRS += ['test']
 
 XPIDL_SOURCES += [
     'nsIConsoleAPIStorage.idl',
     'nsIDOMDOMCursor.idl',
     'nsIDOMDOMRequest.idl',
     'nsIEntropyCollector.idl',
     'nsIScriptChannel.idl',
     'nsISiteSpecificUserAgent.idl',
+    'nsISlowScriptDebug.idl',
 ]
 
 XPIDL_MODULE = 'dom'
 
 EXPORTS += [
     'Crypto.h',
     'nsContentPermissionHelper.h',
     'nsDOMCID.h',
@@ -123,16 +124,18 @@ SOURCES += [
     'nsPluginArray.cpp',
 ]
 
 EXTRA_COMPONENTS += [
     'ConsoleAPI.manifest',
     'ConsoleAPIStorage.js',
     'SiteSpecificUserAgent.js',
     'SiteSpecificUserAgent.manifest',
+    'SlowScriptDebug.js',
+    'SlowScriptDebug.manifest',
 ]
 
 EXTRA_JS_MODULES += [
     'DOMRequestHelper.jsm',
     'IndexedDBHelper.jsm',
     'ObjectWrapper.jsm',
 ]
 
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -480,20 +480,16 @@ static const nsConstructorFuncMapData kC
 
 nsIXPConnect *nsDOMClassInfo::sXPConnect = nullptr;
 nsIScriptSecurityManager *nsDOMClassInfo::sSecMan = nullptr;
 bool nsDOMClassInfo::sIsInitialized = false;
 
 
 jsid nsDOMClassInfo::sLocation_id        = JSID_VOID;
 jsid nsDOMClassInfo::sConstructor_id     = JSID_VOID;
-jsid nsDOMClassInfo::sLength_id          = JSID_VOID;
-jsid nsDOMClassInfo::sItem_id            = JSID_VOID;
-jsid nsDOMClassInfo::sNamedItem_id       = JSID_VOID;
-jsid nsDOMClassInfo::sEnumerate_id       = JSID_VOID;
 jsid nsDOMClassInfo::sTop_id             = JSID_VOID;
 jsid nsDOMClassInfo::sDocument_id        = JSID_VOID;
 jsid nsDOMClassInfo::sWrappedJSObject_id = JSID_VOID;
 
 static const JSClass *sObjectClass = nullptr;
 
 /**
  * Set our JSClass pointer for the Object class
@@ -590,20 +586,16 @@ nsDOMClassInfo::DefineStaticJSVals(JSCon
 #define SET_JSID_TO_STRING(_id, _cx, _str)                                    \
   if (JSString *str = ::JS_InternString(_cx, _str))                           \
       _id = INTERNED_STRING_TO_JSID(_cx, str);                                \
   else                                                                        \
       return NS_ERROR_OUT_OF_MEMORY;
 
   SET_JSID_TO_STRING(sLocation_id,        cx, "location");
   SET_JSID_TO_STRING(sConstructor_id,     cx, "constructor");
-  SET_JSID_TO_STRING(sLength_id,          cx, "length");
-  SET_JSID_TO_STRING(sItem_id,            cx, "item");
-  SET_JSID_TO_STRING(sNamedItem_id,       cx, "namedItem");
-  SET_JSID_TO_STRING(sEnumerate_id,       cx, "enumerateProperties");
   SET_JSID_TO_STRING(sTop_id,             cx, "top");
   SET_JSID_TO_STRING(sDocument_id,        cx, "document");
   SET_JSID_TO_STRING(sWrappedJSObject_id, cx, "wrappedJSObject");
 
   return NS_OK;
 }
 
 // static
@@ -1712,19 +1704,16 @@ nsDOMClassInfo::ShutDown()
 
     for (i = 0; i < eDOMClassInfoIDCount; i++) {
       NS_IF_RELEASE(sClassInfoData[i].mCachedClassInfo);
     }
   }
 
   sLocation_id        = JSID_VOID;
   sConstructor_id     = JSID_VOID;
-  sLength_id          = JSID_VOID;
-  sItem_id            = JSID_VOID;
-  sEnumerate_id       = JSID_VOID;
   sTop_id             = JSID_VOID;
   sDocument_id        = JSID_VOID;
   sWrappedJSObject_id = JSID_VOID;
 
   NS_IF_RELEASE(sXPConnect);
   NS_IF_RELEASE(sSecMan);
   sIsInitialized = false;
 }
--- a/dom/base/nsDOMClassInfo.h
+++ b/dom/base/nsDOMClassInfo.h
@@ -161,20 +161,16 @@ protected:
   // nsIXPCScriptable code
   static nsresult DefineStaticJSVals(JSContext *cx);
 
   static bool sIsInitialized;
 
 public:
   static jsid sLocation_id;
   static jsid sConstructor_id;
-  static jsid sLength_id;
-  static jsid sItem_id;
-  static jsid sNamedItem_id;
-  static jsid sEnumerate_id;
   static jsid sTop_id;
   static jsid sDocument_id;
   static jsid sWrappedJSObject_id;
 };
 
 // THIS ONE ISN'T SAFE!! It assumes that the private of the JSObject is
 // an nsISupports.
 inline
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -30,16 +30,17 @@
 #include "mozilla/dom/WakeLock.h"
 #include "mozilla/dom/power/PowerManagerService.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsIPermissionManager.h"
 #include "nsIScriptContext.h"
 #include "nsIScriptTimeoutHandler.h"
 #include "nsIController.h"
 #include "nsScriptNameSpaceManager.h"
+#include "nsISlowScriptDebug.h"
 #include "nsWindowMemoryReporter.h"
 #include "WindowNamedPropertiesHandler.h"
 
 // Helper Classes
 #include "nsJSUtils.h"
 #include "jsapi.h"              // for JSAutoRequest
 #include "js/OldDebugAPI.h"     // for JS_ClearWatchPointsForObject
 #include "jswrapper.h"
@@ -1339,22 +1340,24 @@ nsGlobalWindow::~nsGlobalWindow()
     ac->RemoveWindowAsListener(this);
 
   nsLayoutStatics::Release();
 }
 
 void
 nsGlobalWindow::AddEventTargetObject(DOMEventTargetHelper* aObject)
 {
+  MOZ_ASSERT(IsInnerWindow());
   mEventTargetObjects.PutEntry(aObject);
 }
 
 void
 nsGlobalWindow::RemoveEventTargetObject(DOMEventTargetHelper* aObject)
 {
+  MOZ_ASSERT(IsInnerWindow());
   mEventTargetObjects.RemoveEntry(aObject);
 }
 
 // static
 void
 nsGlobalWindow::ShutDown()
 {
   if (gDumpFile && gDumpFile != stdout) {
@@ -1981,18 +1984,20 @@ nsGlobalWindow::OuterObject(JSContext* a
     Throw(aCx, NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
   return winObj;
 }
 
 bool
-nsGlobalWindow::WouldReuseInnerWindow(nsIDocument *aNewDocument)
-{
+nsGlobalWindow::WouldReuseInnerWindow(nsIDocument* aNewDocument)
+{
+  MOZ_ASSERT(IsOuterWindow());
+
   // We reuse the inner window when:
   // a. We are currently at our original document.
   // b. At least one of the following conditions are true:
   // -- The new document is the same as the old document. This means that we're
   //    getting called from document.open().
   // -- The new document has the same origin as what we have loaded right now.
 
   if (!mDoc || !aNewDocument) {
@@ -2022,17 +2027,17 @@ nsGlobalWindow::WouldReuseInnerWindow(ns
   }
 
   return false;
 }
 
 void
 nsGlobalWindow::SetInitialPrincipalToSubject()
 {
-  FORWARD_TO_OUTER_VOID(SetInitialPrincipalToSubject, ());
+  MOZ_ASSERT(IsOuterWindow());
 
   // First, grab the subject principal.
   nsCOMPtr<nsIPrincipal> newWindowPrincipal = nsContentUtils::SubjectPrincipal();
 
   // Now, if we're about to use the system principal or an nsExpandedPrincipal,
   // make sure we're not using it for a content docshell.
   if (nsContentUtils::IsSystemOrExpandedPrincipal(newWindowPrincipal) &&
       GetDocShell()->ItemType() != nsIDocShellTreeItem::typeChrome) {
@@ -2291,29 +2296,27 @@ nsGlobalWindow::SetNewDocument(nsIDocume
       mDoc &&
       mDoc->NodePrincipal() != aDocument->NodePrincipal()) {
     NS_ERROR("Attempted forced inner window reuse while changing principal");
     return NS_ERROR_UNEXPECTED;
   }
 
   nsCOMPtr<nsIDocument> oldDoc = mDoc;
 
-  nsIScriptContext *scx = GetContextInternal();
-  NS_ENSURE_TRUE(scx, NS_ERROR_NOT_INITIALIZED);
-
-  JSContext *cx = scx->GetNativeContext();
-
 #ifndef MOZ_DISABLE_CRYPTOLEGACY
   // clear smartcard events, our document has gone away.
   if (mCrypto && XRE_GetProcessType() != GeckoProcessType_Content) {
     nsresult rv = mCrypto->SetEnableSmartCardEvents(false);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 #endif
 
+  AutoJSAPI jsapi;
+  JSContext *cx = jsapi.cx();
+
   if (!mDoc) {
     // First document load.
 
     // Get our private root. If it is equal to us, then we need to
     // attach our global key bindings that handles browser scrolling
     // and other browser commands.
     nsIDOMWindow* privateRoot = nsGlobalWindow::GetPrivateRoot();
 
@@ -2360,19 +2363,16 @@ nsGlobalWindow::SetNewDocument(nsIDocume
     currentInner->mNavigator->OnNavigation();
   }
 
   nsRefPtr<nsGlobalWindow> newInnerWindow;
   bool createdInnerWindow = false;
 
   bool thisChrome = IsChromeWindow();
 
-  nsCxPusher cxPusher;
-  cxPusher.Push(cx);
-
   // Check if we're near the stack limit before we get anywhere near the
   // transplanting code.
   JS_CHECK_RECURSION(cx, return NS_ERROR_FAILURE);
 
   nsCOMPtr<WindowStateHolder> wsh = do_QueryInterface(aState);
   NS_ASSERTION(!aState || wsh, "What kind of weird state are you giving me here?");
 
   JS::Rooted<JSObject*> newInnerGlobal(cx);
@@ -2655,18 +2655,21 @@ nsGlobalWindow::SetNewDocument(nsIDocume
   }
 
   mContext->GC(JS::gcreason::SET_NEW_DOCUMENT);
   mContext->DidInitializeContext();
 
   // We wait to fire the debugger hook until the window is all set up and hooked
   // up with the outer. See bug 969156.
   if (createdInnerWindow) {
-    JS::Rooted<JSObject*> global(cx, newInnerWindow->GetWrapper());
-    JS_FireOnNewGlobalObject(cx, global);
+    // AutoEntryScript required to invoke debugger hook, which is a
+    // Gecko-specific concept at present.
+    AutoEntryScript aes(newInnerWindow);
+    JS::Rooted<JSObject*> global(aes.cx(), newInnerWindow->GetWrapper());
+    JS_FireOnNewGlobalObject(aes.cx(), global);
   }
 
   if (newInnerWindow && !newInnerWindow->mHasNotifiedGlobalCreated && mDoc) {
     // We should probably notify. However if this is the, arguably bad,
     // situation when we're creating a temporary non-chrome-about-blank
     // document in a chrome docshell, don't notify just yet. Instead wait
     // until we have a real chrome doc.
     if (!mDocShell ||
@@ -2681,16 +2684,18 @@ nsGlobalWindow::SetNewDocument(nsIDocume
   PreloadLocalStorage();
 
   return NS_OK;
 }
 
 void
 nsGlobalWindow::PreloadLocalStorage()
 {
+  MOZ_ASSERT(IsOuterWindow());
+
   if (!Preferences::GetBool(kStorageEnabled)) {
     return;
   }
 
   if (IsChromeWindow()) {
     return;
   }
 
@@ -2708,16 +2713,18 @@ nsGlobalWindow::PreloadLocalStorage()
   }
 
   storageManager->PrecacheStorage(principal);
 }
 
 void
 nsGlobalWindow::DispatchDOMWindowCreated()
 {
+  MOZ_ASSERT(IsOuterWindow());
+
   if (!mDoc) {
     return;
   }
 
   // Fire DOMWindowCreated at chrome event listeners
   nsContentUtils::DispatchChromeEvent(mDoc, mDoc, NS_LITERAL_STRING("DOMWindowCreated"),
                                       true /* bubbles */,
                                       false /* not cancellable */);
@@ -3058,16 +3065,18 @@ nsGlobalWindow::ShouldPromptToBlockDialo
   }
 
   return topWindow->DialogsAreBeingAbused();
 }
 
 bool
 nsGlobalWindow::AreDialogsEnabled()
 {
+  MOZ_ASSERT(IsOuterWindow());
+
   nsGlobalWindow *topWindow = GetScriptableTop();
   if (!topWindow) {
     NS_ERROR("AreDialogsEnabled() called without a top window?");
     return false;
   }
 
   // TODO: Warn if no top window?
   topWindow = topWindow->GetCurrentInnerWindowInternal();
@@ -6259,16 +6268,22 @@ nsGlobalWindow::AlertOrConfirm(bool aAle
                prompt->Alert(title.get(), final.get()) :
                prompt->Confirm(title.get(), final.get(), &result);
   }
 
   return result;
 }
 
 void
+nsGlobalWindow::Alert(mozilla::ErrorResult& aError)
+{
+  Alert(EmptyString(), aError);
+}
+
+void
 nsGlobalWindow::Alert(const nsAString& aMessage, mozilla::ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(Alert, (aMessage, aError), aError, );
   AlertOrConfirm(/* aAlert = */ true, aMessage, aError);
 }
 
 NS_IMETHODIMP
 nsGlobalWindow::Alert(const nsAString& aString)
@@ -8211,18 +8226,21 @@ public:
     return NS_OK;
   }
 
 };
 
 bool
 nsGlobalWindow::CanClose()
 {
-  if (!mDocShell)
+  MOZ_ASSERT(IsOuterWindow());
+
+  if (!mDocShell) {
     return true;
+  }
 
   // Ask the content viewer whether the toplevel window can close.
   // If the content viewer returns false, it is responsible for calling
   // Close() as soon as it is possible for the window to close.
   // This allows us to not close the window while printing is happening.
 
   nsCOMPtr<nsIContentViewer> cv;
   mDocShell->GetContentViewer(getter_AddRefs(cv));
@@ -8306,54 +8324,57 @@ nsGlobalWindow::Close(ErrorResult& aErro
   if (!DispatchCustomEvent("DOMWindowClose")) {
     // Someone chose to prevent the default action for this event, if
     // so, let's not close this window after all...
 
     mInClose = wasInClose;
     return;
   }
 
-  aError = FinalClose();
+  FinalClose();
 }
 
 NS_IMETHODIMP
 nsGlobalWindow::Close()
 {
   ErrorResult rv;
   Close(rv);
 
   return rv.ErrorCode();
 }
 
-nsresult
+void
 nsGlobalWindow::ForceClose()
 {
+  MOZ_ASSERT(IsOuterWindow());
+
   if (IsFrame() || !mDocShell) {
     // This may be a frame in a frameset, or a window that's already closed.
     // Ignore such calls.
-
-    return NS_OK;
+    return;
   }
 
   if (mHavePendingClose) {
     // We're going to be closed anyway; do nothing since we don't want
     // to double-close
-    return NS_OK;
+    return;
   }
 
   mInClose = true;
 
   DispatchCustomEvent("DOMWindowClose");
 
-  return FinalClose();
-}
-
-nsresult
+  FinalClose();
+}
+
+void
 nsGlobalWindow::FinalClose()
 {
+  MOZ_ASSERT(IsOuterWindow());
+
   // Flag that we were closed.
   mIsClosed = true;
 
   // This stuff is non-sensical but incredibly fragile. The reasons for the
   // behavior here don't make sense today and may not have ever made sense,
   // but various bits of frontend code break when you change them. If you need
   // to fix up this behavior, feel free to. It's a righteous task, but involves
   // wrestling with various download manager tests, frontend code, and possible
@@ -8367,18 +8388,16 @@ nsGlobalWindow::FinalClose()
   bool indirect = GetContextInternal() && // Occasionally null. See bug 877390.
                   (nsContentUtils::GetCurrentJSContext() ==
                    GetContextInternal()->GetNativeContext());
   if (NS_FAILED(nsCloseEvent::PostCloseEvent(this, indirect))) {
     ReallyCloseWindow();
   } else {
     mHavePendingClose = true;
   }
-
-  return NS_OK;
 }
 
 
 void
 nsGlobalWindow::ReallyCloseWindow()
 {
   FORWARD_TO_OUTER_VOID(ReallyCloseWindow, ());
 
@@ -10744,37 +10763,53 @@ nsGlobalWindow::ShowSlowScriptDialog()
   nsCOMPtr<nsIPrompt> prompt = do_GetInterface(ds);
   NS_ENSURE_TRUE(prompt, KillSlowScript);
 
   // Check if we should offer the option to debug
   JS::AutoFilename filename;
   unsigned lineno;
   bool hasFrame = JS::DescribeScriptedCaller(cx, &filename, &lineno);
 
-  bool debugPossible = hasFrame && js::CanCallContextDebugHandler(cx);
-#ifdef MOZ_JSDEBUGGER
-  // Get the debugger service if necessary.
-  if (debugPossible) {
-    bool jsds_IsOn = false;
-    const char jsdServiceCtrID[] = "@mozilla.org/js/jsd/debugger-service;1";
-    nsCOMPtr<jsdIExecutionHook> jsdHook;
-    nsCOMPtr<jsdIDebuggerService> jsds = do_GetService(jsdServiceCtrID, &rv);
-
-    // Check if there's a user for the debugger service that's 'on' for us
+  // Prioritize the SlowScriptDebug interface over JSD1.
+  nsCOMPtr<nsISlowScriptDebugCallback> debugCallback;
+  bool oldDebugPossible = false;
+
+  if (hasFrame) {
+    const char *debugCID = "@mozilla.org/dom/slow-script-debug;1";
+    nsCOMPtr<nsISlowScriptDebug> debugService = do_GetService(debugCID, &rv);
     if (NS_SUCCEEDED(rv)) {
-      jsds->GetDebuggerHook(getter_AddRefs(jsdHook));
-      jsds->GetIsOn(&jsds_IsOn);
-    }
-
-    // If there is a debug handler registered for this runtime AND
-    // ((jsd is on AND has a hook) OR (jsd isn't on (something else debugs)))
-    // then something useful will be done with our request to debug.
-    debugPossible = ((jsds_IsOn && (jsdHook != nullptr)) || !jsds_IsOn);
-  }
-#endif
+      debugService->GetActivationHandler(getter_AddRefs(debugCallback));
+    }
+
+    if (!debugCallback) {
+      oldDebugPossible = js::CanCallContextDebugHandler(cx);
+#ifdef MOZ_JSDEBUGGER
+      // Get the debugger service if necessary.
+      if (oldDebugPossible) {
+        bool jsds_IsOn = false;
+        const char jsdServiceCtrID[] = "@mozilla.org/js/jsd/debugger-service;1";
+        nsCOMPtr<jsdIExecutionHook> jsdHook;
+        nsCOMPtr<jsdIDebuggerService> jsds = do_GetService(jsdServiceCtrID, &rv);
+
+        // Check if there's a user for the debugger service that's 'on' for us
+        if (NS_SUCCEEDED(rv)) {
+          jsds->GetDebuggerHook(getter_AddRefs(jsdHook));
+          jsds->GetIsOn(&jsds_IsOn);
+        }
+
+        // If there is a debug handler registered for this runtime AND
+        // ((jsd is on AND has a hook) OR (jsd isn't on (something else debugs)))
+        // then something useful will be done with our request to debug.
+        oldDebugPossible = ((jsds_IsOn && (jsdHook != nullptr)) || !jsds_IsOn);
+      }
+#endif
+    }
+  }
+
+  bool showDebugButton = debugCallback || oldDebugPossible;
 
   // Get localizable strings
   nsXPIDLString title, msg, stopButton, waitButton, debugButton, neverShowDlg;
 
   rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
                                           "KillScriptTitle",
                                           title);
 
@@ -10795,17 +10830,17 @@ nsGlobalWindow::ShowSlowScriptDialog()
   tmp = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
                                            "DontAskAgain",
                                            neverShowDlg);
   if (NS_FAILED(tmp)) {
     rv = tmp;
   }
 
 
-  if (debugPossible) {
+  if (showDebugButton) {
     tmp = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
                                              "DebugScriptButton",
                                              debugButton);
     if (NS_FAILED(tmp)) {
       rv = tmp;
     }
 
     tmp = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
@@ -10821,17 +10856,17 @@ nsGlobalWindow::ShowSlowScriptDialog()
                                              msg);
     if (NS_FAILED(tmp)) {
       rv = tmp;
     }
   }
 
   // GetStringFromName can return NS_OK and still give nullptr string
   if (NS_FAILED(rv) || !title || !msg || !stopButton || !waitButton ||
-      (!debugButton && debugPossible) || !neverShowDlg) {
+      (!debugButton && showDebugButton) || !neverShowDlg) {
     NS_ERROR("Failed to get localized strings.");
     return ContinueSlowScript;
   }
 
   // Append file and line number information, if available
   if (filename.get()) {
     nsXPIDLString scriptLocation;
     NS_ConvertUTF8toUTF16 filenameUTF16(filename.get());
@@ -10851,35 +10886,42 @@ nsGlobalWindow::ShowSlowScriptDialog()
 
   int32_t buttonPressed = 0; // In case the user exits dialog by clicking X.
   bool neverShowDlgChk = false;
   uint32_t buttonFlags = nsIPrompt::BUTTON_POS_1_DEFAULT +
                          (nsIPrompt::BUTTON_TITLE_IS_STRING *
                           (nsIPrompt::BUTTON_POS_0 + nsIPrompt::BUTTON_POS_1));
 
   // Add a third button if necessary.
-  if (debugPossible)
+  if (showDebugButton)
     buttonFlags += nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_2;
 
   // Null out the operation callback while we're re-entering JS here.
   JSRuntime* rt = JS_GetRuntime(cx);
   JSInterruptCallback old = JS_SetInterruptCallback(rt, nullptr);
 
   // Open the dialog.
   rv = prompt->ConfirmEx(title, msg, buttonFlags, waitButton, stopButton,
                          debugButton, neverShowDlg, &neverShowDlgChk,
                          &buttonPressed);
 
   JS_SetInterruptCallback(rt, old);
 
   if (NS_SUCCEEDED(rv) && (buttonPressed == 0)) {
     return neverShowDlgChk ? AlwaysContinueSlowScript : ContinueSlowScript;
   }
-  if ((buttonPressed == 2) && debugPossible) {
-    return js_CallContextDebugHandler(cx) ? ContinueSlowScript : KillSlowScript;
+  if (buttonPressed == 2) {
+    if (debugCallback) {
+      rv = debugCallback->HandleSlowScriptDebug(this);
+      return NS_SUCCEEDED(rv) ? ContinueSlowScript : KillSlowScript;
+    }
+
+    if (oldDebugPossible) {
+      return js_CallContextDebugHandler(cx) ? ContinueSlowScript : KillSlowScript;
+    }
   }
   JS_ClearPendingException(cx);
   return KillSlowScript;
 }
 
 uint32_t
 nsGlobalWindow::FindInsertionIndex(IdleObserverHolder* aIdleObserver)
 {
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -429,16 +429,17 @@ public:
   virtual NS_HIDDEN_(nsPIDOMWindow*) GetPrivateRoot();
 
   // Outer windows only.
   virtual NS_HIDDEN_(void) ActivateOrDeactivate(bool aActivate);
   virtual NS_HIDDEN_(void) SetActive(bool aActive);
   virtual NS_HIDDEN_(void) SetIsBackground(bool aIsBackground);
   virtual NS_HIDDEN_(void) SetChromeEventHandler(mozilla::dom::EventTarget* aChromeEventHandler);
 
+  // Outer windows only.
   virtual NS_HIDDEN_(void) SetInitialPrincipalToSubject();
 
   virtual NS_HIDDEN_(PopupControlState) PushPopupControlState(PopupControlState state, bool aForce) const;
   virtual NS_HIDDEN_(void) PopPopupControlState(PopupControlState state) const;
   virtual NS_HIDDEN_(PopupControlState) GetPopupControlState() const;
 
   virtual already_AddRefed<nsISupports> SaveWindowState();
   virtual NS_HIDDEN_(nsresult) RestoreWindowState(nsISupports *aState);
@@ -448,35 +449,40 @@ public:
   virtual NS_HIDDEN_(uint32_t) TimeoutSuspendCount();
   virtual NS_HIDDEN_(nsresult) FireDelayedDOMEvents();
   virtual NS_HIDDEN_(bool) IsFrozen() const
   {
     return mIsFrozen;
   }
   virtual NS_HIDDEN_(bool) IsRunningTimeout() { return mTimeoutFiringDepth > 0; }
 
-  virtual NS_HIDDEN_(bool) WouldReuseInnerWindow(nsIDocument *aNewDocument);
+  // Outer windows only.
+  virtual NS_HIDDEN_(bool) WouldReuseInnerWindow(nsIDocument* aNewDocument);
 
   virtual NS_HIDDEN_(void) SetDocShell(nsIDocShell* aDocShell);
   virtual void DetachFromDocShell();
   virtual NS_HIDDEN_(nsresult) SetNewDocument(nsIDocument *aDocument,
                                               nsISupports *aState,
                                               bool aForceReuseInnerWindow);
+
+  // Outer windows only.
   void DispatchDOMWindowCreated();
+
   virtual NS_HIDDEN_(void) SetOpenerWindow(nsIDOMWindow* aOpener,
                                            bool aOriginalOpener);
 
   // Outer windows only.
   virtual NS_HIDDEN_(void) EnsureSizeUpToDate();
 
   virtual NS_HIDDEN_(void) EnterModalState();
   virtual NS_HIDDEN_(void) LeaveModalState();
 
+  // Outer windows only.
   virtual NS_HIDDEN_(bool) CanClose();
-  virtual NS_HIDDEN_(nsresult) ForceClose();
+  virtual NS_HIDDEN_(void) ForceClose();
 
   virtual NS_HIDDEN_(void) MaybeUpdateTouchState();
   virtual NS_HIDDEN_(void) UpdateTouchState();
   virtual NS_HIDDEN_(bool) DispatchCustomEvent(const char *aEventName);
   virtual NS_HIDDEN_(bool) DispatchResizeEvent(const nsIntSize& aSize);
   virtual NS_HIDDEN_(void) RefreshCompartmentPrincipal();
   virtual NS_HIDDEN_(nsresult) SetFullScreenInternal(bool aIsFullScreen, bool aRequireTrust);
 
@@ -552,16 +558,17 @@ public:
   bool ShouldPromptToBlockDialogs();
   // Inner windows only.
   bool DialogsAreBeingAbused();
 
   // These functions are used for controlling and determining whether dialogs
   // (alert, prompt, confirm) are currently allowed in this window.
   void EnableDialogs();
   void DisableDialogs();
+  // Outer windows only.
   bool AreDialogsEnabled();
 
   nsIScriptContext *GetContextInternal()
   {
     if (mOuterWindow) {
       return GetOuterWindowInternal()->mContext;
     }
 
@@ -691,16 +698,17 @@ public:
   static WindowByIdTable* GetWindowsTable() {
     return sWindowsById;
   }
 
   void AddSizeOfIncludingThis(nsWindowSizes* aWindowSizes) const;
 
   void UnmarkGrayTimers();
 
+  // Inner windows only.
   void AddEventTargetObject(mozilla::DOMEventTargetHelper* aObject);
   void RemoveEventTargetObject(mozilla::DOMEventTargetHelper* aObject);
 
   void NotifyIdleObserver(IdleObserverHolder* aIdleObserverHolder,
                           bool aCallOnidle);
   nsresult HandleIdleActiveEvent();
   bool ContainsIdleObserver(nsIIdleObserver* aIdleObserver, uint32_t timeInS);
   void HandleIdleObserverCallback();
@@ -843,16 +851,17 @@ public:
                   mozilla::ErrorResult& aRv);
   already_AddRefed<mozilla::dom::External> GetExternal(mozilla::ErrorResult& aRv);
 
 protected:
   bool AlertOrConfirm(bool aAlert, const nsAString& aMessage,
                       mozilla::ErrorResult& aError);
 
 public:
+  void Alert(mozilla::ErrorResult& aError);
   void Alert(const nsAString& aMessage, mozilla::ErrorResult& aError);
   bool Confirm(const nsAString& aMessage, mozilla::ErrorResult& aError);
   void Prompt(const nsAString& aMessage, const nsAString& aInitial,
               nsAString& aReturn, mozilla::ErrorResult& aError);
   void Print(mozilla::ErrorResult& aError);
   JS::Value ShowModalDialog(JSContext* aCx, const nsAString& aUrl, JS::Handle<JS::Value> aArgument, const nsAString& aOptions, mozilla::ErrorResult& aError);
   void PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                       const nsAString& aTargetOrigin,
@@ -1039,17 +1048,18 @@ protected:
   friend class HashchangeCallback;
   friend class mozilla::dom::BarProp;
 
   // Object Management
   virtual ~nsGlobalWindow();
   void DropOuterWindowDocs();
   void CleanUp();
   void ClearControllers();
-  nsresult FinalClose();
+  // Outer windows only.
+  void FinalClose();
 
   inline void MaybeClearInnerWindow(nsGlobalWindow* aExpectedInner)
   {
     if(mInnerWindow == aExpectedInner) {
       mInnerWindow = nullptr;
     }
   }
 
@@ -1338,16 +1348,17 @@ protected:
                            const nsAString& aPseudoElt,
                            bool aDefaultStylesOnly,
                            mozilla::ErrorResult& aError);
   nsresult GetComputedStyleHelper(nsIDOMElement* aElt,
                                   const nsAString& aPseudoElt,
                                   bool aDefaultStylesOnly,
                                   nsIDOMCSSStyleDeclaration** aReturn);
 
+  // Outer windows only.
   void PreloadLocalStorage();
 
   // Returns device pixels.  Outer windows only.
   nsIntPoint GetScreenXY(mozilla::ErrorResult& aError);
 
   int32_t RequestAnimationFrame(const nsIDocument::FrameRequestCallbackHolder& aCallback,
                                 mozilla::ErrorResult& aError);
 
new file mode 100644
--- /dev/null
+++ b/dom/base/nsISlowScriptDebug.idl
@@ -0,0 +1,19 @@
+/* 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"
+
+interface nsIDOMWindow;
+
+[scriptable, function, uuid(f7dbb80c-5d1e-4fd9-b55c-a9ffda4a75b1)]
+interface nsISlowScriptDebugCallback : nsISupports
+{
+  void handleSlowScriptDebug(in nsIDOMWindow aWindow);
+};
+
+[scriptable, uuid(f75d4164-3aa7-4395-ba44-a5f95b2e8427)]
+interface nsISlowScriptDebug : nsISupports
+{
+  attribute nsISlowScriptDebugCallback activationHandler;
+};
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -272,23 +272,21 @@ nsJSEnvironmentObserver::Observe(nsISupp
     if(StringBeginsWith(nsDependentString(aData),
                         NS_LITERAL_STRING("low-memory-ongoing"))) {
       // Don't GC/CC if we are in an ongoing low-memory state since its very
       // slow and it likely won't help us anyway.
       return NS_OK;
     }
     nsJSContext::GarbageCollectNow(JS::gcreason::MEM_PRESSURE,
                                    nsJSContext::NonIncrementalGC,
-                                   nsJSContext::NonCompartmentGC,
                                    nsJSContext::ShrinkingGC);
     nsJSContext::CycleCollectNow();
     if (NeedsGCAfterCC()) {
       nsJSContext::GarbageCollectNow(JS::gcreason::MEM_PRESSURE,
                                      nsJSContext::NonIncrementalGC,
-                                     nsJSContext::NonCompartmentGC,
                                      nsJSContext::ShrinkingGC);
     }
   } else if (!nsCRT::strcmp(aTopic, "quit-application")) {
     sShuttingDown = true;
     KillTimers();
   }
 
   return NS_OK;
@@ -383,20 +381,18 @@ AsyncErrorReporter::AsyncErrorReporter(J
   if (mErrorMsg.IsEmpty() && aFallbackMessage) {
     mErrorMsg.AssignWithConversion(aFallbackMessage);
   }
 
   mCategory = aIsChromeError ? NS_LITERAL_CSTRING("chrome javascript") :
                                NS_LITERAL_CSTRING("content javascript");
 
   mInnerWindowID = 0;
-  if (aWindow && aWindow->IsOuterWindow()) {
-    aWindow = aWindow->GetCurrentInnerWindow();
-  }
   if (aWindow) {
+    MOZ_ASSERT(aWindow->IsInnerWindow());
     mInnerWindowID = aWindow->WindowID();
   }
 }
 
 void
 AsyncErrorReporter::ReportError()
 {
   nsCOMPtr<nsIScriptError> errorObject =
@@ -424,59 +420,59 @@ AsyncErrorReporter::ReportError()
 }
 
 } // namespace dom
 } // namespace mozilla
 
 class ScriptErrorEvent : public AsyncErrorReporter
 {
 public:
-  ScriptErrorEvent(nsIScriptGlobalObject* aScriptGlobal,
-                   JSRuntime* aRuntime,
+  ScriptErrorEvent(JSRuntime* aRuntime,
                    JSErrorReport* aErrorReport,
                    const char* aFallbackMessage,
                    nsIPrincipal* aScriptOriginPrincipal,
                    nsIPrincipal* aGlobalPrincipal,
                    nsPIDOMWindow* aWindow,
                    JS::Handle<JS::Value> aError,
                    bool aDispatchEvent)
     // Pass an empty category, then compute ours
     : AsyncErrorReporter(aRuntime, aErrorReport, aFallbackMessage,
                          nsContentUtils::IsSystemPrincipal(aGlobalPrincipal),
                          aWindow)
-    , mScriptGlobal(aScriptGlobal)
     , mOriginPrincipal(aScriptOriginPrincipal)
     , mDispatchEvent(aDispatchEvent)
     , mError(aRuntime, aError)
+    , mWindow(aWindow)
   {
+    MOZ_ASSERT_IF(mWindow, mWindow->IsInnerWindow());
   }
 
   NS_IMETHOD Run()
   {
     nsEventStatus status = nsEventStatus_eIgnore;
-    // First, notify the DOM that we have a script error.
-    if (mDispatchEvent) {
-      nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(mScriptGlobal));
-      nsIDocShell* docShell = win ? win->GetDocShell() : nullptr;
+    // First, notify the DOM that we have a script error, but only if
+    // our window is still the current inner, if we're associated with a window.
+    if (mDispatchEvent && (!mWindow || mWindow->IsCurrentInnerWindow())) {
+      nsIDocShell* docShell = mWindow ? mWindow->GetDocShell() : nullptr;
       if (docShell &&
           !JSREPORT_IS_WARNING(mFlags) &&
           !sHandlingScriptError) {
         AutoRestore<bool> recursionGuard(sHandlingScriptError);
         sHandlingScriptError = true;
 
         nsRefPtr<nsPresContext> presContext;
         docShell->GetPresContext(getter_AddRefs(presContext));
 
         ThreadsafeAutoJSContext cx;
         RootedDictionary<ErrorEventInit> init(cx);
         init.mCancelable = true;
         init.mFilename = mFileName;
         init.mBubbles = true;
 
-        nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(win));
+        nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(mWindow));
         NS_ENSURE_STATE(sop);
         nsIPrincipal* p = sop->GetPrincipal();
         NS_ENSURE_STATE(p);
 
         bool sameOrigin = !mOriginPrincipal;
 
         if (p && !sameOrigin) {
           if (NS_FAILED(p->Subsumes(mOriginPrincipal, &sameOrigin))) {
@@ -492,37 +488,37 @@ public:
           init.mError = mError;
         } else {
           NS_WARNING("Not same origin error!");
           init.mMessage = xoriginMsg;
           init.mLineno = 0;
         }
 
         nsRefPtr<ErrorEvent> event =
-          ErrorEvent::Constructor(static_cast<nsGlobalWindow*>(win.get()),
+          ErrorEvent::Constructor(static_cast<nsGlobalWindow*>(mWindow.get()),
                                   NS_LITERAL_STRING("error"), init);
         event->SetTrusted(true);
 
-        EventDispatcher::DispatchDOMEvent(win, nullptr, event, presContext,
+        EventDispatcher::DispatchDOMEvent(mWindow, nullptr, event, presContext,
                                           &status);
       }
     }
 
     if (status != nsEventStatus_eConsumeNoDefault) {
       AsyncErrorReporter::ReportError();
     }
 
     return NS_OK;
   }
 
 private:
-  nsCOMPtr<nsIScriptGlobalObject> mScriptGlobal;
   nsCOMPtr<nsIPrincipal>          mOriginPrincipal;
   bool                            mDispatchEvent;
   JS::PersistentRootedValue       mError;
+  nsCOMPtr<nsPIDOMWindow>         mWindow;
 
   static bool sHandlingScriptError;
 };
 
 bool ScriptErrorEvent::sHandlingScriptError = false;
 
 // NOTE: This function could be refactored to use the above.  The only reason
 // it has not been done is that the code below only fills the error event
@@ -570,23 +566,25 @@ NS_ScriptErrorReporter(JSContext *cx,
   ::JS_ClearPendingException(cx);
 
   if (context) {
     nsIScriptGlobalObject *globalObject = context->GetGlobalObject();
 
     if (globalObject) {
 
       nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(globalObject);
+      if (win) {
+        win = win->GetCurrentInnerWindow();
+      }
       nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal =
         do_QueryInterface(globalObject);
       NS_ASSERTION(scriptPrincipal, "Global objects must implement "
                    "nsIScriptObjectPrincipal");
       nsContentUtils::AddScriptRunner(
-        new ScriptErrorEvent(globalObject,
-                             JS_GetRuntime(cx),
+        new ScriptErrorEvent(JS_GetRuntime(cx),
                              report,
                              message,
                              nsJSPrincipals::get(report->originPrincipals),
                              scriptPrincipal->GetPrincipal(),
                              win,
                              exception,
                              /* We do not try to report Out Of Memory via a dom
                               * event because the dom event handler would
@@ -1703,17 +1701,16 @@ FullGCTimerFired(nsITimer* aTimer, void*
   nsJSContext::GarbageCollectNow(static_cast<JS::gcreason::Reason>(reason),
                                  nsJSContext::IncrementalGC);
 }
 
 //static
 void
 nsJSContext::GarbageCollectNow(JS::gcreason::Reason aReason,
                                IsIncremental aIncremental,
-                               IsCompartment aCompartment,
                                IsShrinking aShrinking,
                                int64_t aSliceMillis)
 {
   PROFILER_LABEL("GC", "GarbageCollectNow");
 
   MOZ_ASSERT_IF(aSliceMillis, aIncremental == IncrementalGC);
 
   KillGCTimer();
@@ -2196,30 +2193,28 @@ nsJSContext::EndCycleCollectionCallback(
 
 // static
 void
 InterSliceGCTimerFired(nsITimer *aTimer, void *aClosure)
 {
   nsJSContext::KillInterSliceGCTimer();
   nsJSContext::GarbageCollectNow(JS::gcreason::INTER_SLICE_GC,
                                  nsJSContext::IncrementalGC,
-                                 nsJSContext::CompartmentGC,
                                  nsJSContext::NonShrinkingGC,
                                  NS_INTERSLICE_GC_BUDGET);
 }
 
 // static
 void
 GCTimerFired(nsITimer *aTimer, void *aClosure)
 {
   nsJSContext::KillGCTimer();
   uintptr_t reason = reinterpret_cast<uintptr_t>(aClosure);
   nsJSContext::GarbageCollectNow(static_cast<JS::gcreason::Reason>(reason),
-                                 nsJSContext::IncrementalGC,
-                                 nsJSContext::CompartmentGC);
+                                 nsJSContext::IncrementalGC);
 }
 
 void
 ShrinkGCBuffersTimerFired(nsITimer *aTimer, void *aClosure)
 {
   nsJSContext::KillShrinkGCBuffersTimer();
   nsJSContext::ShrinkGCBuffersNow();
 }
--- a/dom/base/nsJSEnvironment.h
+++ b/dom/base/nsJSEnvironment.h
@@ -63,37 +63,31 @@ public:
 
   virtual void SetWindowProxy(JS::Handle<JSObject*> aWindowProxy) MOZ_OVERRIDE;
   virtual JSObject* GetWindowProxy() MOZ_OVERRIDE;
   virtual JSObject* GetWindowProxyPreserveColor() MOZ_OVERRIDE;
 
   static void LoadStart();
   static void LoadEnd();
 
-  enum IsCompartment {
-    CompartmentGC,
-    NonCompartmentGC
-  };
-
   enum IsShrinking {
     ShrinkingGC,
     NonShrinkingGC
   };
 
   enum IsIncremental {
     IncrementalGC,
     NonIncrementalGC
   };
 
   // Setup all the statics etc - safe to call multiple times after Startup().
   void EnsureStatics();
 
   static void GarbageCollectNow(JS::gcreason::Reason reason,
                                 IsIncremental aIncremental = NonIncrementalGC,
-                                IsCompartment aCompartment = NonCompartmentGC,
                                 IsShrinking aShrinking = NonShrinkingGC,
                                 int64_t aSliceMillis = 0);
   static void ShrinkGCBuffersNow();
 
   // If aExtraForgetSkippableCalls is -1, forgetSkippable won't be
   // called even if the previous collection was GC.
   static void CycleCollectNow(nsICycleCollectorListener *aListener = nullptr,
                               int32_t aExtraForgetSkippableCalls = 0);
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -273,16 +273,17 @@ public:
       win = this;
     }
 
     return win->mIsHandlingResizeEvent;
   }
 
   // Set the window up with an about:blank document with the current subject
   // principal.
+  // Outer windows only.
   virtual void SetInitialPrincipalToSubject() = 0;
 
   virtual PopupControlState PushPopupControlState(PopupControlState aState,
                                                   bool aForce) const = 0;
   virtual void PopPopupControlState(PopupControlState state) const = 0;
   virtual PopupControlState GetPopupControlState() const = 0;
 
   // Returns an object containing the window's state.  This also suspends
@@ -359,17 +360,18 @@ public:
        mOuterWindow->GetCurrentInnerWindow()->GetDoc() == mDoc);
   }
 
   bool IsOuterWindow() const
   {
     return !IsInnerWindow();
   }
 
-  virtual bool WouldReuseInnerWindow(nsIDocument *aNewDocument) = 0;
+  // Outer windows only.
+  virtual bool WouldReuseInnerWindow(nsIDocument* aNewDocument) = 0;
 
   /**
    * Get the docshell in this window.
    */
   nsIDocShell *GetDocShell()
   {
     if (mOuterWindow) {
       return mOuterWindow->mDocShell;
@@ -419,18 +421,19 @@ public:
 
   /**
    * Callback for notifying a window about a modal dialog being
    * opened/closed with the window as a parent.
    */
   virtual void EnterModalState() = 0;
   virtual void LeaveModalState() = 0;
 
+  // Outer windows only.
   virtual bool CanClose() = 0;
-  virtual nsresult ForceClose() = 0;
+  virtual void ForceClose() = 0;
 
   bool IsModalContentWindow() const
   {
     return mIsModalContentWindow;
   }
 
   /**
    * Call this to indicate that some node (this window, its document,
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -43,22 +43,19 @@ support-files =
 [test_messageChannel_pref.html]
 [test_messageChannel_start.html]
 [test_messagemanager_targetchain.html]
 [test_messageChannel_transferable.html]
 [test_messageChannel_unshipped.html]
 [test_named_frames.html]
 [test_navigator_resolve_identity.html]
 [test_navigator_language.html]
-[test_nondomexception.html]
 [test_openDialogChromeOnly.html]
-
 [test_open_null_features.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Fails on b2g-desktop, tracked in bug 1011874
-
 [test_postMessage_solidus.html]
 [test_screen_orientation.html]
 [test_settimeout_extra_arguments.html]
 [test_settimeout_inner.html]
 [test_setting_opener.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_url.html]
 [test_url_empty_port.html]
deleted file mode 100644
--- a/dom/base/test/test_nondomexception.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!-- intentionally omiting doctype because this test requires document.all -->
-<html>
-<head>
-  <title>Test for non-DOM module exceptions</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-</head>
-<body>
-<p id="display"></p>
-<div id="content" style="display: none">
-
-</div>
-<pre id="test">
-<script class="testbody" type="application/javascript;version=1.7">
-"use strict";
-
-try {
-  document.all();
-} catch (e) {
-  is(typeof e, "object");
-  is(e.filename, location);
-  is(e.lineNumber, 18);
-}
-
-</script>
-</pre>
-</body>
-</html>
--- a/dom/events/DOMEventTargetHelper.cpp
+++ b/dom/events/DOMEventTargetHelper.cpp
@@ -86,17 +86,17 @@ DOMEventTargetHelper::~DOMEventTargetHel
     mListenerManager->Disconnect();
   }
   ReleaseWrapper(this);
 }
 
 void
 DOMEventTargetHelper::BindToOwner(nsPIDOMWindow* aOwner)
 {
-  MOZ_ASSERT(!aOwner || aOwner->IsInnerWindow());
+  MOZ_ASSERT_IF(aOwner, aOwner->IsInnerWindow());
   nsCOMPtr<nsIGlobalObject> glob = do_QueryInterface(aOwner);
   BindToOwner(glob);
 }
 
 void
 DOMEventTargetHelper::BindToOwner(nsIGlobalObject* aOwner)
 {
   if (mParentObject) {
@@ -107,16 +107,17 @@ DOMEventTargetHelper::BindToOwner(nsIGlo
     mParentObject = nullptr;
     mHasOrHasHadOwnerWindow = false;
   }
   if (aOwner) {
     mParentObject = aOwner;
     // Let's cache the result of this QI for fast access and off main thread usage
     mOwnerWindow = nsCOMPtr<nsPIDOMWindow>(do_QueryInterface(aOwner)).get();
     if (mOwnerWindow) {
+      MOZ_ASSERT(mOwnerWindow->IsInnerWindow());
       mHasOrHasHadOwnerWindow = true;
       static_cast<nsGlobalWindow*>(mOwnerWindow)->AddEventTargetObject(this);
     }
   }
 }
 
 void
 DOMEventTargetHelper::BindToOwner(DOMEventTargetHelper* aOther)
@@ -129,16 +130,17 @@ DOMEventTargetHelper::BindToOwner(DOMEve
   }
   if (aOther) {
     mHasOrHasHadOwnerWindow = aOther->HasOrHasHadOwner();
     if (aOther->GetParentObject()) {
       mParentObject = aOther->GetParentObject();
       // Let's cache the result of this QI for fast access and off main thread usage
       mOwnerWindow = nsCOMPtr<nsPIDOMWindow>(do_QueryInterface(mParentObject)).get();
       if (mOwnerWindow) {
+        MOZ_ASSERT(mOwnerWindow->IsInnerWindow());
         mHasOrHasHadOwnerWindow = true;
         static_cast<nsGlobalWindow*>(mOwnerWindow)->AddEventTargetObject(this);
       }
     }
   }
 }
 
 void
--- a/dom/events/DOMEventTargetHelper.h
+++ b/dom/events/DOMEventTargetHelper.h
@@ -115,22 +115,18 @@ public:
   virtual nsIDOMWindow* GetOwnerGlobal() MOZ_OVERRIDE
   {
     return nsPIDOMWindow::GetOuterFromCurrentInner(GetOwner());
   }
 
   nsresult CheckInnerWindowCorrectness()
   {
     NS_ENSURE_STATE(!mHasOrHasHadOwnerWindow || mOwnerWindow);
-    if (mOwnerWindow) {
-      NS_ASSERTION(mOwnerWindow->IsInnerWindow(), "Should have inner window here!\n");
-      nsPIDOMWindow* outer = mOwnerWindow->GetOuterWindow();
-      if (!outer || outer->GetCurrentInnerWindow() != mOwnerWindow) {
-        return NS_ERROR_FAILURE;
-      }
+    if (mOwnerWindow && !mOwnerWindow->IsCurrentInnerWindow()) {
+      return NS_ERROR_FAILURE;
     }
     return NS_OK;
   }
 
   nsPIDOMWindow* GetOwner() const { return mOwnerWindow; }
   void BindToOwner(nsIGlobalObject* aOwner);
   void BindToOwner(nsPIDOMWindow* aOwner);
   void BindToOwner(DOMEventTargetHelper* aOther);
@@ -154,17 +150,17 @@ protected:
   nsresult DispatchTrustedEvent(const nsAString& aEventName);
   // Make |event| trusted and dispatch |aEvent| to |this|.
   nsresult DispatchTrustedEvent(nsIDOMEvent* aEvent);
 
   virtual void LastRelease() {}
 private:
   // Inner window or sandbox.
   nsIGlobalObject*           mParentObject;
-  // mParentObject pre QI-ed and cached
+  // mParentObject pre QI-ed and cached (inner window)
   // (it is needed for off main thread access)
   nsPIDOMWindow*             mOwnerWindow;
   bool                       mHasOrHasHadOwnerWindow;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(DOMEventTargetHelper,
                               NS_DOMEVENTTARGETHELPER_IID)
 
--- a/dom/indexedDB/IDBTransaction.cpp
+++ b/dom/indexedDB/IDBTransaction.cpp
@@ -655,25 +655,20 @@ IDBTransaction::GetMode(ErrorResult& aRv
     case MODE_INVALID:
     default:
       aRv.Throw(NS_ERROR_UNEXPECTED);
       return mozilla::dom::IDBTransactionMode::Readonly;
   }
 }
 
 DOMError*
-IDBTransaction::GetError(ErrorResult& aRv)
+IDBTransaction::GetError() const
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  if (IsOpen()) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return nullptr;
-  }
-
   return mError;
 }
 
 already_AddRefed<DOMStringList>
 IDBTransaction::GetObjectStoreNames(ErrorResult& aRv)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
--- a/dom/indexedDB/IDBTransaction.h
+++ b/dom/indexedDB/IDBTransaction.h
@@ -237,17 +237,17 @@ public:
   IDBDatabase*
   Db() const
   {
     NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     return mDatabase;
   }
 
   DOMError*
-  GetError(ErrorResult& aRv);
+  GetError() const;
 
   already_AddRefed<IDBObjectStore>
   ObjectStore(const nsAString& aName, ErrorResult& aRv);
 
   void
   Abort(ErrorResult& aRv)
   {
     NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
--- a/dom/indexedDB/ipc/unit/xpcshell.ini
+++ b/dom/indexedDB/ipc/unit/xpcshell.ini
@@ -53,15 +53,16 @@ tail =
 [test_request_readyState.js]
 [test_setVersion.js]
 [test_setVersion_abort.js]
 [test_setVersion_events.js]
 [test_setVersion_exclusion.js]
 [test_success_events_after_abort.js]
 [test_traffic_jam.js]
 [test_transaction_abort.js]
+[test_transaction_error.js]
 [test_transaction_lifetimes.js]
 [test_transaction_lifetimes_nested.js]
 [test_transaction_ordering.js]
 [test_unique_index_update.js]
 [test_writer_starvation.js]
 
 # When adding files here please also update test/unit/xpcshell.ini!
--- a/dom/indexedDB/test/mochitest.ini
+++ b/dom/indexedDB/test/mochitest.ini
@@ -187,16 +187,18 @@ skip-if = (buildapp == 'b2g' && (toolkit
 [test_third_party.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || toolkit == 'android' #TIMED_OUT #Bug 931116, b2g desktop specific, initial triage
 [test_traffic_jam.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_transaction_abort.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_transaction_abort_hang.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
+[test_transaction_error.html]
+skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_transaction_lifetimes.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_transaction_lifetimes_nested.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_transaction_ordering.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_unique_index_update.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/test_transaction_error.html
@@ -0,0 +1,18 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>Indexed Database Test</title>
+
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+  <script type="text/javascript;version=1.7" src="unit/test_transaction_error.js"></script>
+  <script type="text/javascript;version=1.7" src="helpers.js"></script>
+</head>
+
+<body onload="runTest();"></body>
+
+</html>
--- a/dom/indexedDB/test/unit/mochitest.ini
+++ b/dom/indexedDB/test/unit/mochitest.ini
@@ -59,13 +59,14 @@
 [test_setVersion_abort.js]
 [test_setVersion_events.js]
 [test_setVersion_exclusion.js]
 [test_success_events_after_abort.js]
 [test_temporary_storage.js]
 [test_traffic_jam.js]
 [test_transaction_abort.js]
 [test_transaction_abort_hang.js]
+[test_transaction_error.js]
 [test_transaction_lifetimes.js]
 [test_transaction_lifetimes_nested.js]
 [test_transaction_ordering.js]
 [test_unique_index_update.js]
 [test_writer_starvation.js]
--- a/dom/indexedDB/test/unit/test_transaction_abort.js
+++ b/dom/indexedDB/test/unit/test_transaction_abort.js
@@ -29,23 +29,17 @@ function testSteps()
   db.onabort = abortListener;
 
   let transaction;
   let objectStore;
   let index;
 
   transaction = event.target.transaction;
 
-  try {
-    let error = transaction.error;
-    ok(false, "Expect an exception");
-  } catch(e) {
-    ok(true, "Got an exception.");
-    is(e.name, "InvalidStateError", "Got the right exception");
-  }
+  is(transaction.error, null, "Expect a null error");
 
   objectStore = db.createObjectStore("foo", { autoIncrement: true });
   index = objectStore.createIndex("fooindex", "indexKey", { unique: true });
 
   is(transaction.db, db, "Correct database");
   is(transaction.mode, "versionchange", "Correct mode");
   is(transaction.objectStoreNames.length, 1, "Correct names length");
   is(transaction.objectStoreNames.item(0), "foo", "Correct name");
@@ -329,17 +323,17 @@ function testSteps()
   transaction.objectStore("foo").get(1).onsuccess = function(event) {
     executeSoon(function() {
       transaction.abort();
       expectedAbortEventCount++;
       continueToNextStep();
     });
   };
   yield undefined;
-  
+
   // During COMMITTING
   transaction = db.transaction("foo", "readwrite");
   transaction.objectStore("foo").put({hello: "world"}, 1).onsuccess = function(event) {
     continueToNextStep();
   };
   yield undefined;
   try {
     transaction.abort();
@@ -384,9 +378,9 @@ function testSteps()
   event = yield undefined;
   is(event.type, "abort", "transaction should fail");
   is(event.target, transaction, "transaction should fail");
 
   ok(abortFired, "Abort should have fired!");
 
   finishTest();
   yield undefined;
-}
\ No newline at end of file
+}
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/unit/test_transaction_error.js
@@ -0,0 +1,116 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+let testGenerator = testSteps();
+
+function testSteps() {
+  const dbName = this.window ?
+                 window.location.pathname :
+                 "test_transaction_error";
+  const dbVersion = 1;
+  const objectStoreName = "foo";
+  const data = { };
+  const dataKey = 1;
+  const expectedError = "ConstraintError";
+
+  let request = indexedDB.open(dbName, dbVersion);
+  request.onerror = errorHandler;
+  request.onupgradeneeded = grabEventAndContinueHandler;
+  request.onsuccess = unexpectedSuccessHandler;
+
+  let event = yield undefined;
+
+  info("Creating database");
+
+  let db = event.target.result;
+  let objectStore = db.createObjectStore(objectStoreName);
+  objectStore.add(data, dataKey);
+
+  request.onupgradeneeded = unexpectedSuccessHandler;
+  request.onsuccess = grabEventAndContinueHandler;
+
+  event = yield undefined;
+
+  db = event.target.result;
+
+  let transaction = db.transaction(objectStoreName, "readwrite");
+  transaction.onerror = grabEventAndContinueHandler;
+  transaction.oncomplete = grabEventAndContinueHandler;
+
+  objectStore = transaction.objectStore(objectStoreName);
+
+  info("Adding duplicate entry with preventDefault()");
+
+  let request = objectStore.add(data, dataKey);
+  request.onsuccess = unexpectedSuccessHandler;
+  request.onerror = grabEventAndContinueHandler;
+  event = yield undefined;
+
+  is(event.type, "error", "Got an error event");
+  is(event.target, request, "Error event targeted request");
+  is(event.currentTarget, request, "Got request error first");
+  is(event.currentTarget.error.name, expectedError,
+     "Request has correct error");
+  event.preventDefault();
+
+  event = yield undefined;
+
+  is(event.type, "error", "Got an error event");
+  is(event.target, request, "Error event targeted request");
+  is(event.currentTarget, transaction, "Got transaction error second");
+  is(event.currentTarget.error, null, "Transaction has null error");
+
+  event = yield undefined;
+
+  is(event.type, "complete", "Got a complete event");
+  is(event.target, transaction, "Complete event targeted transaction");
+  is(event.currentTarget, transaction,
+     "Complete event only targeted transaction");
+  is(event.currentTarget.error, null, "Transaction has null error");
+
+  // Try again without preventDefault().
+
+  transaction = db.transaction(objectStoreName, "readwrite");
+  transaction.onerror = grabEventAndContinueHandler;
+  transaction.onabort = grabEventAndContinueHandler;
+
+  objectStore = transaction.objectStore(objectStoreName);
+
+  info("Adding duplicate entry without preventDefault()");
+
+  if ("SimpleTest" in this) {
+    SimpleTest.expectUncaughtException();
+  }
+
+  request = objectStore.add(data, dataKey);
+  request.onsuccess = unexpectedSuccessHandler;
+  request.onerror = grabEventAndContinueHandler;
+  event = yield undefined;
+
+  is(event.type, "error", "Got an error event");
+  is(event.target, request, "Error event targeted request");
+  is(event.currentTarget, request, "Got request error first");
+  is(event.currentTarget.error.name, expectedError,
+     "Request has correct error");
+
+  event = yield undefined;
+
+  is(event.type, "error", "Got an error event");
+  is(event.target, request, "Error event targeted request");
+  is(event.currentTarget, transaction, "Got transaction error second");
+  is(event.currentTarget.error, null, "Transaction has null error");
+
+  event = yield undefined;
+
+  is(event.type, "abort", "Got an abort event");
+  is(event.target, transaction, "Abort event targeted transaction");
+  is(event.currentTarget, transaction,
+     "Abort event only targeted transaction");
+  is(event.currentTarget.error.name, expectedError,
+     "Transaction has correct error");
+
+  finishTest();
+  yield undefined;
+}
--- a/dom/indexedDB/test/unit/xpcshell.ini
+++ b/dom/indexedDB/test/unit/xpcshell.ini
@@ -74,15 +74,16 @@ skip-if = os == "mac" || os == "android"
 [test_setVersion_exclusion.js]
 [test_success_events_after_abort.js]
 [test_temporary_storage.js]
 # bug 951017: intermittent failure on Android x86 emulator
 skip-if = os == "android" && processor == "x86"
 [test_traffic_jam.js]
 [test_transaction_abort.js]
 [test_transaction_abort_hang.js]
+[test_transaction_error.js]
 [test_transaction_lifetimes.js]
 [test_transaction_lifetimes_nested.js]
 [test_transaction_ordering.js]
 [test_unique_index_update.js]
 [test_writer_starvation.js]
 
 # When adding files here please also update ipc/unit/xpcshell.ini!
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -18,16 +18,17 @@
 #include "TabChild.h"
 
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/asmjscache/PAsmJSCacheEntryChild.h"
 #include "mozilla/dom/ExternalHelperAppChild.h"
 #include "mozilla/dom/PCrashReporterChild.h"
 #include "mozilla/dom/DOMStorageIPC.h"
+#include "mozilla/dom/Promise.h"
 #include "mozilla/hal_sandbox/PHalChild.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/FileDescriptorUtils.h"
 #include "mozilla/ipc/GeckoChildProcessHost.h"
 #include "mozilla/ipc/TestShellChild.h"
 #include "mozilla/layers/CompositorChild.h"
 #include "mozilla/layers/ImageBridgeChild.h"
 #include "mozilla/layers/SharedBufferManagerChild.h"
@@ -1432,16 +1433,32 @@ nsresult
 ContentChild::AddRemoteAlertObserver(const nsString& aData,
                                      nsIObserver* aObserver)
 {
     NS_ASSERTION(aObserver, "Adding a null observer?");
     mAlertObservers.AppendElement(new AlertObserver(aObserver, aData));
     return NS_OK;
 }
 
+
+bool
+ContentChild::RecvSystemMemoryAvailable(const uint64_t& aGetterId,
+                                        const uint32_t& aMemoryAvailable)
+{
+  nsRefPtr<Promise> p = dont_AddRef(reinterpret_cast<Promise*>(aGetterId));
+
+  if (!aMemoryAvailable) {
+    p->MaybeReject(NS_LITERAL_STRING("Abnormal"));
+    return true;
+  }
+
+  p->MaybeResolve((int)aMemoryAvailable);
+  return true;
+}
+
 bool
 ContentChild::RecvPreferenceUpdate(const PrefSetting& aPref)
 {
     Preferences::SetPreference(aPref);
     return true;
 }
 
 bool
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -213,16 +213,19 @@ public:
     virtual bool RecvSetOffline(const bool& offline) MOZ_OVERRIDE;
 
     virtual bool RecvSpeakerManagerNotify() MOZ_OVERRIDE;
 
     virtual bool RecvNotifyVisited(const URIParams& aURI) MOZ_OVERRIDE;
     // auto remove when alertfinished is received.
     nsresult AddRemoteAlertObserver(const nsString& aData, nsIObserver* aObserver);
 
+    virtual bool RecvSystemMemoryAvailable(const uint64_t& aGetterId,
+                                           const uint32_t& aMemoryAvailable) MOZ_OVERRIDE;
+
     virtual bool RecvPreferenceUpdate(const PrefSetting& aPref) MOZ_OVERRIDE;
 
     virtual bool RecvNotifyAlertsObserver(const nsCString& aType,
                                           const nsString& aData) MOZ_OVERRIDE;
 
     virtual bool RecvAsyncMessage(const nsString& aMsg,
                                   const ClonedMessageData& aData,
                                   const InfallibleTArray<CpowEntry>& aCpows,
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -114,16 +114,20 @@
 #include "nsIDocShell.h"
 #include "mozilla/net/NeckoMessageUtils.h"
 #include "gfxPrefs.h"
 
 #if defined(ANDROID) || defined(LINUX)
 #include "nsSystemInfo.h"
 #endif
 
+#if defined(XP_LINUX)
+#include "mozilla/Hal.h"
+#endif
+
 #ifdef ANDROID
 # include "gfxAndroidPlatform.h"
 #endif
 
 #ifdef MOZ_PERMISSIONS
 # include "nsPermissionManager.h"
 #endif
 
@@ -3052,16 +3056,31 @@ ContentParent::RecvGetRandomValues(const
     memcpy(randomValues->Elements(), buf, length);
 
     NS_Free(buf);
 
     return true;
 }
 
 bool
+ContentParent::RecvGetSystemMemory(const uint64_t& aGetterId)
+{
+  uint32_t memoryTotal = 0;
+
+#if defined(XP_LINUX)
+  memoryTotal = mozilla::hal::GetTotalSystemMemoryLevel();
+#endif
+
+  unused << SendSystemMemoryAvailable(aGetterId, memoryTotal);
+
+  return true;
+}
+
+
+bool
 ContentParent::RecvLoadURIExternal(const URIParams& uri)
 {
     nsCOMPtr<nsIExternalProtocolService> extProtService(do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID));
     if (!extProtService) {
         return true;
     }
     nsCOMPtr<nsIURI> ourURI = DeserializeURI(uri);
     if (!ourURI) {
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -518,17 +518,17 @@ private:
     virtual bool RecvAudioChannelUnregisterType(const AudioChannel& aChannel,
                                                 const bool& aElementHidden,
                                                 const bool& aWithVideo) MOZ_OVERRIDE;
 
     virtual bool RecvAudioChannelChangedNotification() MOZ_OVERRIDE;
 
     virtual bool RecvAudioChannelChangeDefVolChannel(const int32_t& aChannel,
                                                      const bool& aHidden) MOZ_OVERRIDE;
-
+    virtual bool RecvGetSystemMemory(const uint64_t& getterId) MOZ_OVERRIDE;
     virtual bool RecvBroadcastVolume(const nsString& aVolumeName) MOZ_OVERRIDE;
 
     virtual bool RecvSpeakerManagerGetSpeakerStatus(bool* aValue) MOZ_OVERRIDE;
 
     virtual bool RecvSpeakerManagerForceSpeaker(const bool& aEnable) MOZ_OVERRIDE;
 
     virtual bool RecvSystemMessageHandled() MOZ_OVERRIDE;
 
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -352,16 +352,18 @@ child:
 
     RegisterChrome(ChromePackage[] packages, ResourceMapping[] resources,
                    OverrideMapping[] overrides, nsCString locale);
 
     async SetOffline(bool offline);
 
     async NotifyVisited(URIParams uri);
 
+    async SystemMemoryAvailable(uint64_t getterId, uint32_t memoryAvailable);
+
     PreferenceUpdate(PrefSetting pref);
 
     NotifyAlertsObserver(nsCString topic, nsString data);
 
     GeolocationUpdate(GeoPosition somewhere);
 
     // nsIPermissionManager messages
     AddPermission(Permission permission);
@@ -431,16 +433,18 @@ parent:
 
     PFileSystemRequest(FileSystemParams params);
 
     sync PCrashReporter(NativeThreadId tid, uint32_t processType);
 
     sync GetRandomValues(uint32_t length)
         returns (uint8_t[] randomValues);
 
+    async GetSystemMemory(uint64_t getterId);
+
     PHal();
 
     PIndexedDB();
 
     PNecko();
 
     PSms();
 
--- a/dom/promise/Promise.cpp
+++ b/dom/promise/Promise.cpp
@@ -163,16 +163,219 @@ public:
   bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
   {
     RunInternal();
     return true;
   }
 };
 
+enum {
+  SLOT_PROMISE = 0,
+  SLOT_DATA
+};
+
+/*
+ * Utilities for thenable callbacks.
+ *
+ * A thenable is a { then: function(resolve, reject) { } }.
+ * `then` is called with a resolve and reject callback pair.
+ * Since only one of these should be called at most once (first call wins), the
+ * two keep a reference to each other in SLOT_DATA. When either of them is
+ * called, the references are cleared. Further calls are ignored.
+ */
+namespace {
+void
+LinkThenableCallables(JSContext* aCx, JS::Handle<JSObject*> aResolveFunc,
+                      JS::Handle<JSObject*> aRejectFunc)
+{
+  js::SetFunctionNativeReserved(aResolveFunc, SLOT_DATA,
+                                JS::ObjectValue(*aRejectFunc));
+  js::SetFunctionNativeReserved(aRejectFunc, SLOT_DATA,
+                                JS::ObjectValue(*aResolveFunc));
+}
+
+/*
+ * Returns false if callback was already called before, otherwise breaks the
+ * links and returns true.
+ */
+bool
+MarkAsCalledIfNotCalledBefore(JSContext* aCx, JS::Handle<JSObject*> aFunc)
+{
+  JS::Value otherFuncVal = js::GetFunctionNativeReserved(aFunc, SLOT_DATA);
+
+  if (!otherFuncVal.isObject()) {
+    return false;
+  }
+
+  JSObject* otherFuncObj = &otherFuncVal.toObject();
+  MOZ_ASSERT(js::GetFunctionNativeReserved(otherFuncObj, SLOT_DATA).isObject());
+
+  // Break both references.
+  js::SetFunctionNativeReserved(aFunc, SLOT_DATA, JS::UndefinedValue());
+  js::SetFunctionNativeReserved(otherFuncObj, SLOT_DATA, JS::UndefinedValue());
+
+  return true;
+}
+
+Promise*
+GetPromise(JSContext* aCx, JS::Handle<JSObject*> aFunc)
+{
+  JS::Value promiseVal = js::GetFunctionNativeReserved(aFunc, SLOT_PROMISE);
+
+  MOZ_ASSERT(promiseVal.isObject());
+
+  Promise* promise;
+  UNWRAP_OBJECT(Promise, &promiseVal.toObject(), promise);
+  return promise;
+}
+};
+
+// Equivalent to the specification's ResolvePromiseViaThenableTask.
+class ThenableResolverMixin
+{
+public:
+  ThenableResolverMixin(Promise* aPromise,
+                        JS::Handle<JSObject*> aThenable,
+                        PromiseInit* aThen)
+    : mPromise(aPromise)
+    , mThenable(CycleCollectedJSRuntime::Get()->Runtime(), aThenable)
+    , mThen(aThen)
+  {
+    MOZ_ASSERT(aPromise);
+    MOZ_COUNT_CTOR(ThenableResolverMixin);
+  }
+
+  virtual ~ThenableResolverMixin()
+  {
+    NS_ASSERT_OWNINGTHREAD(ThenableResolverMixin);
+    MOZ_COUNT_DTOR(ThenableResolverMixin);
+  }
+
+protected:
+  void
+  RunInternal()
+  {
+    NS_ASSERT_OWNINGTHREAD(ThenableResolverMixin);
+    ThreadsafeAutoJSContext cx;
+    JS::Rooted<JSObject*> wrapper(cx, mPromise->GetOrCreateWrapper(cx));
+    if (!wrapper) {
+      return;
+    }
+    JSAutoCompartment ac(cx, wrapper);
+
+    JS::Rooted<JSObject*> resolveFunc(cx,
+      mPromise->CreateThenableFunction(cx, mPromise, PromiseCallback::Resolve));
+
+    if (!resolveFunc) {
+      mPromise->HandleException(cx);
+      return;
+    }
+
+    JS::Rooted<JSObject*> rejectFunc(cx,
+      mPromise->CreateThenableFunction(cx, mPromise, PromiseCallback::Reject));
+    if (!rejectFunc) {
+      mPromise->HandleException(cx);
+      return;
+    }
+
+    LinkThenableCallables(cx, resolveFunc, rejectFunc);
+
+    ErrorResult rv;
+
+    JS::Rooted<JSObject*> rootedThenable(cx, mThenable);
+
+    mThen->Call(rootedThenable, resolveFunc, rejectFunc, rv,
+                CallbackObject::eRethrowExceptions);
+
+    rv.WouldReportJSException();
+    if (rv.IsJSException()) {
+      JS::Rooted<JS::Value> exn(cx);
+      rv.StealJSException(cx, &exn);
+
+      bool couldMarkAsCalled = MarkAsCalledIfNotCalledBefore(cx, resolveFunc);
+
+      // If we could mark as called, neither of the callbacks had been called
+      // when the exception was thrown. So we can reject the Promise.
+      if (couldMarkAsCalled) {
+        bool ok = JS_WrapValue(cx, &exn);
+        MOZ_ASSERT(ok);
+        if (!ok) {
+          NS_WARNING("Failed to wrap value into the right compartment.");
+        }
+
+        mPromise->RejectInternal(cx, exn, Promise::SyncTask);
+      }
+      // At least one of resolveFunc or rejectFunc have been called, so ignore
+      // the exception. FIXME(nsm): This should be reported to the error
+      // console though, for debugging.
+    }
+  }
+
+private:
+  nsRefPtr<Promise> mPromise;
+  JS::PersistentRooted<JSObject*> mThenable;
+  nsRefPtr<PromiseInit> mThen;
+  NS_DECL_OWNINGTHREAD;
+};
+
+// Main thread runnable to resolve thenables.
+class ThenableResolverTask MOZ_FINAL : public nsRunnable,
+                                       public ThenableResolverMixin
+{
+public:
+  ThenableResolverTask(Promise* aPromise,
+                       JS::Handle<JSObject*> aThenable,
+                       PromiseInit* aThen)
+    : ThenableResolverMixin(aPromise, aThenable, aThen)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+  }
+
+  ~ThenableResolverTask()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+  }
+
+  NS_IMETHOD Run()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    RunInternal();
+    return NS_OK;
+  }
+};
+
+// Worker thread runnable to resolve thenables.
+class WorkerThenableResolverTask MOZ_FINAL : public WorkerSameThreadRunnable,
+                                             public ThenableResolverMixin
+{
+public:
+  WorkerThenableResolverTask(WorkerPrivate* aWorkerPrivate,
+                             Promise* aPromise,
+                             JS::Handle<JSObject*> aThenable,
+                             PromiseInit* aThen)
+    : WorkerSameThreadRunnable(aWorkerPrivate),
+      ThenableResolverMixin(aPromise, aThenable, aThen)
+  {
+    MOZ_ASSERT(aWorkerPrivate);
+    aWorkerPrivate->AssertIsOnWorkerThread();
+  }
+
+  ~WorkerThenableResolverTask()
+  {}
+
+  bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
+  {
+    aWorkerPrivate->AssertIsOnWorkerThread();
+    RunInternal();
+    return true;
+  }
+};
+
 // Promise
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(Promise)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Promise)
   tmp->MaybeReportRejectedOnce();
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mResolveCallbacks)
@@ -262,21 +465,16 @@ Promise::MaybeResolve(JSContext* aCx,
 
 void
 Promise::MaybeReject(JSContext* aCx,
                      JS::Handle<JS::Value> aValue)
 {
   MaybeRejectInternal(aCx, aValue);
 }
 
-enum {
-  SLOT_PROMISE = 0,
-  SLOT_DATA
-};
-
 /* static */ bool
 Promise::JSCallback(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
 {
   JS::CallArgs args = CallArgsFromVp(aArgc, aVp);
 
   JS::Rooted<JS::Value> v(aCx,
                           js::GetFunctionNativeReserved(&args.callee(),
                                                         SLOT_PROMISE));
@@ -295,72 +493,16 @@ Promise::JSCallback(JSContext* aCx, unsi
   } else {
     promise->MaybeRejectInternal(aCx, args.get(0));
   }
 
   return true;
 }
 
 /*
- * Utilities for thenable callbacks.
- *
- * A thenable is a { then: function(resolve, reject) { } }.
- * `then` is called with a resolve and reject callback pair.
- * Since only one of these should be called at most once (first call wins), the
- * two keep a reference to each other in SLOT_DATA. When either of them is
- * called, the references are cleared. Further calls are ignored.
- */
-namespace {
-void
-LinkThenableCallables(JSContext* aCx, JS::Handle<JSObject*> aResolveFunc,
-                      JS::Handle<JSObject*> aRejectFunc)
-{
-  js::SetFunctionNativeReserved(aResolveFunc, SLOT_DATA,
-                                JS::ObjectValue(*aRejectFunc));
-  js::SetFunctionNativeReserved(aRejectFunc, SLOT_DATA,
-                                JS::ObjectValue(*aResolveFunc));
-}
-
-/*
- * Returns false if callback was already called before, otherwise breaks the
- * links and returns true.
- */
-bool
-MarkAsCalledIfNotCalledBefore(JSContext* aCx, JS::Handle<JSObject*> aFunc)
-{
-  JS::Value otherFuncVal = js::GetFunctionNativeReserved(aFunc, SLOT_DATA);
-
-  if (!otherFuncVal.isObject()) {
-    return false;
-  }
-
-  JSObject* otherFuncObj = &otherFuncVal.toObject();
-  MOZ_ASSERT(js::GetFunctionNativeReserved(otherFuncObj, SLOT_DATA).isObject());
-
-  // Break both references.
-  js::SetFunctionNativeReserved(aFunc, SLOT_DATA, JS::UndefinedValue());
-  js::SetFunctionNativeReserved(otherFuncObj, SLOT_DATA, JS::UndefinedValue());
-
-  return true;
-}
-
-Promise*
-GetPromise(JSContext* aCx, JS::Handle<JSObject*> aFunc)
-{
-  JS::Value promiseVal = js::GetFunctionNativeReserved(aFunc, SLOT_PROMISE);
-
-  MOZ_ASSERT(promiseVal.isObject());
-
-  Promise* promise;
-  UNWRAP_OBJECT(Promise, &promiseVal.toObject(), promise);
-  return promise;
-}
-};
-
-/*
  * Common bits of (JSCallbackThenableResolver/JSCallbackThenableRejecter).
  * Resolves/rejects the Promise if it is ok to do so, based on whether either of
  * the callbacks have been called before or not.
  */
 /* static */ bool
 Promise::ThenableResolverCommon(JSContext* aCx, uint32_t aTask,
                                 unsigned aArgc, JS::Value* aVp)
 {
@@ -370,19 +512,19 @@ Promise::ThenableResolverCommon(JSContex
     // A function from this pair has been called before.
     return true;
   }
 
   Promise* promise = GetPromise(aCx, thisFunc);
   MOZ_ASSERT(promise);
 
   if (aTask == PromiseCallback::Resolve) {
-    promise->ResolveInternal(aCx, args.get(0), SyncTask);
+    promise->ResolveInternal(aCx, args.get(0));
   } else {
-    promise->RejectInternal(aCx, args.get(0), SyncTask);
+    promise->RejectInternal(aCx, args.get(0));
   }
   return true;
 }
 
 /* static */ bool
 Promise::JSCallbackThenableResolver(JSContext* aCx,
                                     unsigned aArgc, JS::Value* aVp)
 {
@@ -977,62 +1119,30 @@ Promise::ResolveInternal(JSContext* aCx,
     // Thenables.
     JS::Rooted<JS::Value> then(aCx);
     if (!JS_GetProperty(aCx, valueObj, "then", &then)) {
       HandleException(aCx);
       return;
     }
 
     if (then.isObject() && JS_ObjectIsCallable(aCx, &then.toObject())) {
-      JS::Rooted<JSObject*> resolveFunc(aCx,
-        CreateThenableFunction(aCx, this, PromiseCallback::Resolve));
-
-      if (!resolveFunc) {
-        HandleException(aCx);
-        return;
-      }
-
-      JS::Rooted<JSObject*> rejectFunc(aCx,
-        CreateThenableFunction(aCx, this, PromiseCallback::Reject));
-      if (!rejectFunc) {
-        HandleException(aCx);
-        return;
-      }
-
-      LinkThenableCallables(aCx, resolveFunc, rejectFunc);
-
+      // This is the then() function of the thenable aValueObj.
       JS::Rooted<JSObject*> thenObj(aCx, &then.toObject());
       nsRefPtr<PromiseInit> thenCallback =
         new PromiseInit(thenObj, mozilla::dom::GetIncumbentGlobal());
-
-      ErrorResult rv;
-      thenCallback->Call(valueObj, resolveFunc, rejectFunc,
-                         rv, CallbackObject::eRethrowExceptions);
-      rv.WouldReportJSException();
-
-      if (rv.IsJSException()) {
-        JS::Rooted<JS::Value> exn(aCx);
-        rv.StealJSException(aCx, &exn);
-
-        bool couldMarkAsCalled = MarkAsCalledIfNotCalledBefore(aCx, resolveFunc);
-
-        // If we could mark as called, neither of the callbacks had been called
-        // when the exception was thrown. So we can reject the Promise.
-        if (couldMarkAsCalled) {
-          bool ok = JS_WrapValue(aCx, &exn);
-          MOZ_ASSERT(ok);
-          if (!ok) {
-            NS_WARNING("Failed to wrap value into the right compartment.");
-          }
-
-          RejectInternal(aCx, exn, Promise::SyncTask);
-        }
-        // At least one of resolveFunc or rejectFunc have been called, so ignore
-        // the exception. FIXME(nsm): This should be reported to the error
-        // console though, for debugging.
+      if (NS_IsMainThread()) {
+        nsRefPtr<ThenableResolverTask> task =
+          new ThenableResolverTask(this, valueObj, thenCallback);
+        NS_DispatchToCurrentThread(task);
+      } else {
+        WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
+        MOZ_ASSERT(worker);
+        nsRefPtr<WorkerThenableResolverTask> task =
+          new WorkerThenableResolverTask(worker, this, valueObj, thenCallback);
+        task->Dispatch(worker->GetJSContext());
       }
 
       return;
     }
   }
 
   // If the synchronous flag is set, process our resolve callbacks with
   // value. Otherwise, the synchronous flag is unset, queue a task to process
--- a/dom/promise/Promise.h
+++ b/dom/promise/Promise.h
@@ -54,16 +54,17 @@ class Promise MOZ_FINAL : public nsISupp
   friend class PromiseResolverMixin;
   friend class PromiseResolverTask;
   friend class PromiseTask;
   friend class PromiseReportRejectFeature;
   friend class PromiseWorkerProxy;
   friend class PromiseWorkerProxyRunnable;
   friend class RejectPromiseCallback;
   friend class ResolvePromiseCallback;
+  friend class ThenableResolverMixin;
   friend class WorkerPromiseResolverTask;
   friend class WorkerPromiseTask;
   friend class WrapperPromiseCallback;
 
   ~Promise();
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
--- a/dom/promise/tests/test_promise.html
+++ b/dom/promise/tests/test_promise.html
@@ -613,16 +613,69 @@ function promiseResolvePromise() {
   ok(cast instanceof Promise, "Should cast to a Promise.");
   is(cast, original, "Should return original Promise.");
   cast.then(function(v) {
     is(v, true, "Should resolve to true.");
     runTest();
   });
 }
 
+// Bug 1009569.
+// Ensure that thenables are run on a clean stack asynchronously.
+// Test case adopted from
+// https://gist.github.com/getify/d64bb01751b50ed6b281#file-bug1-js.
+function promiseResolveThenableCleanStack() {
+  function immed(s) { x++; s(); }
+  function incX(){ x++; }
+
+  var x = 0;
+  var thenable = { then: immed };
+  var results = [];
+
+  Promise.resolve(thenable).then(incX);
+  results.push(x);
+
+  // check what happens after all "next cycle" steps
+  // have had a chance to complete
+  setTimeout(function(){
+    results.push(x);
+    // Result should be [0, 2] since `thenable` will be called async.
+    is(results[0], 0, "Expected thenable to be called asynchronously");
+    is(results[1], 2, "Expected thenable to be called asynchronously");
+    runTest();
+  },1000);
+}
+
+// Bug 1008467 - Promise fails with "too much recursion".
+// The bug was that the callbacks passed to a thenable would resolve the
+// promise synchronously when the fulfill handler returned a non-thenable.
+//
+// For example:
+// var p = new Promise(function(resolve) {
+//   resolve(5);
+// });
+// var m = Promise.resolve(p);
+//
+// At this point `m` is a Promise that is resolved with a thenable `p`, so it
+// calls `p.then()` with two callbacks, both of which would synchronously resolve
+// `m` when `p` invoked them (on account of itself being resolved, possibly
+// synchronously. A chain of these 'Promise resolved by a Promise' would lead to
+// stack overflow.
+function promiseTestAsyncThenableResolution()
+{
+  var k = 3000;
+  Promise.resolve().then(function next() {
+    k--;
+    if (k > 0) return Promise.resolve().then(next);
+  }).then(function () {
+    ok(true, "Resolution of a chain of thenables should not be synchronous.");
+    runTest();
+  });
+}
+
 var tests = [ promiseResolve, promiseReject,
               promiseException, promiseGC, promiseAsync,
               promiseDoubleThen, promiseThenException,
               promiseThenCatchThen, promiseRejectThenCatchThen,
               promiseRejectThenCatchThen2,
               promiseRejectThenCatchExceptionThen,
               promiseThenCatchOrderingResolve,
               promiseThenCatchOrderingReject,
@@ -643,16 +696,18 @@ var tests = [ promiseResolve, promiseRej
               promiseThenableThrowsAfterCallback,
               promiseThenableRejectThenResolve,
               promiseWithThenReplaced,
               promiseStrictHandlers,
               promiseStrictExecutorThisArg,
               promiseResolveArray,
               promiseResolveThenable,
               promiseResolvePromise,
+              promiseResolveThenableCleanStack,
+              promiseTestAsyncThenableResolution,
             ];
 
 function runTest() {
   if (!tests.length) {
     SimpleTest.finish();
     return;
   }
 
--- a/dom/system/gonk/android_audio/AudioSystem.h
+++ b/dom/system/gonk/android_audio/AudioSystem.h
@@ -12,16 +12,18 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef ANDROID_AUDIOSYSTEM_H_
 #define ANDROID_AUDIOSYSTEM_H_
 
+#pragma GCC visibility push(default)
+
 #include <utils/RefBase.h>
 #include <utils/threads.h>
 #include "IAudioFlinger.h"
 
 #ifndef VANILLA_ANDROID
 /* request to open a direct output with get_output() (by opposition to
  * sharing an output with other AudioTracks)
  */
@@ -956,9 +958,11 @@ public:
 
 private:
     String8 mKeyValuePairs;
     KeyedVector <String8, String8> mParameters;
 };
 
 };  // namespace android
 
+#pragma GCC visibility pop
+
 #endif  /*ANDROID_AUDIOSYSTEM_H_*/
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -377,16 +377,18 @@ var interfaceNamesInGlobalScope =
     {name: "GamepadButton", b2g: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "GamepadEvent", b2g: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HashChangeEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "History",
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    "HTMLAllCollection",
+// IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLAnchorElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLAppletElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLAreaElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLAudioElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
new file mode 100644
--- /dev/null
+++ b/dom/webidl/HTMLAllCollection.webidl
@@ -0,0 +1,13 @@
+/* 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/. */
+
+/* Emulates undefined through Codegen.py. */
+interface HTMLAllCollection {
+  readonly attribute unsigned long length;
+  getter Node? (unsigned long index);
+  Node? item(unsigned long index);
+  (Node or HTMLCollection)? item(DOMString name);
+  legacycaller (Node or HTMLCollection)? (DOMString name);
+  getter (Node or HTMLCollection)? namedItem(DOMString name);
+};
--- a/dom/webidl/HTMLDocument.webidl
+++ b/dom/webidl/HTMLDocument.webidl
@@ -67,18 +67,17 @@ interface HTMLDocument : Document {
 
   [Pure]
   readonly attribute HTMLCollection anchors;
   [Pure]
   readonly attribute HTMLCollection applets;
 
   void clear();
 
-  [Throws]
-  readonly attribute object all;
+  readonly attribute HTMLAllCollection all;
 
   // https://dvcs.w3.org/hg/editing/raw-file/tip/editing.html#selections
   [Throws]
   Selection? getSelection();
 
   // @deprecated These are old Netscape 4 methods. Do not use,
   //             the implementation is no-op.
   // XXXbz do we actually need these anymore?
--- a/dom/webidl/IDBTransaction.webidl
+++ b/dom/webidl/IDBTransaction.webidl
@@ -14,17 +14,16 @@ enum IDBTransactionMode {
     "versionchange"
 };
 
 interface IDBTransaction : EventTarget {
     [Throws]
     readonly    attribute IDBTransactionMode mode;
     readonly    attribute IDBDatabase        db;
 
-    [Throws]
     readonly    attribute DOMError?          error;
 
     [Throws]
     IDBObjectStore objectStore (DOMString name);
 
     [Throws]
     void           abort();
 
--- a/dom/webidl/SVGGraphicsElement.webidl
+++ b/dom/webidl/SVGGraphicsElement.webidl
@@ -5,24 +5,31 @@
  *
  * The origin of this IDL file is
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
+dictionary SVGBoundingBoxOptions {
+  boolean fill = true;
+  boolean stroke = false;
+  boolean markers = false;
+  boolean clipped = false;
+};
+
 interface SVGGraphicsElement : SVGElement {
   readonly attribute SVGAnimatedTransformList transform;
 
   readonly attribute SVGElement? nearestViewportElement;
   readonly attribute SVGElement? farthestViewportElement;
 
   [NewObject, Throws]
-  SVGRect getBBox();
+  SVGRect getBBox(optional SVGBoundingBoxOptions aOptions);
   // Not implemented
   // SVGRect getStrokeBBox();
   SVGMatrix? getCTM();
   SVGMatrix? getScreenCTM();
   [Throws]
   SVGMatrix getTransformToElement(SVGGraphicsElement element);
 };
 
--- a/dom/webidl/Window.webidl
+++ b/dom/webidl/Window.webidl
@@ -67,17 +67,18 @@ typedef any Transferable;
   // the user agent
   [Throws] readonly attribute Navigator navigator; 
 #ifdef HAVE_SIDEBAR
   [Replaceable, Throws] readonly attribute External external;
 #endif
   [Throws] readonly attribute ApplicationCache applicationCache;
 
   // user prompts
-  [Throws] void alert(optional DOMString message = "");
+  [Throws] void alert();
+  [Throws] void alert(DOMString message);
   [Throws] boolean confirm(optional DOMString message = "");
   [Throws] DOMString? prompt(optional DOMString message = "", optional DOMString default = "");
   [Throws] void print();
   //[Throws] any showModalDialog(DOMString url, optional any argument);
   [Throws] any showModalDialog(DOMString url, optional any argument, optional DOMString options = "");
 
   [Throws, CrossOriginCallable] void postMessage(any message, DOMString targetOrigin, optional sequence<Transferable> transfer);
 
@@ -289,19 +290,19 @@ partial interface Window {
 
   /* The maximum offset that the window can be scrolled to
      (i.e., the document width/height minus the scrollport width/height) */
   [Replaceable, Throws] readonly attribute long   scrollMaxX;
   [Replaceable, Throws] readonly attribute long   scrollMaxY;
 
   [Throws] attribute boolean                            fullScreen;
 
-  [Throws] void             back();
-  [Throws] void             forward();
-  [Throws] void             home();
+  [Throws, ChromeOnly] void             back();
+  [Throws, ChromeOnly] void             forward();
+  [Throws, ChromeOnly] void             home();
 
   // XXX Should this be in nsIDOMChromeWindow?
   void                      updateCommands(DOMString action);
 
   /* Find in page.
    * @param str: the search pattern
    * @param caseSensitive: is the search caseSensitive
    * @param backwards: should we search backwards
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -119,16 +119,17 @@ WEBIDL_FILES = [
     'FocusEvent.webidl',
     'FormData.webidl',
     'Function.webidl',
     'GainNode.webidl',
     'Geolocation.webidl',
     'GeometryUtils.webidl',
     'GetUserMediaRequest.webidl',
     'History.webidl',
+    'HTMLAllCollection.webidl',
     'HTMLAnchorElement.webidl',
     'HTMLAppletElement.webidl',
     'HTMLAreaElement.webidl',
     'HTMLAudioElement.webidl',
     'HTMLBaseElement.webidl',
     'HTMLBodyElement.webidl',
     'HTMLBRElement.webidl',
     'HTMLButtonElement.webidl',
--- a/dom/workers/test/promise_worker.js
+++ b/dom/workers/test/promise_worker.js
@@ -649,16 +649,42 @@ function promiseResolvePromise() {
   ok(cast instanceof Promise, "Should cast to a Promise.");
   is(cast, original, "Should return original Promise.");
   cast.then(function(v) {
     is(v, true, "Should resolve to true.");
     runTest();
   });
 }
 
+// Bug 1009569.
+// Ensure that thenables are run on a clean stack asynchronously.
+// Test case adopted from
+// https://gist.github.com/getify/d64bb01751b50ed6b281#file-bug1-js.
+function promiseResolveThenableCleanStack() {
+  function immed(s) { x++; s(); }
+  function incX(){ x++; }
+
+  var x = 0;
+  var thenable = { then: immed };
+  var results = [];
+
+  Promise.resolve(thenable).then(incX);
+  results.push(x);
+
+  // check what happens after all "next cycle" steps
+  // have had a chance to complete
+  setTimeout(function(){
+    results.push(x);
+    // Result should be [0, 2] since `thenable` will be called async.
+    is(results[0], 0, "Expected thenable to be called asynchronously");
+    is(results[1], 2, "Expected thenable to be called asynchronously");
+    runTest();
+  },1000);
+}
+
 var tests = [
     promiseResolve,
     promiseReject,
     promiseException,
     promiseAsync,
     promiseDoubleThen,
     promiseThenException,
     promiseThenCatchThen,
@@ -693,16 +719,18 @@ var tests = [
     promiseRaceValuesArray,
     promiseRacePromiseArray,
     promiseRaceReject,
     promiseRaceThrow,
 
     promiseResolveArray,
     promiseResolveThenable,
     promiseResolvePromise,
+
+    promiseResolveThenableCleanStack,
 ];
 
 function runTest() {
   if (!tests.length) {
     postMessage({ type: 'finish' });
     return;
   }
 
--- a/dom/xslt/xslt/txExecutionState.cpp
+++ b/dom/xslt/xslt/txExecutionState.cpp
@@ -58,17 +58,19 @@ txExecutionState::txExecutionState(txSty
 }
 
 txExecutionState::~txExecutionState()
 {
     MOZ_COUNT_DTOR(txExecutionState);
 
     delete mResultHandler;
     delete mLocalVariables;
-    delete mEvalContext;
+    if (mEvalContext != mInitialEvalContext) {
+        delete mEvalContext;
+    }
     
     txStackIterator varsIter(&mLocalVarsStack);
     while (varsIter.hasNext()) {
         delete (txVariableMap*)varsIter.next();
     }
 
     txStackIterator contextIter(&mEvalContextStack);
     while (contextIter.hasNext()) {
@@ -82,16 +84,18 @@ txExecutionState::~txExecutionState()
     while (handlerIter.hasNext()) {
         delete (txAXMLEventHandler*)handlerIter.next();
     }
 
     txStackIterator paramIter(&mParamStack);
     while (paramIter.hasNext()) {
         delete (txVariableMap*)paramIter.next();
     }
+
+    delete mInitialEvalContext;
 }
 
 nsresult
 txExecutionState::init(const txXPathNode& aNode,
                        txOwningExpandedNameMap<txIGlobalParameter>* aGlobalParams)
 {
     nsresult rv = NS_OK;
 
--- a/editor/libeditor/html/tests/browserscope/lib/richtext2/currentStatus.js
+++ b/editor/libeditor/html/tests/browserscope/lib/richtext2/currentStatus.js
@@ -541,19 +541,16 @@ const knownFailures = {
     "S-Proposed-SM:e.f.lb_P.P.P-1_SI-1-body": true,
     "S-Proposed-SM:e.f.lb_P.P.P-1_SI-1-div": true,
     "S-Proposed-SM:e.b.lb_BR.BR-1_SIR-2-dM": true,
     "S-Proposed-SM:e.b.lb_BR.BR-1_SIR-2-body": true,
     "S-Proposed-SM:e.b.lb_BR.BR-1_SIR-2-div": true,
     "S-Proposed-SM:e.b.lb_P.P.P-1_SIR-2-dM": true,
     "S-Proposed-SM:e.b.lb_P.P.P-1_SIR-2-body": true,
     "S-Proposed-SM:e.b.lb_P.P.P-1_SIR-2-div": true,
-    "S-Proposed-SM:e.f.l_BR.BR-2_SI-1-dM": true,
-    "S-Proposed-SM:e.f.l_BR.BR-2_SI-1-body": true,
-    "S-Proposed-SM:e.f.l_BR.BR-2_SI-1-div": true,
     "A-Proposed-B_TEXT-1_SI-dM": true,
     "A-Proposed-B_TEXT-1_SI-body": true,
     "A-Proposed-B_TEXT-1_SI-div": true,
     "A-Proposed-B_TEXT-1_SIR-dM": true,
     "A-Proposed-B_TEXT-1_SIR-body": true,
     "A-Proposed-B_TEXT-1_SIR-div": true,
     "A-Proposed-B_I-1_SL-dM": true,
     "A-Proposed-B_I-1_SL-body": true,
--- a/extensions/spellcheck/locales/en-US/hunspell/dictionary-sources/upstream-hunspell.diff
+++ b/extensions/spellcheck/locales/en-US/hunspell/dictionary-sources/upstream-hunspell.diff
@@ -9098,562 +9098,569 @@ 11045c16855
 11393d17202
 < altho
 11579a17389
 > analyses
 11631c17441
 < anecdotal
 ---
 > anecdotal/Y
-11804c17614
+11750a17561,17562
+> anonymization/SM
+> anonymize/DSG
+11804c17616
 < anthropomorphizing
 ---
 > anthropomorphize/DSG
-11983c17793
+11983c17795
 < app/S
 ---
 > app/MS
-12137a17948
+12137a17950
 > archaeology/M
-12152c17963
+12152c17965
 < archeological
 ---
 > archeological/Y
-12432c18243
+12432c18245
 < assignees
 ---
 > assignee/MS
-12685c18496
+12685c18498
 < auteur
 ---
 > auteur/MS
-12713a18525
+12713a18527
 > autocomplete/S
-12754a18567
+12754a18569
 > avant-garde
-12827d18639
+12827d18641
 < awol
-12829a18642
+12829a18644
 > axe/M
-13864a19678
+13864a19680
 > biodiesel/M
-14210d20023
+14210d20025
 < blueing's
-14434a20248
+14434a20250
 > bookselling
-14841c20655
+14841c20657
 < broadcast/AMGS
 ---
 > broadcast/AMGSD
-15039,15040c20853
+15039,15040c20855
 < bullshitter's
 < bullshitter/S!
 ---
 > bullshitter/SM!
-15230d21042
+15230d21044
 < byelaw/SM
-15383d21194
+15383d21196
 < callisthenics/M
-15460a21272,21274
+15460a21274,21276
 > cancelled/U
 > canceller/M
 > cancelling
-15559a21374
+15559a21376
 > capita
-15629,15630d21443
+15629,15630d21445
 < carburetter/SM
 < carburettor/SM
-15701a21515
+15701a21517
 > carnitas
-15788d21601
+15788d21603
 < cashpoint/S
-15797d21609
+15797d21611
 < cassino/M
-15832a21645
+15832a21647
 > catalyses
-15940d21752
+15940d21754
 < caviare/M
-16372c22184
+16372c22186
 < chickenshit/S!
 ---
 > chickenshit/SM!
-16404c22216
+16404c22218
 < children
 ---
 > children/M
-16488d22299
+16488d22301
 < chlorophyl/M
-16629,16630c22440
+16629,16630c22442
 < cider's
 < cider/S
 ---
 > cider/MS
-16652a22463
+16652a22465
 > ciphertext/S
-17072d22882
+17072d22884
 < cocain/M
-17102,17103c22912
+17102,17103c22914
 < cocksucker's
 < cocksucker/S!
 ---
 > cocksucker/SM!
-17755c23564
+17755c23566
 < confer/S
 ---
 > confer/SB
-17800a23610
+17800a23612
 > conformant
-18151d23960
+18151d23962
 < convenor/S
-18206c24015
+18206c24017
 < cookie/M
 ---
 > cookie/SM
-18375a24185,24186
+18375a24187,24188
 > corrigibility/IM
 > corrigible/I
-18389a24201
+18389a24203
 > corruptibly/I
-18467a24280
+18467a24282
 > could've
-18999a24813
+18999a24815
 > cryptologist/MS
-19000a24815
+19000a24817
 > cryptosystem/S
-19035a24851
+19035a24853
 > cul-de-sac
-19246c25062
+19246c25064
 < cysteine
 ---
 > cysteine/M
-19935a25752
+19935a25754
 > dequeue/DSG
-20196,20197c26013,26014
+20196,20197c26015,26016
 < dialog/SM
 < dialogue/SM
 ---
 > dialog/SMGD
 > dialogue/SMRGD
-20220a26038
+20220a26040
 > diatomaceous
-20481a26300
+20481a26302
 > disclose/DSG
-20633a26453
+20633a26455
 > dissentious
-20830c26650
+20830c26652
 < dogie/M
 ---
 > dogie/SM
-20895a26716
+20895a26718
 > donator/MS
-21820a27642
+21820a27644
 > elicitor/MS
-22071a27894
+22071a27896
 > encyclopaedia
-22196a28020
+22196a28022
 > enqueue/DSG
-22556a28381
+22556a28383
 > estoppel
-22638c28463
+22638c28465
 < euthanize
 ---
 > euthanize/DSG
-22719a28545
+22719a28547
 > exabyte/MS
-22947a28774
+22947a28776
 > experimentalism
-23207,23208d29033
+23207,23208d29035
 < faecal
 < faeces/M
-23215c29040
+23215c29042
 < faggoting's
 ---
 > faggot/SMG
-23701a29527
+23701a29529
 > filesystem/MS
-24155c29981
+24155c29983
 < fluidized
 ---
 > fluidize/DSG
-24216a30043
+24216a30045
 > foci
-24736d30562
+24736d30564
 < frier/M
-24855,24856c30681,30682
+24855,24856c30683,30684
 < fucker/M!
 < fuckhead/S!
 ---
 > fucker/SM!
 > fuckhead/SM!
-24953d30778
+24953d30780
 < furore/MS
-25125c30950
+25125c30952
 < gaolbird/S
 ---
 > gaolbirds
-25180d31004
+25180d31006
 < gasolene/M
-25190a31015
+25190a31017
 > gastroenterologist/M
-25262c31087
+25262c31089
 < geezer/M
 ---
 > geezer/MS
-25327c31152
+25327c31154
 < genomic
 ---
 > genomic/S
-25462a31288
+25462a31290
 > gigabit/MS
-25464a31291,31293
+25464a31293,31295
 > gigajoule/MS
 > gigapixel/MS
 > gigawatt/MS
-25560d31388
+25560d31390
 < glamourize/DSG
-25674c31502
+25674c31504
 < glycerine's
 ---
 > glycerine/M
-25905c31733
+25905c31735
 < gram/MS
 ---
 > gram/KMS
-25909d31736
+25909d31738
 < gramme/SM
-26063c31890,31891
+26063c31892,31893
 < greybeard
 ---
 > grey/MDRTGSP
 > greybeard/SM
-26066c31894
+26066c31896
 < greyness
 ---
 > greyness/M
-26246,26247d32073
+26246,26247d32075
 < guerilla's
 < guerillas
-26432,26436d32257
+26432,26436d32259
 < haemoglobin's
 < haemophilia/M
 < haemorrhage/DSMG
 < haemorrhoid/S
 < haemorrhoids/M
-27167c32988
+27167c32990
 < hexane
 ---
 > hexane/SM
-27273a33095
+27273a33097
 > hippopotami
-27875d33696
+27875d33698
 < hyaena/SM
-28017c33838
+28017c33840
 < iPod/M
 ---
 > iPod/MS
-28105a33927
+28105a33929
 > idolator/SM
-28513c34335
+28227c34051
+< immerse/XDSGN
+---
+> immerse/XDSGNV
+28513c34337
 < inbound
 ---
 > inbound/s
-28590,28591c34412
+28590,28591c34414
 < incorrigibility/M
 < incorrigible
 ---
 > incorrigibleness
-28593d34413
+28593d34415
 < incorruptibly
-28650a34471
+28650a34473
 > indices
-28812d34632
+28812d34634
 < inflexion/SM
-29216a35037
+29216a35039
 > intern/GDL
-29266a35088
+29266a35090
 > interruptible/U
-29272a35095,35098
+29272a35097,35100
 > intersex
 > intersexual/MS
 > intersexualism
 > intersexuality
-29724c35550
+29724c35552
 < jewellery's
 ---
 > jewellery/M
-29870a35697
+29870a35699
 > judgement/MS
-30066c35893
+30066c35895
 < kiddie/M
 ---
 > kiddie/SM
-30262,30263c36089
+30262,30263c36091
 < kraut's
 < kraut/S!
 ---
 > kraut/MS!
-30665a36492
+30665a36494
 > lector/MS
-31031c36858
+31031c36860
 < linguini's
 ---
 > linguini/M
-31034c36861
+31034c36863
 < linguistically
 ---
 > linguistical/Y
-31151,31152c36978
+31151,31152c36980
 < liver's
 < liver/S
 ---
 > liver/MS
-32230c38056
+32230c38058
 < meanie/M
 ---
 > meanie/MS
-32317,32318c38143
+32317,32318c38145
 < megadeath/M
 < megadeaths
 ---
 > megadeath/SM
-32320c38145
+32320c38147
 < megajoules
 ---
 > megajoule/SM
-32329c38154
+32329c38156
 < megapixel/S
 ---
 > megapixel/MS
-32708a38534
+32708a38536
 > might've
-32717a38544
+32717a38546
 > migrator/SM
-32760a38588
+32760a38590
 > millennia
-32777d38604
+32777d38606
 < millionnaire/M
-32934a38762
+32934a38764
 > miscommunication/S
-32991a38820
+32991a38822
 > misjudgement/MS
-33784a39614
+33784a39616
 > must've
-33963c39793
+33963c39795
 < native/MS
 ---
 > native/MSY
-34169,34171c39999,40000
+34169,34171c40001,40002
 < neurone/S
 < neurophysiology
 < neuroscience
 ---
 > neurophysiology/M
 > neuroscience/MS
-34275c40104
+34275c40106
 < nightie/M
 ---
 > nightie/SM
-35104a40934
+35104a40936
 > octopi
-35219d41048
+35219d41050
 < oleomargarin/M
-35226a41056
+35226a41058
 > oligo
-35913c41743
+35913c41745
 < oversize/D
 ---
 > oversize
-36056,36059d41885
+36056,36059d41887
 < paederast/S
 < paediatrician's
 < paediatricians
 < paediatrics/M
-36291a42118
+36291a42120
 > paralyses
-36403d42229
+36403d42231
 < parrakeet/MS
-36449d42274
+36449d42276
 < partizan/SM
-37093a42919
+37093a42921
 > petabyte/MS
-37102c42928
+37102c42930
 < petitioner/M
 ---
 > petitioner/MS
-37264a43091
+37264a43093
 > phosphorylate/DSGN
-37316d43142
+37316d43144
 < phrenetic
-37630a43457
+37630a43459
 > plaintext
-37796a43624
+37796a43626
 > plugin/MS
-37987c43815
+37987c43817
 < polypeptide/S
 ---
 > polypeptide/MS
-38291d44118
+38291d44120
 < practise's
-38451a44279
+38451a44281
 > prejudgement/MS
-38805a44634
+38805a44636
 > profiler/SM
-38835a44665
+38835a44667
 > programmatically
-38891a44722,44723
+38891a44724,44725
 > pronate/DSGN
 > pronator/MS
-38951c44783
+38951c44785
 < proprietorship/M
 ---
 > proprietorship/MS
-39039a44872
+39039a44874
 > provender/M
-39095a44929
+39095a44931
 > pseudorandom/Y
-39564a45399
+39564a45401
 > quinoa
-39873a45709,45710
+39873a45711,45712
 > rasterization/M
 > rasterize/SGDR
-40036a45874
+40036a45876
 > recency
-40140a45979
+40140a45981
 > recurse/DGSV
-40141a45981
+40141a45983
 > recuse/DGS
-40208a46049
+40208a46051
 > refactor/SMDG
-40244d46084
+40244d46086
 < reflexion/SM
-40659d46498
+40659d46500
 < resizing
-40829c46668
+40829c46670
 < reverie/M
 ---
 > reverie/MS
-41415a47255
+41415a47257
 > sabre/MS
-41914c47754
+41914c47756
 < schnaps's
 ---
 > schnaps/M
-41949c47789
+41949c47791
 < schrod's
 ---
 > schrod/SM
-41998a47839
+41998a47841
 > scot-free
-42883,42885c48724
+42883,42885c48726
 < shit's
 < shit/S!
 < shite/S!
 ---
 > shit/MS!
-42887,42888c48726,48727
+42887,42888c48728,48729
 < shithead/S!
 < shitload/!
 ---
 > shithead/MS!
 > shitload/MS!
-42891c48730
+42891c48732
 < shitty/RT!
 ---
 > shitty/TR!
-42976a48816
+42976a48818
 > should've
-43008c48848
+43008c48850
 < showtime
 ---
 > showtime/MS
-43328c49168
+43328c49170
 < size/MGBDRS
 ---
 > size/AMGBDRS
-43724,43726c49564
+43724,43726c49566
 < smoulder's
 < smouldered
 < smoulders
 ---
 > smoulder/GSMD
-43766a49605,49606
+43766a49607,49608
 > snarkily
 > snarky/TR
-44062c49902
+44062c49904
 < sonofabitch
 ---
 > sonofabitch/!
-44346a50187
+44346a50189
 > spelled
-44348a50190
+44348a50192
 > spelt
-44371a50214
+44371a50216
 > spick/S!
-44383c50226
+44383c50228
 < spik/S
 ---
 > spik/S!
-46106a51950
+46106a51952
 > syllabi
-46160c52004
+46160c52006
 < synch/GMD
 ---
 > synch/GMDS
-46167d52010
+46167d52012
 < synchs
-46203,46204c52046,52047
+46203,46204c52048,52049
 < sysadmin/S
 < sysop/S
 ---
 > sysadmin/MS
 > sysop/MS
-46752a52596
+46752a52598
 > terabit/MS
-46753a52598,52599
+46753a52600,52601
 > terahertz/M
 > terapixel/MS
-46817a52664
+46817a52666
 > testcase/MS
-46831a52679
+46831a52681
 > testsuite/MS
-46845a52694
+46845a52696
 > textbox/SM
-46925a52775
+46925a52777
 > theremin/MS
-47455c53305
+47455c53307
 < toolbar
 ---
 > toolbar/MS
-47755a53606
+47755a53608
 > transfect/DSMG
-47774a53626,53627
+47774a53628,53629
 > transgenderism
 > transgene/MS
-47951c53804
+47951c53806
 < triage/M
 ---
 > triage/MG
-48869a54723
+48869a54725
 > unlikeable
-49211c55065
+49211c55067
 < vagina/M
 ---
 > vagina/MS
-49368,49369c55222
+49368,49369c55224
 < velour's
 < velours's
 ---
 > velour/MS
-49478a55332
+49478a55334
 > vertices
-50148a56003
+50148a56005
 > weaponize/DSG
-50260,50261d56114
+50260,50261d56116
 < werwolf/M
 < werwolves
-50728c56581
+50728c56583
 < women
 ---
 > women/M
-50794c56647
+50794c56649
 < wop/S!
 ---
 > wop/MS!
--- a/extensions/spellcheck/locales/en-US/hunspell/en-US.dic
+++ b/extensions/spellcheck/locales/en-US/hunspell/en-US.dic
@@ -1,9 +1,9 @@
-57469
+57471
 0/nm
 0th/pt
 1/n1
 1st/p
 1th/tc
 2/nm
 2nd/p
 2th/tc
@@ -17797,16 +17797,18 @@ anode/MS
 anodize/GDS
 anodyne/MS
 anoint/GDLS
 anointment/M
 anomalous/Y
 anomaly/SM
 anon/S
 anonymity/M
+anonymization/SM
+anonymize/DSG
 anonymous/Y
 anopheles/M
 anorak/MS
 anorectic/SM
 anorexia/M
 anorexic/MS
 another
 answer/BMDRGS
@@ -34331,17 +34333,17 @@ immeasurable
 immeasurably
 immediacies/M
 immediacy/SM
 immediate/YP
 immediateness/M
 immemorial/Y
 immense/RYTP
 immensity/SM
-immerse/XDSGN
+immerse/XDSGNV
 immersible
 immersion/M
 immigrant/SM
 immigrate/DSGNX
 immigration/M
 imminence/M
 imminent/Y
 immiscible
--- a/gfx/2d/DrawTargetCG.cpp
+++ b/gfx/2d/DrawTargetCG.cpp
@@ -1248,16 +1248,17 @@ DrawTargetCG::CopySurface(SourceSurface 
     // Quartz seems to copy A8 surfaces incorrectly if we don't initialize them
     // to transparent first.
     if (mFormat == SurfaceFormat::A8) {
       CGContextClearRect(mCg, flippedRect);
     }
     CGContextDrawImage(mCg, flippedRect, image);
 
     CGContextRestoreGState(mCg);
+    CGImageRelease(image);
   }
 }
 
 void
 DrawTargetCG::DrawSurfaceWithShadow(SourceSurface *aSurface, const Point &aDest, const Color &aColor, const Point &aOffset, Float aSigma, CompositionOp aOperator)
 {
   MarkChanged();
 
--- a/hal/Hal.cpp
+++ b/hal/Hal.cpp
@@ -1227,11 +1227,16 @@ StopDiskSpaceWatcher()
 }
 
 uint32_t
 GetTotalSystemMemory()
 {
   return hal_impl::GetTotalSystemMemory();
 }
 
+uint32_t
+GetTotalSystemMemoryLevel()
+{
+  return hal_impl::GetTotalSystemMemoryLevel();
+}
 
 } // namespace hal
 } // namespace mozilla
--- a/hal/Hal.h
+++ b/hal/Hal.h
@@ -611,16 +611,24 @@ void StopDiskSpaceWatcher();
 
 /**
  * Get total system memory of device being run on in bytes.
  *
  * Returns 0 if we are unable to determine this information from /proc/meminfo.
  */
 uint32_t GetTotalSystemMemory();
 
+/**
+ * Get the level of total system memory on device in MiB.
+ * (round the value up to the next power of two)
+ *
+ * Returns 0 if we are unable to determine this information from /proc/meminfo.
+ */
+uint32_t GetTotalSystemMemoryLevel();
+
 } // namespace MOZ_HAL_NAMESPACE
 } // namespace mozilla
 
 #ifdef MOZ_DEFINED_HAL_NAMESPACE
 # undef MOZ_DEFINED_HAL_NAMESPACE
 # undef MOZ_HAL_NAMESPACE
 #endif
 
--- a/hal/fallback/FallbackMemory.cpp
+++ b/hal/fallback/FallbackMemory.cpp
@@ -11,10 +11,16 @@ namespace mozilla {
 namespace hal_impl {
 
 uint32_t
 GetTotalSystemMemory()
 {
 	return 0;
 }
 
+uint32_t
+GetTotalSystemMemoryLevel()
+{
+	return 0;
+}
+
 } // namespace hal_impl
 } // namespace mozilla
--- a/hal/linux/LinuxMemory.cpp
+++ b/hal/linux/LinuxMemory.cpp
@@ -30,10 +30,42 @@ GetTotalSystemMemory()
     if (fclose(fd) || rv != 1) {
       return 0;
     }
   }
 
   return sTotalMemory * 1024;
 }
 
+uint32_t
+GetTotalSystemMemoryLevel()
+{
+  static uint32_t sTotalMemoryLevel = 1;
+  uint32_t sTotalMemory;
+  static bool sTotalMemoryObtained = false;
+
+  if (!sTotalMemoryObtained) {
+    sTotalMemoryObtained = true;
+
+    FILE* fd = fopen("/proc/meminfo", "r");
+    if (!fd) {
+      return 0;
+    }
+
+    int rv = fscanf(fd, "MemTotal: %i kB", &sTotalMemory);
+
+    if (fclose(fd) || rv != 1) {
+      return 0;
+    }
+
+    // From KB to MiB
+    sTotalMemory /= 1024;
+
+    while (sTotalMemoryLevel <= sTotalMemory) {
+      sTotalMemoryLevel *= 2;
+    }
+  }
+
+  return sTotalMemoryLevel;
+}
+
 } // namespace hal_impl
 } // namespace mozilla
--- a/js/jsd/jsd_stak.cpp
+++ b/js/jsd/jsd_stak.cpp
@@ -265,17 +265,18 @@ jsd_GetCallObjectForStackFrame(JSDContex
 {
     JSObject* obj;
     JSDValue* jsdval = nullptr;
 
     JSD_LOCK_THREADSTATES(jsdc);
 
     if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
     {
-        obj = jsdframe->frame.callObject(jsdthreadstate->context);
+        AutoPushJSContext cx(jsdthreadstate->context);
+        obj = jsdframe->frame.callObject(cx);
         if(obj)                                                             
             jsdval = JSD_NewValue(jsdc, OBJECT_TO_JSVAL(obj));              
     }
 
     JSD_UNLOCK_THREADSTATES(jsdc);
 
     return jsdval;
 }
@@ -287,19 +288,18 @@ jsd_GetScopeChainForStackFrame(JSDContex
 {
     JS::RootedObject obj(jsdthreadstate->context);
     JSDValue* jsdval = nullptr;
 
     JSD_LOCK_THREADSTATES(jsdc);
 
     if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
     {
-        JS_BeginRequest(jsdthreadstate->context);
-        obj = jsdframe->frame.scopeChain(jsdthreadstate->context);
-        JS_EndRequest(jsdthreadstate->context);
+        AutoPushJSContext cx(jsdthreadstate->context);
+        obj = jsdframe->frame.scopeChain(cx);
         if(obj)
             jsdval = JSD_NewValue(jsdc, OBJECT_TO_JSVAL(obj));
     }
 
     JSD_UNLOCK_THREADSTATES(jsdc);
 
     return jsdval;
 }
@@ -311,19 +311,18 @@ jsd_GetThisForStackFrame(JSDContext* jsd
 {
     JSDValue* jsdval = nullptr;
     JSD_LOCK_THREADSTATES(jsdc);
 
     if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) )
     {
         bool ok;
         JS::RootedValue thisval(jsdthreadstate->context);
-        JS_BeginRequest(jsdthreadstate->context);
-        ok = jsdframe->frame.getThisValue(jsdthreadstate->context, &thisval);
-        JS_EndRequest(jsdthreadstate->context);
+        AutoPushJSContext cx(jsdthreadstate->context);
+        ok = jsdframe->frame.getThisValue(cx, &thisval);
         if(ok)
             jsdval = JSD_NewValue(jsdc, thisval);
     }
 
     JSD_UNLOCK_THREADSTATES(jsdc);
     return jsdval;
 }
 
@@ -468,27 +467,25 @@ JSString*
 jsd_ValToStringInStackFrame(JSDContext* jsdc, 
                             JSDThreadState* jsdthreadstate,
                             JSDStackFrameInfo* jsdframe,
                             jsval val)
 {
     bool valid;
     JSString* retval;
     JSExceptionState* exceptionState;
-    JSContext* cx;
 
     JSD_LOCK_THREADSTATES(jsdc);
     valid = jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe);
     JSD_UNLOCK_THREADSTATES(jsdc);
 
     if( ! valid )
         return nullptr;
 
-    cx = jsdthreadstate->context;
-    MOZ_ASSERT(cx);
+    AutoPushJSContext cx(jsdthreadstate->context);
 
     JS::RootedValue v(cx, val);
     exceptionState = JS_SaveExceptionState(cx);
     retval = JS::ToString(cx, v);
     JS_RestoreExceptionState(cx, exceptionState);
 
     return retval;
 }
--- a/js/public/GCAPI.h
+++ b/js/public/GCAPI.h
@@ -460,17 +460,17 @@ ExposeGCThingToActiveJS(void *thing, JSG
 
     shadow::Runtime *rt = js::gc::GetGCThingRuntime(thing);
 #ifdef JSGC_GENERATIONAL
     /*
      * GC things residing in the nursery cannot be gray: they have no mark bits.
      * All live objects in the nursery are moved to tenured at the beginning of
      * each GC slice, so the gray marker never sees nursery things.
      */
-    if (js::gc::IsInsideNursery(rt, thing))
+    if (js::gc::IsInsideNursery((js::gc::Cell *)thing))
         return;
 #endif
     if (IsIncrementalBarrierNeededOnGCThing(rt, thing, kind))
         IncrementalReferenceBarrier(thing, kind);
     else if (GCThingIsMarkedGray(thing))
         UnmarkGrayGCThingRecursively(thing, kind);
 }
 
@@ -493,17 +493,17 @@ ExposeObjectToActiveJS(JSObject *obj)
 static MOZ_ALWAYS_INLINE void
 MarkGCThingAsLive(JSRuntime *rt_, void *thing, JSGCTraceKind kind)
 {
     shadow::Runtime *rt = shadow::Runtime::asShadowRuntime(rt_);
 #ifdef JSGC_GENERATIONAL
     /*
      * Any object in the nursery will not be freed during any GC running at that time.
      */
-    if (js::gc::IsInsideNursery(rt, thing))
+    if (js::gc::IsInsideNursery((js::gc::Cell *)thing))
         return;
 #endif
     if (IsIncrementalBarrierNeededOnGCThing(rt, thing, kind))
         IncrementalReferenceBarrier(thing, kind);
 }
 
 static MOZ_ALWAYS_INLINE void
 MarkStringAsLive(Zone *zone, JSString *string)
--- a/js/public/HeapAPI.h
+++ b/js/public/HeapAPI.h
@@ -56,24 +56,75 @@ static const uint32_t GRAY = 1;
 
 /*
  * Constants used to indicate whether a chunk is part of the tenured heap or the
  * nusery.
  */
 const uintptr_t ChunkLocationNursery = 0;
 const uintptr_t ChunkLocationTenuredHeap = 1;
 
+#ifdef JS_DEBUG
+/* When downcasting, ensure we are actually the right type. */
+extern JS_FRIEND_API(void)
+AssertGCThingHasType(js::gc::Cell *cell, JSGCTraceKind kind);
+#else
+inline void
+AssertGCThingHasType(js::gc::Cell *cell, JSGCTraceKind kind) {}
+#endif
+
 } /* namespace gc */
 } /* namespace js */
 
 namespace JS {
 struct Zone;
-} /* namespace JS */
+
+/*
+ * We cannot expose the class hierarchy: the implementation is hidden. Instead
+ * we provide cast functions with strong debug-mode assertions.
+ */
+static MOZ_ALWAYS_INLINE js::gc::Cell *
+AsCell(JSObject *obj)
+{
+    js::gc::Cell *cell = reinterpret_cast<js::gc::Cell *>(obj);
+    AssertGCThingHasType(cell, JSTRACE_OBJECT);
+    return cell;
+}
+
+static MOZ_ALWAYS_INLINE js::gc::Cell *
+AsCell(JSFunction *fun)
+{
+    js::gc::Cell *cell = reinterpret_cast<js::gc::Cell *>(fun);
+    AssertGCThingHasType(cell, JSTRACE_OBJECT);
+    return cell;
+}
 
-namespace JS {
+static MOZ_ALWAYS_INLINE js::gc::Cell *
+AsCell(JSString *str)
+{
+    js::gc::Cell *cell = reinterpret_cast<js::gc::Cell *>(str);
+    AssertGCThingHasType(cell, JSTRACE_STRING);
+    return cell;
+}
+
+static MOZ_ALWAYS_INLINE js::gc::Cell *
+AsCell(JSFlatString *flat)
+{
+    js::gc::Cell *cell = reinterpret_cast<js::gc::Cell *>(flat);
+    AssertGCThingHasType(cell, JSTRACE_STRING);
+    return cell;
+}
+
+static MOZ_ALWAYS_INLINE js::gc::Cell *
+AsCell(JSScript *script)
+{
+    js::gc::Cell *cell = reinterpret_cast<js::gc::Cell *>(script);
+    AssertGCThingHasType(cell, JSTRACE_SCRIPT);
+    return cell;
+}
+
 namespace shadow {
 
 struct ArenaHeader
 {
     JS::Zone *zone;
 };
 
 struct Zone
@@ -160,26 +211,16 @@ static MOZ_ALWAYS_INLINE JS::shadow::Are
 GetGCThingArena(void *thing)
 {
     uintptr_t addr = uintptr_t(thing);
     addr &= ~js::gc::ArenaMask;
     return reinterpret_cast<JS::shadow::ArenaHeader *>(addr);
 }
 
 MOZ_ALWAYS_INLINE bool
-IsInsideNursery(const JS::shadow::Runtime *runtime, const void *p)
-{
-#ifdef JSGC_GENERATIONAL
-    return uintptr_t(p) >= runtime->gcNurseryStart_ && uintptr_t(p) < runtime->gcNurseryEnd_;
-#else
-    return false;
-#endif
-}
-
-MOZ_ALWAYS_INLINE bool
 IsInsideNursery(const js::gc::Cell *cell)
 {
 #ifdef JSGC_GENERATIONAL
     if (!cell)
         return false;
     uintptr_t addr = uintptr_t(cell);
     addr &= ~js::gc::ChunkMask;
     addr |= js::gc::ChunkLocationOffset;
@@ -215,18 +256,17 @@ static MOZ_ALWAYS_INLINE bool
 GCThingIsMarkedGray(void *thing)
 {
 #ifdef JSGC_GENERATIONAL
     /*
      * GC things residing in the nursery cannot be gray: they have no mark bits.
      * All live objects in the nursery are moved to tenured at the beginning of
      * each GC slice, so the gray marker never sees nursery things.
      */
-    JS::shadow::Runtime *rt = js::gc::GetGCThingRuntime(thing);
-    if (js::gc::IsInsideNursery(rt, thing))
+    if (js::gc::IsInsideNursery((js::gc::Cell *)thing))
         return false;
 #endif
     uintptr_t *word, mask;
     js::gc::GetGCThingMarkWordAndMask(thing, js::gc::GRAY, &word, &mask);
     return *word & mask;
 }
 
 static MOZ_ALWAYS_INLINE bool
--- a/js/public/Id.h
+++ b/js/public/Id.h
@@ -112,17 +112,17 @@ JSID_TO_OBJECT(jsid id)
 }
 
 static MOZ_ALWAYS_INLINE jsid
 OBJECT_TO_JSID(JSObject *obj)
 {
     jsid id;
     MOZ_ASSERT(obj != nullptr);
     MOZ_ASSERT(((size_t)obj & JSID_TYPE_MASK) == 0);
-    JS_ASSERT(!js::gc::IsInsideNursery(js::gc::GetGCThingRuntime(obj), obj));
+    JS_ASSERT(!js::gc::IsInsideNursery(JS::AsCell(obj)));
     JSID_BITS(id) = ((size_t)obj | JSID_TYPE_OBJECT);
     return id;
 }
 
 static MOZ_ALWAYS_INLINE bool
 JSID_IS_GCTHING(jsid id)
 {
     return JSID_IS_STRING(id) || JSID_IS_OBJECT(id);
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -2078,27 +2078,29 @@ AC_LANG_CPLUSPLUS
 MOZ_CXX11
 
 dnl Check for .hidden assembler directive and visibility attribute.
 dnl Borrowed from glibc configure.in
 dnl ===============================================================
 if test "$GNU_CC" -a "$OS_TARGET" != WINNT; then
   AC_DEFINE(HAVE_VISIBILITY_HIDDEN_ATTRIBUTE)
   AC_DEFINE(HAVE_VISIBILITY_ATTRIBUTE)
-  if test -n "$gonkdir"; then
-    visibility_target=Gonk
-  else
-    visibility_target=$OS_TARGET
-  fi
-  case "$visibility_target" in
-  Darwin|Gonk)
+  case "$OS_TARGET" in
+  Darwin)
     VISIBILITY_FLAGS='-fvisibility=hidden'
     ;;
   *)
-    VISIBILITY_FLAGS='-I$(DIST)/system_wrappers -include $(topsrcdir)/config/gcc_hidden.h'
+    case $GCC_VERSION in
+    4.4*)
+      VISIBILITY_FLAGS='-I$(DIST)/system_wrappers -include $(topsrcdir)/config/gcc_hidden_dso_handle.h'
+      ;;
+    *)
+      VISIBILITY_FLAGS='-I$(DIST)/system_wrappers -include $(topsrcdir)/config/gcc_hidden.h'
+      ;;
+    esac
     WRAP_SYSTEM_INCLUDES=1
     ;;
   esac
 fi         # GNU_CC
 
 # visibility hidden flag for Sun Studio on Solaris
 if test "$SOLARIS_SUNPRO_CC"; then
 VISIBILITY_FLAGS='-xldscope=hidden'
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -1084,18 +1084,17 @@ Cell::isAligned() const
 {
     return Arena::isAligned(address(), arenaHeader()->getThingSize());
 }
 
 bool
 Cell::isTenured() const
 {
 #ifdef JSGC_GENERATIONAL
-    JS::shadow::Runtime *rt = js::gc::GetGCThingRuntime(this);
-    return !IsInsideNursery(rt, this);
+    return !IsInsideNursery(this);
 #endif
     return true;
 }
 #endif
 
 inline uintptr_t
 Cell::address() const
 {
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -167,17 +167,17 @@ CheckMarkedThing(JSTracer *trc, T **thin
         MOZ_ReportAssertionFailure(msgbuf, __FILE__, __LINE__);
         MOZ_CRASH();
     }
 #endif
     JS_ASSERT(*thingp);
 
 #ifdef DEBUG
     /* This function uses data that's not available in the nursery. */
-    if (IsInsideNursery(trc->runtime(), thing))
+    if (IsInsideNursery(thing))
         return;
 
     /*
      * Permanent atoms are not associated with this runtime, but will be ignored
      * during marking.
      */
     if (ThingIsPermanentAtom(thing))
         return;
@@ -230,17 +230,17 @@ MarkInternal(JSTracer *trc, T **thingp)
 
     if (!trc->callback) {
         /*
          * We may mark a Nursery thing outside the context of the
          * MinorCollectionTracer because of a pre-barrier. The pre-barrier is
          * not needed in this case because we perform a minor collection before
          * each incremental slice.
          */
-        if (IsInsideNursery(trc->runtime(), thing))
+        if (IsInsideNursery(thing))
             return;
 
         /*
          * Don't mark permanent atoms, as they may be associated with another
          * runtime. Note that PushMarkStack() also checks this, but the tests
          * and maybeAlive write below should only be done on the main thread.
          */
         if (ThingIsPermanentAtom(thing))
@@ -352,19 +352,20 @@ namespace gc {
 
 template <typename T>
 static bool
 IsMarked(T **thingp)
 {
     JS_ASSERT(thingp);
     JS_ASSERT(*thingp);
 #ifdef JSGC_GENERATIONAL
-    Nursery &nursery = (*thingp)->runtimeFromMainThread()->gc.nursery;
-    if (nursery.isInside(*thingp))
+    if (IsInsideNursery(*thingp)) {
+        Nursery &nursery = (*thingp)->runtimeFromMainThread()->gc.nursery;
         return nursery.getForwardedPointer(thingp);
+    }
 #endif
     Zone *zone = (*thingp)->tenuredZone();
     if (!zone->isCollecting() || zone->isGCFinished())
         return true;
     return (*thingp)->isMarked();
 }
 
 template <typename T>
@@ -378,19 +379,19 @@ IsAboutToBeFinalized(T **thingp)
     JSRuntime *rt = thing->runtimeFromAnyThread();
 
     /* Permanent atoms are never finalized by non-owning runtimes. */
     if (ThingIsPermanentAtom(thing) && !TlsPerThreadData.get()->associatedWith(rt))
         return false;
 
 #ifdef JSGC_GENERATIONAL
     Nursery &nursery = rt->gc.nursery;
-    JS_ASSERT_IF(!rt->isHeapMinorCollecting(), !nursery.isInside(thing));
+    JS_ASSERT_IF(!rt->isHeapMinorCollecting(), !IsInsideNursery(thing));
     if (rt->isHeapMinorCollecting()) {
-        if (nursery.isInside(thing))
+        if (IsInsideNursery(thing))
             return !nursery.getForwardedPointer(thingp);
         return false;
     }
 #endif
 
     if (!thing->tenuredZone()->isGCSweeping())
         return false;
 
@@ -407,17 +408,17 @@ IsAboutToBeFinalized(T **thingp)
 }
 
 template <typename T>
 T *
 UpdateIfRelocated(JSRuntime *rt, T **thingp)
 {
     JS_ASSERT(thingp);
 #ifdef JSGC_GENERATIONAL
-    if (*thingp && rt->isHeapMinorCollecting() && rt->gc.nursery.isInside(*thingp))
+    if (*thingp && rt->isHeapMinorCollecting() && IsInsideNursery(*thingp))
         rt->gc.nursery.getForwardedPointer(thingp);
 #endif
     return *thingp;
 }
 
 #define DeclMarkerImpl(base, type)                                                                \
 void                                                                                              \
 Mark##base(JSTracer *trc, BarrieredBase<type*> *thing, const char *name)                          \
@@ -781,17 +782,17 @@ static bool
 ShouldMarkCrossCompartment(JSTracer *trc, JSObject *src, Cell *cell)
 {
     if (!IS_GC_MARKING_TRACER(trc))
         return true;
 
     uint32_t color = AsGCMarker(trc)->getMarkColor();
     JS_ASSERT(color == BLACK || color == GRAY);
 
-    if (IsInsideNursery(trc->runtime(), cell)) {
+    if (IsInsideNursery(cell)) {
         JS_ASSERT(color == BLACK);
         return false;
     }
 
     JS::Zone *zone = cell->tenuredZone();
     if (color == BLACK) {
         /*
          * Having black->gray edges violates our promise to the cycle
@@ -871,121 +872,121 @@ gc::IsCellAboutToBeFinalized(Cell **thin
 #define JS_COMPARTMENT_ASSERT_STR(rt, thing)                            \
     JS_ASSERT((thing)->zone()->isGCMarking() ||                         \
               (rt)->isAtomsZone((thing)->zone()));
 
 static void
 PushMarkStack(GCMarker *gcmarker, ObjectImpl *thing)
 {
     JS_COMPARTMENT_ASSERT(gcmarker->runtime(), thing);
-    JS_ASSERT(!IsInsideNursery(gcmarker->runtime(), thing));
+    JS_ASSERT(!IsInsideNursery(thing));
 
     if (thing->markIfUnmarked(gcmarker->getMarkColor()))
         gcmarker->pushObject(thing);
 }
 
 /*
  * PushMarkStack for BaseShape unpacks its children directly onto the mark
  * stack. For a pre-barrier between incremental slices, this may result in
  * objects in the nursery getting pushed onto the mark stack. It is safe to
  * ignore these objects because they will be marked by the matching
  * post-barrier during the minor GC at the start of each incremental slice.
  */
 static void
 MaybePushMarkStackBetweenSlices(GCMarker *gcmarker, JSObject *thing)
 {
-    JSRuntime *rt = gcmarker->runtime();
+    DebugOnly<JSRuntime *> rt = gcmarker->runtime();
     JS_COMPARTMENT_ASSERT(rt, thing);
-    JS_ASSERT_IF(rt->isHeapBusy(), !IsInsideNursery(rt, thing));
+    JS_ASSERT_IF(rt->isHeapBusy(), !IsInsideNursery(thing));
 
-    if (!IsInsideNursery(rt, thing) && thing->markIfUnmarked(gcmarker->getMarkColor()))
+    if (!IsInsideNursery(thing) && thing->markIfUnmarked(gcmarker->getMarkColor()))
         gcmarker->pushObject(thing);
 }
 
 static void
 PushMarkStack(GCMarker *gcmarker, JSFunction *thing)
 {
     JS_COMPARTMENT_ASSERT(gcmarker->runtime(), thing);
-    JS_ASSERT(!IsInsideNursery(gcmarker->runtime(), thing));
+    JS_ASSERT(!IsInsideNursery(thing));
 
     if (thing->markIfUnmarked(gcmarker->getMarkColor()))
         gcmarker->pushObject(thing);
 }
 
 static void
 PushMarkStack(GCMarker *gcmarker, types::TypeObject *thing)
 {
     JS_COMPARTMENT_ASSERT(gcmarker->runtime(), thing);
-    JS_ASSERT(!IsInsideNursery(gcmarker->runtime(), thing));
+    JS_ASSERT(!IsInsideNursery(thing));
 
     if (thing->markIfUnmarked(gcmarker->getMarkColor()))
         gcmarker->pushType(thing);
 }
 
 static void
 PushMarkStack(GCMarker *gcmarker, JSScript *thing)
 {
     JS_COMPARTMENT_ASSERT(gcmarker->runtime(), thing);
-    JS_ASSERT(!IsInsideNursery(gcmarker->runtime(), thing));
+    JS_ASSERT(!IsInsideNursery(thing));
 
     /*
      * We mark scripts directly rather than pushing on the stack as they can
      * refer to other scripts only indirectly (like via nested functions) and
      * we cannot get to deep recursion.
      */
     if (thing->markIfUnmarked(gcmarker->getMarkColor()))
         MarkChildren(gcmarker, thing);
 }
 
 static void
 PushMarkStack(GCMarker *gcmarker, LazyScript *thing)
 {
     JS_COMPARTMENT_ASSERT(gcmarker->runtime(), thing);
-    JS_ASSERT(!IsInsideNursery(gcmarker->runtime(), thing));
+    JS_ASSERT(!IsInsideNursery(thing));
 
     /*
      * We mark lazy scripts directly rather than pushing on the stack as they
      * only refer to normal scripts and to strings, and cannot recurse.
      */
     if (thing->markIfUnmarked(gcmarker->getMarkColor()))
         MarkChildren(gcmarker, thing);
 }
 
 static void
 ScanShape(GCMarker *gcmarker, Shape *shape);
 
 static void
 PushMarkStack(GCMarker *gcmarker, Shape *thing)
 {
     JS_COMPARTMENT_ASSERT(gcmarker->runtime(), thing);
-    JS_ASSERT(!IsInsideNursery(gcmarker->runtime(), thing));
+    JS_ASSERT(!IsInsideNursery(thing));
 
     /* We mark shapes directly rather than pushing on the stack. */
     if (thing->markIfUnmarked(gcmarker->getMarkColor()))
         ScanShape(gcmarker, thing);
 }
 
 static void
 PushMarkStack(GCMarker *gcmarker, jit::JitCode *thing)
 {
     JS_COMPARTMENT_ASSERT(gcmarker->runtime(), thing);
-    JS_ASSERT(!IsInsideNursery(gcmarker->runtime(), thing));
+    JS_ASSERT(!IsInsideNursery(thing));
 
     if (thing->markIfUnmarked(gcmarker->getMarkColor()))
         gcmarker->pushJitCode(thing);
 }
 
 static inline void
 ScanBaseShape(GCMarker *gcmarker, BaseShape *base);
 
 static void
 PushMarkStack(GCMarker *gcmarker, BaseShape *thing)
 {
     JS_COMPARTMENT_ASSERT(gcmarker->runtime(), thing);
-    JS_ASSERT(!IsInsideNursery(gcmarker->runtime(), thing));
+    JS_ASSERT(!IsInsideNursery(thing));
 
     /* We mark base shapes directly rather than pushing on the stack. */
     if (thing->markIfUnmarked(gcmarker->getMarkColor()))
         ScanBaseShape(gcmarker, thing);
 }
 
 static void
 ScanShape(GCMarker *gcmarker, Shape *shape)
@@ -1757,17 +1758,17 @@ UnmarkGrayChildren(JSTracer *trc, void *
          * If we run out of stack, we take a more drastic measure: require that
          * we GC again before the next CC.
          */
         trc->runtime()->gc.grayBitsValid = false;
         return;
     }
 
     UnmarkGrayTracer *tracer = static_cast<UnmarkGrayTracer *>(trc);
-    if (!IsInsideNursery(trc->runtime(), thing)) {
+    if (!IsInsideNursery(static_cast<Cell *>(thing))) {
         if (!JS::GCThingIsMarkedGray(thing))
             return;
 
         UnmarkGrayGCThing(thing);
         tracer->unmarkedAny = true;
     }
 
     /*
@@ -1804,17 +1805,17 @@ UnmarkGrayChildren(JSTracer *trc, void *
 JS_FRIEND_API(bool)
 JS::UnmarkGrayGCThingRecursively(void *thing, JSGCTraceKind kind)
 {
     JS_ASSERT(kind != JSTRACE_SHAPE);
 
     JSRuntime *rt = static_cast<Cell *>(thing)->runtimeFromMainThread();
 
     bool unmarkedArg = false;
-    if (!IsInsideNursery(rt, thing)) {
+    if (!IsInsideNursery(static_cast<Cell *>(thing))) {
         if (!JS::GCThingIsMarkedGray(thing))
             return false;
 
         UnmarkGrayGCThing(thing);
         unmarkedArg = true;
     }
 
     UnmarkGrayTracer trc(rt);
--- a/js/src/gc/Nursery-inl.h
+++ b/js/src/gc/Nursery-inl.h
@@ -67,17 +67,17 @@ class RelocationOverlay
 } /* namespace gc */
 } /* namespace js */
 
 template <typename T>
 MOZ_ALWAYS_INLINE bool
 js::Nursery::getForwardedPointer(T **ref)
 {
     JS_ASSERT(ref);
-    JS_ASSERT(isInside(*ref));
+    JS_ASSERT(isInside((void *)*ref));
     const gc::RelocationOverlay *overlay = reinterpret_cast<const gc::RelocationOverlay *>(*ref);
     if (!overlay->isForwarded())
         return false;
     /* This static cast from Cell* restricts T to valid (GC thing) types. */
     *ref = static_cast<T *>(overlay->forwardingAddress());
     return true;
 }
 
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -43,29 +43,26 @@ using namespace mozilla;
  * Print timing information for minor GCs that take longer than this time in microseconds.
  */
 static int64_t GCReportThreshold = INT64_MAX;
 #endif
 
 bool
 js::Nursery::init()
 {
-    JS_ASSERT(start() == 0);
-
     if (!hugeSlots.init())
         return false;
 
     void *heap = runtime()->gc.pageAllocator.mapAlignedPages(NurserySize, Alignment);
     if (!heap)
         return false;
 
-    JSRuntime *rt = runtime();
-    rt->gcNurseryStart_ = uintptr_t(heap);
+    heapStart_ = uintptr_t(heap);
     currentStart_ = start();
-    rt->gcNurseryEnd_ = chunk(LastNurseryChunk).end();
+    heapEnd_ = chunk(LastNurseryChunk).end();
     numActiveChunks_ = 1;
     JS_POISON(heap, JS_FRESH_NURSERY_PATTERN, NurserySize);
     setCurrentChunk(0);
     updateDecommittedRegion();
 
 #ifdef PROFILE_NURSERY
     char *env = getenv("JS_MINORGC_TIME");
     if (env)
@@ -190,17 +187,17 @@ js::Nursery::allocate(size_t size)
 
 /* Internally, this function is used to allocate elements as well as slots. */
 HeapSlot *
 js::Nursery::allocateSlots(JSContext *cx, JSObject *obj, uint32_t nslots)
 {
     JS_ASSERT(obj);
     JS_ASSERT(nslots > 0);
 
-    if (!isInside(obj))
+    if (!IsInsideNursery(obj))
         return cx->pod_malloc<HeapSlot>(nslots);
 
     if (nslots > MaxNurserySlots)
         return allocateHugeSlots(cx, nslots);
 
     size_t size = sizeof(HeapSlot) * nslots;
     HeapSlot *slots = static_cast<HeapSlot *>(allocate(size));
     if (slots)
@@ -218,17 +215,17 @@ js::Nursery::allocateElements(JSContext 
 
 HeapSlot *
 js::Nursery::reallocateSlots(JSContext *cx, JSObject *obj, HeapSlot *oldSlots,
                              uint32_t oldCount, uint32_t newCount)
 {
     size_t oldSize = oldCount * sizeof(HeapSlot);
     size_t newSize = newCount * sizeof(HeapSlot);
 
-    if (!isInside(obj))
+    if (!IsInsideNursery(obj))
         return static_cast<HeapSlot *>(cx->realloc_(oldSlots, oldSize, newSize));
 
     if (!isInside(oldSlots)) {
         HeapSlot *newSlots = static_cast<HeapSlot *>(cx->realloc_(oldSlots, oldSize, newSize));
         if (oldSlots != newSlots) {
             hugeSlots.remove(oldSlots);
             /* If this put fails, we will only leak the slots. */
             (void)hugeSlots.put(newSlots);
@@ -270,17 +267,17 @@ js::Nursery::allocateHugeSlots(JSContext
     /* If this put fails, we will only leak the slots. */
     (void)hugeSlots.put(slots);
     return slots;
 }
 
 void
 js::Nursery::notifyInitialSlots(Cell *cell, HeapSlot *slots)
 {
-    if (isInside(cell) && !isInside(slots)) {
+    if (IsInsideNursery(cell) && !isInside(slots)) {
         /* If this put fails, we will only leak the slots. */
         (void)hugeSlots.put(slots);
     }
 }
 
 namespace js {
 namespace gc {
 
@@ -362,17 +359,17 @@ class MinorCollectionTracer : public JST
 
 static AllocKind
 GetObjectAllocKindForCopy(JSRuntime *rt, JSObject *obj)
 {
     if (obj->is<ArrayObject>()) {
         JS_ASSERT(obj->numFixedSlots() == 0);
 
         /* Use minimal size object if we are just going to copy the pointer. */
-        if (!IsInsideNursery(rt, (void *)obj->getElementsHeader()))
+        if (!rt->gc.nursery.isInside(obj->getElementsHeader()))
             return FINALIZE_OBJECT0_BACKGROUND;
 
         size_t nelements = obj->getDenseCapacity();
         return GetBackgroundAllocKind(GetGCArrayKind(nelements));
     }
 
     if (obj->is<JSFunction>())
         return obj->as<JSFunction>().getAllocKind();
@@ -529,17 +526,17 @@ js::Nursery::markSlots(MinorCollectionTr
 
 MOZ_ALWAYS_INLINE void
 js::Nursery::markSlot(MinorCollectionTracer *trc, HeapSlot *slotp)
 {
     if (!slotp->isObject())
         return;
 
     JSObject *obj = &slotp->toObject();
-    if (!isInside(obj))
+    if (!IsInsideNursery(obj))
         return;
 
     if (getForwardedPointer(&obj)) {
         slotp->unsafeGet()->setObject(*obj);
         return;
     }
 
     JSObject *tenured = static_cast<JSObject*>(moveToTenured(trc, obj));
@@ -682,17 +679,17 @@ js::Nursery::moveElementsToTenured(JSObj
     return nslots * sizeof(HeapSlot);
 }
 
 static bool
 ShouldMoveToTenured(MinorCollectionTracer *trc, void **thingp)
 {
     Cell *cell = static_cast<Cell *>(*thingp);
     Nursery &nursery = *trc->nursery;
-    return !nursery.isInside(thingp) && nursery.isInside(cell) &&
+    return !nursery.isInside(thingp) && IsInsideNursery(cell) &&
            !nursery.getForwardedPointer(thingp);
 }
 
 /* static */ void
 js::Nursery::MinorGCCallback(JSTracer *jstrc, void **thingp, JSGCTraceKind kind)
 {
     MinorCollectionTracer *trc = static_cast<MinorCollectionTracer *>(jstrc);
     if (ShouldMoveToTenured(trc, thingp))
--- a/js/src/gc/Nursery.h
+++ b/js/src/gc/Nursery.h
@@ -56,35 +56,41 @@ class Nursery
     static const int LastNurseryChunk = NumNurseryChunks - 1;
     static const size_t Alignment = gc::ChunkSize;
     static const size_t ChunkShift = gc::ChunkShift;
     static const size_t NurserySize = gc::ChunkSize * NumNurseryChunks;
 
     explicit Nursery(JSRuntime *rt)
       : runtime_(rt),
         position_(0),
+        heapStart_(0),
+        heapEnd_(0),
         currentStart_(0),
         currentEnd_(0),
         currentChunk_(0),
         numActiveChunks_(0)
     {}
     ~Nursery();
 
     bool init();
 
     void enable();
     void disable();
     bool isEnabled() const { return numActiveChunks_ != 0; }
 
     /* Return true if no allocations have been made since the last collection. */
     bool isEmpty() const;
 
-    template <typename T>
-    MOZ_ALWAYS_INLINE bool isInside(const T *p) const {
-        return gc::IsInsideNursery((JS::shadow::Runtime *)runtime_, p);
+    /*
+     * Check whether an arbitrary pointer is within the nursery. This is
+     * slower than IsInsideNursery(Cell*), but works on all types of pointers.
+     */
+    MOZ_ALWAYS_INLINE bool isInside(gc::Cell *cellp) const MOZ_DELETE;
+    MOZ_ALWAYS_INLINE bool isInside(const void *p) const {
+        return uintptr_t(p) >= heapStart_ && uintptr_t(p) < heapEnd_;
     }
 
     /*
      * Allocate and return a pointer to a new GC object with its |slots|
      * pointer pre-filled. Returns nullptr if the Nursery is full.
      */
     JSObject *allocateObject(JSContext *cx, size_t size, size_t numDynamic);
 
@@ -137,23 +143,21 @@ class Nursery
         size_t total = 0;
         for (HugeSlotsSet::Range r = hugeSlots.all(); !r.empty(); r.popFront())
             total += mallocSizeOf(r.front());
         total += hugeSlots.sizeOfExcludingThis(mallocSizeOf);
         return total;
     }
 
     MOZ_ALWAYS_INLINE uintptr_t start() const {
-        JS_ASSERT(runtime_);
-        return ((JS::shadow::Runtime *)runtime_)->gcNurseryStart_;
+        return heapStart_;
     }
 
     MOZ_ALWAYS_INLINE uintptr_t heapEnd() const {
-        JS_ASSERT(runtime_);
-        return ((JS::shadow::Runtime *)runtime_)->gcNurseryEnd_;
+        return heapEnd_;
     }
 
 #ifdef JS_GC_ZEAL
     /*
      * In debug and zeal builds, these bytes indicate the state of an unused
      * segment of nursery-allocated memory.
      */
     void enterZealMode() {
@@ -178,16 +182,20 @@ class Nursery
      * inline the isInsideNursery check into embedder code. Use the start()
      * and heapEnd() functions to access these values.
      */
     JSRuntime *runtime_;
 
     /* Pointer to the first unallocated byte in the nursery. */
     uintptr_t position_;
 
+    /* Pointer to first and last address of the total nursery allocation. */
+    uintptr_t heapStart_;
+    uintptr_t heapEnd_;
+
     /* Pointer to the logical start of the Nursery. */
     uintptr_t currentStart_;
 
     /* Pointer to the last byte of space in the current chunk. */
     uintptr_t currentEnd_;
 
     /* The index of the chunk that is currently being allocated from. */
     int currentChunk_;
--- a/js/src/gc/StoreBuffer.cpp
+++ b/js/src/gc/StoreBuffer.cpp
@@ -21,17 +21,17 @@ using mozilla::ReentrancyGuard;
 
 /*** Edges ***/
 
 void
 StoreBuffer::SlotsEdge::mark(JSTracer *trc)
 {
     JSObject *obj = object();
 
-    if (trc->runtime()->gc.nursery.isInside(obj))
+    if (IsInsideNursery(obj))
         return;
 
     if (!obj->isNative()) {
         const Class *clasp = obj->getClass();
         if (clasp)
             clasp->trace(trc, obj);
         return;
     }
--- a/js/src/gc/StoreBuffer.h
+++ b/js/src/gc/StoreBuffer.h
@@ -239,17 +239,17 @@ class StoreBuffer
     {
         Cell **edge;
 
         explicit CellPtrEdge(Cell **v) : edge(v) {}
         bool operator==(const CellPtrEdge &other) const { return edge == other.edge; }
         bool operator!=(const CellPtrEdge &other) const { return edge != other.edge; }
 
         bool maybeInRememberedSet(const Nursery &nursery) const {
-            JS_ASSERT(nursery.isInside(*edge));
+            JS_ASSERT(IsInsideNursery(*edge));
             return !nursery.isInside(edge);
         }
 
         void mark(JSTracer *trc);
 
         CellPtrEdge tagged() const { return CellPtrEdge((Cell **)(uintptr_t(edge) | 1)); }
         CellPtrEdge untagged() const { return CellPtrEdge((Cell **)(uintptr_t(edge) & ~1)); }
         bool isTagged() const { return bool(uintptr_t(edge) & 1); }
@@ -260,20 +260,20 @@ class StoreBuffer
     struct ValueEdge
     {
         JS::Value *edge;
 
         explicit ValueEdge(JS::Value *v) : edge(v) {}
         bool operator==(const ValueEdge &other) const { return edge == other.edge; }
         bool operator!=(const ValueEdge &other) const { return edge != other.edge; }
 
-        void *deref() const { return edge->isGCThing() ? edge->toGCThing() : nullptr; }
+        Cell *deref() const { return edge->isGCThing() ? static_cast<Cell *>(edge->toGCThing()) : nullptr; }
 
         bool maybeInRememberedSet(const Nursery &nursery) const {
-            JS_ASSERT(nursery.isInside(deref()));
+            JS_ASSERT(IsInsideNursery(deref()));
             return !nursery.isInside(edge);
         }
 
         void mark(JSTracer *trc);
 
         ValueEdge tagged() const { return ValueEdge((JS::Value *)(uintptr_t(edge) | 1)); }
         ValueEdge untagged() const { return ValueEdge((JS::Value *)(uintptr_t(edge) & ~1)); }
         bool isTagged() const { return bool(uintptr_t(edge) & 1); }
@@ -308,18 +308,18 @@ class StoreBuffer
                    start_ == other.start_ &&
                    count_ == other.count_;
         }
 
         bool operator!=(const SlotsEdge &other) const {
             return !(*this == other);
         }
 
-        bool maybeInRememberedSet(const Nursery &nursery) const {
-            return !nursery.isInside(object());
+        bool maybeInRememberedSet(const Nursery &) const {
+            return !IsInsideNursery(JS::AsCell(object()));
         }
 
         void mark(JSTracer *trc);
 
         typedef struct {
             typedef SlotsEdge Lookup;
             static HashNumber hash(const Lookup &l) { return l.objectAndKind_ ^ l.start_ ^ l.count_; }
             static bool match(const SlotsEdge &k, const Lookup &l) { return k == l; }
@@ -332,17 +332,17 @@ class StoreBuffer
 
         explicit WholeCellEdges(Cell *cell) : edge(cell) {
             JS_ASSERT(edge->isTenured());
         }
 
         bool operator==(const WholeCellEdges &other) const { return edge == other.edge; }
         bool operator!=(const WholeCellEdges &other) const { return edge != other.edge; }
 
-        bool maybeInRememberedSet(const Nursery &nursery) const { return true; }
+        bool maybeInRememberedSet(const Nursery &) const { return true; }
 
         static bool supportsDeduplication() { return true; }
         void *deduplicationKey() const { return (void *)edge; }
 
         void mark(JSTracer *trc);
 
         typedef PointerEdgeHasher<WholeCellEdges> Hasher;
     };
--- a/js/src/gc/Verifier.cpp
+++ b/js/src/gc/Verifier.cpp
@@ -115,17 +115,17 @@ struct VerifyPreTracer : JSTracer
  * This function builds up the heap snapshot by adding edges to the current
  * node.
  */
 static void
 AccumulateEdge(JSTracer *jstrc, void **thingp, JSGCTraceKind kind)
 {
     VerifyPreTracer *trc = (VerifyPreTracer *)jstrc;
 
-    JS_ASSERT(!IsInsideNursery(trc->runtime(), *(uintptr_t **)thingp));
+    JS_ASSERT(!IsInsideNursery(*reinterpret_cast<Cell **>(thingp)));
 
     trc->edgeptr += sizeof(EdgeValue);
     if (trc->edgeptr >= trc->term) {
         trc->edgeptr = trc->term;
         return;
     }
 
     VerifyNode *node = trc->curnode;
@@ -428,17 +428,17 @@ PostVerifierCollectStoreBufferEdges(JSTr
     VerifyPostTracer *trc = (VerifyPostTracer *)jstrc;
 
     /* The nursery only stores objects. */
     if (kind != JSTRACE_OBJECT)
         return;
 
     /* The store buffer may store extra, non-cross-generational edges. */
     JSObject *dst = *reinterpret_cast<JSObject **>(thingp);
-    if (trc->runtime()->gc.nursery.isInside(thingp) || !trc->runtime()->gc.nursery.isInside(dst))
+    if (trc->runtime()->gc.nursery.isInside(thingp) || !IsInsideNursery(dst))
         return;
 
     /*
      * Values will be unpacked to the stack before getting here. However, the
      * only things that enter this callback are marked by the store buffer. The
      * store buffer ensures that the real tracing location is set correctly.
      */
     void **loc = trc->tracingLocation(thingp);
@@ -466,17 +466,17 @@ PostVerifierVisitEdge(JSTracer *jstrc, v
 
     /* The nursery only stores objects. */
     if (kind != JSTRACE_OBJECT)
         return;
 
     /* Filter out non cross-generational edges. */
     JS_ASSERT(!trc->runtime()->gc.nursery.isInside(thingp));
     JSObject *dst = *reinterpret_cast<JSObject **>(thingp);
-    if (!trc->runtime()->gc.nursery.isInside(dst))
+    if (!IsInsideNursery(dst))
         return;
 
     /*
      * Values will be unpacked to the stack before getting here. However, the
      * only things that enter this callback are marked by the JS_TraceChildren
      * below. Since ObjectImpl::markChildren handles this, the real trace
      * location will be set correctly in these cases.
      */
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/saved-stacks/bug-1012646-strlen-crasher.js
@@ -0,0 +1,4 @@
+// |jit-test| exitstatus: 3
+
+enableTrackAllocations();
+evaluate("throw Error();", {fileName: null});
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -3397,17 +3397,17 @@ IsCacheableGetPropCall(JSContext *cx, JS
     if (!shape->getterValue().isObject() || !shape->getterObject()->is<JSFunction>())
         return false;
 
     JSFunction *func = &shape->getterObject()->as<JSFunction>();
 
 #ifdef JSGC_GENERATIONAL
     // Information from get prop call ICs may be used directly from Ion code,
     // and should not be nursery allocated.
-    if (cx->runtime()->gc.nursery.isInside(holder) || cx->runtime()->gc.nursery.isInside(func))
+    if (IsInsideNursery(holder) || IsInsideNursery(func))
         return false;
 #endif
 
     if (func->isNative()) {
         *isScripted = false;
         return true;
     }
 
@@ -3516,17 +3516,17 @@ IsCacheableSetPropCall(JSContext *cx, JS
     if (!shape->setterValue().isObject() || !shape->setterObject()->is<JSFunction>())
         return false;
 
     JSFunction *func = &shape->setterObject()->as<JSFunction>();
 
 #ifdef JSGC_GENERATIONAL
     // Information from set prop call ICs may be used directly from Ion code,
     // and should not be nursery allocated.
-    if (cx->runtime()->gc.nursery.isInside(holder) || cx->runtime()->gc.nursery.isInside(func))
+    if (IsInsideNursery(holder) || IsInsideNursery(func))
         return false;
 #endif
 
     if (func->isNative()) {
         *isScripted = false;
         return true;
     }
 
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -639,16 +639,19 @@ CodeGenerator::testValueTruthy(const Val
 {
     testValueTruthyKernel(value, scratch1, scratch2, fr, ifTruthy, ifFalsy, ool, valueMIR);
     masm.jump(ifTruthy);
 }
 
 Label *
 CodeGenerator::getJumpLabelForBranch(MBasicBlock *block)
 {
+    // Skip past trivial blocks.
+    block = skipTrivialBlocks(block);
+
     if (!labelForBackedgeWithImplicitCheck(block))
         return block->lir()->label();
 
     // We need to use a patchable jump for this backedge, but want to treat
     // this as a normal label target to simplify codegen. Efficiency isn't so
     // important here as these tests are extremely unlikely to be used in loop
     // backedges, so emit inline code for the patchable jump. Heap allocating
     // the label allows it to be used by out of line blocks.
@@ -712,27 +715,27 @@ CodeGenerator::visitFunctionDispatch(LFu
     Register input = ToRegister(lir->input());
     Label *lastLabel;
     size_t casesWithFallback;
 
     // Determine if the last case is fallback or an ordinary case.
     if (!mir->hasFallback()) {
         JS_ASSERT(mir->numCases() > 0);
         casesWithFallback = mir->numCases();
-        lastLabel = mir->getCaseBlock(mir->numCases() - 1)->lir()->label();
+        lastLabel = skipTrivialBlocks(mir->getCaseBlock(mir->numCases() - 1))->lir()->label();
     } else {
         casesWithFallback = mir->numCases() + 1;
-        lastLabel = mir->getFallback()->lir()->label();
+        lastLabel = skipTrivialBlocks(mir->getFallback())->lir()->label();
     }
 
     // Compare function pointers, except for the last case.
     for (size_t i = 0; i < casesWithFallback - 1; i++) {
         JS_ASSERT(i < mir->numCases());
         JSFunction *func = mir->getCase(i);
-        LBlock *target = mir->getCaseBlock(i)->lir();
+        LBlock *target = skipTrivialBlocks(mir->getCaseBlock(i))->lir();
         masm.branchPtr(Assembler::Equal, input, ImmGCPtr(func), target->label());
     }
 
     // Jump to the last case.
     masm.jump(lastLabel);
 
     return true;
 }
@@ -746,31 +749,31 @@ CodeGenerator::visitTypeObjectDispatch(L
 
     // Hold the incoming TypeObject.
     masm.loadPtr(Address(input, JSObject::offsetOfType()), temp);
 
     // Compare TypeObjects.
     InlinePropertyTable *propTable = mir->propTable();
     for (size_t i = 0; i < mir->numCases(); i++) {
         JSFunction *func = mir->getCase(i);
-        LBlock *target = mir->getCaseBlock(i)->lir();
+        LBlock *target = skipTrivialBlocks(mir->getCaseBlock(i))->lir();
 
         DebugOnly<bool> found = false;
         for (size_t j = 0; j < propTable->numEntries(); j++) {
             if (propTable->getFunction(j) != func)
                 continue;
             types::TypeObject *typeObj = propTable->getTypeObject(j);
             masm.branchPtr(Assembler::Equal, temp, ImmGCPtr(typeObj), target->label());
             found = true;
         }
         JS_ASSERT(found);
     }
 
     // Unknown function: jump to fallback block.
-    LBlock *fallback = mir->getFallback()->lir();
+    LBlock *fallback = skipTrivialBlocks(mir->getFallback())->lir();
     masm.jump(fallback->label());
     return true;
 }
 
 bool
 CodeGenerator::visitBooleanToString(LBooleanToString *lir)
 {
     Register input = ToRegister(lir->input());
@@ -1258,17 +1261,17 @@ CodeGenerator::visitInterruptCheckImplic
     masm.bind(ool->rejoin());
     return true;
 }
 
 bool
 CodeGenerator::visitTableSwitch(LTableSwitch *ins)
 {
     MTableSwitch *mir = ins->mir();
-    Label *defaultcase = mir->getDefault()->lir()->label();
+    Label *defaultcase = skipTrivialBlocks(mir->getDefault())->lir()->label();
     const LAllocation *temp;
 
     if (mir->getOperand(0)->type() != MIRType_Int32) {
         temp = ins->tempInt()->output();
 
         // The input is a double, so try and convert it to an integer.
         // If it does not fit in an integer, take the default case.
         masm.convertDoubleToInt32(ToFloatRegister(ins->index()), ToRegister(temp), defaultcase, false);
@@ -1278,17 +1281,17 @@ CodeGenerator::visitTableSwitch(LTableSw
 
     return emitTableSwitchDispatch(mir, ToRegister(temp), ToRegisterOrInvalid(ins->tempPointer()));
 }
 
 bool
 CodeGenerator::visitTableSwitchV(LTableSwitchV *ins)
 {
     MTableSwitch *mir = ins->mir();
-    Label *defaultcase = mir->getDefault()->lir()->label();
+    Label *defaultcase = skipTrivialBlocks(mir->getDefault())->lir()->label();
 
     Register index = ToRegister(ins->tempInt());
     ValueOperand value = ToValue(ins, LTableSwitchV::InputValue);
     Register tag = masm.extractTag(value, index);
     masm.branchTestNumber(Assembler::NotEqual, tag, defaultcase);
 
     Label unboxInt, isInt;
     masm.branchTestInt32(Assembler::Equal, tag, &unboxInt);
@@ -1941,18 +1944,17 @@ CodeGenerator::visitPostWriteBarrierO(LP
     OutOfLineCallPostWriteBarrier *ool = new(alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object());
     if (!addOutOfLineCode(ool))
         return false;
 
     Register temp = ToTempRegisterOrInvalid(lir->temp());
 
     if (lir->object()->isConstant()) {
 #ifdef DEBUG
-        const Nursery &nursery = GetIonContext()->runtime->gcNursery();
-        JS_ASSERT(!nursery.isInside(&lir->object()->toConstant()->toObject()));
+        JS_ASSERT(!IsInsideNursery(&lir->object()->toConstant()->toObject()));
 #endif
     } else {
         masm.branchPtrInNurseryRange(Assembler::Equal, ToRegister(lir->object()), temp,
                                      ool->rejoin());
     }
 
     masm.branchPtrInNurseryRange(Assembler::Equal, ToRegister(lir->value()), temp, ool->entry());
 
@@ -1968,18 +1970,17 @@ CodeGenerator::visitPostWriteBarrierV(LP
     OutOfLineCallPostWriteBarrier *ool = new(alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object());
     if (!addOutOfLineCode(ool))
         return false;
 
     Register temp = ToTempRegisterOrInvalid(lir->temp());
 
     if (lir->object()->isConstant()) {
 #ifdef DEBUG
-        const Nursery &nursery = GetIonContext()->runtime->gcNursery();
-        JS_ASSERT(!nursery.isInside(&lir->object()->toConstant()->toObject()));
+        JS_ASSERT(!IsInsideNursery(&lir->object()->toConstant()->toObject()));
 #endif
     } else {
         masm.branchPtrInNurseryRange(Assembler::Equal, ToRegister(lir->object()), temp,
                                      ool->rejoin());
     }
 
     ValueOperand value = ToValue(lir, LPostWriteBarrierV::Input);
     masm.branchValueIsNurseryObject(Assembler::Equal, value, temp, ool->entry());
@@ -3362,16 +3363,23 @@ CodeGenerator::generateBody()
 #if defined(JS_ION_PERF)
     PerfSpewer *perfSpewer = &perfSpewer_;
     if (gen->compilingAsmJS())
         perfSpewer = &gen->perfSpewer();
 #endif
 
     for (size_t i = 0; i < graph.numBlocks(); i++) {
         current = graph.getBlock(i);
+
+        // Don't emit any code for trivial blocks, containing just a goto. Such
+        // blocks are created to split critical edges, and if we didn't end up
+        // putting any instructions in them, we can skip them.
+        if (current->isTrivial())
+            continue; 
+
         masm.bind(current->label());
 
         mozilla::Maybe<ScriptCountBlockState> blockCounts;
         if (counts) {
             blockCounts.construct(&counts->block(i), &masm);
             if (!blockCounts.ref().init())
                 return false;
         }
--- a/js/src/jit/CompileWrappers.cpp
+++ b/js/src/jit/CompileWrappers.cpp
@@ -145,17 +145,17 @@ CompileRuntime::positiveInfinityValue()
 {
     return runtime()->positiveInfinityValue;
 }
 
 #ifdef DEBUG
 bool
 CompileRuntime::isInsideNursery(gc::Cell *cell)
 {
-    return UninlinedIsInsideNursery(runtime(), cell);
+    return UninlinedIsInsideNursery(cell);
 }
 #endif
 
 const DOMCallbacks *
 CompileRuntime::DOMcallbacks()
 {
     return GetDOMCallbacks(runtime());
 }
--- a/js/src/jit/EffectiveAddressAnalysis.cpp
+++ b/js/src/jit/EffectiveAddressAnalysis.cpp
@@ -40,17 +40,17 @@ AnalyzeLsh(TempAllocator &alloc, MLsh *l
         MUseIterator use = last->usesBegin();
         if (!use->consumer()->isDefinition() || !use->consumer()->toDefinition()->isAdd())
             break;
 
         MAdd *add = use->consumer()->toDefinition()->toAdd();
         if (add->specialization() != MIRType_Int32 || !add->isTruncated())
             break;
 
-        MDefinition *other = add->getOperand(1 - use->index());
+        MDefinition *other = add->getOperand(1 - add->indexOf(*use));
 
         if (other->isConstant()) {
             displacement += other->toConstant()->value().toInt32();
         } else {
             if (base)
                 break;
             base = other;
         }
@@ -66,17 +66,17 @@ AnalyzeLsh(TempAllocator &alloc, MLsh *l
         if (!last->hasOneUse())
             return;
 
         MUseIterator use = last->usesBegin();
         if (!use->consumer()->isDefinition() || !use->consumer()->toDefinition()->isBitAnd())
             return;
 
         MBitAnd *bitAnd = use->consumer()->toDefinition()->toBitAnd();
-        MDefinition *other = bitAnd->getOperand(1 - use->index());
+        MDefinition *other = bitAnd->getOperand(1 - bitAnd->indexOf(*use));
         if (!other->isConstant() || !other->toConstant()->value().isInt32())
             return;
 
         uint32_t bitsClearedByShift = elemSize - 1;
         uint32_t bitsClearedByMask = ~uint32_t(other->toConstant()->value().toInt32());
         if ((bitsClearedByShift & bitsClearedByMask) != bitsClearedByMask)
             return;
 
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -1626,26 +1626,16 @@ GenerateLIR(MIRGenerator *mir)
           default:
             MOZ_ASSUME_UNREACHABLE("Bad regalloc");
         }
 
         if (mir->shouldCancel("Allocate Registers"))
             return nullptr;
     }
 
-    {
-        AutoTraceLog log(logger, TraceLogger::UnsplitEdges);
-        // Now that all optimization and register allocation is done, re-introduce
-        // critical edges to avoid unnecessary jumps.
-        if (!UnsplitEdges(lir))
-            return nullptr;
-        IonSpewPass("Unsplit Critical Edges");
-        AssertBasicGraphCoherency(graph);
-    }
-
     return lir;
 }
 
 CodeGenerator *
 GenerateCode(MIRGenerator *mir, LIRGraph *lir)
 {
     TraceLogger *logger;
     if (GetIonContext()->runtime->onMainThread())
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -102,69 +102,72 @@ jit::EliminateDeadResumePointOperands(MI
             }
 
             // If the instruction's behavior has been constant folded into a
             // separate instruction, we can't determine precisely where the
             // instruction becomes dead and can't eliminate its uses.
             if (ins->isImplicitlyUsed())
                 continue;
 
-            // If the instruction's is captured by one of the resume point, then
-            // it might be observed indirectly while the frame is live on the
-            // stack, so it has to be computed.
-            if (ins->isObserved())
-                continue;
-
             // Check if this instruction's result is only used within the
             // current block, and keep track of its last use in a definition
             // (not resume point). This requires the instructions in the block
             // to be numbered, ensured by running this immediately after alias
             // analysis.
             uint32_t maxDefinition = 0;
-            for (MUseDefIterator uses(*ins); uses; uses++) {
-                if (uses.def()->block() != *block ||
-                    uses.def()->isBox() ||
-                    uses.def()->isPhi())
-                {
+            for (MUseIterator uses(ins->usesBegin()); uses != ins->usesEnd(); uses++) {
+                MNode *consumer = uses->consumer();
+                if (consumer->isResumePoint()) {
+                    // If the instruction's is captured by one of the resume point, then
+                    // it might be observed indirectly while the frame is live on the
+                    // stack, so it has to be computed.
+                    MResumePoint *resume = consumer->toResumePoint();
+                    if (resume->isObservableOperand(*uses)) {
+                        maxDefinition = UINT32_MAX;
+                        break;
+                    }
+                    continue;
+                }
+
+                MDefinition *def = consumer->toDefinition();
+                if (def->block() != *block || def->isBox() || def->isPhi()) {
                     maxDefinition = UINT32_MAX;
                     break;
                 }
-                maxDefinition = Max(maxDefinition, uses.def()->id());
+                maxDefinition = Max(maxDefinition, def->id());
             }
             if (maxDefinition == UINT32_MAX)
                 continue;
 
             // Walk the uses a second time, removing any in resume points after
             // the last use in a definition.
             for (MUseIterator uses(ins->usesBegin()); uses != ins->usesEnd(); ) {
-                if (uses->consumer()->isDefinition()) {
-                    uses++;
+                MUse *use = *uses++;
+                if (use->consumer()->isDefinition())
                     continue;
-                }
-                MResumePoint *mrp = uses->consumer()->toResumePoint();
+                MResumePoint *mrp = use->consumer()->toResumePoint();
                 if (mrp->block() != *block ||
                     !mrp->instruction() ||
                     mrp->instruction() == *ins ||
                     mrp->instruction()->id() <= maxDefinition)
                 {
-                    uses++;
                     continue;
                 }
 
                 // Store an optimized out magic value in place of all dead
                 // resume point operands. Making any such substitution can in
                 // general alter the interpreter's behavior, even though the
                 // code is dead, as the interpreter will still execute opcodes
                 // whose effects cannot be observed. If the magic value value
                 // were to flow to, say, a dead property access the
                 // interpreter could throw an exception; we avoid this problem
                 // by removing dead operands before removing dead code.
                 MConstant *constant = MConstant::New(graph.alloc(), MagicValue(JS_OPTIMIZED_OUT));
                 block->insertBefore(*(block->begin()), constant);
-                uses = mrp->replaceOperand(uses, constant);
+                use->replaceProducer(constant);
             }
         }
     }
 
     return true;
 }
 
 // Instructions are useless if they are unused and have no side effects.
@@ -208,34 +211,32 @@ IsPhiObservable(MPhi *phi, Observability
     // Check for uses of this phi node outside of other phi nodes.
     // Note that, initially, we skip reading resume points, which we
     // don't count as actual uses. If the only uses are resume points,
     // then the SSA name is never consumed by the program.  However,
     // after optimizations have been performed, it's possible that the
     // actual uses in the program have been (incorrectly) optimized
     // away, so we must be more conservative and consider resume
     // points as well.
-    switch (observe) {
-      case AggressiveObservability:
-        for (MUseDefIterator iter(phi); iter; iter++) {
-            if (!iter.def()->isPhi())
+    for (MUseIterator iter(phi->usesBegin()); iter != phi->usesEnd(); iter++) {
+        MNode *consumer = iter->consumer();
+        if (consumer->isResumePoint()) {
+            MResumePoint *resume = consumer->toResumePoint();
+            if (observe == ConservativeObservability)
+                return true;
+            if (resume->isObservableOperand(*iter))
+                return true;
+        } else {
+            MDefinition *def = consumer->toDefinition();
+            if (!def->isPhi())
                 return true;
         }
-        break;
-
-      case ConservativeObservability:
-        for (MUseIterator iter(phi->usesBegin()); iter != phi->usesEnd(); iter++) {
-            if (!iter->consumer()->isDefinition() ||
-                !iter->consumer()->toDefinition()->isPhi())
-                return true;
-        }
-        break;
     }
 
-    return phi->isObserved();
+    return false;
 }
 
 // Handles cases like:
 //    x is phi(a, x) --> a
 //    x is phi(a, a) --> a
 static inline MDefinition *
 IsPhiRedundant(MPhi *phi)
 {
@@ -1908,107 +1909,16 @@ jit::EliminateRedundantChecks(MIRGraph &
         }
         index++;
     }
 
     JS_ASSERT(index == graph.numBlocks());
     return true;
 }
 
-// If the given block contains a goto and nothing interesting before that,
-// return the goto. Return nullptr otherwise.
-static LGoto *
-FindLeadingGoto(LBlock *bb)
-{
-    for (LInstructionIterator ins(bb->begin()); ins != bb->end(); ins++) {
-        // Ignore labels.
-        if (ins->isLabel())
-            continue;
-        // If we have a goto, we're good to go.
-        if (ins->isGoto())
-            return ins->toGoto();
-        break;
-    }
-    return nullptr;
-}
-
-// Eliminate blocks containing nothing interesting besides gotos. These are
-// often created by optimizer, which splits all critical edges. If these
-// splits end up being unused after optimization and register allocation,
-// fold them back away to avoid unnecessary branching.
-bool
-jit::UnsplitEdges(LIRGraph *lir)
-{
-    for (size_t i = 0; i < lir->numBlocks(); i++) {
-        LBlock *bb = lir->getBlock(i);
-        MBasicBlock *mirBlock = bb->mir();
-
-        // Renumber the MIR blocks as we go, since we may remove some.
-        mirBlock->setId(i);
-
-        // Register allocation is done by this point, so we don't need the phis
-        // anymore. Clear them to avoid needed to keep them current as we edit
-        // the CFG.
-        bb->clearPhis();
-        mirBlock->discardAllPhis();
-
-        // First make sure the MIR block looks sane. Some of these checks may be
-        // over-conservative, but we're attempting to keep everything in MIR
-        // current as we modify the LIR, so only proceed if the MIR is simple.
-        if (mirBlock->numPredecessors() == 0 || mirBlock->numSuccessors() != 1 ||
-            !mirBlock->begin()->isGoto())
-        {
-            continue;
-        }
-
-        // The MIR block is empty, but check the LIR block too (in case the
-        // register allocator inserted spill code there, or whatever).
-        LGoto *theGoto = FindLeadingGoto(bb);
-        if (!theGoto)
-            continue;
-        MBasicBlock *target = theGoto->target();
-        if (target == mirBlock || target != mirBlock->getSuccessor(0))
-            continue;
-
-        // If we haven't yet cleared the phis for the successor, do so now so
-        // that the CFG manipulation routines don't trip over them.
-        if (!target->phisEmpty()) {
-            target->discardAllPhis();
-            target->lir()->clearPhis();
-        }
-
-        // Edit the CFG to remove lir/mirBlock and reconnect all its edges.
-        for (size_t j = 0; j < mirBlock->numPredecessors(); j++) {
-            MBasicBlock *mirPred = mirBlock->getPredecessor(j);
-
-            for (size_t k = 0; k < mirPred->numSuccessors(); k++) {
-                if (mirPred->getSuccessor(k) == mirBlock) {
-                    mirPred->replaceSuccessor(k, target);
-                    if (!target->addPredecessorWithoutPhis(mirPred))
-                        return false;
-                }
-            }
-
-            LInstruction *predTerm = *mirPred->lir()->rbegin();
-            for (size_t k = 0; k < predTerm->numSuccessors(); k++) {
-                if (predTerm->getSuccessor(k) == mirBlock)
-                    predTerm->setSuccessor(k, target);
-            }
-        }
-        target->removePredecessor(mirBlock);
-
-        // Zap the block.
-        lir->removeBlock(i);
-        lir->mir().removeBlock(mirBlock);
-        --i;
-    }
-
-    return true;
-}
-
 bool
 LinearSum::multiply(int32_t scale)
 {
     for (size_t i = 0; i < terms_.length(); i++) {
         if (!SafeMul(scale, terms_[i].scale, &terms_[i].scale))
             return false;
     }
     return SafeMul(scale, constant_, &constant_);
@@ -2554,17 +2464,17 @@ jit::AnalyzeArgumentsUsage(JSContext *cx
 
     for (MUseDefIterator uses(argumentsValue); uses; uses++) {
         MDefinition *use = uses.def();
 
         // Don't track |arguments| through assignments to phis.
         if (!use->isInstruction())
             return true;
 
-        if (!ArgumentsUseCanBeLazy(cx, script, use->toInstruction(), uses.index(),
+        if (!ArgumentsUseCanBeLazy(cx, script, use->toInstruction(), use->indexOf(uses.use()),
                                    &argumentsContentsObserved))
         {
             return true;
         }
     }
 
     // If a script explicitly accesses the contents of 'arguments', and has
     // formals which may be stored as part of a call object, don't use lazy
--- a/js/src/jit/IonAnalysis.h
+++ b/js/src/jit/IonAnalysis.h
@@ -59,19 +59,16 @@ void
 AssertGraphCoherency(MIRGraph &graph);
 
 void
 AssertExtendedGraphCoherency(MIRGraph &graph);
 
 bool
 EliminateRedundantChecks(MIRGraph &graph);
 
-bool
-UnsplitEdges(LIRGraph *lir);
-
 class MDefinition;
 
 // Simple linear sum of the form 'n' or 'x + n'.
 struct SimpleLinearSum
 {
     MDefinition *term;
     int32_t constant;
 
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -439,29 +439,34 @@ IonBuilder::analyzeNewLoopTypes(MBasicBl
 
     // If we restarted processing of an outer loop then get loop header types
     // directly from the last time we have previously processed this loop. This
     // both avoids repeated work from the bytecode traverse below, and will
     // also pick up types discovered while previously building the loop body.
     for (size_t i = 0; i < loopHeaders_.length(); i++) {
         if (loopHeaders_[i].pc == start) {
             MBasicBlock *oldEntry = loopHeaders_[i].header;
-            MResumePoint *oldEntryRp = oldEntry->entryResumePoint();
-            size_t stackDepth = oldEntryRp->numOperands();
-            for (size_t slot = 0; slot < stackDepth; slot++) {
-                MDefinition *oldDef = oldEntryRp->getOperand(slot);
-                if (!oldDef->isPhi()) {
-                    MOZ_ASSERT(oldDef->block()->id() < oldEntry->id());
-                    MOZ_ASSERT(oldDef == entry->getSlot(slot));
-                    continue;
+
+            // If this block has been discarded, its resume points will have
+            // already discarded their operands.
+            if (!oldEntry->isDead()) {
+                MResumePoint *oldEntryRp = oldEntry->entryResumePoint();
+                size_t stackDepth = oldEntryRp->numOperands();
+                for (size_t slot = 0; slot < stackDepth; slot++) {
+                    MDefinition *oldDef = oldEntryRp->getOperand(slot);
+                    if (!oldDef->isPhi()) {
+                        MOZ_ASSERT(oldDef->block()->id() < oldEntry->id());
+                        MOZ_ASSERT(oldDef == entry->getSlot(slot));
+                        continue;
+                    }
+                    MPhi *oldPhi = oldDef->toPhi();
+                    MPhi *newPhi = entry->getSlot(slot)->toPhi();
+                    if (!newPhi->addBackedgeType(oldPhi->type(), oldPhi->resultTypeSet()))
+                        return false;
                 }
-                MPhi *oldPhi = oldDef->toPhi();
-                MPhi *newPhi = entry->getSlot(slot)->toPhi();
-                if (!newPhi->addBackedgeType(oldPhi->type(), oldPhi->resultTypeSet()))
-                    return false;
             }
 
             // Update the most recent header for this loop encountered, in case
             // new types flow to the phis and the loop is processed at least
             // three times.
             loopHeaders_[i].header = entry;
             return true;
         }
@@ -7435,17 +7440,22 @@ IonBuilder::getTypedArrayLength(MDefinit
 MInstruction *
 IonBuilder::getTypedArrayElements(MDefinition *obj)
 {
     if (obj->isConstant() && obj->toConstant()->value().isObject()) {
         TypedArrayObject *tarr = &obj->toConstant()->value().toObject().as<TypedArrayObject>();
         void *data = tarr->viewData();
         // Bug 979449 - Optimistically embed the elements and use TI to
         //              invalidate if we move them.
-        if (!gc::IsInsideNursery(tarr->runtimeFromMainThread(), data)) {
+#ifdef JSGC_GENERATIONAL
+        bool isTenured = !tarr->runtimeFromMainThread()->gc.nursery.isInside(data);
+#else
+        bool isTenured = true;
+#endif
+        if (isTenured) {
             // The 'data' pointer can change in rare circumstances
             // (ArrayBufferObject::changeContents).
             types::TypeObjectKey *tarrType = types::TypeObjectKey::get(tarr);
             if (!tarrType->unknownProperties()) {
                 tarrType->watchStateChangeForTypedArrayBuffer(constraints());
 
                 obj->setImplicitlyUsedUnchecked();
                 return MConstantElements::New(alloc(), data);
@@ -7742,18 +7752,20 @@ IonBuilder::setElemTryTypedStatic(bool *
     if (!object->resultTypeSet())
         return true;
     JSObject *tarrObj = object->resultTypeSet()->getSingleton();
     if (!tarrObj)
         return true;
 
     TypedArrayObject *tarr = &tarrObj->as<TypedArrayObject>();
 
-    if (gc::IsInsideNursery(tarr->runtimeFromMainThread(), tarr->viewData()))
-        return true;
+#ifdef JSGC_GENERATIONAL
+    if (tarr->runtimeFromMainThread()->gc.nursery.isInside(tarr->viewData()))
+        return true;
+#endif
 
     ArrayBufferView::ViewType viewType = (ArrayBufferView::ViewType) tarr->type();
 
     MDefinition *ptr = convertShiftToMaskForStaticTypedArray(index, viewType);
     if (!ptr)
         return true;
 
     // Emit StoreTypedArrayElementStatic.
@@ -8885,17 +8897,17 @@ IonBuilder::getPropTryCommonGetter(bool 
     return true;
 }
 
 static bool
 CanInlinePropertyOpShapes(const BaselineInspector::ShapeVector &shapes)
 {
     for (size_t i = 0; i < shapes.length(); i++) {
         // We inline the property access as long as the shape is not in
-        // dictionary made. We cannot be sure that the shape is still a
+        // dictionary mode. We cannot be sure that the shape is still a
         // lastProperty, and calling Shape::search() on dictionary mode
         // shapes that aren't lastProperty is invalid.
         if (shapes[i]->inDictionary())
             return false;
     }
 
     return true;
 }
--- a/js/src/jit/IonMacroAssembler.cpp
+++ b/js/src/jit/IonMacroAssembler.cpp
@@ -243,27 +243,27 @@ template void MacroAssembler::guardType(
 template void MacroAssembler::guardType(const ValueOperand &value, types::Type type,
                                         Register scratch, Label *miss);
 
 void
 MacroAssembler::branchNurseryPtr(Condition cond, const Address &ptr1, ImmMaybeNurseryPtr ptr2,
                                  Label *label)
 {
 #ifdef JSGC_GENERATIONAL
-    if (ptr2.value && gc::IsInsideNursery(GetIonContext()->cx->runtime(), (void *)ptr2.value))
+    if (ptr2.value && gc::IsInsideNursery(ptr2.value))
         embedsNurseryPointers_ = true;
 #endif
     branchPtr(cond, ptr1, ptr2, label);
 }
 
 void
 MacroAssembler::moveNurseryPtr(ImmMaybeNurseryPtr ptr, Register reg)
 {
 #ifdef JSGC_GENERATIONAL
-    if (ptr.value && gc::IsInsideNursery(GetIonContext()->cx->runtime(), (void *)ptr.value))
+    if (ptr.value && gc::IsInsideNursery(ptr.value))
         embedsNurseryPointers_ = true;
 #endif
     movePtr(ptr, reg);
 }
 
 template<typename S, typename T>
 static void
 StoreToTypedFloatArray(MacroAssembler &masm, int arrayType, const S &value, const T &dest)
--- a/js/src/jit/IonSpewer.h
+++ b/js/src/jit/IonSpewer.h
@@ -19,19 +19,19 @@ namespace js {
 namespace jit {
 
 // New channels may be added below.
 #define IONSPEW_CHANNEL_LIST(_)             \
     /* Used to abort SSA construction */    \
     _(Abort)                                \
     /* Information about compiled scripts */\
     _(Scripts)                              \
-    /* Information during MIR building */   \
+    /* Info about failing to log script */  \
     _(Logs)                                 \
-    /* Info about failing to log script */  \
+    /* Information during MIR building */   \
     _(MIR)                                  \
     /* Information during alias analysis */ \
     _(Alias)                                \
     /* Information during GVN */            \
     _(GVN)                                  \
     /* Information during Range analysis */ \
     _(Range)                                \
     /* Information during LICM */           \
--- a/js/src/jit/LIR.cpp
+++ b/js/src/jit/LIR.cpp
@@ -58,16 +58,31 @@ LIRGraph::noteNeedsSafepoint(LInstructio
 }
 
 void
 LIRGraph::removeBlock(size_t i)
 {
     blocks_.erase(blocks_.begin() + i);
 }
 
+void
+LIRGraph::dump(FILE *fp) const
+{
+    for (size_t i = 0; i < numBlocks(); i++) {
+        getBlock(i)->dump(fp);
+        fprintf(fp, "\n");
+    }
+}
+
+void
+LIRGraph::dump() const
+{
+    dump(stderr);
+}
+
 uint32_t
 LBlock::firstId()
 {
     if (phis_.length()) {
         return phis_[0]->id();
     } else {
         for (LInstructionIterator i(instructions_.begin()); i != instructions_.end(); i++) {
             if (i->id())
@@ -105,16 +120,32 @@ LBlock::getExitMoveGroup(TempAllocator &
 {
     if (exitMoveGroup_)
         return exitMoveGroup_;
     exitMoveGroup_ = LMoveGroup::New(alloc);
     insertBefore(*rbegin(), exitMoveGroup_);
     return exitMoveGroup_;
 }
 
+void
+LBlock::dump(FILE *fp)
+{
+    fprintf(fp, "block%u:\n", mir()->id());
+    for (LInstructionIterator iter = begin(); iter != end(); iter++) {
+        iter->dump(fp);
+        fprintf(fp, "\n");
+    }
+}
+
+void
+LBlock::dump()
+{
+    dump(stderr);
+}
+
 static size_t
 TotalOperandCount(LRecoverInfo *recoverInfo)
 {
     LRecoverInfo::OperandIter it(recoverInfo->begin());
     LRecoverInfo::OperandIter end(recoverInfo->end());
     size_t accum = 0;
 
     for (; it != end; ++it) {
@@ -422,16 +453,26 @@ LInstruction::dump(FILE *fp)
         fprintf(fp, " t=(");
         for (size_t i = 0; i < numTemps(); i++) {
             PrintDefinition(fp, *getTemp(i));
             if (i != numTemps() - 1)
                 fprintf(fp, ", ");
         }
         fprintf(fp, ")");
     }
+
+    if (numSuccessors()) {
+        fprintf(fp, " s=(");
+        for (size_t i = 0; i < numSuccessors(); i++) {
+            fprintf(fp, "block%u", getSuccessor(i)->id());
+            if (i != numSuccessors() - 1)
+                fprintf(fp, ", ");
+        }
+        fprintf(fp, ")");
+    }
 }
 
 void
 LInstruction::dump()
 {
     dump(stderr);
     fprintf(stderr, "\n");
 }
--- a/js/src/jit/LIR.h
+++ b/js/src/jit/LIR.h
@@ -792,21 +792,37 @@ class LBlock : public TempObject
         instructions_.insertAfter(at, ins);
     }
     void insertBefore(LInstruction *at, LInstruction *ins) {
         JS_ASSERT(!at->isLabel());
         instructions_.insertBefore(at, ins);
     }
     uint32_t firstId();
     uint32_t lastId();
+
+    // Return the label to branch to when branching to this block.
     Label *label() {
+        JS_ASSERT(!isTrivial());
         return &label_;
     }
+
     LMoveGroup *getEntryMoveGroup(TempAllocator &alloc);
     LMoveGroup *getExitMoveGroup(TempAllocator &alloc);
+
+    // Test whether this basic block is empty except for a simple goto, and
+    // which is not forming a loop. No code will be emitted for such blocks.
+    bool isTrivial() {
+        LInstructionIterator ins(begin());
+        while (ins->isLabel())
+            ++ins;
+        return ins->isGoto() && !mir()->isLoopHeader();
+    }
+
+    void dump(FILE *fp);
+    void dump();
 };
 
 template <size_t Defs, size_t Operands, size_t Temps>
 class LInstructionHelper : public LInstruction
 {
     mozilla::Array<LDefinition, Defs> defs_;
     mozilla::Array<LAllocation, Operands> operands_;
     mozilla::Array<LDefinition, Temps> temps_;
@@ -1589,16 +1605,19 @@ class LIRGraph
     }
     size_t numSafepoints() const {
         return safepoints_.length();
     }
     LInstruction *getSafepoint(size_t i) const {
         return safepoints_[i];
     }
     void removeBlock(size_t i);
+
+    void dump(FILE *fp) const;
+    void dump() const;
 };
 
 LAllocation::LAllocation(AnyRegister reg)
 {
     if (reg.isFloat())
         *this = LFloatReg(reg.fpu());
     else
         *this = LGeneralReg(reg.gpr());
--- a/js/src/jit/LiveRangeAllocator.cpp
+++ b/js/src/jit/LiveRangeAllocator.cpp
@@ -71,16 +71,17 @@ LiveInterval::Range::intersect(const Ran
     if (innerFrom != innerTo)
         *inside = Range(innerFrom, innerTo);
 }
 
 bool
 LiveInterval::addRangeAtHead(CodePosition from, CodePosition to)
 {
     JS_ASSERT(from < to);
+    JS_ASSERT(ranges_.empty() || from <= ranges_.back().from);
 
     Range newRange(from, to);
 
     if (ranges_.empty())
         return ranges_.append(newRange);
 
     Range &first = ranges_.back();
     if (to < first.from)
@@ -105,44 +106,49 @@ bool
 LiveInterval::addRange(CodePosition from, CodePosition to)
 {
     JS_ASSERT(from < to);
 
     Range newRange(from, to);
 
     Range *i;
     // Find the location to insert the new range
-    for (i = ranges_.end() - 1; i >= ranges_.begin(); i--) {
-        if (newRange.from <= i->to) {
-            if (i->from < newRange.from)
-                newRange.from = i->from;
+    for (i = ranges_.end(); i > ranges_.begin(); i--) {
+        if (newRange.from <= i[-1].to) {
+            if (i[-1].from < newRange.from)
+                newRange.from = i[-1].from;
             break;
         }
     }
     // Perform coalescing on overlapping ranges
-    for (; i >= ranges_.begin(); i--) {
-        if (newRange.to < i->from)
+    Range *coalesceEnd = i;
+    for (; i > ranges_.begin(); i--) {
+        if (newRange.to < i[-1].from)
             break;
-        if (newRange.to < i->to)
-            newRange.to = i->to;
-        ranges_.erase(i);
+        if (newRange.to < i[-1].to)
+            newRange.to = i[-1].to;
     }
 
-    return ranges_.insert(i + 1, newRange);
+    if (i == coalesceEnd)
+        return ranges_.insert(i, newRange);
+
+    i[0] = newRange;
+    ranges_.erase(i + 1, coalesceEnd);
+    return true;
 }
 
 void
 LiveInterval::setFrom(CodePosition from)
 {
     while (!ranges_.empty()) {
         if (ranges_.back().to < from) {
-            ranges_.erase(&ranges_.back());
+            ranges_.popBack();
         } else {
             if (from == ranges_.back().to)
-                ranges_.erase(&ranges_.back());
+                ranges_.popBack();
             else
                 ranges_.back().from = from;
             break;
         }
     }
 }
 
 bool
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -1946,25 +1946,29 @@ MustCloneRegExp(MRegExp *regexp)
     // it escape, we don't have to clone it.
 
     for (MUseIterator iter(regexp->usesBegin()); iter != regexp->usesEnd(); iter++) {
         MNode *node = iter->consumer();
         if (!node->isDefinition())
             return true;
 
         MDefinition *def = node->toDefinition();
-        if (def->isRegExpTest() && iter->index() == 1) {
-            // Optimized RegExp.prototype.test.
-            JS_ASSERT(def->toRegExpTest()->regexp() == regexp);
-            continue;
+        if (def->isRegExpTest()) {
+            MRegExpTest *test = def->toRegExpTest();
+            if (test->indexOf(*iter) == 1) {
+                // Optimized RegExp.prototype.test.
+                JS_ASSERT(test->regexp() == regexp);
+                continue;
+            }
+        } else if (def->isCall()) {
+            MCall *call = def->toCall();
+            if (!MustCloneRegExpForCall(call, call->indexOf(*iter)))
+                continue;
         }
 
-        if (def->isCall() && !MustCloneRegExpForCall(def->toCall(), iter->index()))
-            continue;
-
         return true;
     }
     return false;
 }
 
 bool
 LIRGenerator::visitRegExp(MRegExp *ins)
 {
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -1914,38 +1914,37 @@ IonBuilder::inlineAssertFloat32(CallInfo
 
 IonBuilder::InliningStatus
 IonBuilder::inlineBoundFunction(CallInfo &nativeCallInfo, JSFunction *target)
 {
      if (!target->getBoundFunctionTarget()->is<JSFunction>())
          return InliningStatus_NotInlined;
 
     JSFunction *scriptedTarget = &(target->getBoundFunctionTarget()->as<JSFunction>());
-    JSRuntime *runtime = scriptedTarget->runtimeFromMainThread();
 
     // Don't optimize if we're constructing and the callee is not a
     // constructor, so that CallKnown does not have to handle this case
     // (it should always throw).
     if (nativeCallInfo.constructing() && !scriptedTarget->isInterpretedConstructor() &&
         !scriptedTarget->isNativeConstructor())
     {
         return InliningStatus_NotInlined;
     }
 
-    if (gc::IsInsideNursery(runtime, scriptedTarget))
+    if (gc::IsInsideNursery(scriptedTarget))
         return InliningStatus_NotInlined;
 
     for (size_t i = 0; i < target->getBoundFunctionArgumentCount(); i++) {
         const Value val = target->getBoundFunctionArgument(i);
-        if (val.isObject() && gc::IsInsideNursery(runtime, &val.toObject()))
+        if (val.isObject() && gc::IsInsideNursery(&val.toObject()))
             return InliningStatus_NotInlined;
     }
 
     const Value thisVal = target->getBoundFunctionThis();
-    if (thisVal.isObject() && gc::IsInsideNursery(runtime, &thisVal.toObject()))
+    if (thisVal.isObject() && gc::IsInsideNursery(&thisVal.toObject()))
         return InliningStatus_NotInlined;
 
     size_t argc = target->getBoundFunctionArgumentCount() + nativeCallInfo.argc();
     if (argc > ARGS_LENGTH_MAX)
         return InliningStatus_NotInlined;
 
     nativeCallInfo.thisArg()->setImplicitlyUsedUnchecked();
 
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -25,16 +25,23 @@
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::NumbersAreIdentical;
 using mozilla::IsFloat32Representable;
 using mozilla::Maybe;
 
+#ifdef DEBUG
+size_t MUse::index() const
+{
+    return consumer()->indexOf(this);
+}
+#endif
+
 template<size_t Op> static void
 ConvertDefinitionToDouble(TempAllocator &alloc, MDefinition *def, MInstruction *consumer)
 {
     MInstruction *replace = MToDouble::New(alloc, def);
     consumer->replaceOperand(Op, replace);
     consumer->block()->insertBefore(consumer, replace);
 }
 
@@ -375,90 +382,30 @@ MDefinition::hasLiveDefUses() const
             continue;
         if (!ins->toDefinition()->isRecoveredOnBailout())
             return true;
     }
 
     return false;
 }
 
-MUseIterator
-MDefinition::removeUse(MUseIterator use)
-{
-    return uses_.removeAt(use);
-}
-
-MUseIterator
-MNode::replaceOperand(MUseIterator use, MDefinition *def)
-{
-    JS_ASSERT(def != nullptr);
-    uint32_t index = use->index();
-    MDefinition *prev = use->producer();
-
-    JS_ASSERT(use->index() < numOperands());
-    JS_ASSERT(use->producer() == getOperand(index));
-    JS_ASSERT(use->consumer() == this);
-
-    if (prev == def)
-        return use;
-
-    MUseIterator result(prev->removeUse(use));
-    setOperand(index, def);
-    return result;
-}
-
-void
-MNode::replaceOperand(size_t index, MDefinition *def)
-{
-    JS_ASSERT(def != nullptr);
-    MUse *use = getUseFor(index);
-    MDefinition *prev = use->producer();
-
-    JS_ASSERT(use->index() == index);
-    JS_ASSERT(use->index() < numOperands());
-    JS_ASSERT(use->producer() == getOperand(index));
-    JS_ASSERT(use->consumer() == this);
-
-    if (prev == def)
-        return;
-
-    prev->removeUse(use);
-    setOperand(index, def);
-}
-
-void
-MNode::discardOperand(size_t index)
-{
-    MUse *use = getUseFor(index);
-
-    JS_ASSERT(use->index() == index);
-    JS_ASSERT(use->producer() == getOperand(index));
-    JS_ASSERT(use->consumer() == this);
-
-    use->producer()->removeUse(use);
-
-#ifdef DEBUG
-    // Causes any producer/consumer lookups to trip asserts.
-    use->set(nullptr, nullptr, index);
-#endif
-}
-
 void
 MDefinition::replaceAllUsesWith(MDefinition *dom)
 {
     JS_ASSERT(dom != nullptr);
     if (dom == this)
         return;
 
     for (size_t i = 0, e = numOperands(); i < e; i++)
         getOperand(i)->setUseRemovedUnchecked();
 
     for (MUseIterator i(usesBegin()); i != usesEnd(); ) {
-        JS_ASSERT(i->producer() == this);
-        i = i->consumer()->replaceOperand(i, dom);
+        MUse *use = *i++;
+        JS_ASSERT(use->producer() == this);
+        use->replaceProducer(dom);
     }
 }
 
 bool
 MDefinition::emptyResultTypeSet() const
 {
     return resultTypeSet() && resultTypeSet()->empty();
 }
@@ -619,17 +566,17 @@ MCloneLiteral::New(TempAllocator &alloc,
     return new(alloc) MCloneLiteral(obj);
 }
 
 void
 MControlInstruction::printOpcode(FILE *fp) const
 {
     MDefinition::printOpcode(fp);
     for (size_t j = 0; j < numSuccessors(); j++)
-        fprintf(fp, " block%d", getSuccessor(j)->id());
+        fprintf(fp, " block%u", getSuccessor(j)->id());
 }
 
 void
 MCompare::printOpcode(FILE *fp) const
 {
     MDefinition::printOpcode(fp);
     fprintf(fp, " %s", js_CodeName[jsop()]);
 }
@@ -963,42 +910,41 @@ MTypeBarrier::printOpcode(FILE *fp) cons
     PrintOpcodeName(fp, op());
     fprintf(fp, " ");
     getOperand(0)->printName(fp);
 }
 
 void
 MPhi::removeOperand(size_t index)
 {
-    MUse *use = getUseFor(index);
-
-    JS_ASSERT(index < inputs_.length());
-    JS_ASSERT(inputs_.length() > 1);
-
-    JS_ASSERT(use->index() == index);
-    JS_ASSERT(use->producer() == getOperand(index));
-    JS_ASSERT(use->consumer() == this);
-
-    // Remove use from producer's use chain.
-    use->producer()->removeUse(use);
+    JS_ASSERT(index < numOperands());
+    JS_ASSERT(numOperands() > 1);
+    JS_ASSERT(getUseFor(index)->index() == index);
+    JS_ASSERT(getUseFor(index)->consumer() == this);
 
     // If we have phi(..., a, b, c, d, ..., z) and we plan
     // on removing a, then first shift downward so that we have
     // phi(..., b, c, d, ..., z, z):
     size_t length = inputs_.length();
-    for (size_t i = index; i < length - 1; i++) {
-        MUse *next = MPhi::getUseFor(i + 1);
-        next->producer()->removeUse(next);
-        MPhi::setOperand(i, next->producer());
-    }
+    for (size_t i = index; i < length - 1; i++)
+        inputs_[i].replaceProducer(inputs_[i + 1].producer());
 
     // truncate the inputs_ list:
+    inputs_[length - 1].discardProducer();
     inputs_.shrinkBy(1);
 }
 
+void
+MPhi::removeAllOperands()
+{
+    for (size_t i = 0; i < inputs_.length(); i++)
+        inputs_[i].discardProducer();
+    inputs_.clear();
+}
+
 MDefinition *
 MPhi::operandIfRedundant()
 {
     JS_ASSERT(inputs_.length() != 0);
 
     // If this phi is redundant (e.g., phi(a,a) or b=phi(a,this)),
     // returns the operand that it will always be equal to (a, in
     // those two cases).
@@ -1195,19 +1141,18 @@ MPhi::typeIncludes(MDefinition *def)
 
 void
 MPhi::addInput(MDefinition *ins)
 {
     // This can only been done if the length was reserved through reserveLength,
     // else the slower addInputSlow need to get called.
     JS_ASSERT(inputs_.length() < capacity_);
 
-    uint32_t index = inputs_.length();
     inputs_.append(MUse());
-    MPhi::setOperand(index, ins);
+    inputs_.back().init(ins, this);
 }
 
 bool
 MPhi::addInputSlow(MDefinition *ins, bool *ptypeChange)
 {
     // The list of inputs to an MPhi is given as a vector of MUse nodes,
     // each of which is in the list of the producer MDefinition.
     // Because appending to a vector may reallocate the vector, it is possible
@@ -1224,17 +1169,17 @@ MPhi::addInputSlow(MDefinition *ins, boo
             use->producer()->removeUse(use);
         }
     }
 
     // Insert the new input.
     if (!inputs_.append(MUse()))
         return false;
 
-    MPhi::setOperand(index, ins);
+    inputs_.back().init(ins, this);
 
     if (ptypeChange) {
         MIRType resultType = this->type();
         types::TemporaryTypeSet *resultTypeSet = this->resultTypeSet();
 
         if (!MergeTypes(&resultType, &resultTypeSet, ins->type(), ins->resultTypeSet()))
             return false;
 
@@ -1256,17 +1201,18 @@ MPhi::addInputSlow(MDefinition *ins, boo
     return true;
 }
 
 void
 MCall::addArg(size_t argnum, MDefinition *arg)
 {
     // The operand vector is initialized in reverse order by the IonBuilder.
     // It cannot be checked for consistency until all arguments are added.
-    setOperand(argnum + NumNonArgumentOperands, arg);
+    // FixedList doesn't initialize its elements, so do an unchecked init.
+    initOperand(argnum + NumNonArgumentOperands, arg);
 }
 
 void
 MBitNot::infer()
 {
     if (getOperand(0)->mightBeType(MIRType_Object))
         specialization_ = MIRType_None;
     else
@@ -2319,32 +2265,35 @@ MResumePoint::New(TempAllocator &alloc, 
         return nullptr;
     resume->inherit(block);
     return resume;
 }
 
 MResumePoint::MResumePoint(MBasicBlock *block, jsbytecode *pc, MResumePoint *caller,
                            Mode mode)
   : MNode(block),
-    stackDepth_(block->stackDepth()),
     pc_(pc),
     caller_(caller),
     instruction_(nullptr),
     mode_(mode)
 {
     block->addResumePoint(this);
 }
 
+bool MResumePoint::init(TempAllocator &alloc)
+{
+    return operands_.init(alloc, block()->stackDepth());
+}
+
 void
 MResumePoint::inherit(MBasicBlock *block)
 {
-    for (size_t i = 0; i < stackDepth(); i++) {
-        MDefinition *def = block->getSlot(i);
-        setOperand(i, def);
-    }
+    // FixedList doesn't initialize its elements, so do unchecked inits.
+    for (size_t i = 0; i < stackDepth(); i++)
+        initOperand(i, block->getSlot(i));
 }
 
 void
 MResumePoint::dump(FILE *fp) const
 {
     fprintf(fp, "resumepoint mode=");
 
     switch (mode()) {
@@ -2374,16 +2323,22 @@ MResumePoint::dump(FILE *fp) const
 
 void
 MResumePoint::dump() const
 {
     dump(stderr);
 }
 
 bool
+MResumePoint::isObservableOperand(MUse *u) const
+{
+    return isObservableOperand(indexOf(u));
+}
+
+bool
 MResumePoint::isObservableOperand(size_t index) const
 {
     return block()->info().isObservableSlot(index);
 }
 
 MDefinition *
 MToInt32::foldsTo(TempAllocator &alloc, bool useValueNumbers)
 {
@@ -3089,20 +3044,21 @@ MAsmJSCall::New(TempAllocator &alloc, co
 
     if (!call->argRegs_.init(alloc, args.length()))
         return nullptr;
     for (size_t i = 0; i < call->argRegs_.length(); i++)
         call->argRegs_[i] = args[i].reg;
 
     if (!call->operands_.init(alloc, call->argRegs_.length() + (callee.which() == Callee::Dynamic ? 1 : 0)))
         return nullptr;
+    // FixedList doesn't initialize its elements, so do an unchecked init.
     for (size_t i = 0; i < call->argRegs_.length(); i++)
-        call->setOperand(i, args[i].def);
+        call->initOperand(i, args[i].def);
     if (callee.which() == Callee::Dynamic)
-        call->setOperand(call->argRegs_.length(), callee.dynamic());
+        call->initOperand(call->argRegs_.length(), callee.dynamic());
 
     return call;
 }
 
 void
 MSqrt::trySpecializeFloat32(TempAllocator &alloc) {
     if (!input()->canProduceFloat32() || !CheckUsesAreFloat32Consumers(this)) {
         if (input()->type() == MIRType_Float32)
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -62,17 +62,16 @@ MIRType MIRTypeFromValue(const js::Value
 #define MIR_FLAG_LIST(_)                                                        \
     _(InWorklist)                                                               \
     _(EmittedAtUses)                                                            \
     _(LoopInvariant)                                                            \
     _(Commutative)                                                              \
     _(Movable)       /* Allow LICM and GVN to move this instruction */          \
     _(Lowered)       /* (Debug only) has a virtual register */                  \
     _(Guard)         /* Not removable if uses == 0 */                           \
-    _(Observed)      /* Cannot be optimized out */                              \
                                                                                 \
     /* Keep the flagged instruction in resume points and do not substitute this
      * instruction by an UndefinedValue. This might be used by call inlining
      * when a function argument is not used by the inlined instructions.
      */                                                                         \
     _(ImplicitlyUsed)                                                           \
                                                                                 \
     /* The instruction has been marked dead for lazy removal from resume
@@ -110,51 +109,57 @@ class MControlInstruction;
 
 // Represents a use of a node.
 class MUse : public TempObject, public InlineListNode<MUse>
 {
     friend class MDefinition;
 
     MDefinition *producer_; // MDefinition that is being used.
     MNode *consumer_;       // The node that is using this operand.
-    uint32_t index_;        // The index of this operand in its consumer.
-
-    MUse(MDefinition *producer, MNode *consumer, uint32_t index)
+
+    MUse(MDefinition *producer, MNode *consumer)
       : producer_(producer),
-        consumer_(consumer),
-        index_(index)
+        consumer_(consumer)
     { }
 
   public:
     // Default constructor for use in vectors.
     MUse()
-      : producer_(nullptr), consumer_(nullptr), index_(0)
+      : producer_(nullptr), consumer_(nullptr)
     { }
 
-    // Set data inside the MUse.
-    void set(MDefinition *producer, MNode *consumer, uint32_t index) {
-        producer_ = producer;
-        consumer_ = consumer;
-        index_ = index;
-    }
+    // Set this use, which was previously clear.
+    inline void init(MDefinition *producer, MNode *consumer);
+    // Like init, but works even when the use contains uninitialized data.
+    inline void initUnchecked(MDefinition *producer, MNode *consumer);
+    // Like initUnchecked, but set the producer to nullptr.
+    inline void initUncheckedWithoutProducer(MNode *consumer);
+    // Set this use, which was not previously clear.
+    inline void replaceProducer(MDefinition *producer);
+    // Clear this use.
+    inline void discardProducer();
 
     MDefinition *producer() const {
         JS_ASSERT(producer_ != nullptr);
         return producer_;
     }
     bool hasProducer() const {
         return producer_ != nullptr;
     }
     MNode *consumer() const {
         JS_ASSERT(consumer_ != nullptr);
         return consumer_;
     }
-    uint32_t index() const {
-        return index_;
-    }
+
+#ifdef DEBUG
+    // Return the operand index of this MUse in its consumer. This is DEBUG-only
+    // as normal code should instead to call indexOf on the casted consumer
+    // directly, to allow it to be devirtualized and inlined.
+    size_t index() const;
+#endif
 };
 
 typedef InlineList<MUse>::iterator MUseIterator;
 
 // A node is an entry in the MIR graph. It has two kinds:
 //   MInstruction: an instruction which appears in the IR stream.
 //   MResumePoint: a list of instructions that correspond to the state of the
 //                 interpreter/Baseline stack.
@@ -182,16 +187,17 @@ class MNode : public TempObject
       : block_(block)
     { }
 
     virtual Kind kind() const = 0;
 
     // Returns the definition at a given operand.
     virtual MDefinition *getOperand(size_t index) const = 0;
     virtual size_t numOperands() const = 0;
+    virtual size_t indexOf(const MUse *u) const = 0;
 
     bool isDefinition() const {
         return kind() == Definition;
     }
     bool isResumePoint() const {
         return kind() == ResumePoint;
     }
     MBasicBlock *block() const {
@@ -199,40 +205,38 @@ class MNode : public TempObject
     }
 
     // Instructions needing to hook into type analysis should return a
     // TypePolicy.
     virtual TypePolicy *typePolicy() {
         return nullptr;
     }
 
-    // Replaces an already-set operand during iteration over a use chain.
-    MUseIterator replaceOperand(MUseIterator use, MDefinition *ins);
-
-    // Replaces an already-set operand, updating use information.
-    void replaceOperand(size_t index, MDefinition *ins);
+    // Sets an already set operand, updating use information. If you're looking
+    // for setOperand, this is probably what you want.
+    virtual void replaceOperand(size_t index, MDefinition *operand) = 0;
 
     // Resets the operand to an uninitialized state, breaking the link
     // with the previous operand's producer.
-    void discardOperand(size_t index);
+    void discardOperand(size_t index) {
+        getUseFor(index)->discardProducer();
+    }
 
     inline MDefinition *toDefinition();
     inline MResumePoint *toResumePoint();
 
     virtual bool writeRecoverData(CompactBufferWriter &writer) const;
 
     virtual void dump(FILE *fp) const = 0;
     virtual void dump() const = 0;
 
   protected:
-    // Sets an unset operand, updating use information.
-    virtual void setOperand(size_t index, MDefinition *operand) = 0;
-
     // Gets the MUse corresponding to given operand.
     virtual MUse *getUseFor(size_t index) = 0;
+    virtual const MUse *getUseFor(size_t index) const = 0;
 };
 
 class AliasSet {
   private:
     uint32_t flags_;
 
   public:
     enum Flag {
@@ -579,17 +583,16 @@ class MDefinition : public MNode
         return uses_.end();
     }
 
     bool canEmitAtUses() const {
         return !isEmittedAtUses();
     }
 
     // Removes a use at the given position
-    MUseIterator removeUse(MUseIterator use);
     void removeUse(MUse *use) {
         uses_.remove(use);
     }
 
 #ifdef DEBUG
     // Number of uses of this instruction. This function is only available
     // in DEBUG mode since it requires traversing the list. Most users should
     // use hasUses() or hasOneUse() instead.
@@ -617,16 +620,19 @@ class MDefinition : public MNode
     // (only counting MDefinitions, ignoring MResumePoints)
     bool hasLiveDefUses() const;
 
     bool hasUses() const {
         return !uses_.empty();
     }
 
     void addUse(MUse *use) {
+        // The use can't be in the list at all, but we only check the first
+        // element for now, as that's where it's most likely to be.
+        MOZ_ASSERT(uses_.empty() || use != *uses_.begin());
         uses_.pushFront(use);
     }
     void replaceAllUsesWith(MDefinition *dom);
 
     // Mark this instruction as having replaced all uses of ins, as during GVN,
     // returning false if the replacement should not be performed. For use when
     // GVN eliminates instructions which are not equivalent to one another.
     virtual bool updateForReplacement(MDefinition *ins) {
@@ -734,19 +740,16 @@ class MUseDefIterator
         return old;
     }
     MUse *use() const {
         return *current_;
     }
     MDefinition *def() const {
         return current_->consumer()->toDefinition();
     }
-    size_t index() const {
-        return current_->index();
-    }
 };
 
 // An instruction is an SSA name that is inserted into a basic block's IR
 // stream.
 class MInstruction
   : public MDefinition,
     public InlineListNode<MInstruction>
 {
@@ -777,61 +780,70 @@ class MInstruction
     }                                                                       \
     bool accept(MInstructionVisitor *visitor) {                             \
         return visitor->visit##opcode(this);                                \
     }
 
 template <size_t Arity>
 class MAryInstruction : public MInstruction
 {
+    mozilla::Array<MUse, Arity> operands_;
+
   protected:
-    mozilla::Array<MUse, Arity> operands_;
-
-    void setOperand(size_t index, MDefinition *operand) MOZ_FINAL MOZ_OVERRIDE {
-        operands_[index].set(operand, this, index);
-        operand->addUse(&operands_[index]);
-    }
-
     MUse *getUseFor(size_t index) MOZ_FINAL MOZ_OVERRIDE {
         return &operands_[index];
     }
+    const MUse *getUseFor(size_t index) const MOZ_FINAL MOZ_OVERRIDE {
+        return &operands_[index];
+    }
+    void initOperand(size_t index, MDefinition *operand) {
+        operands_[index].init(operand, this);
+    }
 
   public:
     MDefinition *getOperand(size_t index) const MOZ_FINAL MOZ_OVERRIDE {
         return operands_[index].producer();
     }
     size_t numOperands() const MOZ_FINAL MOZ_OVERRIDE {
         return Arity;
     }
+    size_t indexOf(const MUse *u) const MOZ_FINAL MOZ_OVERRIDE {
+        MOZ_ASSERT(u >= &operands_[0]);
+        MOZ_ASSERT(u <= &operands_[numOperands() - 1]);
+        return u - &operands_[0];
+    }
+    void replaceOperand(size_t index, MDefinition *operand) MOZ_FINAL MOZ_OVERRIDE {
+        operands_[index].replaceProducer(operand);
+    }
 };
 
 class MNullaryInstruction : public MAryInstruction<0>
 { };
 
 class MUnaryInstruction : public MAryInstruction<1>
 {
   protected:
     MUnaryInstruction(MDefinition *ins)
     {
-        setOperand(0, ins);
+        initOperand(0, ins);
     }
 
   public:
     MDefinition *input() const {
         return getOperand(0);
     }
 };
 
 class MBinaryInstruction : public MAryInstruction<2>
 {
   protected:
     MBinaryInstruction(MDefinition *left, MDefinition *right)
     {
-        setOperand(0, left);
-        setOperand(1, right);
+        initOperand(0, left);
+        initOperand(1, right);
     }
 
   public:
     MDefinition *lhs() const {
         return getOperand(0);
     }
     MDefinition *rhs() const {
         return getOperand(1);
@@ -891,19 +903,19 @@ class MBinaryInstruction : public MAryIn
     bool tryUseUnsignedOperands();
 };
 
 class MTernaryInstruction : public MAryInstruction<3>
 {
   protected:
     MTernaryInstruction(MDefinition *first, MDefinition *second, MDefinition *third)
     {
-        setOperand(0, first);
-        setOperand(1, second);
-        setOperand(2, third);
+        initOperand(0, first);
+        initOperand(1, second);
+        initOperand(2, third);
     }
 
   protected:
     HashNumber valueHash() const
     {
         MDefinition *first = getOperand(0);
         MDefinition *second = getOperand(1);
         MDefinition *third = getOperand(2);
@@ -913,20 +925,20 @@ class MTernaryInstruction : public MAryI
 };
 
 class MQuaternaryInstruction : public MAryInstruction<4>
 {
   protected:
     MQuaternaryInstruction(MDefinition *first, MDefinition *second,
                            MDefinition *third, MDefinition *fourth)
     {
-        setOperand(0, first);
-        setOperand(1, second);
-        setOperand(2, third);
-        setOperand(3, fourth);
+        initOperand(0, first);
+        initOperand(1, second);
+        initOperand(2, third);
+        initOperand(3, fourth);
     }
 
   protected:
     HashNumber valueHash() const
     {
         MDefinition *first = getOperand(0);
         MDefinition *second = getOperand(1);
         MDefinition *third = getOperand(2);
@@ -1155,35 +1167,39 @@ class MTableSwitch MOZ_FINAL
 
     // Contains the blocks/cases that still need to get build
     Vector<MBasicBlock*, 0, IonAllocPolicy> blocks_;
 
     MUse operand_;
     int32_t low_;
     int32_t high_;
 
+    void initOperand(size_t index, MDefinition *operand) {
+        JS_ASSERT(index == 0);
+        operand_.init(operand, this);
+    }
+
     MTableSwitch(TempAllocator &alloc, MDefinition *ins,
                  int32_t low, int32_t high)
       : successors_(alloc),
         cases_(alloc),
         blocks_(alloc),
         low_(low),
         high_(high)
     {
-        setOperand(0, ins);
+        initOperand(0, ins);
     }
 
   protected:
-    void setOperand(size_t index, MDefinition *operand) {
+    MUse *getUseFor(size_t index) {
         JS_ASSERT(index == 0);
-        operand_.set(operand, this, index);
-        operand->addUse(&operand_);
-    }
-
-    MUse *getUseFor(size_t index) {
+        return &operand_;
+    }
+
+    const MUse *getUseFor(size_t index) const {
         JS_ASSERT(index == 0);
         return &operand_;
     }
 
   public:
     INSTRUCTION_HEADER(TableSwitch)
     static MTableSwitch *New(TempAllocator &alloc, MDefinition *ins, int32_t low, int32_t high);
 
@@ -1259,47 +1275,67 @@ class MTableSwitch MOZ_FINAL
         JS_ASSERT(index == 0);
         return operand_.producer();
     }
 
     size_t numOperands() const {
         return 1;
     }
 
+    size_t indexOf(const MUse *u) const MOZ_FINAL MOZ_OVERRIDE {
+        MOZ_ASSERT(u == getUseFor(0));
+        return 0;
+    }
+
+    void replaceOperand(size_t index, MDefinition *operand) MOZ_FINAL MOZ_OVERRIDE {
+        JS_ASSERT(index == 0);
+        operand_.replaceProducer(operand);
+    }
+
     TypePolicy *typePolicy() {
         return this;
     }
 };
 
 template <size_t Arity, size_t Successors>
 class MAryControlInstruction : public MControlInstruction
 {
     mozilla::Array<MUse, Arity> operands_;
     mozilla::Array<MBasicBlock *, Successors> successors_;
 
   protected:
-    void setOperand(size_t index, MDefinition *operand) MOZ_FINAL MOZ_OVERRIDE {
-        operands_[index].set(operand, this, index);
-        operand->addUse(&operands_[index]);
-    }
     void setSuccessor(size_t index, MBasicBlock *successor) {
         successors_[index] = successor;
     }
 
     MUse *getUseFor(size_t index) MOZ_FINAL MOZ_OVERRIDE {
         return &operands_[index];
     }
+    const MUse *getUseFor(size_t index) const MOZ_FINAL MOZ_OVERRIDE {
+        return &operands_[index];
+    }
+    void initOperand(size_t index, MDefinition *operand) {
+        operands_[index].init(operand, this);
+    }
 
   public:
     MDefinition *getOperand(size_t index) const MOZ_FINAL MOZ_OVERRIDE {
         return operands_[index].producer();
     }
     size_t numOperands() const MOZ_FINAL MOZ_OVERRIDE {
         return Arity;
     }
+    size_t indexOf(const MUse *u) const MOZ_FINAL MOZ_OVERRIDE {
+        MOZ_ASSERT(u >= &operands_[0]);
+        MOZ_ASSERT(u <= &operands_[numOperands() - 1]);
+        return u - &operands_[0];
+    }
+    void replaceOperand(size_t index, MDefinition *operand) MOZ_FINAL MOZ_OVERRIDE {
+        operands_[index].replaceProducer(operand);
+    }
     size_t numSuccessors() const MOZ_FINAL MOZ_OVERRIDE {
         return Successors;
     }
     MBasicBlock *getSuccessor(size_t i) const MOZ_FINAL MOZ_OVERRIDE {
         return successors_[i];
     }
     void replaceSuccessor(size_t i, MBasicBlock *succ) MOZ_FINAL MOZ_OVERRIDE {
         successors_[i] = succ;
@@ -1342,17 +1378,17 @@ class MTest
   : public MAryControlInstruction<1, 2>,
     public TestPolicy
 {
     bool operandMightEmulateUndefined_;
 
     MTest(MDefinition *ins, MBasicBlock *if_true, MBasicBlock *if_false)
       : operandMightEmulateUndefined_(true)
     {
-        setOperand(0, ins);
+        initOperand(0, ins);
         setSuccessor(0, if_true);
         setSuccessor(1, if_false);
     }
 
   public:
     INSTRUCTION_HEADER(Test)
     static MTest *New(TempAllocator &alloc, MDefinition *ins,
                       MBasicBlock *ifTrue, MBasicBlock *ifFalse);
@@ -1401,17 +1437,17 @@ class MTest
 };
 
 // Returns from this function to the previous caller.
 class MReturn
   : public MAryControlInstruction<1, 0>,
     public BoxInputsPolicy
 {
     MReturn(MDefinition *ins) {
-        setOperand(0, ins);
+        initOperand(0, ins);
     }
 
   public:
     INSTRUCTION_HEADER(Return)
     static MReturn *New(TempAllocator &alloc, MDefinition *ins) {
         return new(alloc) MReturn(ins);
     }
 
@@ -1426,17 +1462,17 @@ class MReturn
     }
 };
 
 class MThrow
   : public MAryControlInstruction<1, 0>,
     public BoxInputsPolicy
 {
     MThrow(MDefinition *ins) {
-        setOperand(0, ins);
+        initOperand(0, ins);
     }
 
   public:
     INSTRUCTION_HEADER(Throw)
     static MThrow *New(TempAllocator &alloc, MDefinition *ins) {
         return new(alloc) MThrow(ins);
     }
 
@@ -1709,18 +1745,18 @@ class MAbortPar : public MAryControlInst
 // Setting __proto__ in an object literal.
 class MMutateProto
   : public MAryInstruction<2>,
     public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >
 {
   protected:
     MMutateProto(MDefinition *obj, MDefinition *value)
     {
-        setOperand(0, obj);
-        setOperand(1, value);
+        initOperand(0, obj);
+        initOperand(1, value);
         setResultType(MIRType_None);
     }
 
   public:
     INSTRUCTION_HEADER(MutateProto)
 
     static MMutateProto *New(TempAllocator &alloc, MDefinition *obj, MDefinition *value)
     {
@@ -1749,18 +1785,18 @@ class MInitProp
 {
   public:
     CompilerRootPropertyName name_;
 
   protected:
     MInitProp(MDefinition *obj, PropertyName *name, MDefinition *value)
       : name_(name)
     {
-        setOperand(0, obj);
-        setOperand(1, value);
+        initOperand(0, obj);
+        initOperand(1, value);
         setResultType(MIRType_None);
     }
 
   public:
     INSTRUCTION_HEADER(InitProp)
 
     static MInitProp *New(TempAllocator &alloc, MDefinition *obj, PropertyName *name,
                           MDefinition *value)
@@ -1821,19 +1857,19 @@ class MInitPropGetterSetter
 };
 
 class MInitElem
   : public MAryInstruction<3>,
     public Mix3Policy<ObjectPolicy<0>, BoxPolicy<1>, BoxPolicy<2> >
 {
     MInitElem(MDefinition *obj, MDefinition *id, MDefinition *value)
     {
-        setOperand(0, obj);
-        setOperand(1, id);
-        setOperand(2, value);
+        initOperand(0, obj);
+        initOperand(1, id);
+        initOperand(2, value);
         setResultType(MIRType_None);
     }
 
   public:
     INSTRUCTION_HEADER(InitElem)
 
     static MInitElem *New(TempAllocator &alloc, MDefinition *obj, MDefinition *id,
                           MDefinition *value)
@@ -1892,32 +1928,42 @@ class MInitElemGetterSetter
 class MVariadicInstruction : public MInstruction
 {
     FixedList<MUse> operands_;
 
   protected:
     bool init(TempAllocator &alloc, size_t length) {
         return operands_.init(alloc, length);
     }
+    void initOperand(size_t index, MDefinition *operand) {
+        // FixedList doesn't initialize its elements, so do an unchecked init.
+        operands_[index].initUnchecked(operand, this);
+    }
+    MUse *getUseFor(size_t index) MOZ_FINAL MOZ_OVERRIDE {
+        return &operands_[index];
+    }
+    const MUse *getUseFor(size_t index) const MOZ_FINAL MOZ_OVERRIDE {
+        return &operands_[index];
+    }
 
   public:
     // Will assert if called before initialization.
     MDefinition *getOperand(size_t index) const MOZ_FINAL MOZ_OVERRIDE {
         return operands_[index].producer();
     }
     size_t numOperands() const MOZ_FINAL MOZ_OVERRIDE {
         return operands_.length();
     }
-    void setOperand(size_t index, MDefinition *operand) MOZ_FINAL MOZ_OVERRIDE {
-        operands_[index].set(operand, this, index);
-        operand->addUse(&operands_[index]);
-    }
-
-    MUse *getUseFor(size_t index) MOZ_FINAL MOZ_OVERRIDE {
-        return &operands_[index];
+    size_t indexOf(const MUse *u) const MOZ_FINAL MOZ_OVERRIDE {
+        MOZ_ASSERT(u >= &operands_[0]);
+        MOZ_ASSERT(u <= &operands_[numOperands() - 1]);
+        return u - &operands_[0];
+    }
+    void replaceOperand(size_t index, MDefinition *operand) MOZ_FINAL MOZ_OVERRIDE {
+        operands_[index].replaceProducer(operand);
     }
 };
 
 class MCall
   : public MVariadicInstruction,
     public CallPolicy
 {
   private:
@@ -1946,17 +1992,17 @@ class MCall
     }
 
   public:
     INSTRUCTION_HEADER(Call)
     static MCall *New(TempAllocator &alloc, JSFunction *target, size_t maxArgc, size_t numActualArgs,
                       bool construct, bool isDOMCall);
 
     void initFunction(MDefinition *func) {
-        return setOperand(FunctionOperandIndex, func);
+        initOperand(FunctionOperandIndex, func);
     }
 
     bool needsArgCheck() const {
         return needsArgCheck_;
     }
 
     void disableArgCheck() {
         needsArgCheck_ = false;
@@ -2109,19 +2155,19 @@ class MApplyArgs
 {
   protected:
     // Monomorphic cache of single target from TI, or nullptr.
     CompilerRootFunction target_;
 
     MApplyArgs(JSFunction *target, MDefinition *fun, MDefinition *argc, MDefinition *self)
       : target_(target)
     {
-        setOperand(0, fun);
-        setOperand(1, argc);
-        setOperand(2, self);
+        initOperand(0, fun);
+        initOperand(1, argc);
+        initOperand(2, self);
         setResultType(MIRType_Value);
     }
 
   public:
     INSTRUCTION_HEADER(ApplyArgs)
     static MApplyArgs *New(TempAllocator &alloc, JSFunction *target, MDefinition *fun,
                            MDefinition *argc, MDefinition *self);
 
@@ -2194,18 +2240,18 @@ class MAssertFloat32 : public MUnaryInst
 
 class MGetDynamicName
   : public MAryInstruction<2>,
     public MixPolicy<ObjectPolicy<0>, ConvertToStringPolicy<1> >
 {
   protected:
     MGetDynamicName(MDefinition *scopeChain, MDefinition *name)
     {
-        setOperand(0, scopeChain);
-        setOperand(1, name);
+        initOperand(0, scopeChain);
+        initOperand(1, name);
         setResultType(MIRType_Value);
     }
 
   public:
     INSTRUCTION_HEADER(GetDynamicName)
 
     static MGetDynamicName *
     New(TempAllocator &alloc, MDefinition *scopeChain, MDefinition *name) {
@@ -2230,17 +2276,17 @@ class MGetDynamicName
 // Bailout if the input string contains 'arguments' or 'eval'.
 class MFilterArgumentsOrEval
   : public MAryInstruction<1>,
     public BoxExceptPolicy<0, MIRType_String>
 {
   protected:
     MFilterArgumentsOrEval(MDefinition *string)
     {
-        setOperand(0, string);
+        initOperand(0, string);
         setGuard();
         setResultType(MIRType_None);
     }
 
   public:
     INSTRUCTION_HEADER(FilterArgumentsOrEval)
 
     static MFilterArgumentsOrEval *New(TempAllocator &alloc, MDefinition *string) {
@@ -2264,19 +2310,19 @@ class MCallDirectEval
     public MixPolicy<ObjectPolicy<0>,
                      MixPolicy<BoxExceptPolicy<1, MIRType_String>, BoxPolicy<2> > >
 {
   protected:
     MCallDirectEval(MDefinition *scopeChain, MDefinition *string, MDefinition *thisValue,
                     jsbytecode *pc)
         : pc_(pc)
     {
-        setOperand(0, scopeChain);
-        setOperand(1, string);
-        setOperand(2, thisValue);
+        initOperand(0, scopeChain);
+        initOperand(1, string);
+        initOperand(2, thisValue);
         setResultType(MIRType_Value);
     }
 
   public:
     INSTRUCTION_HEADER(CallDirectEval)
 
     static MCallDirectEval *
     New(TempAllocator &alloc, MDefinition *scopeChain, MDefinition *string, MDefinition *thisValue,
@@ -2942,18 +2988,18 @@ class MRunOncePrologue
 // If the Value may be safely unboxed to an Object, return Object(A).
 // Otherwise, return B.
 // Used to implement return behavior for inlined constructors.
 class MReturnFromCtor
   : public MAryInstruction<2>,
     public MixPolicy<BoxPolicy<0>, ObjectPolicy<1> >
 {
     MReturnFromCtor(MDefinition *value, MDefinition *object) {
-        setOperand(0, value);
-        setOperand(1, object);
+        initOperand(0, value);
+        initOperand(1, object);
         setResultType(MIRType_Object);
     }
 
   public:
     INSTRUCTION_HEADER(ReturnFromCtor)
     static MReturnFromCtor *New(TempAllocator &alloc, MDefinition *value, MDefinition *object)
     {
         return new(alloc) MReturnFromCtor(value, object);
@@ -4706,16 +4752,24 @@ class MPhi MOZ_FINAL : public MDefinitio
 
 #if DEBUG
     bool specialized_;
     uint32_t capacity_;
 #endif
 
   protected:
     MUse *getUseFor(size_t index) {
+        // Note: after the initial IonBuilder pass, it is OK to change phi
+        // operands such that they do not include the type sets of their
+        // operands. This can arise during e.g. value numbering, where
+        // definitions producing the same value may have different type sets.
+        JS_ASSERT(index < numOperands());
+        return &inputs_[index];
+    }
+    const MUse *getUseFor(size_t index) const {
         return &inputs_[index];
     }
 
   public:
     INSTRUCTION_HEADER(Phi)
 
     MPhi(TempAllocator &alloc, MIRType resultType)
       : inputs_(alloc),
@@ -4731,34 +4785,33 @@ class MPhi MOZ_FINAL : public MDefinitio
     {
         setResultType(resultType);
     }
 
     static MPhi *New(TempAllocator &alloc, MIRType resultType = MIRType_Value) {
         return new(alloc) MPhi(alloc, resultType);
     }
 
-    void setOperand(size_t index, MDefinition *operand) {
-        // Note: after the initial IonBuilder pass, it is OK to change phi
-        // operands such that they do not include the type sets of their
-        // operands. This can arise during e.g. value numbering, where
-        // definitions producing the same value may have different type sets.
-        JS_ASSERT(index < numOperands());
-        inputs_[index].set(operand, this, index);
-        operand->addUse(&inputs_[index]);
-    }
-
     void removeOperand(size_t index);
+    void removeAllOperands();
 
     MDefinition *getOperand(size_t index) const {
         return inputs_[index].producer();
     }
     size_t numOperands() const {
         return inputs_.length();
     }
+    size_t indexOf(const MUse *u) const MOZ_FINAL MOZ_OVERRIDE {
+        MOZ_ASSERT(u >= &inputs_[0]);
+        MOZ_ASSERT(u <= &inputs_[numOperands() - 1]);
+        return u - &inputs_[0];
+    }
+    void replaceOperand(size_t index, MDefinition *operand) MOZ_FINAL MOZ_OVERRIDE {
+        inputs_[index].replaceProducer(operand);
+    }
     bool hasBackedgeType() const {
         return hasBackedgeType_;
     }
     bool triedToSpecialize() const {
         return triedToSpecialize_;
     }
     void specialize(MIRType type) {
         triedToSpecialize_ = true;
@@ -5659,18 +5712,18 @@ class MInitializedLength
 };
 
 // Store to the initialized length in an elements header. Note the input is an
 // *index*, one less than the desired length.
 class MSetInitializedLength
   : public MAryInstruction<2>
 {
     MSetInitializedLength(MDefinition *elements, MDefinition *index) {
-        setOperand(0, elements);
-        setOperand(1, index);
+        initOperand(0, elements);
+        initOperand(1, index);
     }
 
   public:
     INSTRUCTION_HEADER(SetInitializedLength)
 
     static MSetInitializedLength *New(TempAllocator &alloc, MDefinition *elements, MDefinition *index) {
         return new(alloc) MSetInitializedLength(elements, index);
     }
@@ -5718,18 +5771,18 @@ class MArrayLength
 };
 
 // Store to the length in an elements header. Note the input is an *index*, one
 // less than the desired length.
 class MSetArrayLength
   : public MAryInstruction<2>
 {
     MSetArrayLength(MDefinition *elements, MDefinition *index) {
-        setOperand(0, elements);
-        setOperand(1, index);
+        initOperand(0, elements);
+        initOperand(1, index);
     }
 
   public:
     INSTRUCTION_HEADER(SetArrayLength)
 
     static MSetArrayLength *New(TempAllocator &alloc, MDefinition *elements, MDefinition *index) {
         return new(alloc) MSetArrayLength(elements, index);
     }
@@ -6260,19 +6313,19 @@ class MStoreElementCommon
 class MStoreElement
   : public MAryInstruction<3>,
     public MStoreElementCommon,
     public MixPolicy<SingleObjectPolicy, NoFloatPolicy<2> >
 {
     bool needsHoleCheck_;
 
     MStoreElement(MDefinition *elements, MDefinition *index, MDefinition *value, bool needsHoleCheck) {
-        setOperand(0, elements);
-        setOperand(1, index);
-        setOperand(2, value);
+        initOperand(0, elements);
+        initOperand(1, index);
+        initOperand(2, value);
         needsHoleCheck_ = needsHoleCheck;
         JS_ASSERT(elements->type() == MIRType_Elements);
         JS_ASSERT(index->type() == MIRType_Int32);
     }
 
   public:
     INSTRUCTION_HEADER(StoreElement)
 
@@ -6309,20 +6362,20 @@ class MStoreElement
 // vector.
 class MStoreElementHole
   : public MAryInstruction<4>,
     public MStoreElementCommon,
     public MixPolicy<SingleObjectPolicy, NoFloatPolicy<3> >
 {
     MStoreElementHole(MDefinition *object, MDefinition *elements,
                       MDefinition *index, MDefinition *value) {
-        setOperand(0, object);
-        setOperand(1, elements);
-        setOperand(2, index);
-        setOperand(3, value);
+        initOperand(0, object);
+        initOperand(1, elements);
+        initOperand(2, index);
+        initOperand(3, value);
         JS_ASSERT(elements->type() == MIRType_Elements);
         JS_ASSERT(index->type() == MIRType_Int32);
     }
 
   public:
     INSTRUCTION_HEADER(StoreElementHole)
 
     static MStoreElementHole *New(TempAllocator &alloc, MDefinition *object, MDefinition *elements,
@@ -6717,34 +6770,34 @@ class MStoreTypedArrayElement
         return racy_;
     }
     void setRacy() {
         racy_ = true;
     }
     TruncateKind operandTruncateKind(size_t index) const;
 
     bool canConsumeFloat32(MUse *use) const {
-        return use->index() == 2 && arrayType_ == ScalarTypeDescr::TYPE_FLOAT32;
+        return use == getUseFor(2) && arrayType_ == ScalarTypeDescr::TYPE_FLOAT32;
     }
 };
 
 class MStoreTypedArrayElementHole
   : public MAryInstruction<4>,
     public StoreTypedArrayHolePolicy
 {
     int arrayType_;
 
     MStoreTypedArrayElementHole(MDefinition *elements, MDefinition *length, MDefinition *index,
                                 MDefinition *value, int arrayType)
       : MAryInstruction<4>(), arrayType_(arrayType)
     {
-        setOperand(0, elements);
-        setOperand(1, length);
-        setOperand(2, index);
-        setOperand(3, value);
+        initOperand(0, elements);
+        initOperand(1, length);
+        initOperand(2, index);
+        initOperand(3, value);
         setMovable();
         JS_ASSERT(elements->type() == MIRType_Elements);
         JS_ASSERT(length->type() == MIRType_Int32);
         JS_ASSERT(index->type() == MIRType_Int32);
         JS_ASSERT(arrayType >= 0 && arrayType < ScalarTypeDescr::TYPE_MAX);
     }
 
   public:
@@ -6785,17 +6838,17 @@ class MStoreTypedArrayElementHole
         return getOperand(3);
     }
     AliasSet getAliasSet() const {
         return AliasSet::Store(AliasSet::TypedArrayElement);
     }
     TruncateKind operandTruncateKind(size_t index) const;
 
     bool canConsumeFloat32(MUse *use) const {
-        return use->index() == 3 && arrayType_ == ScalarTypeDescr::TYPE_FLOAT32;
+        return use == getUseFor(3) && arrayType_ == ScalarTypeDescr::TYPE_FLOAT32;
     }
 };
 
 // Store a value infallibly to a statically known typed array.
 class MStoreTypedArrayElementStatic :
     public MBinaryInstruction
   , public StoreTypedArrayElementStaticPolicy
 {
@@ -6832,17 +6885,17 @@ class MStoreTypedArrayElementStatic :
     MDefinition *ptr() const { return getOperand(0); }
     MDefinition *value() const { return getOperand(1); }
     AliasSet getAliasSet() const {
         return AliasSet::Store(AliasSet::TypedArrayElement);
     }
     TruncateKind operandTruncateKind(size_t index) const;
 
     bool canConsumeFloat32(MUse *use) const {
-        return use->index() == 1 && typedArray_->type() == ScalarTypeDescr::TYPE_FLOAT32;
+        return use == getUseFor(1) && typedArray_->type() == ScalarTypeDescr::TYPE_FLOAT32;
     }
 };
 
 // Compute an "effective address", i.e., a compound computation of the form:
 //   base + index * scale + displacement
 class MEffectiveAddress : public MBinaryInstruction
 {
     MEffectiveAddress(MDefinition *base, MDefinition *index, Scale scale, int32_t displacement)
@@ -7333,40 +7386,52 @@ class MDispatchInstruction
         { }
     };
     Vector<Entry, 4, IonAllocPolicy> map_;
 
     // An optional fallback path that uses MCall.
     MBasicBlock *fallback_;
     MUse operand_;
 
+    void initOperand(size_t index, MDefinition *operand) {
+        JS_ASSERT(index == 0);
+        operand_.init(operand, this);
+    }
+
   public:
     MDispatchInstruction(TempAllocator &alloc, MDefinition *input)
       : map_(alloc), fallback_(nullptr)
     {
-        setOperand(0, input);
+        initOperand(0, input);
     }
 
   protected:
-    void setOperand(size_t index, MDefinition *operand) MOZ_FINAL MOZ_OVERRIDE {
+    MUse *getUseFor(size_t index) MOZ_FINAL MOZ_OVERRIDE {
         JS_ASSERT(index == 0);
-        operand_.set(operand, this, 0);
-        operand->addUse(&operand_);
-    }
-    MUse *getUseFor(size_t index) MOZ_FINAL MOZ_OVERRIDE {
+        return &operand_;
+    }
+    const MUse *getUseFor(size_t index) const MOZ_FINAL MOZ_OVERRIDE {
         JS_ASSERT(index == 0);
         return &operand_;
     }
     MDefinition *getOperand(size_t index) const MOZ_FINAL MOZ_OVERRIDE {
         JS_ASSERT(index == 0);
         return operand_.producer();
     }
     size_t numOperands() const MOZ_FINAL MOZ_OVERRIDE {
         return 1;
     }
+    size_t indexOf(const MUse *u) const MOZ_FINAL MOZ_OVERRIDE {
+        JS_ASSERT(u == getUseFor(0));
+        return 0;
+    }
+    void replaceOperand(size_t index, MDefinition *operand) MOZ_FINAL MOZ_OVERRIDE {
+        JS_ASSERT(index == 0);
+        operand_.replaceProducer(operand);
+    }
 
   public:
     void setSuccessor(size_t i, MBasicBlock *successor) {
         JS_ASSERT(i < numSuccessors());
         if (i == map_.length())
             fallback_ = successor;
         else
             map_[i].block = successor;
@@ -8294,17 +8359,17 @@ class MSetElementCache
     bool guardHoles() const {
         return guardHoles_;
     }
 
     TypePolicy *typePolicy() {
         return this;
     }
 
-    bool canConsumeFloat32(MUse *use) const { return use->index() == 2; }
+    bool canConsumeFloat32(MUse *use) const { return use == getUseFor(2); }
 };
 
 class MCallGetProperty
   : public MUnaryInstruction,
     public BoxInputsPolicy
 {
     CompilerRootPropertyName name_;
     bool idempotent_;
@@ -8411,18 +8476,18 @@ class MCallInitElementArray
   : public MAryInstruction<2>,
     public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >
 {
     uint32_t index_;
 
     MCallInitElementArray(MDefinition *obj, uint32_t index, MDefinition *val)
       : index_(index)
     {
-        setOperand(0, obj);
-        setOperand(1, val);
+        initOperand(0, obj);
+        initOperand(1, val);
     }
 
   public:
     INSTRUCTION_HEADER(CallInitElementArray)
 
     static MCallInitElementArray *New(TempAllocator &alloc, MDefinition *obj, uint32_t index,
                                       MDefinition *val)
     {
@@ -8453,18 +8518,18 @@ class MSetDOMProperty
   : public MAryInstruction<2>,
     public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >
 {
     const JSJitSetterOp func_;
 
     MSetDOMProperty(const JSJitSetterOp func, MDefinition *obj, MDefinition *val)
       : func_(func)
     {
-        setOperand(0, obj);
-        setOperand(1, val);
+        initOperand(0, obj);
+        initOperand(1, val);
     }
 
   public:
     INSTRUCTION_HEADER(SetDOMProperty)
 
     static MSetDOMProperty *New(TempAllocator &alloc, const JSJitSetterOp func, MDefinition *obj,
                                 MDefinition *val)
     {
@@ -8501,20 +8566,20 @@ class MGetDOMProperty
 
   protected:
     MGetDOMProperty(const JSJitInfo *jitinfo, MDefinition *obj, MDefinition *guard)
       : info_(jitinfo)
     {
         JS_ASSERT(jitinfo);
         JS_ASSERT(jitinfo->type() == JSJitInfo::Getter);
 
-        setOperand(0, obj);
+        initOperand(0, obj);
 
         // Pin the guard as an operand if we want to hoist later
-        setOperand(1, guard);
+        initOperand(1, guard);
 
         // We are movable iff the jitinfo says we can be.
         if (isDomMovable()) {
             JS_ASSERT(jitinfo->aliasSet() != JSJitInfo::AliasEverything);
             setMovable();
         } else {
             // If we're not movable, that means we shouldn't be DCEd either,
             // because we might throw an exception when called, and getting rid
@@ -9401,17 +9466,17 @@ class MPostWriteBarrier : public MBinary
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 
 #ifdef DEBUG
     bool isConsistentFloat32Use(MUse *use) const {
         // During lowering, values that neither have object nor value MIR type
         // are ignored, thus Float32 can show up at this point without any issue.
-        return use->index() == 1;
+        return use == getUseFor(1);
     }
 #endif
 };
 
 class MNewDeclEnvObject : public MNullaryInstruction
 {
     CompilerRootObject templateObj_;
 
@@ -9674,75 +9739,75 @@ class MResumePoint MOZ_FINAL : public MN
         Outer        // State before inlining.
     };
 
   private:
     friend class MBasicBlock;
     friend void AssertBasicGraphCoherency(MIRGraph &graph);
 
     FixedList<MUse> operands_;
-    uint32_t stackDepth_;
     jsbytecode *pc_;
     MResumePoint *caller_;
     MInstruction *instruction_;
     Mode mode_;
 
     MResumePoint(MBasicBlock *block, jsbytecode *pc, MResumePoint *parent, Mode mode);
     void inherit(MBasicBlock *state);
 
   protected:
     // Initializes operands_ to an empty array of a fixed length.
     // The array may then be filled in by inherit().
-    bool init(TempAllocator &alloc) {
-        return operands_.init(alloc, stackDepth_);
-    }
-
-    // Overwrites an operand without updating its Uses.
-    void setOperand(size_t index, MDefinition *operand) {
-        JS_ASSERT(index < stackDepth_);
-        // Note: We do not remove the isObserved flag, as this would imply that
-        // we check the list of uses of the removed MDefinition.
-        operands_[index].set(operand, this, index);
-        operand->addUse(&operands_[index]);
-        if (!operand->isObserved() && isObservableOperand(index))
-            operand->setObserved();
-    }
+    bool init(TempAllocator &alloc);
 
     void clearOperand(size_t index) {
-        JS_ASSERT(index < stackDepth_);
-        operands_[index].set(nullptr, this, index);
+        // FixedList doesn't initialize its elements, so do an unchecked init.
+        operands_[index].initUncheckedWithoutProducer(this);
     }
 
     MUse *getUseFor(size_t index) {
         return &operands_[index];
     }
+    const MUse *getUseFor(size_t index) const {
+        return &operands_[index];
+    }
 
   public:
     static MResumePoint *New(TempAllocator &alloc, MBasicBlock *block, jsbytecode *pc,
                              MResumePoint *parent, Mode mode);
 
     MNode::Kind kind() const {
         return MNode::ResumePoint;
     }
     size_t numOperands() const {
-        return stackDepth_;
-    }
-
+        return operands_.length();
+    }
+    size_t indexOf(const MUse *u) const MOZ_FINAL MOZ_OVERRIDE {
+        MOZ_ASSERT(u >= &operands_[0]);
+        MOZ_ASSERT(u <= &operands_[numOperands() - 1]);
+        return u - &operands_[0];
+    }
+    void initOperand(size_t index, MDefinition *operand) {
+        // FixedList doesn't initialize its elements, so do an unchecked init.
+        operands_[index].initUnchecked(operand, this);
+    }
+    void replaceOperand(size_t index, MDefinition *operand) MOZ_FINAL MOZ_OVERRIDE {
+        operands_[index].replaceProducer(operand);
+    }
+
+    bool isObservableOperand(MUse *u) const;
     bool isObservableOperand(size_t index) const;
 
     MDefinition *getOperand(size_t index) const {
-        JS_ASSERT(index < stackDepth_);
-        MOZ_ASSERT_IF(isObservableOperand(index), operands_[index].producer()->isObserved());
         return operands_[index].producer();
     }
     jsbytecode *pc() const {
         return pc_;
     }
     uint32_t stackDepth() const {
-        return stackDepth_;
+        return operands_.length();
     }
     MResumePoint *caller() const {
         return caller_;
     }
     void setCaller(MResumePoint *caller) {
         caller_ = caller;
     }
     uint32_t frameCount() const {
@@ -9757,19 +9822,19 @@ class MResumePoint MOZ_FINAL : public MN
     void setInstruction(MInstruction *ins) {
         instruction_ = ins;
     }
     Mode mode() const {
         return mode_;
     }
 
     void discardUses() {
-        for (size_t i = 0; i < stackDepth_; i++) {
+        for (size_t i = 0; i < operands_.length(); i++) {
             if (operands_[i].hasProducer())
-                operands_[i].producer()->removeUse(&operands_[i]);
+                operands_[i].discardProducer();
         }
     }
 
     bool writeRecoverData(CompactBufferWriter &writer) const;
 
     virtual void dump(FILE *fp) const;
     virtual void dump() const;
 };
@@ -10106,17 +10171,17 @@ class MAsmJSParameter : public MNullaryI
     }
 
     ABIArg abi() const { return abi_; }
 };
 
 class MAsmJSReturn : public MAryControlInstruction<1, 0>
 {
     MAsmJSReturn(MDefinition *ins) {
-        setOperand(0, ins);
+        initOperand(0, ins);
     }
 
   public:
     INSTRUCTION_HEADER(AsmJSReturn);
     static MAsmJSReturn *New(TempAllocator &alloc, MDefinition *ins) {
         return new(alloc) MAsmJSReturn(ins);
     }
 };
@@ -10186,28 +10251,32 @@ class MAsmJSCall MOZ_FINAL : public MIns
     };
 
     CallSiteDesc desc_;
     Callee callee_;
     FixedList<MUse> operands_;
     FixedList<AnyRegister> argRegs_;
     size_t spIncrement_;
 
+    void initOperand(size_t index, MDefinition *operand) {
+        // FixedList doesn't initialize its elements, so do an unchecked init.
+        operands_[index].initUnchecked(operand, this);
+    }
+
     MAsmJSCall(const CallSiteDesc &desc, Callee callee, size_t spIncrement)
      : desc_(desc), callee_(callee), spIncrement_(spIncrement)
     { }
 
   protected:
-    void setOperand(size_t index, MDefinition *operand) {
-        operands_[index].set(operand, this, index);
-        operand->addUse(&operands_[index]);
-    }
     MUse *getUseFor(size_t index) {
         return &operands_[index];
     }
+    const MUse *getUseFor(size_t index) const {
+        return &operands_[index];
+    }
 
   public:
     INSTRUCTION_HEADER(AsmJSCall);
 
     struct Arg {
         AnyRegister reg;
         MDefinition *def;
         Arg(AnyRegister reg, MDefinition *def) : reg(reg), def(def) {}
@@ -10215,16 +10284,24 @@ class MAsmJSCall MOZ_FINAL : public MIns
     typedef Vector<Arg, 8> Args;
 
     static MAsmJSCall *New(TempAllocator &alloc, const CallSiteDesc &desc, Callee callee,
                            const Args &args, MIRType resultType, size_t spIncrement);
 
     size_t numOperands() const {
         return operands_.length();
     }
+    size_t indexOf(const MUse *u) const MOZ_FINAL MOZ_OVERRIDE {
+        MOZ_ASSERT(u >= &operands_[0]);
+        MOZ_ASSERT(u <= &operands_[numOperands() - 1]);
+        return u - &operands_[0];
+    }
+    void replaceOperand(size_t index, MDefinition *operand) MOZ_FINAL MOZ_OVERRIDE {
+        operands_[index].replaceProducer(operand);
+    }
     MDefinition *getOperand(size_t index) const {
         JS_ASSERT(index < numOperands());
         return operands_[index].producer();
     }
     size_t numArgs() const {
         return argRegs_.length();
     }
     AnyRegister registerForArg(size_t index) const {
@@ -10246,16 +10323,53 @@ class MAsmJSCall MOZ_FINAL : public MIns
         return spIncrement_;
     }
 
     bool possiblyCalls() const {
         return true;
     }
 };
 
+void MUse::init(MDefinition *producer, MNode *consumer)
+{
+    MOZ_ASSERT(!consumer_, "Initializing MUse that already has a consumer");
+    MOZ_ASSERT(!producer_, "Initializing MUse that already has a producer");
+    initUnchecked(producer, consumer);
+}
+
+void MUse::initUnchecked(MDefinition *producer, MNode *consumer)
+{
+    MOZ_ASSERT(consumer, "Initializing to null consumer");
+    consumer_ = consumer;
+    producer_ = producer;