Merge m-c to b2g-inbound
authorWes Kocher <wkocher@mozilla.com>
Fri, 21 Feb 2014 18:54:57 -0800
changeset 170429 327e902f0e309f17a7e1595a6901b5b49584950f
parent 170428 ab4ec3f07096663be712af1af868497fed448b18 (current diff)
parent 170394 84c9885475e798fc1849a71eb50c4dba51e2ce34 (diff)
child 170430 4c440d92a00353d470e5916d9990d7696074bf61
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
milestone30.0a1
Merge m-c to b2g-inbound
mobile/android/base/GeckoScreenOrientationListener.java
--- a/browser/base/content/browser-gestureSupport.js
+++ b/browser/base/content/browser-gestureSupport.js
@@ -22,20 +22,16 @@ let gGestureSupport = {
 
   /**
    * Add or remove mouse gesture event listeners
    *
    * @param aAddListener
    *        True to add/init listeners and false to remove/uninit
    */
   init: function GS_init(aAddListener) {
-    // Bug 863514 - Make gesture support work in electrolysis
-    if (gMultiProcessBrowser)
-      return;
-
     const gestureEvents = ["SwipeGestureStart",
       "SwipeGestureUpdate", "SwipeGestureEnd", "SwipeGesture",
       "MagnifyGestureStart", "MagnifyGestureUpdate", "MagnifyGesture",
       "RotateGestureStart", "RotateGestureUpdate", "RotateGesture",
       "TapGesture", "PressTapGesture"];
 
     let addRemove = aAddListener ? window.addEventListener :
       window.removeEventListener;
--- a/browser/base/content/newtab/drop.js
+++ b/browser/base/content/newtab/drop.js
@@ -52,17 +52,17 @@ let gDrop = {
       this._repinSitesAfterDrop(aCell);
 
     // Pin the dragged or insert the new site.
     this._pinDraggedSite(aCell, aEvent);
 
     this._cancelDelayedArrange();
 
     // Update the grid and move all sites to their new places.
-    gUpdater.updateGrid();
+    gUpdater.updateGrid(gDrag.draggedSite);
   },
 
   /**
    * Re-pins all pinned sites in their (new) positions.
    * @param aCell The drop target cell.
    */
   _repinSitesAfterDrop: function Drop_repinSitesAfterDrop(aCell) {
     let sites = gDropPreview.rearrange(aCell);
@@ -140,11 +140,11 @@ let gDrop = {
    */
   _rearrange: function Drop_rearrange(aCell) {
     let sites = gGrid.sites;
 
     // We need to rearrange the grid only if there's a current drop target.
     if (aCell)
       sites = gDropPreview.rearrange(aCell);
 
-    gTransformation.rearrangeSites(sites, {unfreeze: !aCell});
+    gTransformation.rearrangeSites(sites, gDrag.draggedSite, {unfreeze: !aCell});
   }
 };
--- a/browser/base/content/newtab/newTab.js
+++ b/browser/base/content/newtab/newTab.js
@@ -7,17 +7,17 @@
 let Cu = Components.utils;
 let Ci = Components.interfaces;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/PageThumbs.jsm");
 Cu.import("resource://gre/modules/BackgroundPageThumbs.jsm");
 Cu.import("resource://gre/modules/NewTabUtils.jsm");
-Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js");
+Cu.import("resource://gre/modules/Promise.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Rect",
   "resource://gre/modules/Geometry.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
   "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
 let {
   links: gLinks,
--- a/browser/base/content/newtab/sites.js
+++ b/browser/base/content/newtab/sites.js
@@ -55,17 +55,17 @@ Site.prototype = {
     if (typeof aIndex == "undefined")
       aIndex = this.cell.index;
 
     this._updateAttributes(true);
     gPinnedLinks.pin(this._link, aIndex);
   },
 
   /**
-   * Unpins the site and calls the given callback when done.
+   * Unpins the site.
    */
   unpin: function Site_unpin() {
     if (this.isPinned()) {
       this._updateAttributes(false);
       gPinnedLinks.unpin(this._link);
       gUpdater.updateGrid();
     }
   },
@@ -74,18 +74,17 @@ Site.prototype = {
    * Checks whether this site is pinned.
    * @return Whether this site is pinned.
    */
   isPinned: function Site_isPinned() {
     return gPinnedLinks.isPinned(this._link);
   },
 
   /**
-   * Blocks the site (removes it from the grid) and calls the given callback
-   * when done.
+   * Blocks the site (removes it from the grid).
    */
   block: function Site_block() {
     if (!gBlockedLinks.isBlocked(this._link)) {
       gUndoDialog.show(this);
       gBlockedLinks.block(this._link);
       gUpdater.updateGrid();
     }
   },
--- a/browser/base/content/newtab/transformations.js
+++ b/browser/base/content/newtab/transformations.js
@@ -34,55 +34,33 @@ let gTransformation = {
    * @return A Rect instance with the position.
    */
   getNodePosition: function Transformation_getNodePosition(aNode) {
     let {left, top, width, height} = aNode.getBoundingClientRect();
     return new Rect(left + scrollX, top + scrollY, width, height);
   },
 
   /**
-   * Fades a given node from zero to full opacity.
-   * @param aNode The node to fade.
-   * @param aCallback The callback to call when finished.
-   */
-  fadeNodeIn: function Transformation_fadeNodeIn(aNode, aCallback) {
-    this._setNodeOpacity(aNode, 1, function () {
-      // Clear the style property.
-      aNode.style.opacity = "";
-
-      if (aCallback)
-        aCallback();
-    });
-  },
-
-  /**
-   * Fades a given node from full to zero opacity.
-   * @param aNode The node to fade.
-   * @param aCallback The callback to call when finished.
-   */
-  fadeNodeOut: function Transformation_fadeNodeOut(aNode, aCallback) {
-    this._setNodeOpacity(aNode, 0, aCallback);
-  },
-
-  /**
    * Fades a given site from zero to full opacity.
    * @param aSite The site to fade.
-   * @param aCallback The callback to call when finished.
    */
-  showSite: function Transformation_showSite(aSite, aCallback) {
-    this.fadeNodeIn(aSite.node, aCallback);
+  showSite: function (aSite) {
+    let node = aSite.node;
+    return this._setNodeOpacity(node, 1).then(() => {
+      // Clear the style property.
+      node.style.opacity = "";
+    });
   },
 
   /**
    * Fades a given site from full to zero opacity.
    * @param aSite The site to fade.
-   * @param aCallback The callback to call when finished.
    */
-  hideSite: function Transformation_hideSite(aSite, aCallback) {
-    this.fadeNodeOut(aSite.node, aCallback);
+  hideSite: function (aSite) {
+    return this._setNodeOpacity(aSite.node, 0);
   },
 
   /**
    * Allows to set a site's position.
    * @param aSite The site to re-position.
    * @param aPosition The desired position for the given site.
    */
   setSitePosition: function Transformation_setSitePosition(aSite, aPosition) {
@@ -124,144 +102,128 @@ let gTransformation = {
   },
 
   /**
    * Slides the given site to the target node's position.
    * @param aSite The site to move.
    * @param aTarget The slide target.
    * @param aOptions Set of options (see below).
    *        unfreeze - unfreeze the site after sliding
-   *        callback - the callback to call when finished
    */
-  slideSiteTo: function Transformation_slideSiteTo(aSite, aTarget, aOptions) {
+  slideSiteTo: function (aSite, aTarget, aOptions) {
     let currentPosition = this.getNodePosition(aSite.node);
     let targetPosition = this.getNodePosition(aTarget.node)
-    let callback = aOptions && aOptions.callback;
-
-    let self = this;
-
-    function finish() {
-      if (aOptions && aOptions.unfreeze)
-        self.unfreezeSitePosition(aSite);
-
-      if (callback)
-        callback();
-    }
+    let promise;
 
     // We need to take the width of a cell's border into account.
     targetPosition.left += this._cellBorderWidths.left;
     targetPosition.top += this._cellBorderWidths.top;
 
     // Nothing to do here if the positions already match.
     if (currentPosition.left == targetPosition.left &&
         currentPosition.top == targetPosition.top) {
-      finish();
+      promise = Promise.resolve();
     } else {
       this.setSitePosition(aSite, targetPosition);
-      this._whenTransitionEnded(aSite.node, ["left", "top"], finish);
+      promise = this._whenTransitionEnded(aSite.node, ["left", "top"]);
     }
+
+    if (aOptions && aOptions.unfreeze) {
+      promise = promise.then(() => this.unfreezeSitePosition(aSite));
+    }
+
+    return promise;
   },
 
   /**
    * Rearranges a given array of sites and moves them to their new positions or
    * fades in/out new/removed sites.
    * @param aSites An array of sites to rearrange.
+   * @param aDraggedSite The currently dragged site, may be null.
    * @param aOptions Set of options (see below).
    *        unfreeze - unfreeze the site after rearranging
-   *        callback - the callback to call when finished
    */
-  rearrangeSites: function Transformation_rearrangeSites(aSites, aOptions) {
-    let batch = [];
+  rearrangeSites: function (aSites, aDraggedSite, aOptions) {
+    let self = this;
     let cells = gGrid.cells;
-    let callback = aOptions && aOptions.callback;
     let unfreeze = aOptions && aOptions.unfreeze;
 
-    aSites.forEach(function (aSite, aIndex) {
-      // Do not re-arrange empty cells or the dragged site.
-      if (!aSite || aSite == gDrag.draggedSite)
-        return;
-
-      let deferred = Promise.defer();
-      batch.push(deferred.promise);
-      let cb = function () deferred.resolve();
+    function* promises() {
+      let index = 0;
 
-      if (!cells[aIndex])
-        // The site disappeared from the grid, hide it.
-        this.hideSite(aSite, cb);
-      else if (this._getNodeOpacity(aSite.node) != 1)
-        // The site disappeared before but is now back, show it.
-        this.showSite(aSite, cb);
-      else
-        // The site's position has changed, move it around.
-        this._moveSite(aSite, aIndex, {unfreeze: unfreeze, callback: cb});
-    }, this);
+      for (let site of aSites) {
+        if (site && site !== aDraggedSite) {
+          if (!cells[index]) {
+            // The site disappeared from the grid, hide it.
+            yield self.hideSite(site);
+          } else if (self._getNodeOpacity(site.node) != 1) {
+            // The site disappeared before but is now back, show it.
+            yield self.showSite(site);
+          } else {
+            // The site's position has changed, move it around.
+            yield self._moveSite(site, index, {unfreeze: unfreeze});
+          }
+        }
+        index++;
+      }
+    }
 
-    let wait = Promise.promised(function () callback && callback());
-    wait.apply(null, batch);
+    return Promise.all([p for (p of promises())]);
   },
 
   /**
-   * Listens for the 'transitionend' event on a given node and calls the given
-   * callback.
+   * Listens for the 'transitionend' event on a given node.
    * @param aNode The node that is transitioned.
    * @param aProperties The properties we'll wait to be transitioned.
-   * @param aCallback The callback to call when finished.
    */
-  _whenTransitionEnded:
-    function Transformation_whenTransitionEnded(aNode, aProperties, aCallback) {
-
+  _whenTransitionEnded: function (aNode, aProperties) {
+    let deferred = Promise.defer();
     let props = new Set(aProperties);
     aNode.addEventListener("transitionend", function onEnd(e) {
       if (props.has(e.propertyName)) {
         aNode.removeEventListener("transitionend", onEnd);
-        aCallback();
+        deferred.resolve();
       }
     });
+    return deferred.promise;
   },
 
   /**
    * Gets a given node's opacity value.
    * @param aNode The node to get the opacity value from.
    * @return The node's opacity value.
    */
   _getNodeOpacity: function Transformation_getNodeOpacity(aNode) {
     let cstyle = window.getComputedStyle(aNode, null);
     return cstyle.getPropertyValue("opacity");
   },
 
   /**
    * Sets a given node's opacity.
    * @param aNode The node to set the opacity value for.
    * @param aOpacity The opacity value to set.
-   * @param aCallback The callback to call when finished.
    */
-  _setNodeOpacity:
-    function Transformation_setNodeOpacity(aNode, aOpacity, aCallback) {
-
+  _setNodeOpacity: function (aNode, aOpacity) {
     if (this._getNodeOpacity(aNode) == aOpacity) {
-      if (aCallback)
-        aCallback();
-    } else {
-      if (aCallback) {
-        this._whenTransitionEnded(aNode, ["opacity"], aCallback);
-      }
+      return Promise.resolve();
+    }
 
-      aNode.style.opacity = aOpacity;
-    }
+    aNode.style.opacity = aOpacity;
+    return this._whenTransitionEnded(aNode, ["opacity"]);
   },
 
   /**
    * Moves a site to the cell with the given index.
    * @param aSite The site to move.
    * @param aIndex The target cell's index.
    * @param aOptions Options that are directly passed to slideSiteTo().
    */
-  _moveSite: function Transformation_moveSite(aSite, aIndex, aOptions) {
+  _moveSite: function (aSite, aIndex, aOptions) {
     this.freezeSitePosition(aSite);
-    this.slideSiteTo(aSite, gGrid.cells[aIndex], aOptions);
+    return this.slideSiteTo(aSite, gGrid.cells[aIndex], aOptions);
   },
 
   /**
    * Checks whether a site is currently frozen.
    * @param aSite The site to check.
    * @return Whether the given site is frozen.
    */
   _isFrozen: function Transformation_isFrozen(aSite) {
--- a/browser/base/content/newtab/updater.js
+++ b/browser/base/content/newtab/updater.js
@@ -7,42 +7,40 @@
 /**
  * This singleton provides functionality to update the current grid to a new
  * set of pinned and blocked sites. It adds, moves and removes sites.
  */
 let gUpdater = {
   /**
    * Updates the current grid according to its pinned and blocked sites.
    * This removes old, moves existing and creates new sites to fill gaps.
-   * @param aCallback The callback to call when finished.
    */
-  updateGrid: function Updater_updateGrid(aCallback) {
+  updateGrid: function Updater_updateGrid(draggedSite = null) {
     let links = gLinks.getLinks().slice(0, gGrid.cells.length);
 
     // Find all sites that remain in the grid.
     let sites = this._findRemainingSites(links);
 
-    let self = this;
-
     // Remove sites that are no longer in the grid.
-    this._removeLegacySites(sites, function () {
+    this._removeLegacySites(sites).then(() => {
       // Freeze all site positions so that we can move their DOM nodes around
       // without any visual impact.
-      self._freezeSitePositions(sites);
+      this._freezeSitePositions(sites);
 
       // Move the sites' DOM nodes to their new position in the DOM. This will
       // have no visual effect as all the sites have been frozen and will
       // remain in their current position.
-      self._moveSiteNodes(sites);
+      this._moveSiteNodes(sites);
 
       // Now it's time to animate the sites actually moving to their new
       // positions.
-      self._rearrangeSites(sites, function () {
+      let opts = {unfreeze: true};
+      gTransformation.rearrangeSites(sites, draggedSite, opts).then(() => {
         // Try to fill empty cells and finish.
-        self._fillEmptyCells(links, aCallback);
+        this._fillEmptyCells(links);
 
         // Update other pages that might be open to keep them synced.
         gAllPages.update(gPage);
       });
     });
   },
 
   /**
@@ -105,82 +103,56 @@ let gUpdater = {
         // Put the new site in place, if any.
         if (aSite)
           cellNode.appendChild(aSite.node);
       }
     }, this);
   },
 
   /**
-   * Rearranges the given sites and slides them to their new positions.
-   * @param aSites The array of sites to re-arrange.
-   * @param aCallback The callback to call when finished.
-   */
-  _rearrangeSites: function Updater_rearrangeSites(aSites, aCallback) {
-    let options = {callback: aCallback, unfreeze: true};
-    gTransformation.rearrangeSites(aSites, options);
-  },
-
-  /**
    * Removes all sites from the grid that are not in the given links array or
    * exceed the grid.
    * @param aSites The array of sites remaining in the grid.
-   * @param aCallback The callback to call when finished.
    */
-  _removeLegacySites: function Updater_removeLegacySites(aSites, aCallback) {
-    let batch = [];
-
-    // Delete sites that were removed from the grid.
-    gGrid.sites.forEach(function (aSite) {
-      // The site must be valid and not in the current grid.
-      if (!aSite || aSites.indexOf(aSite) != -1)
-        return;
+  _removeLegacySites: function (aSites) {
+    let remainingSites = new Set(aSites);
 
-      let deferred = Promise.defer();
-      batch.push(deferred.promise);
-
-      // Fade out the to-be-removed site.
-      gTransformation.hideSite(aSite, function () {
-        let node = aSite.node;
+    function* promises() {
+      for (let site of gGrid.sites) {
+        // The site must be valid and not in the current grid.
+        if (site && !remainingSites.has(site)) {
+          // Hide the site and remove it from the DOM.
+          let remove = site.node.remove.bind(site.node);
+          yield gTransformation.hideSite(site).then(remove);
+        }
+      }
+    }
 
-        // Remove the site from the DOM.
-        node.parentNode.removeChild(node);
-        deferred.resolve();
-      });
-    });
-
-    let wait = Promise.promised(aCallback);
-    wait.apply(null, batch);
+    return Promise.all([p for (p of promises())]);
   },
 
   /**
    * Tries to fill empty cells with new links if available.
    * @param aLinks The array of links.
-   * @param aCallback The callback to call when finished.
    */
-  _fillEmptyCells: function Updater_fillEmptyCells(aLinks, aCallback) {
+  _fillEmptyCells: function (aLinks) {
     let {cells, sites} = gGrid;
-    let batch = [];
+    let index = 0;
 
     // Find empty cells and fill them.
-    sites.forEach(function (aSite, aIndex) {
-      if (aSite || !aLinks[aIndex])
-        return;
+    for (let site of sites) {
+      if (!site && aLinks[index]) {
+        // Create the new site and fade it in.
+        site = gGrid.createSite(aLinks[index], cells[index]);
 
-      let deferred = Promise.defer();
-      batch.push(deferred.promise);
-
-      // Create the new site and fade it in.
-      let site = gGrid.createSite(aLinks[aIndex], cells[aIndex]);
+        // Set the site's initial opacity to zero.
+        site.node.style.opacity = 0;
 
-      // Set the site's initial opacity to zero.
-      site.node.style.opacity = 0;
+        // Flush all style changes for the dynamically inserted site to make
+        // the fade-in transition work.
+        window.getComputedStyle(site.node).opacity;
+        gTransformation.showSite(site);
+      }
 
-      // Flush all style changes for the dynamically inserted site to make
-      // the fade-in transition work.
-      window.getComputedStyle(site.node).opacity;
-      gTransformation.showSite(site, function () deferred.resolve());
-    });
-
-    let wait = Promise.promised(aCallback);
-    wait.apply(null, batch);
+      index++;
+    }
   }
 };
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -46,16 +46,20 @@ support-files =
   file_bug902156_2.html
   file_bug902156_3.html
   file_bug906190_1.html
   file_bug906190_2.html
   file_bug906190_3_4.html
   file_bug906190_redirected.html
   file_bug906190.js
   file_bug906190.sjs
+  file_bug970276_popup1.html
+  file_bug970276_popup2.html
+  file_bug970276_favicon1.ico
+  file_bug970276_favicon2.ico
   file_dom_notifications.html
   file_fullscreen-window-open.html
   head.js
   healthreport_testRemoteCommands.html
   moz.png
   offlineQuotaNotification.cacheManifest
   offlineQuotaNotification.html
   page_style_sample.html
@@ -307,16 +311,17 @@ skip-if = true  # disabled until the tre
                 # back to the clear recent history dialog (sanitize.xul), if
                 # it ever is (bug 480169)
 [browser_save_link-perwindowpb.js]
 [browser_save_private_link_perwindowpb.js]
 skip-if = os == "linux" # bug 857427
 [browser_save_video.js]
 [browser_scope.js]
 [browser_selectTabAtIndex.js]
+[browser_subframe_favicons_not_used.js]
 [browser_tabDrop.js]
 [browser_tabMatchesInAwesomebar_perwindowpb.js]
 [browser_tab_drag_drop_perwindow.js]
 [browser_tab_dragdrop.js]
 [browser_tab_dragdrop2.js]
 [browser_tabbar_big_widgets.js]
 skip-if = os == "linux" || os == "mac" # No tabs in titlebar on linux
                                        # Disabled on OS X because of bug 967917
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_subframe_favicons_not_used.js
@@ -0,0 +1,20 @@
+/* Make sure <link rel="..."> isn't respected in sub-frames. */
+
+function test() {
+  waitForExplicitFinish();
+
+  let testPath = getRootDirectory(gTestPath);
+
+  let tab = gBrowser.addTab(testPath + "file_bug970276_popup1.html");
+
+  tab.linkedBrowser.addEventListener("load", function() {
+    tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
+
+    let expectedIcon = testPath + "file_bug970276_favicon1.ico";
+    is(gBrowser.getIcon(tab), expectedIcon, "Correct icon.");
+
+    gBrowser.removeTab(tab);
+
+    finish();
+  }, true);
+}
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..d44438903b751f4732f5365783eb0229b0501f9a
GIT binary patch
literal 1406
zc${sNu}d656o=n};bd8o)56N#Jtc-%Pi19LTfxTKZb)No6UbH8fq*bwiX@OCjeo&a
zDdN=H#L7bO4x$jyCB@74W-i8r?B3hk_q&<*`*z;kiWHpvzNF*jBasEZpA#kxh(c!b
zkA?g>iii0yePlSyUv_dc8jWQ1Z6w!UKg!I^jLgo?N>x=dH#a8>3k$NixG2lZ%d)z<
zD!aS8vc0`68yg$4wzek2S1LO@DsSafbQ0yu>32E3yp%VeKI7mBPXr?7)NyfW4$YaS
zd5A~%+<0`4?ln&f=m9;D1oWV(ltlE19?>I0L|1Yp*O;x+>4`v?rrDm0gTcYzU~n)b
zBp4hF4h9E<gCSwU;9zhtI2arZ2^|IpgM-1r;9ziy28V;g8HZ|qyhLJWAR?jpkzw(e
zdyG6qJuGP%_|bFYVezn}Wr4%P;o<PXG|fu|hljz#;9;<(^Kf_=JPZ*p<(ifdpM1vY
z^14J6oaLIA8t(v8z%*bGU<!B#ehd@|!D?x4NAgC;Kj0tmw-yEbgXu5?7=mhQTkeEU
z?nY+qjW9&~B5V<kh+k^0aj*svj%XY_SZX(uA}kS>2t>p%Z2?#!6C;BnLWIS(MKl(A
z5@Ct99*M<vgLNUKt)wt03<`t7pfIHE1Ve;DVNe(%3~7tOpfD&53WLHxX#yPvg+XCZ
z02Kd7F=e7M$b0J%eN^+X+BU|^l6;W8y*)WNIFQ4`LpeG+l9Q7YIX*s?v$HcfKR=g?
ziwn8Bx{{ll8@ao?lk1-<@87-Se{BC~O&_tkDYLqks+9jZdPPy~Pwz!~n{79!=kH$}
z<GSmLr_<Msl#^avbPO2hT+6EM$RF49<MriRDSK7X4Z4C$(Ttb&wO_ZL%_!=6ywx`4
zgyE!JUClFXORIXNvkFB!=(@IFPD&fltZ*GPJq*jH8?3a&i}`+QhPP$YHpR1|=~tQH
w$7Vff2Tfh~eiKjn_2%S0`}?qUxPHyMZW^-w&wKL0`*KK);S<IY*Xw!iFYO$~&;S4c
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..d44438903b751f4732f5365783eb0229b0501f9a
GIT binary patch
literal 1406
zc${sNu}d656o=n};bd8o)56N#Jtc-%Pi19LTfxTKZb)No6UbH8fq*bwiX@OCjeo&a
zDdN=H#L7bO4x$jyCB@74W-i8r?B3hk_q&<*`*z;kiWHpvzNF*jBasEZpA#kxh(c!b
zkA?g>iii0yePlSyUv_dc8jWQ1Z6w!UKg!I^jLgo?N>x=dH#a8>3k$NixG2lZ%d)z<
zD!aS8vc0`68yg$4wzek2S1LO@DsSafbQ0yu>32E3yp%VeKI7mBPXr?7)NyfW4$YaS
zd5A~%+<0`4?ln&f=m9;D1oWV(ltlE19?>I0L|1Yp*O;x+>4`v?rrDm0gTcYzU~n)b
zBp4hF4h9E<gCSwU;9zhtI2arZ2^|IpgM-1r;9ziy28V;g8HZ|qyhLJWAR?jpkzw(e
zdyG6qJuGP%_|bFYVezn}Wr4%P;o<PXG|fu|hljz#;9;<(^Kf_=JPZ*p<(ifdpM1vY
z^14J6oaLIA8t(v8z%*bGU<!B#ehd@|!D?x4NAgC;Kj0tmw-yEbgXu5?7=mhQTkeEU
z?nY+qjW9&~B5V<kh+k^0aj*svj%XY_SZX(uA}kS>2t>p%Z2?#!6C;BnLWIS(MKl(A
z5@Ct99*M<vgLNUKt)wt03<`t7pfIHE1Ve;DVNe(%3~7tOpfD&53WLHxX#yPvg+XCZ
z02Kd7F=e7M$b0J%eN^+X+BU|^l6;W8y*)WNIFQ4`LpeG+l9Q7YIX*s?v$HcfKR=g?
ziwn8Bx{{ll8@ao?lk1-<@87-Se{BC~O&_tkDYLqks+9jZdPPy~Pwz!~n{79!=kH$}
z<GSmLr_<Msl#^avbPO2hT+6EM$RF49<MriRDSK7X4Z4C$(Ttb&wO_ZL%_!=6ywx`4
zgyE!JUClFXORIXNvkFB!=(@IFPD&fltZ*GPJq*jH8?3a&i}`+QhPP$YHpR1|=~tQH
w$7Vff2Tfh~eiKjn_2%S0`}?qUxPHyMZW^-w&wKL0`*KK);S<IY*Xw!iFYO$~&;S4c
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/file_bug970276_popup1.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test file for bug 970276.</title>
+
+  <!--Set a favicon; that's the whole point of this file.-->
+  <link rel="icon" href="file_bug970276_favicon1.ico">
+</head>
+<body>
+  Test file for bug 970276.
+
+  <iframe src="file_bug970276_popup2.html">
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/file_bug970276_popup2.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test file for bug 970276.</title>
+
+  <!--Set a favicon; that's the whole point of this file.-->
+  <link rel="icon" href="file_bug970276_favicon2.ico">
+</head>
+<body>
+  Test inner file for bug 970276.
+</body>
+</html>
--- a/browser/components/customizableui/content/toolbar.xml
+++ b/browser/components/customizableui/content/toolbar.xml
@@ -527,21 +527,24 @@
           }
 
           this._isModifying = true;
           // Temporarily add it here so it can have a width, then ditch it:
           this.appendChild(node);
           this.evictNode(node);
           this._isModifying = false;
           this._updateMigratedSet();
-          // We will now have moved stuff around; kick off an aftercustomization event
+          // We will now have moved stuff around; kick off some events
           // so add-ons know we've just moved their stuff:
-          if (window.gCustomizeMode) {
-            window.gCustomizeMode.dispatchToolboxEvent("aftercustomization");
-          }
+          // XXXgijs: only in this window. It's hard to know for sure what's the right
+          // thing to do here - typically insertItem is used on each window, so
+          // this seems to make the most sense, even if some of the effects of
+          // evictNode might affect multiple windows.
+          CustomizableUI.dispatchToolboxEvent("customizationchange", {}, window);
+          CustomizableUI.dispatchToolboxEvent("aftercustomization", {}, window);
           return node;
         ]]></body>
       </method>
       <method name="getMigratedItems">
         <body><![CDATA[
           return [... this._currentSetMigrated];
         ]]></body>
       </method>
--- a/browser/components/customizableui/src/CustomizableUI.jsm
+++ b/browser/components/customizableui/src/CustomizableUI.jsm
@@ -1753,16 +1753,34 @@ let CustomizableUIInternal = {
           listener[aEvent].apply(listener, aArgs);
         }
       } catch (e) {
         ERROR(e + " -- " + e.fileName + ":" + e.lineNumber);
       }
     }
   },
 
+  _dispatchToolboxEventToWindow: function(aEventType, aDetails, aWindow) {
+    let evt = new aWindow.CustomEvent(aEventType, {
+      bubbles: true,
+      cancelable: true,
+      detail: aDetails
+    });
+    aWindow.gNavToolbox.dispatchEvent(evt);
+  },
+
+  dispatchToolboxEvent: function(aEventType, aDetails={}, aWindow=null) {
+    if (aWindow) {
+      return this._dispatchToolboxEventToWindow(aEventType, aDetails, aWindow);
+    }
+    for (let [win, ] of gBuildWindows) {
+      this._dispatchToolboxEventToWindow(aEventType, aDetails, win);
+    }
+  },
+
   createWidget: function(aProperties) {
     let widget = this.normalizeWidget(aProperties, CustomizableUI.SOURCE_EXTERNAL);
     //XXXunf This should probably throw.
     if (!widget) {
       return;
     }
 
     gPalette.set(widget.id, widget);
@@ -3075,16 +3093,29 @@ this.CustomizableUI = {
   /**
    * Notify listeners that a window is exiting customize mode. For use from
    * Customize Mode only, do not use otherwise.
    * @param aWindow the window exiting customize mode
    */
   notifyEndCustomizing: function(aWindow) {
     CustomizableUIInternal.notifyListeners("onCustomizeEnd", aWindow);
   },
+
+  /**
+   * Notify toolbox(es) of a particular event. If you don't pass aWindow,
+   * all toolboxes will be notified. For use from Customize Mode only,
+   * do not use otherwise.
+   * @param aEvent the name of the event to send.
+   * @param aDetails optional, the details of the event.
+   * @param aWindow optional, the window in which to send the event.
+   */
+  dispatchToolboxEvent: function(aEvent, aDetails={}, aWindow=null) {
+    CustomizableUIInternal.dispatchToolboxEvent(aEvent, aDetails, aWindow);
+  },
+
   /**
    * Check whether an area is overflowable.
    *
    * @param aAreaId the ID of an area to check for overflowable-ness
    * @return true if the area is overflowable, false otherwise.
    */
   isAreaOverflowable: function(aAreaId) {
     let area = gAreas.get(aAreaId);
--- a/browser/components/customizableui/src/CustomizeMode.jsm
+++ b/browser/components/customizableui/src/CustomizeMode.jsm
@@ -165,17 +165,17 @@ CustomizeMode.prototype = {
         toolbarVisibilityBtn.removeAttribute("hidden");
       }
 
       // Disable lightweight themes while in customization mode since
       // they don't have large enough images to pad the full browser window.
       if (this.document.documentElement._lightweightTheme)
         this.document.documentElement._lightweightTheme.disable();
 
-      this.dispatchToolboxEvent("beforecustomization");
+      CustomizableUI.dispatchToolboxEvent("beforecustomization", {}, window);
       CustomizableUI.notifyStartCustomizing(this.window);
 
       // Add a keypress listener to the document so that we can quickly exit
       // customization mode when pressing ESC.
       document.addEventListener("keypress", this);
 
       // Same goes for the menu button - if we're customizing, a mousedown to the
       // menu button means a quick exit from customization mode.
@@ -215,17 +215,17 @@ CustomizeMode.prototype = {
 
       let customizer = document.getElementById("customization-container");
       customizer.parentNode.selectedPanel = customizer;
       customizer.hidden = false;
 
       yield this._doTransition(true);
 
       // Let everybody in this window know that we're about to customize.
-      this.dispatchToolboxEvent("customizationstarting");
+      CustomizableUI.dispatchToolboxEvent("customizationstarting", {}, window);
 
       this._mainViewContext = mainView.getAttribute("context");
       if (this._mainViewContext) {
         mainView.removeAttribute("context");
       }
 
       this._showPanelCustomizationPlaceholders();
 
@@ -261,17 +261,17 @@ CustomizeMode.prototype = {
       // Show the palette now that the transition has finished.
       this.visiblePalette.hidden = false;
       this.paletteSpacer.hidden = true;
       this._updateEmptyPaletteNotice();
 
       this._handler.isEnteringCustomizeMode = false;
       panelContents.removeAttribute("customize-transitioning");
 
-      this.dispatchToolboxEvent("customizationready");
+      CustomizableUI.dispatchToolboxEvent("customizationready", {}, window);
       if (!this._wantToBeInCustomizeMode) {
         this.exit();
       }
     }.bind(this)).then(null, function(e) {
       ERROR(e);
       // We should ensure this has been called, and calling it again doesn't hurt:
       window.PanelUI.endBatchUpdate();
       this._handler.isEnteringCustomizeMode = false;
@@ -354,17 +354,17 @@ CustomizeMode.prototype = {
         this.persistCurrentSets();
       }
 
       // And drop all area references.
       this.areas = [];
 
       // Let everybody in this window know that we're starting to
       // exit customization mode.
-      this.dispatchToolboxEvent("customizationending");
+      CustomizableUI.dispatchToolboxEvent("customizationending", {}, window);
 
       window.PanelUI.setMainView(window.PanelUI.mainView);
       window.PanelUI.menuButton.disabled = false;
 
       let customizeButton = document.getElementById("PanelUI-customize");
       customizeButton.setAttribute("exitLabel", customizeButton.getAttribute("label"));
       customizeButton.setAttribute("label", customizeButton.getAttribute("enterLabel"));
       customizeButton.setAttribute("exitTooltiptext", customizeButton.getAttribute("tooltiptext"));
@@ -419,18 +419,18 @@ CustomizeMode.prototype = {
       let customizableToolbars = document.querySelectorAll("toolbar[customizable=true]:not([autohide=true])");
       for (let toolbar of customizableToolbars)
         toolbar.removeAttribute("customizing");
 
       this.window.PanelUI.endBatchUpdate();
       this._changed = false;
       this._transitioning = false;
       this._handler.isExitingCustomizeMode = false;
-      this.dispatchToolboxEvent("aftercustomization");
-      CustomizableUI.notifyEndCustomizing(this.window);
+      CustomizableUI.dispatchToolboxEvent("aftercustomization", {}, window);
+      CustomizableUI.notifyEndCustomizing(window);
 
       if (this._wantToBeInCustomizeMode) {
         this.enter();
       }
     }.bind(this)).then(null, function(e) {
       ERROR(e);
       // We should ensure this has been called, and calling it again doesn't hurt:
       window.PanelUI.endBatchUpdate();
@@ -472,17 +472,17 @@ CustomizeMode.prototype = {
 
         if (!aEntering) {
           this.document.documentElement.removeAttribute("customize-exiting");
           this.document.documentElement.removeAttribute("customizing");
         } else {
           this.document.documentElement.setAttribute("customize-entered", true);
           this.document.documentElement.removeAttribute("customize-entering");
         }
-        this.dispatchToolboxEvent("customization-transitionend", aEntering);
+        CustomizableUI.dispatchToolboxEvent("customization-transitionend", aEntering, this.window);
 
         deferred.resolve();
       }.bind(this), 0);
     }.bind(this);
     deck.addEventListener("transitionend", customizeTransitionEnd);
 
     if (gDisableAnimation) {
       deck.setAttribute("fastcustomizeanimation", true);
@@ -495,22 +495,16 @@ CustomizeMode.prototype = {
       this.document.documentElement.removeAttribute("customize-entered");
     }
 
     let catchAll = () => customizeTransitionEnd("timedout");
     let catchAllTimeout = this.window.setTimeout(catchAll, kMaxTransitionDurationMs);
     return deferred.promise;
   },
 
-  dispatchToolboxEvent: function(aEventType, aDetails={}) {
-    let evt = this.document.createEvent("CustomEvent");
-    evt.initCustomEvent(aEventType, true, true, {changed: this._changed});
-    let result = this.window.gNavToolbox.dispatchEvent(evt);
-  },
-
   _getCustomizableChildForNode: function(aNode) {
     // NB: adjusted from _getCustomizableParent to keep that method fast
     // (it's used during drags), and avoid multiple DOM loops
     let areas = CustomizableUI.areas;
     // Caching this length is important because otherwise we'll also iterate
     // over items we add to the end from within the loop.
     let numberOfAreas = areas.length;
     for (let i = 0; i < numberOfAreas; i++) {
@@ -539,39 +533,39 @@ CustomizeMode.prototype = {
 
   addToToolbar: function(aNode) {
     aNode = this._getCustomizableChildForNode(aNode);
     if (aNode.localName == "toolbarpaletteitem" && aNode.firstChild) {
       aNode = aNode.firstChild;
     }
     CustomizableUI.addWidgetToArea(aNode.id, CustomizableUI.AREA_NAVBAR);
     if (!this._customizing) {
-      this.dispatchToolboxEvent("customizationchange");
+      CustomizableUI.dispatchToolboxEvent("customizationchange");
     }
   },
 
   addToPanel: function(aNode) {
     aNode = this._getCustomizableChildForNode(aNode);
     if (aNode.localName == "toolbarpaletteitem" && aNode.firstChild) {
       aNode = aNode.firstChild;
     }
     CustomizableUI.addWidgetToArea(aNode.id, CustomizableUI.AREA_PANEL);
     if (!this._customizing) {
-      this.dispatchToolboxEvent("customizationchange");
+      CustomizableUI.dispatchToolboxEvent("customizationchange");
     }
   },
 
   removeFromArea: function(aNode) {
     aNode = this._getCustomizableChildForNode(aNode);
     if (aNode.localName == "toolbarpaletteitem" && aNode.firstChild) {
       aNode = aNode.firstChild;
     }
     CustomizableUI.removeWidgetFromArea(aNode.id);
     if (!this._customizing) {
-      this.dispatchToolboxEvent("customizationchange");
+      CustomizableUI.dispatchToolboxEvent("customizationchange");
     }
   },
 
   populatePalette: function() {
     let fragment = this.document.createDocumentFragment();
     let toolboxPalette = this.window.gNavToolbox.palette;
 
     return Task.spawn(function() {
@@ -1012,17 +1006,17 @@ CustomizeMode.prototype = {
 
   _onUIChange: function() {
     this._changed = true;
     if (!this.resetting) {
       this._updateResetButton();
       this._updateUndoResetButton();
       this._updateEmptyPaletteNotice();
     }
-    this.dispatchToolboxEvent("customizationchange");
+    CustomizableUI.dispatchToolboxEvent("customizationchange");
   },
 
   _updateEmptyPaletteNotice: function() {
     let paletteItems = this.visiblePalette.getElementsByTagName("toolbarpaletteitem");
     this.paletteEmptyNotice.hidden = !!paletteItems.length;
   },
 
   _updateResetButton: function() {
--- a/browser/components/customizableui/test/browser.ini
+++ b/browser/components/customizableui/test/browser.ini
@@ -63,9 +63,10 @@ skip-if = os == "linux"
 [browser_945739_showInPrivateBrowsing_customize_mode.js]
 [browser_947987_removable_default.js]
 [browser_948985_non_removable_defaultArea.js]
 [browser_952963_areaType_getter_no_area.js]
 [browser_956602_remove_special_widget.js]
 [browser_969427_recreate_destroyed_widget_after_reset.js]
 [browser_969661_character_encoding_navbar_disabled.js]
 [browser_970511_undo_restore_default.js]
+[browser_972267_customizationchange_events.js]
 [browser_panel_toggle.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/customizableui/test/browser_972267_customizationchange_events.js
@@ -0,0 +1,47 @@
+/* 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";
+
+// Create a new window, then move the home button to the menu and check both windows have
+// customizationchange events fire on the toolbox:
+add_task(function() {
+  let newWindow = yield openAndLoadWindow();
+  let otherToolbox = newWindow.gNavToolbox;
+
+  let handlerCalledCount = 0;
+  let handler = (ev) => {
+    handlerCalledCount++;
+  };
+
+  let homeButton = document.getElementById("home-button");
+
+  gNavToolbox.addEventListener("customizationchange", handler);
+  otherToolbox.addEventListener("customizationchange", handler);
+
+  gCustomizeMode.addToPanel(homeButton);
+
+  is(handlerCalledCount, 2, "Should be called for both windows.");
+
+  // If the test is run in isolation and the panel has never been open,
+  // the button will be in the palette. Deal with this case:
+  if (homeButton.parentNode.id == "BrowserToolbarPalette") {
+    yield PanelUI.ensureReady();
+    isnot(homeButton.parentNode.id, "BrowserToolbarPalette", "Home button should now be in panel");
+  }
+
+  handlerCalledCount = 0;
+  gCustomizeMode.addToToolbar(homeButton);
+  is(handlerCalledCount, 2, "Should be called for both windows.");
+
+  gNavToolbox.removeEventListener("customizationchange", handler);
+  otherToolbox.removeEventListener("customizationchange", handler);
+
+  newWindow.close();
+});
+
+add_task(function asyncCleanup() {
+  yield resetCustomization();
+});
+
--- a/browser/components/sessionstore/src/SessionHistory.jsm
+++ b/browser/components/sessionstore/src/SessionHistory.jsm
@@ -180,17 +180,17 @@ let SessionHistoryInternal = {
 
     if (shEntry.srcdocData)
       entry.srcdocData = shEntry.srcdocData;
 
     if (shEntry.isSrcdocEntry)
       entry.isSrcdocEntry = shEntry.isSrcdocEntry;
 
     if (shEntry.baseURI)
-      entry.baseURI = shEntry.baseURI;
+      entry.baseURI = shEntry.baseURI.spec;
 
     if (shEntry.contentType)
       entry.contentType = shEntry.contentType;
 
     let x = {}, y = {};
     shEntry.getScrollPosition(x, y);
     if (x.value != 0 || y.value != 0)
       entry.scroll = x.value + "," + y.value;
@@ -327,17 +327,17 @@ let SessionHistoryInternal = {
     shEntry.loadType = Ci.nsIDocShellLoadInfo.loadHistory;
     if (entry.contentType)
       shEntry.contentType = entry.contentType;
     if (entry.referrer)
       shEntry.referrerURI = Utils.makeURI(entry.referrer);
     if (entry.isSrcdocEntry)
       shEntry.srcdocData = entry.srcdocData;
     if (entry.baseURI)
-      shEntry.baseURI = entry.baseURI;
+      shEntry.baseURI = Utils.makeURI(entry.baseURI);
 
     if (entry.cacheKey) {
       var cacheKey = Cc["@mozilla.org/supports-PRUint32;1"].
                      createInstance(Ci.nsISupportsPRUint32);
       cacheKey.data = entry.cacheKey;
       shEntry.cacheKey = cacheKey;
     }
 
--- a/browser/metro/base/content/WebProgress.js
+++ b/browser/metro/base/content/WebProgress.js
@@ -96,16 +96,17 @@ const WebProgress = {
 
   _locationChange: function _locationChange(aJson, aTab) {
     let spec = aJson.location;
     let location = spec.split("#")[0]; // Ignore fragment identifier changes.
 
     if (aTab == Browser.selectedTab) {
       BrowserUI.updateURI();
       BrowserUI.update();
+      BrowserUI.updateStartURIAttributes(aJson.location);
     }
 
     let locationHasChanged = (location != aTab.browser.lastLocation);
     if (locationHasChanged) {
       Browser.getNotificationBox(aTab.browser).removeTransientNotifications();
       aTab.browser.lastLocation = location;
       aTab.browser.userTypedValue = "";
       aTab.browser.appIcon = { href: null, size:-1 };
--- a/browser/metro/base/content/browser-ui.js
+++ b/browser/metro/base/content/browser-ui.js
@@ -257,17 +257,16 @@ var BrowserUI = {
    * Content visibility
    */
 
   get isContentShowing() {
     return Elements.contentShowing.getAttribute("disabled") != true;
   },
 
   showContent: function showContent(aURI) {
-    this.updateStartURIAttributes(aURI);
     ContextUI.dismissTabs();
     ContextUI.dismissContextAppbar();
     FlyoutPanelsUI.hide();
     PanelUI.hide();
   },
 
   /*********************************
    * Crash reporting
--- a/browser/modules/UITour.jsm
+++ b/browser/modules/UITour.jsm
@@ -922,16 +922,22 @@ this.UITour = {
       }
     });
     UITour.appMenuOpenForAnnotation.clear();
   },
 
   recreatePopup: function(aPanel) {
     // After changing popup attributes that relate to how the native widget is created
     // (e.g. @noautohide) we need to re-create the frame/widget for it to take effect.
+    if (aPanel.hidden) {
+      // If the panel is already hidden, we don't need to recreate it but flush
+      // in case someone just hid it.
+      aPanel.clientWidth; // flush
+      return;
+    }
     aPanel.hidden = true;
     aPanel.clientWidth; // flush
     aPanel.hidden = false;
   },
 
   startUrlbarCapture: function(aWindow, aExpectedText, aUrl) {
     let urlbar = aWindow.document.getElementById("urlbar");
     this.urlbarCapture.set(aWindow, {
--- a/browser/themes/shared/customizableui/panelUIOverlay.inc.css
+++ b/browser/themes/shared/customizableui/panelUIOverlay.inc.css
@@ -245,30 +245,42 @@ toolbaritem[cui-areatype="menu-panel"][s
 .panelUI-grid .toolbarbutton-1:not([buttonover])@buttonStateHover@ > .toolbarbutton-menubutton-dropmarker:-moz-locale-dir(rtl) {
   border-radius: 0 0 2px 0;
 }
 
 .panel-combined-button[disabled] > .toolbarbutton-icon {
   opacity: .5;
 }
 
-toolbaritem[cui-areatype="menu-panel"][sdkstylewidget="true"] {
+toolbaritem[cui-areatype="menu-panel"][sdkstylewidget="true"]:not(.panel-wide-item) {
   width: calc(@menuPanelButtonWidth@);
   margin: 0 !important;
 }
 
-toolbaritem[cui-areatype="menu-panel"][sdkstylewidget="true"]:not(.panel-wide-widget) {
+toolbaritem[cui-areatype="menu-panel"][sdkstylewidget="true"]:not(.panel-wide-item) {
   -moz-box-align: center;
   -moz-box-pack: center;
 }
 
 toolbaritem[cui-areatype="menu-panel"][sdkstylewidget="true"] > iframe {
   margin: 4px auto;
 }
 
+/*
+ * XXXgijs: this is a workaround for a layout issue that was caused by these iframes,
+ * which was affecting subview display. Because of this, we're hiding the iframe *only*
+ * when displaying a subview. The discerning user might notice this, but it's not nearly
+ * as bad as the brokenness.
+ * This hack should be removed once https://bugzilla.mozilla.org/show_bug.cgi?id=975375
+ * is addressed.
+ */
+#PanelUI-multiView[viewtype="subview"] toolbaritem[cui-areatype="menu-panel"][sdkstylewidget="true"]:not(.panel-wide-item) > iframe {
+  visibility: hidden;
+}
+
 toolbaritem[cui-areatype="menu-panel"][sdkstylewidget="true"]:not(.panel-wide-item) > .toolbarbutton-text {
   text-align: center;
 }
 
 .panelUI-grid .toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-icon,
 .panelUI-grid .toolbarbutton-1 > .toolbarbutton-icon,
 .customization-palette .toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-icon,
 .customization-palette .toolbarbutton-1 > .toolbarbutton-icon,
--- a/content/base/src/Link.cpp
+++ b/content/base/src/Link.cpp
@@ -212,17 +212,19 @@ Link::SetPathname(const nsAString &aPath
   (void)url->SetFilePath(NS_ConvertUTF16toUTF8(aPathname));
   SetHrefAttribute(uri);
 }
 
 void
 Link::SetSearch(const nsAString& aSearch)
 {
   SetSearchInternal(aSearch);
-  UpdateURLSearchParams();
+  if (mSearchParams) {
+    mSearchParams->Invalidate();
+  }
 }
 
 void
 Link::SetSearchInternal(const nsAString& aSearch)
 {
   nsCOMPtr<nsIURI> uri(GetURIToMutate());
   nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
   if (!url) {
@@ -480,17 +482,19 @@ Link::ResetLinkState(bool aNotify, bool 
     UnregisterFromHistory();
   }
 
   // If we have an href, we should register with the history.
   mNeedsRegistration = aHasHref;
 
   // If we've cached the URI, reset always invalidates it.
   mCachedURI = nullptr;
-  UpdateURLSearchParams();
+  if (mSearchParams) {
+    mSearchParams->Invalidate();
+  }
 
   // Update our state back to the default.
   mLinkState = defaultState;
 
   // We have to be very careful here: if aNotify is false we do NOT
   // want to call UpdateState, because that will call into LinkState()
   // and try to start off loads, etc.  But ResetLinkState is called
   // with aNotify false when things are in inconsistent states, so
@@ -580,65 +584,66 @@ Link::GetSearchParams()
 
 void
 Link::SetSearchParams(URLSearchParams* aSearchParams)
 {
   if (!aSearchParams) {
     return;
   }
 
-  if (mSearchParams) {
-    mSearchParams->RemoveObserver(this);
+  if (!aSearchParams->HasURLAssociated()) {
+    MOZ_ASSERT(aSearchParams->IsValid());
+
+    mSearchParams = aSearchParams;
+    mSearchParams->SetObserver(this);
+  } else {
+    CreateSearchParamsIfNeeded();
+    mSearchParams->CopyFromURLSearchParams(*aSearchParams);
   }
 
-  mSearchParams = aSearchParams;
-  mSearchParams->AddObserver(this);
-
   nsAutoString search;
   mSearchParams->Serialize(search);
   SetSearchInternal(search);
 }
 
 void
 Link::URLSearchParamsUpdated()
 {
-  MOZ_ASSERT(mSearchParams);
+  MOZ_ASSERT(mSearchParams && mSearchParams->IsValid());
 
   nsString search;
   mSearchParams->Serialize(search);
   SetSearchInternal(search);
 }
 
 void
-Link::UpdateURLSearchParams()
+Link::URLSearchParamsNeedsUpdates()
 {
-  if (!mSearchParams) {
-    return;
-  }
+  MOZ_ASSERT(mSearchParams);
 
   nsAutoCString search;
   nsCOMPtr<nsIURI> uri(GetURI());
   nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
   if (url) {
     nsresult rv = url->GetQuery(search);
     if (NS_FAILED(rv)) {
       NS_WARNING("Failed to get the query from a nsIURL.");
     }
   }
 
-  mSearchParams->ParseInput(search, this);
+  mSearchParams->ParseInput(search);
 }
 
 void
 Link::CreateSearchParamsIfNeeded()
 {
   if (!mSearchParams) {
     mSearchParams = new URLSearchParams();
-    mSearchParams->AddObserver(this);
-    UpdateURLSearchParams();
+    mSearchParams->SetObserver(this);
+    mSearchParams->Invalidate();
   }
 }
 
 void
 Link::Unlink()
 {
   mSearchParams = nullptr;
 }
--- a/content/base/src/Link.h
+++ b/content/base/src/Link.h
@@ -109,16 +109,17 @@ public:
 
   virtual size_t
     SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
   bool ElementHasHref() const;
 
   // URLSearchParamsObserver
   void URLSearchParamsUpdated() MOZ_OVERRIDE;
+  void URLSearchParamsNeedsUpdates() MOZ_OVERRIDE;
 
 protected:
   virtual ~Link();
 
   /**
    * Return true if the link has associated URI.
    */
   bool HasURI() const
@@ -128,18 +129,16 @@ protected:
     }
 
     return !!GetURI();
   }
 
   nsIURI* GetCachedURI() const { return mCachedURI; }
   bool HasCachedURI() const { return !!mCachedURI; }
 
-  void UpdateURLSearchParams();
-
   // CC methods
   void Unlink();
   void Traverse(nsCycleCollectionTraversalCallback &cb);
 
 private:
   /**
    * Unregisters from History so this node no longer gets notifications about
    * changes to visitedness.
--- a/dom/base/URL.cpp
+++ b/dom/base/URL.cpp
@@ -216,17 +216,20 @@ URL::SetHref(const nsAString& aHref, Err
   rv = ioService->NewURI(href, nullptr, nullptr, getter_AddRefs(uri));
   if (NS_FAILED(rv)) {
     nsAutoString label(aHref);
     aRv.ThrowTypeError(MSG_INVALID_URL, &label);
     return;
   }
 
   aRv = mURI->SetSpec(href);
-  UpdateURLSearchParams();
+
+  if (mSearchParams) {
+    mSearchParams->Invalidate();
+  }
 }
 
 void
 URL::GetOrigin(nsString& aOrigin) const
 {
   nsContentUtils::GetUTFNonNullOrigin(mURI, aOrigin);
 }
 
@@ -296,40 +299,38 @@ void
 URL::SetHost(const nsAString& aHost)
 {
   mURI->SetHostPort(NS_ConvertUTF16toUTF8(aHost));
 }
 
 void
 URL::URLSearchParamsUpdated()
 {
-  MOZ_ASSERT(mSearchParams);
+  MOZ_ASSERT(mSearchParams && mSearchParams->IsValid());
 
   nsAutoString search;
   mSearchParams->Serialize(search);
   SetSearchInternal(search);
 }
 
 void
-URL::UpdateURLSearchParams()
+URL::URLSearchParamsNeedsUpdates()
 {
-  if (!mSearchParams) {
-    return;
-  }
+  MOZ_ASSERT(mSearchParams);
 
   nsAutoCString search;
   nsCOMPtr<nsIURL> url(do_QueryInterface(mURI));
   if (url) {
     nsresult rv = url->GetQuery(search);
     if (NS_FAILED(rv)) {
       NS_WARNING("Failed to get the query from a nsIURL.");
     }
   }
 
-  mSearchParams->ParseInput(search, this);
+  mSearchParams->ParseInput(search);
 }
 
 void
 URL::GetHostname(nsString& aHostname) const
 {
   URL_GETTER(aHostname, GetHost);
 }
 
@@ -420,17 +421,20 @@ URL::GetSearch(nsString& aSearch) const
     CopyUTF8toUTF16(NS_LITERAL_CSTRING("?") + search, aSearch);
   }
 }
 
 void
 URL::SetSearch(const nsAString& aSearch)
 {
   SetSearchInternal(aSearch);
-  UpdateURLSearchParams();
+
+  if (mSearchParams) {
+    mSearchParams->Invalidate();
+  }
 }
 
 void
 URL::SetSearchInternal(const nsAString& aSearch)
 {
   nsCOMPtr<nsIURL> url(do_QueryInterface(mURI));
   if (!url) {
     // Ignore failures to be compatible with NS4.
@@ -449,23 +453,25 @@ URL::GetSearchParams()
 
 void
 URL::SetSearchParams(URLSearchParams* aSearchParams)
 {
   if (!aSearchParams) {
     return;
   }
 
-  if (mSearchParams) {
-    mSearchParams->RemoveObserver(this);
-  }
+  if (!aSearchParams->HasURLAssociated()) {
+    MOZ_ASSERT(aSearchParams->IsValid());
 
-  // the observer will be cleared using the cycle collector.
-  mSearchParams = aSearchParams;
-  mSearchParams->AddObserver(this);
+    mSearchParams = aSearchParams;
+    mSearchParams->SetObserver(this);
+  } else {
+    CreateSearchParamsIfNeeded();
+    mSearchParams->CopyFromURLSearchParams(*aSearchParams);
+  }
 
   nsAutoString search;
   mSearchParams->Serialize(search);
   SetSearchInternal(search);
 }
 
 void
 URL::GetHash(nsString& aHash) const
@@ -495,15 +501,15 @@ bool IsChromeURI(nsIURI* aURI)
   return false;
 }
 
 void
 URL::CreateSearchParamsIfNeeded()
 {
   if (!mSearchParams) {
     mSearchParams = new URLSearchParams();
-    mSearchParams->AddObserver(this);
-    UpdateURLSearchParams();
+    mSearchParams->SetObserver(this);
+    mSearchParams->Invalidate();
   }
 }
 
 }
 }
--- a/dom/base/URL.h
+++ b/dom/base/URL.h
@@ -120,29 +120,28 @@ public:
 
   void Stringify(nsString& aRetval) const
   {
     GetHref(aRetval);
   }
 
   // URLSearchParamsObserver
   void URLSearchParamsUpdated() MOZ_OVERRIDE;
+  void URLSearchParamsNeedsUpdates() MOZ_OVERRIDE;
 
 private:
   nsIURI* GetURI() const
   {
     return mURI;
   }
 
   void CreateSearchParamsIfNeeded();
 
   void SetSearchInternal(const nsAString& aSearch);
 
-  void UpdateURLSearchParams();
-
   static void CreateObjectURLInternal(const GlobalObject& aGlobal,
                                       nsISupports* aObject,
                                       const nsACString& aScheme,
                                       const objectURLOptions& aOptions,
                                       nsString& aResult,
                                       ErrorResult& aError);
 
   nsCOMPtr<nsIURI> mURI;
--- a/dom/base/URLSearchParams.cpp
+++ b/dom/base/URLSearchParams.cpp
@@ -4,26 +4,27 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "URLSearchParams.h"
 #include "mozilla/dom/URLSearchParamsBinding.h"
 
 namespace mozilla {
 namespace dom {
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(URLSearchParams, mObservers)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(URLSearchParams, mObserver)
 NS_IMPL_CYCLE_COLLECTING_ADDREF(URLSearchParams)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(URLSearchParams)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(URLSearchParams)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 URLSearchParams::URLSearchParams()
+  : mValid(false)
 {
   SetIsDOMBinding();
 }
 
 URLSearchParams::~URLSearchParams()
 {
   DeleteAll();
 }
@@ -35,33 +36,33 @@ URLSearchParams::WrapObject(JSContext* a
 }
 
 /* static */ already_AddRefed<URLSearchParams>
 URLSearchParams::Constructor(const GlobalObject& aGlobal,
                              const nsAString& aInit,
                              ErrorResult& aRv)
 {
   nsRefPtr<URLSearchParams> sp = new URLSearchParams();
-  sp->ParseInput(NS_ConvertUTF16toUTF8(aInit), nullptr);
+  sp->ParseInput(NS_ConvertUTF16toUTF8(aInit));
   return sp.forget();
 }
 
 /* static */ already_AddRefed<URLSearchParams>
 URLSearchParams::Constructor(const GlobalObject& aGlobal,
                              URLSearchParams& aInit,
                              ErrorResult& aRv)
 {
   nsRefPtr<URLSearchParams> sp = new URLSearchParams();
   aInit.mSearchParams.EnumerateRead(CopyEnumerator, sp);
+  sp->mValid = true;
   return sp.forget();
 }
 
 void
-URLSearchParams::ParseInput(const nsACString& aInput,
-                            URLSearchParamsObserver* aObserver)
+URLSearchParams::ParseInput(const nsACString& aInput)
 {
   // Remove all the existing data before parsing a new input.
   DeleteAll();
 
   nsACString::const_iterator start, end;
   aInput.BeginReading(start);
   aInput.EndReading(end);
   nsACString::const_iterator iter(start);
@@ -103,17 +104,17 @@ URLSearchParams::ParseInput(const nsACSt
 
     nsAutoCString decodedValue;
     DecodeString(value, decodedValue);
 
     AppendInternal(NS_ConvertUTF8toUTF16(decodedName),
                    NS_ConvertUTF8toUTF16(decodedValue));
   }
 
-  NotifyObservers(aObserver);
+  mValid = true;
 }
 
 void
 URLSearchParams::DecodeString(const nsACString& aInput, nsACString& aOutput)
 {
   nsACString::const_iterator start, end;
   aInput.BeginReading(start);
   aInput.EndReading(end);
@@ -159,89 +160,114 @@ URLSearchParams::DecodeString(const nsAC
       }
     }
 
     aOutput.Append(*start);
     ++start;
   }
 }
 
+void
+URLSearchParams::CopyFromURLSearchParams(URLSearchParams& aSearchParams)
+{
+  // The other SearchParams must be valid before copying its data.
+  aSearchParams.Validate();
+
+  // Remove all the existing data before parsing a new input.
+  DeleteAll();
+  aSearchParams.mSearchParams.EnumerateRead(CopyEnumerator, this);
+  mValid = true;
+}
+
 /* static */ PLDHashOperator
 URLSearchParams::CopyEnumerator(const nsAString& aName,
                                 nsTArray<nsString>* aArray,
                                 void *userData)
 {
   URLSearchParams* aSearchParams = static_cast<URLSearchParams*>(userData);
 
   nsTArray<nsString>* newArray = new nsTArray<nsString>();
   newArray->AppendElements(*aArray);
 
   aSearchParams->mSearchParams.Put(aName, newArray);
   return PL_DHASH_NEXT;
 }
 
 void
-URLSearchParams::AddObserver(URLSearchParamsObserver* aObserver)
+URLSearchParams::SetObserver(URLSearchParamsObserver* aObserver)
 {
-  MOZ_ASSERT(aObserver);
-  MOZ_ASSERT(!mObservers.Contains(aObserver));
-  mObservers.AppendElement(aObserver);
+  MOZ_ASSERT(!mObserver);
+  mObserver = aObserver;
 }
 
 void
-URLSearchParams::RemoveObserver(URLSearchParamsObserver* aObserver)
+URLSearchParams::Validate()
 {
-  MOZ_ASSERT(aObserver);
-  MOZ_ASSERT(mObservers.Contains(aObserver));
-  mObservers.RemoveElement(aObserver);
+  MOZ_ASSERT(mValid || mObserver);
+  if (!mValid) {
+    mObserver->URLSearchParamsNeedsUpdates();
+    MOZ_ASSERT(mValid);
+  }
 }
 
 void
 URLSearchParams::Get(const nsAString& aName, nsString& aRetval)
 {
+  Validate();
+
   nsTArray<nsString>* array;
   if (!mSearchParams.Get(aName, &array)) {
     aRetval.Truncate();
     return;
   }
 
   aRetval.Assign(array->ElementAt(0));
 }
 
 void
 URLSearchParams::GetAll(const nsAString& aName, nsTArray<nsString>& aRetval)
 {
+  Validate();
+
   nsTArray<nsString>* array;
   if (!mSearchParams.Get(aName, &array)) {
     return;
   }
 
   aRetval.AppendElements(*array);
 }
 
 void
 URLSearchParams::Set(const nsAString& aName, const nsAString& aValue)
 {
+  // Before setting any new value we have to be sure to have all the previous
+  // values in place.
+  Validate();
+
   nsTArray<nsString>* array;
   if (!mSearchParams.Get(aName, &array)) {
     array = new nsTArray<nsString>();
     array->AppendElement(aValue);
     mSearchParams.Put(aName, array);
   } else {
     array->ElementAt(0) = aValue;
   }
 
-  NotifyObservers(nullptr);
+  NotifyObserver();
 }
 
 void
 URLSearchParams::Append(const nsAString& aName, const nsAString& aValue)
 {
+  // Before setting any new value we have to be sure to have all the previous
+  // values in place.
+  Validate();
+
   AppendInternal(aName, aValue);
-  NotifyObservers(nullptr);
+  NotifyObserver();
 }
 
 void
 URLSearchParams::AppendInternal(const nsAString& aName, const nsAString& aValue)
 {
   nsTArray<nsString>* array;
   if (!mSearchParams.Get(aName, &array)) {
     array = new nsTArray<nsString>();
@@ -249,30 +275,35 @@ URLSearchParams::AppendInternal(const ns
   }
 
   array->AppendElement(aValue);
 }
 
 bool
 URLSearchParams::Has(const nsAString& aName)
 {
+  Validate();
   return mSearchParams.Get(aName, nullptr);
 }
 
 void
 URLSearchParams::Delete(const nsAString& aName)
 {
+  // Before deleting any value we have to be sure to have all the previous
+  // values in place.
+  Validate();
+
   nsTArray<nsString>* array;
   if (!mSearchParams.Get(aName, &array)) {
     return;
   }
 
   mSearchParams.Remove(aName);
 
-  NotifyObservers(nullptr);
+  NotifyObserver();
 }
 
 void
 URLSearchParams::DeleteAll()
 {
   mSearchParams.Clear();
 }
 
@@ -307,16 +338,18 @@ public:
       ++p;
     }
   }
 };
 
 void
 URLSearchParams::Serialize(nsAString& aValue) const
 {
+  MOZ_ASSERT(mValid);
+
   SerializeData data;
   mSearchParams.EnumerateRead(SerializeEnumerator, &data);
   aValue.Assign(data.mValue);
 }
 
 /* static */ PLDHashOperator
 URLSearchParams::SerializeEnumerator(const nsAString& aName,
                                      nsTArray<nsString>* aArray,
@@ -335,19 +368,23 @@ URLSearchParams::SerializeEnumerator(con
     data->mValue.Append(NS_LITERAL_STRING("="));
     data->Serialize(NS_ConvertUTF16toUTF8(aArray->ElementAt(i)));
   }
 
   return PL_DHASH_NEXT;
 }
 
 void
-URLSearchParams::NotifyObservers(URLSearchParamsObserver* aExceptObserver)
+URLSearchParams::NotifyObserver()
 {
-  for (uint32_t i = 0; i < mObservers.Length(); ++i) {
-    if (mObservers[i] != aExceptObserver) {
-      mObservers[i]->URLSearchParamsUpdated();
-    }
+  if (mObserver) {
+    mObserver->URLSearchParamsUpdated();
   }
 }
 
+void
+URLSearchParams::Invalidate()
+{
+  mValid = false;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/URLSearchParams.h
+++ b/dom/base/URLSearchParams.h
@@ -18,28 +18,34 @@ namespace mozilla {
 namespace dom {
 
 class URLSearchParamsObserver : public nsISupports
 {
 public:
   virtual ~URLSearchParamsObserver() {}
 
   virtual void URLSearchParamsUpdated() = 0;
+  virtual void URLSearchParamsNeedsUpdates() = 0;
 };
 
 class URLSearchParams MOZ_FINAL : public nsISupports,
                                   public nsWrapperCache
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(URLSearchParams)
 
   URLSearchParams();
   ~URLSearchParams();
 
+  bool HasURLAssociated() const
+  {
+    return !!mObserver;
+  }
+
   // WebIDL methods
   nsISupports* GetParentObject() const
   {
     return nullptr;
   }
 
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
@@ -47,21 +53,28 @@ public:
   static already_AddRefed<URLSearchParams>
   Constructor(const GlobalObject& aGlobal, const nsAString& aInit,
               ErrorResult& aRv);
 
   static already_AddRefed<URLSearchParams>
   Constructor(const GlobalObject& aGlobal, URLSearchParams& aInit,
               ErrorResult& aRv);
 
-  void ParseInput(const nsACString& aInput,
-                  URLSearchParamsObserver* aObserver);
+  void ParseInput(const nsACString& aInput);
+
+  void CopyFromURLSearchParams(URLSearchParams& aSearchParams);
+
+  void SetObserver(URLSearchParamsObserver* aObserver);
 
-  void AddObserver(URLSearchParamsObserver* aObserver);
-  void RemoveObserver(URLSearchParamsObserver* aObserver);
+  void Invalidate();
+
+  bool IsValid() const
+  {
+    return mValid;
+  }
 
   void Serialize(nsAString& aValue) const;
 
   void Get(const nsAString& aName, nsString& aRetval);
 
   void GetAll(const nsAString& aName, nsTArray<nsString >& aRetval);
 
   void Set(const nsAString& aName, const nsAString& aValue);
@@ -69,37 +82,43 @@ public:
   void Append(const nsAString& aName, const nsAString& aValue);
 
   bool Has(const nsAString& aName);
 
   void Delete(const nsAString& aName);
 
   void Stringify(nsString& aRetval)
   {
+    Validate();
     Serialize(aRetval);
   }
 
 private:
   void AppendInternal(const nsAString& aName, const nsAString& aValue);
 
   void DeleteAll();
 
   void DecodeString(const nsACString& aInput, nsACString& aOutput);
 
-  void NotifyObservers(URLSearchParamsObserver* aExceptObserver);
+  void NotifyObserver();
 
   static PLDHashOperator
   CopyEnumerator(const nsAString& aName, nsTArray<nsString>* aArray,
                  void *userData);
 
   static PLDHashOperator
   SerializeEnumerator(const nsAString& aName, nsTArray<nsString>* aArray,
                       void *userData);
 
+  void
+  Validate();
+
   nsClassHashtable<nsStringHashKey, nsTArray<nsString>> mSearchParams;
 
-  nsTArray<nsRefPtr<URLSearchParamsObserver>> mObservers;
+  nsRefPtr<URLSearchParamsObserver> mObserver;
+
+  bool mValid;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_URLSearchParams_h */
--- a/dom/base/test/test_urlSearchParams.html
+++ b/dom/base/test/test_urlSearchParams.html
@@ -132,17 +132,17 @@ https://bugzilla.mozilla.org/show_bug.cg
     is(url.href, 'http://www.example.net/?foo=bar', 'URL right');
 
     url.searchParams = null;
     is(url.searchParams.get('foo'), 'bar', "URL.searchParams.get('foo')");
     is(url.href, 'http://www.example.net/?foo=bar', 'URL right');
 
     var url2 = new URL('http://www.example.net?e=f');
     url.searchParams = url2.searchParams;
-    is(url.searchParams, url2.searchParams, "URL.searchParams is not the same object");
+    isnot(url.searchParams, url2.searchParams, "URL.searchParams is not the same object");
     is(url.searchParams.get('e'), 'f', "URL.searchParams.get('e')");
 
     url.href = "http://www.example.net?bar=foo";
     is(url.searchParams.get('bar'), 'foo', "URL.searchParams.get('bar')");
 
     runTest();
   }
 
@@ -165,17 +165,17 @@ https://bugzilla.mozilla.org/show_bug.cg
     is(e.href, 'http://www.example.net/?foo=bar', 'e is right');
 
     e.searchParams = null;
     is(e.searchParams.get('foo'), 'bar', "e.searchParams.get('foo')");
     is(e.href, 'http://www.example.net/?foo=bar', 'e is right');
 
     var url2 = new URL('http://www.example.net?e=f');
     e.searchParams = url2.searchParams;
-    is(e.searchParams, url2.searchParams, "e.searchParams is not the same object");
+    isnot(e.searchParams, url2.searchParams, "e.searchParams is not the same object");
     is(e.searchParams.get('e'), 'f', "e.searchParams.get('e')");
 
     e.href = "http://www.example.net?bar=foo";
     is(e.searchParams.get('bar'), 'foo', "e.searchParams.get('bar')");
 
     e.setAttribute('href', "http://www.example.net?bar2=foo2");
     is(e.searchParams.get('bar2'), 'foo2', "e.searchParams.get('bar2')");
 
@@ -198,64 +198,24 @@ https://bugzilla.mozilla.org/show_bug.cg
 
       var url2 = new URL(url.href);
       is(url2.searchParams.get('a'), encoding[i][0], 'a is still there');
     }
 
     runTest();
   }
 
-  function testMultiURL() {
-    var a = new URL('http://www.example.net?a=b&c=d');
-    var b = new URL('http://www.example.net?e=f');
-    var c = document.createElement('a');
-    var d = document.createElement('area');
-    ok(a.searchParams.has('a'), "a.searchParams.has('a')");
-    ok(a.searchParams.has('c'), "a.searchParams.has('c')");
-    ok(b.searchParams.has('e'), "b.searchParams.has('e')");
-    ok(c.searchParams, "c.searchParams");
-    ok(d.searchParams, "d.searchParams");
-
-    var u = new URLSearchParams();
-    a.searchParams = b.searchParams = c.searchParams = d.searchParams = u;
-    is(a.searchParams, u, "a.searchParams === u");
-    is(b.searchParams, u, "b.searchParams === u");
-    is(c.searchParams, u, "c.searchParams === u");
-    is(d.searchParams, u, "d.searchParams === u");
-    ok(!a.searchParams.has('a'), "!a.searchParams.has('a')");
-    ok(!a.searchParams.has('c'), "!a.searchParams.has('c')");
-    ok(!b.searchParams.has('e'), "!b.searchParams.has('e')");
-
-    u.append('foo', 'bar');
-    is(a.searchParams.get('foo'), 'bar', "a has foo=bar");
-    is(b.searchParams.get('foo'), 'bar', "b has foo=bar");
-    is(c.searchParams.get('foo'), 'bar', "c has foo=bar");
-    is(d.searchParams.get('foo'), 'bar', "d has foo=bar");
-    is(a + "", b + "", "stringify a == b");
-    is(c.searchParams + "", b.searchParams + "", "stringify c.searchParams == b.searchParams");
-    is(d.searchParams + "", b.searchParams + "", "stringify d.searchParams == b.searchParams");
-
-    a.search = "?bar=foo";
-    is(a.searchParams.get('bar'), 'foo', "a has bar=foo");
-    is(b.searchParams.get('bar'), 'foo', "b has bar=foo");
-    is(c.searchParams.get('bar'), 'foo', "c has bar=foo");
-    is(d.searchParams.get('bar'), 'foo', "d has bar=foo");
-
-    runTest();
-  }
-
   var tests = [
     testSimpleURLSearchParams,
     testCopyURLSearchParams,
     testParserURLSearchParams,
     testURL,
     function() { testElement(document.getElementById('anchor')) },
     function() { testElement(document.getElementById('area')) },
-    testEncoding,
-    testMultiURL
+    testEncoding
   ];
 
   function runTest() {
     if (!tests.length) {
       SimpleTest.finish();
       return;
     }
 
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -1128,17 +1128,16 @@ ResolvePrototypeOrConstructor(JSContext*
   {
     JSAutoCompartment ac(cx, global);
     ProtoAndIfaceArray& protoAndIfaceArray = *GetProtoAndIfaceArray(global);
     JSObject* protoOrIface = protoAndIfaceArray[protoAndIfaceArrayIndex];
     if (!protoOrIface) {
       return false;
     }
     desc.object().set(wrapper);
-    desc.setShortId(0);
     desc.setAttributes(attrs);
     desc.setGetter(JS_PropertyStub);
     desc.setSetter(JS_StrictPropertyStub);
     desc.value().set(JS::ObjectValue(*protoOrIface));
   }
   return JS_WrapPropertyDescriptor(cx, desc);
 }
 
@@ -1507,49 +1506,16 @@ AppendNamedPropertyIds(JSContext* cx, JS
         return false;
       }
     }
   }
 
   return true;
 }
 
-JSObject*
-GetXrayExpandoChain(JSObject* obj)
-{
-  const js::Class* clasp = js::GetObjectClass(obj);
-  JS::Value v;
-  if (IsNonProxyDOMClass(clasp) || IsDOMIfaceAndProtoClass(clasp)) {
-    v = js::GetReservedSlot(obj, DOM_XRAY_EXPANDO_SLOT);
-  } else if (clasp->isProxy()) {
-    MOZ_ASSERT(js::GetProxyHandler(obj)->family() == ProxyFamily());
-    v = js::GetProxyExtra(obj, JSPROXYSLOT_XRAY_EXPANDO);
-  } else {
-    MOZ_ASSERT(JS_IsNativeFunction(obj, Constructor));
-    v = js::GetFunctionNativeReserved(obj, CONSTRUCTOR_XRAY_EXPANDO_SLOT);
-  }
-  return v.isUndefined() ? nullptr : &v.toObject();
-}
-
-void
-SetXrayExpandoChain(JSObject* obj, JSObject* chain)
-{
-  JS::Value v = chain ? JS::ObjectValue(*chain) : JSVAL_VOID;
-  const js::Class* clasp = js::GetObjectClass(obj);
-  if (IsNonProxyDOMClass(clasp) || IsDOMIfaceAndProtoClass(clasp)) {
-    js::SetReservedSlot(obj, DOM_XRAY_EXPANDO_SLOT, v);
-  } else if (clasp->isProxy()) {
-    MOZ_ASSERT(js::GetProxyHandler(obj)->family() == ProxyFamily());
-    js::SetProxyExtra(obj, JSPROXYSLOT_XRAY_EXPANDO, v);
-  } else {
-    MOZ_ASSERT(JS_IsNativeFunction(obj, Constructor));
-    js::SetFunctionNativeReserved(obj, CONSTRUCTOR_XRAY_EXPANDO_SLOT, v);
-  }
-}
-
 bool
 DictionaryBase::ParseJSON(JSContext* aCx,
                           const nsAString& aJSON,
                           JS::MutableHandle<JS::Value> aVal)
 {
   if (aJSON.IsEmpty()) {
     return true;
   }
@@ -1583,17 +1549,16 @@ bool
 NativeToString(JSContext* cx, JS::Handle<JSObject*> wrapper,
                JS::Handle<JSObject*> obj, const char* pre,
                const char* post,
                JS::MutableHandle<JS::Value> v)
 {
   JS::Rooted<JSPropertyDescriptor> toStringDesc(cx);
   toStringDesc.object().set(nullptr);
   toStringDesc.setAttributes(0);
-  toStringDesc.setShortId(0);
   toStringDesc.setGetter(nullptr);
   toStringDesc.setSetter(nullptr);
   toStringDesc.value().set(JS::UndefinedValue());
   JS::Rooted<jsid> id(cx,
     nsXPConnect::GetRuntimeInstance()->GetStringID(XPCJSRuntime::IDX_TO_STRING));
   if (!XrayResolveNativeProperty(cx, wrapper, obj, id, &toStringDesc)) {
     return false;
   }
@@ -1752,17 +1717,16 @@ ReparentWrapper(JSContext* aCx, JS::Hand
         return NS_ERROR_FAILURE;
       }
     } else {
       propertyHolder = nullptr;
     }
 
     // Expandos from other compartments are attached to the target JS object.
     // Copy them over, and let the old ones die a natural death.
-    SetXrayExpandoChain(newobj, nullptr);
     if (!xpc::XrayUtils::CloneExpandoChain(aCx, newobj, aObj)) {
       return NS_ERROR_FAILURE;
     }
 
     // We've set up |newobj|, so we make it own the native by nulling
     // out the reserved slot of |obj|.
     //
     // NB: It's important to do this _after_ copying the properties to
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -1950,24 +1950,22 @@ XrayEnumerateProperties(JSContext* cx, J
 
 extern NativePropertyHooks sWorkerNativePropertyHooks;
 
 // We use one constructor JSNative to represent all DOM interface objects (so
 // we can easily detect when we need to wrap them in an Xray wrapper). We store
 // the real JSNative in the mNative member of a JSNativeHolder in the
 // CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT slot of the JSFunction object for a
 // specific interface object. We also store the NativeProperties in the
-// JSNativeHolder. The CONSTRUCTOR_XRAY_EXPANDO_SLOT is used to store the
-// expando chain of the Xray for the interface object.
+// JSNativeHolder.
 // Note that some interface objects are not yet a JSFunction but a normal
 // JSObject with a DOMJSClass, those do not use these slots.
 
 enum {
-  CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT = 0,
-  CONSTRUCTOR_XRAY_EXPANDO_SLOT
+  CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT = 0
 };
 
 bool
 Constructor(JSContext* cx, unsigned argc, JS::Value* vp);
 
 inline bool
 UseDOMXray(JSObject* obj)
 {
@@ -2002,21 +2000,16 @@ Take(nsAutoPtr<T>& smartPtr, T* ptr)
   smartPtr = ptr;
 }
 
 inline void
 MustInheritFromNonRefcountedDOMObject(NonRefcountedDOMObject*)
 {
 }
 
-// Set the chain of expando objects for various consumers of the given object.
-// For Paris Bindings only. See the relevant infrastructure in XrayWrapper.cpp.
-JSObject* GetXrayExpandoChain(JSObject *obj);
-void SetXrayExpandoChain(JSObject *obj, JSObject *chain);
-
 /**
  * This creates a JSString containing the value that the toString function for
  * obj should create according to the WebIDL specification, ignoring any
  * modifications by script. The value is prefixed with pre and postfixed with
  * post, unless this is called for an object that has a stringifier. It is
  * specifically for use by Xray code.
  *
  * wrapper is the Xray JS object.
--- a/dom/bindings/DOMJSProxyHandler.h
+++ b/dom/bindings/DOMJSProxyHandler.h
@@ -16,18 +16,17 @@
 #define DOM_PROXY_OBJECT_SLOT js::PROXY_PRIVATE_SLOT
 
 namespace mozilla {
 namespace dom {
 
 class DOMClass;
 
 enum {
-  JSPROXYSLOT_EXPANDO = 0,
-  JSPROXYSLOT_XRAY_EXPANDO
+  JSPROXYSLOT_EXPANDO = 0
 };
 
 template<typename T> struct Prefable;
 
 // This variable exists solely to provide a unique address for use as an identifier.
 extern const char HandlerFamily;
 inline const void* ProxyFamily() { return &HandlerFamily; }
 
@@ -141,17 +140,16 @@ IsArrayIndex(int32_t index)
 
 inline void
 FillPropertyDescriptor(JS::MutableHandle<JSPropertyDescriptor> desc, JSObject* obj, bool readonly)
 {
   desc.object().set(obj);
   desc.setAttributes((readonly ? JSPROP_READONLY : 0) | JSPROP_ENUMERATE);
   desc.setGetter(nullptr);
   desc.setSetter(nullptr);
-  desc.setShortId(0);
 }
 
 inline void
 FillPropertyDescriptor(JS::MutableHandle<JSPropertyDescriptor> desc, JSObject* obj, JS::Value v,
                        bool readonly)
 {
   desc.value().set(v);
   FillPropertyDescriptor(desc, obj, readonly);
@@ -161,15 +159,14 @@ inline void
 FillPropertyDescriptor(JS::MutableHandle<JSPropertyDescriptor> desc,
                        JSObject* obj, unsigned attributes, JS::Value v)
 {
   desc.object().set(obj);
   desc.value().set(v);
   desc.setAttributes(attributes);
   desc.setGetter(nullptr);
   desc.setSetter(nullptr);
-  desc.setShortId(0);
 }
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_DOMProxyHandler_h */
--- a/dom/bindings/JSSlots.h
+++ b/dom/bindings/JSSlots.h
@@ -9,42 +9,33 @@
  */
 #ifndef mozilla_dom_DOMSlots_h
 #define mozilla_dom_DOMSlots_h
 
 // We use slot 0 for holding the raw object.  This is safe for both
 // globals and non-globals.
 #define DOM_OBJECT_SLOT 0
 
-// We use slot 1 for holding the expando object. This is not safe for globals
-// until bug 760095 is fixed, so that bug blocks converting Window to new
-// bindings.
-#define DOM_XRAY_EXPANDO_SLOT 1
-
 // We use slot 2 for holding either a JS::ObjectValue which points to the cached
 // SOW or JS::UndefinedValue if this class doesn't need SOWs. This is not safe
 // for globals until bug 760095 is fixed, so that bug blocks converting Window
 // to new bindings.
-#define DOM_OBJECT_SLOT_SOW 2
+#define DOM_OBJECT_SLOT_SOW 1
 
 // The total number of slots non-proxy DOM objects use by default.
 // Specific objects may have more for storing cached values.
-#define DOM_INSTANCE_RESERVED_SLOTS 3
+#define DOM_INSTANCE_RESERVED_SLOTS 2
 
 // NOTE: This is baked into the Ion JIT as 0 in codegen for LGetDOMProperty and
 // LSetDOMProperty. Those constants need to be changed accordingly if this value
 // changes.
 #define DOM_PROTO_INSTANCE_CLASS_SLOT 0
 
 // Interface objects store a number of reserved slots equal to
 // DOM_INTERFACE_SLOTS_BASE + number of named constructors.
-#define DOM_INTERFACE_SLOTS_BASE (DOM_XRAY_EXPANDO_SLOT + 1)
+#define DOM_INTERFACE_SLOTS_BASE 0
 
 // Interface prototype objects store a number of reserved slots equal to
 // DOM_INTERFACE_PROTO_SLOTS_BASE or DOM_INTERFACE_PROTO_SLOTS_BASE + 1 if a
 // slot for the unforgeable holder is needed.
-#define DOM_INTERFACE_PROTO_SLOTS_BASE (DOM_XRAY_EXPANDO_SLOT + 1)
-
-static_assert(DOM_PROTO_INSTANCE_CLASS_SLOT != DOM_XRAY_EXPANDO_SLOT,
-              "Interface prototype object use both of these, so they must "
-              "not be the same slot.");
+#define DOM_INTERFACE_PROTO_SLOTS_BASE (DOM_PROTO_INSTANCE_CLASS_SLOT + 1)
 
 #endif /* mozilla_dom_DOMSlots_h */
--- a/dom/indexedDB/IDBDatabase.cpp
+++ b/dom/indexedDB/IDBDatabase.cpp
@@ -389,17 +389,17 @@ IDBDatabase::OnUnlink()
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   // We've been unlinked, at the very least we should be able to prevent further
   // transactions from starting and unblock any other SetVersion callers.
   CloseInternal(true);
 
   // No reason for the QuotaManager to track us any longer.
   QuotaManager* quotaManager = QuotaManager::Get();
-  if (quotaManager) {
+  if (mRegistered && quotaManager) {
     quotaManager->UnregisterStorage(this);
 
     // Don't try to unregister again in the destructor.
     mRegistered = false;
   }
 }
 
 already_AddRefed<IDBObjectStore>
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -1,8 +1,10 @@
+/* -*- 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 "MediaManager.h"
 
 #include "MediaStreamGraph.h"
 #include "GetUserMediaRequest.h"
@@ -235,18 +237,20 @@ public:
     , mWindowID(aWindowID)
     , mManager(MediaManager::GetInstance()) {}
 
   NS_IMETHOD
   Run()
   {
     NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
-    nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback> success(mSuccess);
-    nsCOMPtr<nsIDOMGetUserMediaErrorCallback> error(mError);
+    nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback> success;
+    nsCOMPtr<nsIDOMGetUserMediaErrorCallback> error;
+    success.swap(mSuccess);
+    error.swap(mError);
 
     // Only run if window is still on our active list.
     if (!mManager->IsWindowStillActive(mWindowID)) {
       return NS_OK;
     }
 
     nsCOMPtr<nsIWritableVariant> devices =
       do_CreateInstance("@mozilla.org/variant;1");
@@ -273,18 +277,18 @@ public:
                           static_cast<const void*>(tmp.Elements())
                         ));
 
     success->OnSuccess(devices);
     return NS_OK;
   }
 
 private:
-  already_AddRefed<nsIGetUserMediaDevicesSuccessCallback> mSuccess;
-  already_AddRefed<nsIDOMGetUserMediaErrorCallback> mError;
+  nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback> mSuccess;
+  nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mError;
   nsAutoPtr<nsTArray<nsCOMPtr<nsIMediaDevice> > > mDevices;
   uint64_t mWindowID;
   nsRefPtr<MediaManager> mManager;
 };
 
 // Handle removing GetUserMediaCallbackMediaStreamListener from main thread
 class GetUserMediaListenerRemove: public nsRunnable
 {
@@ -1082,25 +1086,26 @@ public:
                                           mLoopbackVideoDevice));
     {
       ScopedDeletePtr<SourceSet> s (GetSources(backend, mConstraints.mAudiom,
                                         &MediaEngine::EnumerateAudioDevices,
                                         mLoopbackAudioDevice));
       final->MoveElementsFrom(*s);
     }
     NS_DispatchToMainThread(new DeviceSuccessCallbackRunnable(mWindowId,
-                                                              mSuccess, mError,
+                                                              mSuccess.forget(),
+                                                              mError.forget(),
                                                               final.forget()));
     return NS_OK;
   }
 
 private:
   MediaStreamConstraintsInternal mConstraints;
-  already_AddRefed<nsIGetUserMediaDevicesSuccessCallback> mSuccess;
-  already_AddRefed<nsIDOMGetUserMediaErrorCallback> mError;
+  nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback> mSuccess;
+  nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mError;
   nsRefPtr<MediaManager> mManager;
   uint64_t mWindowId;
   const nsString mCallId;
   // Audio & Video loopback devices to be used based on
   // the preference settings. This is currently used for
   // automated media tests only.
   char* mLoopbackAudioDevice;
   char* mLoopbackVideoDevice;
--- a/dom/workers/URL.cpp
+++ b/dom/workers/URL.cpp
@@ -610,17 +610,19 @@ URL::SetHref(const nsAString& aHref, Err
   nsRefPtr<SetterRunnable> runnable =
     new SetterRunnable(mWorkerPrivate, SetterRunnable::SetterHref, aHref,
                        mURLProxy, aRv);
 
   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
     JS_ReportPendingException(mWorkerPrivate->GetJSContext());
   }
 
-  UpdateURLSearchParams();
+  if (mSearchParams) {
+    mSearchParams->Invalidate();
+  }
 }
 
 void
 URL::GetOrigin(nsString& aOrigin) const
 {
   nsRefPtr<GetterRunnable> runnable =
     new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterOrigin, aOrigin,
                        mURLProxy);
@@ -816,17 +818,20 @@ URL::GetSearch(nsString& aSearch) const
     JS_ReportPendingException(mWorkerPrivate->GetJSContext());
   }
 }
 
 void
 URL::SetSearch(const nsAString& aSearch)
 {
   SetSearchInternal(aSearch);
-  UpdateURLSearchParams();
+
+  if (mSearchParams) {
+    mSearchParams->Invalidate();
+  }
 }
 
 void
 URL::SetSearchInternal(const nsAString& aSearch)
 {
   ErrorResult rv;
   nsRefPtr<SetterRunnable> runnable =
     new SetterRunnable(mWorkerPrivate, SetterRunnable::SetterSearch,
@@ -846,22 +851,26 @@ URL::GetSearchParams()
 
 void
 URL::SetSearchParams(URLSearchParams* aSearchParams)
 {
   if (!aSearchParams) {
     return;
   }
 
-  if (mSearchParams) {
-    mSearchParams->RemoveObserver(this);
+  if (!aSearchParams->HasURLAssociated()) {
+    MOZ_ASSERT(aSearchParams->IsValid());
+
+    mSearchParams = aSearchParams;
+    mSearchParams->SetObserver(this);
+  } else {
+    CreateSearchParamsIfNeeded();
+    mSearchParams->CopyFromURLSearchParams(*aSearchParams);
   }
 
-  mSearchParams = aSearchParams;
-  mSearchParams->AddObserver(this);
 
   nsString search;
   mSearchParams->Serialize(search);
   SetSearchInternal(search);
 }
 
 void
 URL::GetHash(nsString& aHash) const
@@ -937,36 +946,36 @@ URL::RevokeObjectURL(const GlobalObject&
   if (!runnable->Dispatch(cx)) {
     JS_ReportPendingException(cx);
   }
 }
 
 void
 URL::URLSearchParamsUpdated()
 {
-  MOZ_ASSERT(mSearchParams);
+  MOZ_ASSERT(mSearchParams && mSearchParams->IsValid());
 
   nsString search;
   mSearchParams->Serialize(search);
   SetSearchInternal(search);
 }
 
 void
-URL::UpdateURLSearchParams()
+URL::URLSearchParamsNeedsUpdates()
 {
-  if (mSearchParams) {
-    nsString search;
-    GetSearch(search);
-    mSearchParams->ParseInput(NS_ConvertUTF16toUTF8(Substring(search, 1)), this);
-  }
+  MOZ_ASSERT(mSearchParams);
+
+  nsString search;
+  GetSearch(search);
+  mSearchParams->ParseInput(NS_ConvertUTF16toUTF8(Substring(search, 1)));
 }
 
 void
 URL::CreateSearchParamsIfNeeded()
 {
   if (!mSearchParams) {
     mSearchParams = new URLSearchParams();
-    mSearchParams->AddObserver(this);
-    UpdateURLSearchParams();
+    mSearchParams->SetObserver(this);
+    mSearchParams->Invalidate();
   }
 }
 
 END_WORKERS_NAMESPACE
--- a/dom/workers/URL.h
+++ b/dom/workers/URL.h
@@ -114,29 +114,28 @@ public:
 
   void Stringify(nsString& aRetval) const
   {
     GetHref(aRetval);
   }
 
   // IURLSearchParamsObserver
   void URLSearchParamsUpdated() MOZ_OVERRIDE;
+  void URLSearchParamsNeedsUpdates() MOZ_OVERRIDE;
 
 private:
   URLProxy* GetURLProxy() const
   {
     return mURLProxy;
   }
 
   void CreateSearchParamsIfNeeded();
 
   void SetSearchInternal(const nsAString& aSearch);
 
-  void UpdateURLSearchParams();
-
   WorkerPrivate* mWorkerPrivate;
   nsRefPtr<URLProxy> mURLProxy;
   nsRefPtr<URLSearchParams> mSearchParams;
 };
 
 END_WORKERS_NAMESPACE
 
 #endif /* mozilla_dom_workers_url_h__ */
--- a/dom/workers/test/urlSearchParams_worker.js
+++ b/dom/workers/test/urlSearchParams_worker.js
@@ -3,16 +3,21 @@ function ok(a, msg) {
   postMessage({type: 'status', status: !!a, msg: a + ": " + msg });
 }
 
 function is(a, b, msg) {
   dump("IS: " + (a===b) + "  =>  " + a + " | " + b + " " + msg + "\n");
   postMessage({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg });
 }
 
+function isnot(a, b, msg) {
+  dump("ISNOT: " + (a!==b) + "  =>  " + a + " | " + b + " " + msg + "\n");
+  postMessage({type: 'status', status: a !== b, msg: a + " !== " + b + ": " + msg });
+}
+
 onmessage = function() {
   status = false;
   try {
     if ((URLSearchParams instanceof Object)) {
       status = true;
     }
   } catch(e) {
   }
@@ -124,17 +129,17 @@ onmessage = function() {
     is(url.href, 'http://www.example.net/?foo=bar', 'URL right');
 
     url.searchParams = null;
     is(url.searchParams.get('foo'), 'bar', "URL.searchParams.get('foo')");
     is(url.href, 'http://www.example.net/?foo=bar', 'URL right');
 
     var url2 = new URL('http://www.example.net?e=f');
     url.searchParams = url2.searchParams;
-    is(url.searchParams, url2.searchParams, "URL.searchParams is not the same object");
+    isnot(url.searchParams, url2.searchParams, "URL.searchParams is not the same object");
     is(url.searchParams.get('e'), 'f', "URL.searchParams.get('e')");
 
     url.href = "http://www.example.net?bar=foo";
     is(url.searchParams.get('bar'), 'foo', "URL.searchParams.get('bar')");
 
     runTest();
   }
 
@@ -154,49 +159,22 @@ onmessage = function() {
 
       var url2 = new URL(url.href);
       is(url2.searchParams.get('a'), encoding[i][0], 'a is still there');
     }
 
     runTest();
   }
 
-  function testMultiURL() {
-    var a = new URL('http://www.example.net?a=b&c=d');
-    var b = new URL('http://www.example.net?e=f');
-    ok(a.searchParams.has('a'), "a.searchParams.has('a')");
-    ok(a.searchParams.has('c'), "a.searchParams.has('c')");
-    ok(b.searchParams.has('e'), "b.searchParams.has('e')");
-
-    var u = new URLSearchParams();
-    a.searchParams = b.searchParams = u;
-    is(a.searchParams, u, "a.searchParams === u");
-    is(b.searchParams, u, "b.searchParams === u");
-    ok(!a.searchParams.has('a'), "!a.searchParams.has('a')");
-    ok(!a.searchParams.has('c'), "!a.searchParams.has('c')");
-    ok(!b.searchParams.has('e'), "!b.searchParams.has('e')");
-
-    u.append('foo', 'bar');
-    is(a.searchParams.get('foo'), 'bar', "a has foo=bar");
-    is(b.searchParams.get('foo'), 'bar', "b has foo=bar");
-    is(a + "", b + "", "stringify a == b");
-
-    a.search = "?bar=foo";
-    is(a.searchParams.get('bar'), 'foo', "a has bar=foo");
-    is(b.searchParams.get('bar'), 'foo', "b has bar=foo");
-
-    runTest();
-  }
   var tests = [
     testSimpleURLSearchParams,
     testCopyURLSearchParams,
     testParserURLSearchParams,
     testURL,
-    testEncoding,
-    testMultiURL
+    testEncoding
   ];
 
   function runTest() {
     if (!tests.length) {
       postMessage({type: 'finish' });
       return;
     }
 
--- a/gfx/2d/GenericRefCounted.h
+++ b/gfx/2d/GenericRefCounted.h
@@ -48,38 +48,37 @@ class GenericRefCounted : public Generic
     GenericRefCounted() : refCnt(0) { }
 
     virtual ~GenericRefCounted() {
       MOZ_ASSERT(refCnt == detail::DEAD);
     }
 
   public:
     virtual void AddRef() {
-      MOZ_ASSERT(refCnt >= 0);
       ++refCnt;
     }
 
     virtual void Release() {
       MOZ_ASSERT(refCnt > 0);
       if (0 == --refCnt) {
 #ifdef DEBUG
         refCnt = detail::DEAD;
 #endif
         delete this;
       }
     }
 
-    int refCount() const { return refCnt; }
+    MozRefCountType refCount() const { return refCnt; }
     bool hasOneRef() const {
       MOZ_ASSERT(refCnt > 0);
       return refCnt == 1;
     }
 
   private:
-    typename Conditional<Atomicity == AtomicRefCount, Atomic<int>, int>::Type refCnt;
+    typename Conditional<Atomicity == AtomicRefCount, Atomic<MozRefCountType>, MozRefCountType>::Type refCnt;
 };
 
 } // namespace detail
 
 /**
  * This reference-counting base class is virtual instead of
  * being templated, which is useful in cases where one needs
  * genericity at binary code level, but comes at the cost
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -716,63 +716,76 @@ ContainerLayer::ContainerLayer(LayerMana
     mSupportsComponentAlphaChildren(false),
     mMayHaveReadbackChild(false)
 {
   mContentFlags = 0; // Clear NO_TEXT, NO_TEXT_OVER_TRANSPARENT
 }
 
 ContainerLayer::~ContainerLayer() {}
 
-void
+bool
 ContainerLayer::InsertAfter(Layer* aChild, Layer* aAfter)
 {
-  NS_ASSERTION(aChild->Manager() == Manager(),
-               "Child has wrong manager");
-  NS_ASSERTION(!aChild->GetParent(),
-               "aChild already in the tree");
-  NS_ASSERTION(!aChild->GetNextSibling() && !aChild->GetPrevSibling(),
-               "aChild already has siblings?");
-  NS_ASSERTION(!aAfter ||
-               (aAfter->Manager() == Manager() &&
-                aAfter->GetParent() == this),
-               "aAfter is not our child");
+  if(aChild->Manager() != Manager()) {
+    NS_ERROR("Child has wrong manager");
+    return false;
+  }
+  if(aChild->GetParent()) {
+    NS_ERROR("aChild already in the tree");
+    return false;
+  }
+  if (aChild->GetNextSibling() || aChild->GetPrevSibling()) {
+    NS_ERROR("aChild already has siblings?");
+    return false;
+  }
+  if (aAfter && (aAfter->Manager() != Manager() ||
+                 aAfter->GetParent() != this))
+  {
+    NS_ERROR("aAfter is not our child");
+    return false;
+  }
 
   aChild->SetParent(this);
   if (aAfter == mLastChild) {
     mLastChild = aChild;
   }
   if (!aAfter) {
     aChild->SetNextSibling(mFirstChild);
     if (mFirstChild) {
       mFirstChild->SetPrevSibling(aChild);
     }
     mFirstChild = aChild;
     NS_ADDREF(aChild);
     DidInsertChild(aChild);
-    return;
+    return true;
   }
 
   Layer* next = aAfter->GetNextSibling();
   aChild->SetNextSibling(next);
   aChild->SetPrevSibling(aAfter);
   if (next) {
     next->SetPrevSibling(aChild);
   }
   aAfter->SetNextSibling(aChild);
   NS_ADDREF(aChild);
   DidInsertChild(aChild);
+  return true;
 }
 
-void
+bool
 ContainerLayer::RemoveChild(Layer *aChild)
 {
-  NS_ASSERTION(aChild->Manager() == Manager(),
-               "Child has wrong manager");
-  NS_ASSERTION(aChild->GetParent() == this,
-               "aChild not our child");
+  if (aChild->Manager() != Manager()) {
+    NS_ERROR("Child has wrong manager");
+    return false;
+  }
+  if (aChild->GetParent() != this) {
+    NS_ERROR("aChild not our child");
+    return false;
+  }
 
   Layer* prev = aChild->GetPrevSibling();
   Layer* next = aChild->GetNextSibling();
   if (prev) {
     prev->SetNextSibling(next);
   } else {
     this->mFirstChild = next;
   }
@@ -783,38 +796,47 @@ ContainerLayer::RemoveChild(Layer *aChil
   }
 
   aChild->SetNextSibling(nullptr);
   aChild->SetPrevSibling(nullptr);
   aChild->SetParent(nullptr);
 
   this->DidRemoveChild(aChild);
   NS_RELEASE(aChild);
+  return true;
 }
 
 
-void
+bool
 ContainerLayer::RepositionChild(Layer* aChild, Layer* aAfter)
 {
-  NS_ASSERTION(aChild->Manager() == Manager(),
-               "Child has wrong manager");
-  NS_ASSERTION(aChild->GetParent() == this,
-               "aChild not our child");
-  NS_ASSERTION(!aAfter ||
-               (aAfter->Manager() == Manager() &&
-                aAfter->GetParent() == this),
-               "aAfter is not our child");
-  NS_ASSERTION(aChild != aAfter,
-               "aChild cannot be the same as aAfter");
+  if (aChild->Manager() != Manager()) {
+    NS_ERROR("Child has wrong manager");
+    return false;
+  }
+  if (aChild->GetParent() != this) {
+    NS_ERROR("aChild not our child");
+    return false;
+  }
+  if (aAfter && (aAfter->Manager() != Manager() ||
+                 aAfter->GetParent() != this))
+  {
+    NS_ERROR("aAfter is not our child");
+    return false;
+  }
+  if (aChild == aAfter) {
+    NS_ERROR("aChild cannot be the same as aAfter");
+    return false;
+  }
 
   Layer* prev = aChild->GetPrevSibling();
   Layer* next = aChild->GetNextSibling();
   if (prev == aAfter) {
     // aChild is already in the correct position, nothing to do.
-    return;
+    return true;
   }
   if (prev) {
     prev->SetNextSibling(next);
   } else {
     mFirstChild = next;
   }
   if (next) {
     next->SetPrevSibling(prev);
@@ -823,28 +845,29 @@ ContainerLayer::RepositionChild(Layer* a
   }
   if (!aAfter) {
     aChild->SetPrevSibling(nullptr);
     aChild->SetNextSibling(mFirstChild);
     if (mFirstChild) {
       mFirstChild->SetPrevSibling(aChild);
     }
     mFirstChild = aChild;
-    return;
+    return true;
   }
 
   Layer* afterNext = aAfter->GetNextSibling();
   if (afterNext) {
     afterNext->SetPrevSibling(aChild);
   } else {
     mLastChild = aChild;
   }
   aAfter->SetNextSibling(aChild);
   aChild->SetPrevSibling(aAfter);
   aChild->SetNextSibling(afterNext);
+  return true;
 }
 
 void
 ContainerLayer::FillSpecificAttributes(SpecificLayerAttributes& aAttrs)
 {
   aAttrs = ContainerLayerAttributes(GetFrameMetrics(), mPreXScale, mPreYScale,
                                     mInheritedXScale, mInheritedYScale);
 }
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -1556,31 +1556,31 @@ public:
 
   /**
    * CONSTRUCTION PHASE ONLY
    * Insert aChild into the child list of this container. aChild must
    * not be currently in any child list or the root for the layer manager.
    * If aAfter is non-null, it must be a child of this container and
    * we insert after that layer. If it's null we insert at the start.
    */
-  virtual void InsertAfter(Layer* aChild, Layer* aAfter);
+  virtual bool InsertAfter(Layer* aChild, Layer* aAfter);
   /**
    * CONSTRUCTION PHASE ONLY
    * Remove aChild from the child list of this container. aChild must
    * be a child of this container.
    */
-  virtual void RemoveChild(Layer* aChild);
+  virtual bool RemoveChild(Layer* aChild);
   /**
    * CONSTRUCTION PHASE ONLY
    * Reposition aChild from the child list of this container. aChild must
    * be a child of this container.
    * If aAfter is non-null, it must be a child of this container and we
    * reposition after that layer. If it's null, we reposition at the start.
    */
-  virtual void RepositionChild(Layer* aChild, Layer* aAfter);
+  virtual bool RepositionChild(Layer* aChild, Layer* aAfter);
 
   /**
    * CONSTRUCTION PHASE ONLY
    * Set the (sub)document metrics used to render the Layer subtree
    * rooted at this.
    */
   void SetFrameMetrics(const FrameMetrics& aFrameMetrics)
   {
@@ -1957,24 +1957,24 @@ private:
  *
  * Clients will usually want to Connect/Clear() on each transaction to
  * avoid difficulties managing memory across multiple layer subtrees.
  */
 class RefLayer : public ContainerLayer {
   friend class LayerManager;
 
 private:
-  virtual void InsertAfter(Layer* aChild, Layer* aAfter)
-  { MOZ_CRASH(); }
+  virtual bool InsertAfter(Layer* aChild, Layer* aAfter) MOZ_OVERRIDE
+  { MOZ_CRASH(); return false; }
 
-  virtual void RemoveChild(Layer* aChild)
-  { MOZ_CRASH(); }
+  virtual bool RemoveChild(Layer* aChild)
+  { MOZ_CRASH(); return false; }
 
-  virtual void RepositionChild(Layer* aChild, Layer* aAfter)
-  { MOZ_CRASH(); }
+  virtual bool RepositionChild(Layer* aChild, Layer* aAfter)
+  { MOZ_CRASH(); return false; }
 
   using ContainerLayer::SetFrameMetrics;
 
 public:
   /**
    * CONSTRUCTION PHASE ONLY
    * Set the ID of the layer's referent.
    */
--- a/gfx/layers/basic/BasicContainerLayer.h
+++ b/gfx/layers/basic/BasicContainerLayer.h
@@ -29,35 +29,41 @@ public:
   virtual ~BasicContainerLayer();
 
   virtual void SetVisibleRegion(const nsIntRegion& aRegion)
   {
     NS_ASSERTION(BasicManager()->InConstruction(),
                  "Can only set properties in construction phase");
     ContainerLayer::SetVisibleRegion(aRegion);
   }
-  virtual void InsertAfter(Layer* aChild, Layer* aAfter)
+  virtual bool InsertAfter(Layer* aChild, Layer* aAfter)
   {
-    NS_ASSERTION(BasicManager()->InConstruction(),
-                 "Can only set properties in construction phase");
-    ContainerLayer::InsertAfter(aChild, aAfter);
+    if (!BasicManager()->InConstruction()) {
+      NS_ERROR("Can only set properties in construction phase");
+      return false;
+    }
+    return ContainerLayer::InsertAfter(aChild, aAfter);
   }
 
-  virtual void RemoveChild(Layer* aChild)
+  virtual bool RemoveChild(Layer* aChild)
   { 
-    NS_ASSERTION(BasicManager()->InConstruction(),
-                 "Can only set properties in construction phase");
-    ContainerLayer::RemoveChild(aChild);
+    if (!BasicManager()->InConstruction()) {
+      NS_ERROR("Can only set properties in construction phase");
+      return false;
+    }
+    return ContainerLayer::RemoveChild(aChild);
   }
 
-  virtual void RepositionChild(Layer* aChild, Layer* aAfter)
+  virtual bool RepositionChild(Layer* aChild, Layer* aAfter)
   {
-    NS_ASSERTION(BasicManager()->InConstruction(),
-                 "Can only set properties in construction phase");
-    ContainerLayer::RepositionChild(aChild, aAfter);
+    if (!BasicManager()->InConstruction()) {
+      NS_ERROR("Can only set properties in construction phase");
+      return false;
+    }
+    return ContainerLayer::RepositionChild(aChild, aAfter);
   }
 
   virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface);
 
   /**
    * Returns true when:
    * a) no (non-hidden) childrens' visible areas overlap in
    * (aInRect intersected with this layer's visible region).
--- a/gfx/layers/basic/BasicLayers.h
+++ b/gfx/layers/basic/BasicLayers.h
@@ -109,18 +109,18 @@ public:
   virtual already_AddRefed<CanvasLayer> CreateCanvasLayer();
   virtual already_AddRefed<ColorLayer> CreateColorLayer();
   virtual already_AddRefed<ReadbackLayer> CreateReadbackLayer();
   virtual ImageFactory *GetImageFactory();
 
   virtual LayersBackend GetBackendType() { return LayersBackend::LAYERS_BASIC; }
   virtual void GetBackendName(nsAString& name) { name.AssignLiteral("Basic"); }
 
+  bool InConstruction() { return mPhase == PHASE_CONSTRUCTION; }
 #ifdef DEBUG
-  bool InConstruction() { return mPhase == PHASE_CONSTRUCTION; }
   bool InDrawing() { return mPhase == PHASE_DRAWING; }
   bool InForward() { return mPhase == PHASE_FORWARD; }
 #endif
   bool InTransaction() { return mPhase != PHASE_NONE; }
 
   gfxContext* GetTarget() { return mTarget; }
   void SetTarget(gfxContext* aTarget) { mUsingDefaultTarget = false; mTarget = aTarget; }
   bool IsRetained() { return mWidget != nullptr; }
--- a/gfx/layers/client/ClientContainerLayer.h
+++ b/gfx/layers/client/ClientContainerLayer.h
@@ -85,43 +85,61 @@ public:
   }
 
   virtual void SetVisibleRegion(const nsIntRegion& aRegion)
   {
     NS_ASSERTION(ClientManager()->InConstruction(),
                  "Can only set properties in construction phase");
     ContainerLayer::SetVisibleRegion(aRegion);
   }
-  virtual void InsertAfter(Layer* aChild, Layer* aAfter) MOZ_OVERRIDE
+  virtual bool InsertAfter(Layer* aChild, Layer* aAfter) MOZ_OVERRIDE
   {
-    NS_ASSERTION(ClientManager()->InConstruction(),
-                 "Can only set properties in construction phase");
+    if(!ClientManager()->InConstruction()) {
+      NS_ERROR("Can only set properties in construction phase");
+      return false;
+    }
+
+    if (!ContainerLayer::InsertAfter(aChild, aAfter)) {
+      return false;
+    }
+
     ClientManager()->AsShadowForwarder()->InsertAfter(ClientManager()->Hold(this),
                                                       ClientManager()->Hold(aChild),
                                                       aAfter ? ClientManager()->Hold(aAfter) : nullptr);
-    ContainerLayer::InsertAfter(aChild, aAfter);
+    return true;
   }
 
-  virtual void RemoveChild(Layer* aChild) MOZ_OVERRIDE
-  { 
-    NS_ASSERTION(ClientManager()->InConstruction(),
-                 "Can only set properties in construction phase");
-    ClientManager()->AsShadowForwarder()->RemoveChild(ClientManager()->Hold(this),
-                                                      ClientManager()->Hold(aChild));
-    ContainerLayer::RemoveChild(aChild);
+  virtual bool RemoveChild(Layer* aChild) MOZ_OVERRIDE
+  {
+    if (!ClientManager()->InConstruction()) {
+      NS_ERROR("Can only set properties in construction phase");
+      return false;
+    }
+    // hold on to aChild before we remove it!
+    ShadowableLayer *heldChild = ClientManager()->Hold(aChild);
+    if (!ContainerLayer::RemoveChild(aChild)) {
+      return false;
+    }
+    ClientManager()->AsShadowForwarder()->RemoveChild(ClientManager()->Hold(this), heldChild);
+    return true;
   }
 
-  virtual void RepositionChild(Layer* aChild, Layer* aAfter) MOZ_OVERRIDE
+  virtual bool RepositionChild(Layer* aChild, Layer* aAfter) MOZ_OVERRIDE
   {
-    NS_ASSERTION(ClientManager()->InConstruction(),
-                 "Can only set properties in construction phase");
+    if (!ClientManager()->InConstruction()) {
+      NS_ERROR("Can only set properties in construction phase");
+      return false;
+    }
+    if (!ContainerLayer::RepositionChild(aChild, aAfter)) {
+      return false;
+    }
     ClientManager()->AsShadowForwarder()->RepositionChild(ClientManager()->Hold(this),
                                                           ClientManager()->Hold(aChild),
                                                           aAfter ? ClientManager()->Hold(aAfter) : nullptr);
-    ContainerLayer::RepositionChild(aChild, aAfter);
+    return true;
   }
 
   virtual Layer* AsLayer() { return this; }
   virtual ShadowableLayer* AsShadowableLayer() { return this; }
 
   virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface)
   {
     DefaultComputeEffectiveTransforms(aTransformToSurface);
--- a/gfx/layers/client/ClientLayerManager.h
+++ b/gfx/layers/client/ClientLayerManager.h
@@ -134,18 +134,18 @@ public:
    * This is only called if gfxPlatform::UseProgressiveTilePainting() returns
    * true.
    */
   bool ProgressiveUpdateCallback(bool aHasPendingNewThebesContent,
                                  ScreenRect& aCompositionBounds,
                                  CSSToScreenScale& aZoom,
                                  bool aDrawingCritical);
 
+  bool InConstruction() { return mPhase == PHASE_CONSTRUCTION; }
 #ifdef DEBUG
-  bool InConstruction() { return mPhase == PHASE_CONSTRUCTION; }
   bool InDrawing() { return mPhase == PHASE_DRAWING; }
   bool InForward() { return mPhase == PHASE_FORWARD; }
 #endif
   bool InTransaction() { return mPhase != PHASE_NONE; }
 
   void SetNeedsComposite(bool aNeedsComposite)
   {
     mNeedsComposite = aNeedsComposite;
--- a/gfx/layers/ipc/AsyncPanZoomController.cpp
+++ b/gfx/layers/ipc/AsyncPanZoomController.cpp
@@ -1738,16 +1738,21 @@ void AsyncPanZoomController::NotifyLayer
 }
 
 const FrameMetrics& AsyncPanZoomController::GetFrameMetrics() {
   mMonitor.AssertCurrentThreadIn();
   return mFrameMetrics;
 }
 
 void AsyncPanZoomController::ZoomToRect(CSSRect aRect) {
+  if (!aRect.IsFinite()) {
+    NS_WARNING("ZoomToRect got called with a non-finite rect; ignoring...\n");
+    return;
+  }
+
   SetState(ANIMATING_ZOOM);
 
   {
     ReentrantMonitorAutoEnter lock(mMonitor);
 
     ScreenIntRect compositionBounds = mFrameMetrics.mCompositionBounds;
     CSSRect cssPageRect = mFrameMetrics.mScrollableRect;
     CSSPoint scrollOffset = mFrameMetrics.mScrollOffset;
@@ -1968,19 +1973,27 @@ void AsyncPanZoomController::SetContentR
 void AsyncPanZoomController::TimeoutContentResponse() {
   mContentResponseTimeoutTask = nullptr;
   ContentReceivedTouch(false);
 }
 
 void AsyncPanZoomController::UpdateZoomConstraints(const ZoomConstraints& aConstraints) {
   APZC_LOG("%p updating zoom constraints to %d %f %f\n", this, aConstraints.mAllowZoom,
     aConstraints.mMinZoom.scale, aConstraints.mMaxZoom.scale);
+  if (IsFloatNaN(aConstraints.mMinZoom.scale) || IsFloatNaN(aConstraints.mMinZoom.scale)) {
+    NS_WARNING("APZC received zoom constraints with NaN values; dropping...\n");
+    return;
+  }
+  // inf float values and other bad cases should be sanitized by the code below.
   mZoomConstraints.mAllowZoom = aConstraints.mAllowZoom;
   mZoomConstraints.mMinZoom = (MIN_ZOOM > aConstraints.mMinZoom ? MIN_ZOOM : aConstraints.mMinZoom);
   mZoomConstraints.mMaxZoom = (MAX_ZOOM > aConstraints.mMaxZoom ? aConstraints.mMaxZoom : MAX_ZOOM);
+  if (mZoomConstraints.mMaxZoom < mZoomConstraints.mMinZoom) {
+    mZoomConstraints.mMaxZoom = mZoomConstraints.mMinZoom;
+  }
 }
 
 ZoomConstraints
 AsyncPanZoomController::GetZoomConstraints() const
 {
   return mZoomConstraints;
 }
 
--- a/gfx/layers/ipc/LayerTransactionParent.cpp
+++ b/gfx/layers/ipc/LayerTransactionParent.cpp
@@ -265,16 +265,19 @@ LayerTransactionParent::RecvUpdate(const
 
     // Attributes
     case Edit::TOpSetLayerAttributes: {
       MOZ_LAYERS_LOG(("[ParentSide] SetLayerAttributes"));
 
       const OpSetLayerAttributes& osla = edit.get_OpSetLayerAttributes();
       ShadowLayerParent* layerParent = AsLayerComposite(osla);
       Layer* layer = layerParent->AsLayer();
+      if (!layer) {
+        return false;
+      }
       const LayerAttributes& attrs = osla.attrs();
 
       const CommonLayerAttributes& common = attrs.common();
       layer->SetVisibleRegion(common.visibleRegion());
       layer->SetEventRegions(common.eventRegions());
       layer->SetContentFlags(common.contentFlags());
       layer->SetOpacity(common.opacity());
       layer->SetClipRect(common.useClipRect() ? &common.clipRect() : nullptr);
@@ -396,69 +399,113 @@ LayerTransactionParent::RecvUpdate(const
       }
       mRoot = newRoot;
       break;
     }
     case Edit::TOpInsertAfter: {
       MOZ_LAYERS_LOG(("[ParentSide] InsertAfter"));
 
       const OpInsertAfter& oia = edit.get_OpInsertAfter();
-      ShadowContainer(oia)->AsContainerLayerComposite()->InsertAfter(
-        ShadowChild(oia)->AsLayer(), ShadowAfter(oia)->AsLayer());
+      Layer* child = ShadowChild(oia)->AsLayer();
+      if (!child) {
+        return false;
+      }
+      ContainerLayerComposite* container = ShadowContainer(oia)->AsContainerLayerComposite();
+      if (!container ||
+          !container->InsertAfter(child, ShadowAfter(oia)->AsLayer()))
+      {
+        return false;
+      }
       break;
     }
     case Edit::TOpAppendChild: {
       MOZ_LAYERS_LOG(("[ParentSide] AppendChild"));
 
       const OpAppendChild& oac = edit.get_OpAppendChild();
-      ShadowContainer(oac)->AsContainerLayerComposite()->InsertAfter(
-        ShadowChild(oac)->AsLayer(), nullptr);
+      Layer* child = ShadowChild(oac)->AsLayer();
+      if (!child) {
+        return false;
+      }
+      ContainerLayerComposite* container = ShadowContainer(oac)->AsContainerLayerComposite();
+      if (!container ||
+          !container->InsertAfter(child, nullptr))
+      {
+        return false;
+      }
       break;
     }
     case Edit::TOpRemoveChild: {
       MOZ_LAYERS_LOG(("[ParentSide] RemoveChild"));
 
       const OpRemoveChild& orc = edit.get_OpRemoveChild();
       Layer* childLayer = ShadowChild(orc)->AsLayer();
-      ShadowContainer(orc)->AsContainerLayerComposite()->RemoveChild(childLayer);
+      if (!childLayer) {
+        return false;
+      }
+      ContainerLayerComposite* container = ShadowContainer(orc)->AsContainerLayerComposite();
+      if (!container ||
+          !container->RemoveChild(childLayer))
+      {
+        return false;
+      }
       break;
     }
     case Edit::TOpRepositionChild: {
       MOZ_LAYERS_LOG(("[ParentSide] RepositionChild"));
 
       const OpRepositionChild& orc = edit.get_OpRepositionChild();
-      ShadowContainer(orc)->AsContainerLayerComposite()->RepositionChild(
-        ShadowChild(orc)->AsLayer(), ShadowAfter(orc)->AsLayer());
+      Layer* child = ShadowChild(orc)->AsLayer();
+      if (!child) {
+        return false;
+      }
+      ContainerLayerComposite* container = ShadowContainer(orc)->AsContainerLayerComposite();
+      if (!container ||
+          !container->RepositionChild(child, ShadowAfter(orc)->AsLayer()))
+      {
+        return false;
+      }
       break;
     }
     case Edit::TOpRaiseToTopChild: {
       MOZ_LAYERS_LOG(("[ParentSide] RaiseToTopChild"));
 
       const OpRaiseToTopChild& rtc = edit.get_OpRaiseToTopChild();
-      ShadowContainer(rtc)->AsContainerLayerComposite()->RepositionChild(
-        ShadowChild(rtc)->AsLayer(), nullptr);
+      Layer* child = ShadowChild(rtc)->AsLayer();
+      if (!child) {
+        return false;
+      }
+      ContainerLayerComposite* container = ShadowContainer(rtc)->AsContainerLayerComposite();
+      if (!container ||
+          !container->RepositionChild(child, nullptr))
+      {
+        return false;
+      }
       break;
     }
     case Edit::TCompositableOperation: {
       ReceiveCompositableUpdate(edit.get_CompositableOperation(),
                                 replyv);
       break;
     }
     case Edit::TOpAttachCompositable: {
       const OpAttachCompositable& op = edit.get_OpAttachCompositable();
-      Attach(cast(op.layerParent()), cast(op.compositableParent()), false);
+      if (!Attach(cast(op.layerParent()), cast(op.compositableParent()), false)) {
+        return false;
+      }
       cast(op.compositableParent())->SetCompositorID(
         mLayerManager->GetCompositor()->GetCompositorID());
       break;
     }
     case Edit::TOpAttachAsyncCompositable: {
       const OpAttachAsyncCompositable& op = edit.get_OpAttachAsyncCompositable();
       CompositableParent* compositableParent = CompositableMap::Get(op.containerID());
       MOZ_ASSERT(compositableParent, "CompositableParent not found in the map");
-      Attach(cast(op.layerParent()), compositableParent, true);
+      if (!Attach(cast(op.layerParent()), compositableParent, true)) {
+        return false;
+      }
       compositableParent->SetCompositorID(mLayerManager->GetCompositor()->GetCompositorID());
       break;
     }
     default:
       NS_RUNTIMEABORT("not reached");
     }
   }
 
@@ -494,32 +541,40 @@ LayerTransactionParent::RecvUpdate(const
 bool
 LayerTransactionParent::RecvGetOpacity(PLayerParent* aParent,
                                        float* aOpacity)
 {
   if (mDestroyed || !layer_manager() || layer_manager()->IsDestroyed()) {
     return false;
   }
 
-  *aOpacity = cast(aParent)->AsLayer()->GetLocalOpacity();
+  Layer* layer = cast(aParent)->AsLayer();
+  if (!layer) {
+    return false;
+  }
+
+  *aOpacity = layer->GetLocalOpacity();
   return true;
 }
 
 bool
 LayerTransactionParent::RecvGetTransform(PLayerParent* aParent,
                                          gfx3DMatrix* aTransform)
 {
   if (mDestroyed || !layer_manager() || layer_manager()->IsDestroyed()) {
     return false;
   }
 
   // The following code recovers the untranslated transform
   // from the shadow transform by undoing the translations in
   // AsyncCompositionManager::SampleValue.
   Layer* layer = cast(aParent)->AsLayer();
+  if (!layer) {
+    return false;
+  }
   gfx::To3DMatrix(layer->AsLayerComposite()->GetShadowTransform(), *aTransform);
   if (ContainerLayer* c = layer->AsContainerLayer()) {
     aTransform->ScalePost(1.0f/c->GetInheritedXScale(),
                           1.0f/c->GetInheritedYScale(),
                           1.0f);
   }
   float scale = 1;
   gfxPoint3D scaledOrigin;
@@ -537,39 +592,47 @@ LayerTransactionParent::RecvGetTransform
     }
   }
 
   aTransform->Translate(-scaledOrigin);
   *aTransform = nsLayoutUtils::ChangeMatrixBasis(-scaledOrigin - transformOrigin, *aTransform);
   return true;
 }
 
-void
+bool
 LayerTransactionParent::Attach(ShadowLayerParent* aLayerParent,
                                CompositableParent* aCompositable,
                                bool aIsAsyncVideo)
 {
-  LayerComposite* layer = aLayerParent->AsLayer()->AsLayerComposite();
-  MOZ_ASSERT(layer);
+  Layer* baselayer = aLayerParent->AsLayer();
+  if (!baselayer) {
+    return false;
+  }
+  LayerComposite* layer = baselayer->AsLayerComposite();
+  if (!layer) {
+    return false;
+  }
 
   Compositor* compositor
     = static_cast<LayerManagerComposite*>(aLayerParent->AsLayer()->Manager())->GetCompositor();
 
   CompositableHost* compositable = aCompositable->GetCompositableHost();
   MOZ_ASSERT(compositable);
   if (!layer->SetCompositableHost(compositable)) {
     // not all layer types accept a compositable, see bug 967824
-    return;
+    return false;
   }
   compositable->Attach(aLayerParent->AsLayer(),
                        compositor,
                        aIsAsyncVideo
                          ? CompositableHost::ALLOW_REATTACH
                            | CompositableHost::KEEP_ATTACHED
                          : CompositableHost::NO_FLAGS);
+
+  return true;
 }
 
 bool
 LayerTransactionParent::RecvClearCachedResources()
 {
   if (mRoot) {
     // NB: |mRoot| here is the *child* context's root.  In this parent
     // context, it's just a subtree root.  We need to scope the clear
--- a/gfx/layers/ipc/LayerTransactionParent.h
+++ b/gfx/layers/ipc/LayerTransactionParent.h
@@ -106,17 +106,17 @@ protected:
 
   virtual PCompositableParent* AllocPCompositableParent(const TextureInfo& aInfo) MOZ_OVERRIDE;
   virtual bool DeallocPCompositableParent(PCompositableParent* actor) MOZ_OVERRIDE;
 
   virtual PTextureParent* AllocPTextureParent(const SurfaceDescriptor& aSharedData,
                                               const TextureFlags& aFlags) MOZ_OVERRIDE;
   virtual bool DeallocPTextureParent(PTextureParent* actor) MOZ_OVERRIDE;
 
-  void Attach(ShadowLayerParent* aLayerParent,
+  bool Attach(ShadowLayerParent* aLayerParent,
               CompositableParent* aCompositable,
               bool aIsAsyncVideo);
 
   void AddIPDLReference() {
     MOZ_ASSERT(mIPCOpen == false);
     mIPCOpen = true;
     AddRef();
   }
--- a/gfx/tests/gtest/TestAsyncPanZoomController.cpp
+++ b/gfx/tests/gtest/TestAsyncPanZoomController.cpp
@@ -80,20 +80,20 @@ private:
 };
 
 
 class TestAPZCContainerLayer : public ContainerLayer {
   public:
     TestAPZCContainerLayer()
       : ContainerLayer(nullptr, nullptr)
     {}
-  void RemoveChild(Layer* aChild) {}
-  void InsertAfter(Layer* aChild, Layer* aAfter) {}
+  bool RemoveChild(Layer* aChild) { return true; }
+  bool InsertAfter(Layer* aChild, Layer* aAfter) { return true; }
   void ComputeEffectiveTransforms(const Matrix4x4& aTransformToSurface) {}
-  void RepositionChild(Layer* aChild, Layer* aAfter) {}
+  bool RepositionChild(Layer* aChild, Layer* aAfter) { return true; }
 };
 
 class TestAsyncPanZoomController : public AsyncPanZoomController {
 public:
   TestAsyncPanZoomController(uint64_t aLayersId, MockContentController* aMcc,
                              APZCTreeManager* aTreeManager = nullptr,
                              GestureBehavior aBehavior = DEFAULT_GESTURES)
     : AsyncPanZoomController(aLayersId, aTreeManager, aMcc, aBehavior)
--- a/js/ipc/JavaScriptChild.cpp
+++ b/js/ipc/JavaScriptChild.cpp
@@ -162,17 +162,16 @@ JavaScriptChild::AnswerPreventExtensions
     return ok(rs);
 }
 
 static void
 EmptyDesc(PPropertyDescriptor *desc)
 {
     desc->objId() = 0;
     desc->attrs() = 0;
-    desc->shortid() = 0;
     desc->value() = void_t();
     desc->getter() = 0;
     desc->setter() = 0;
 }
 
 bool
 JavaScriptChild::AnswerGetPropertyDescriptor(const ObjectId &objId, const nsString &id,
                                              const uint32_t &flags, ReturnStatus *rs,
--- a/js/ipc/JavaScriptShared.cpp
+++ b/js/ipc/JavaScriptShared.cpp
@@ -322,17 +322,16 @@ static const uint32_t DefaultPropertyOp 
 static const uint32_t GetterOnlyPropertyStub = 2;
 static const uint32_t UnknownPropertyOp = 3;
 
 bool
 JavaScriptShared::fromDescriptor(JSContext *cx, Handle<JSPropertyDescriptor> desc,
                                  PPropertyDescriptor *out)
 {
     out->attrs() = desc.attributes();
-    out->shortid() = desc.shortid();
     if (!toVariant(cx, desc.value(), &out->value()))
         return false;
 
     if (!makeId(cx, desc.object(), &out->objId()))
         return false;
 
     if (!desc.getter()) {
         out->getter() = 0;
@@ -379,17 +378,16 @@ UnknownStrictPropertyStub(JSContext *cx,
     return false;
 }
 
 bool
 JavaScriptShared::toDescriptor(JSContext *cx, const PPropertyDescriptor &in,
                                MutableHandle<JSPropertyDescriptor> out)
 {
     out.setAttributes(in.attrs());
-    out.setShortId(in.shortid());
     if (!toValue(cx, in.value(), out.value()))
         return false;
     Rooted<JSObject*> obj(cx);
     if (!unwrap(cx, in.objId(), &obj))
         return false;
     out.object().set(obj);
 
     if (!in.getter()) {
--- a/js/ipc/JavaScriptTypes.ipdlh
+++ b/js/ipc/JavaScriptTypes.ipdlh
@@ -62,17 +62,16 @@ union JSParam
     void_t;     /* value is strictly an xpc out param */
     JSVariant;  /* actual value to pass through */
 };
 
 struct PPropertyDescriptor
 {
     uint64_t    objId;
     uint32_t    attrs;
-    uint32_t    shortid;
     JSVariant   value;
 
     // How to interpret these values depends on whether JSPROP_GETTER/SETTER
     // are set. If set, the corresponding value is a CPOW or 0 for NULL.
     // Otherwise, the following table is used:
     //
     //  0 - NULL
     //  1 - Default getter or setter.
new file mode 100644
--- /dev/null
+++ b/js/public/WeakMapPtr.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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 js_WeakMapPtr_h
+#define js_WeakMapPtr_h
+
+#include "jspubtd.h"
+
+#include "js/TypeDecls.h"
+
+namespace JS {
+
+// A wrapper around the internal C++ representation of SpiderMonkey WeakMaps,
+// usable outside the engine.
+//
+// The supported template specializations are enumerated in WeakMapPtr.cpp. If
+// you want to use this class for a different key/value combination, add it to
+// the list and the compiler will generate the relevant machinery.
+template <typename K, typename V>
+class JS_PUBLIC_API(WeakMapPtr)
+{
+  public:
+    WeakMapPtr() : ptr(nullptr) {};
+    bool init(JSContext *cx);
+    bool initialized() { return ptr != nullptr; };
+    void destroy();
+    virtual ~WeakMapPtr() { MOZ_ASSERT(!initialized()); }
+    void trace(JSTracer *tracer);
+
+    V lookup(const K &key);
+    bool put(const K &key, const V &value);
+
+  private:
+    void *ptr;
+
+    // WeakMapPtr is neither copyable nor assignable.
+    WeakMapPtr(const WeakMapPtr &wmp) MOZ_DELETE;
+    WeakMapPtr &operator=(const WeakMapPtr &wmp) MOZ_DELETE;
+};
+
+} /* namespace JS */
+
+#endif  /* js_WeakMapPtr_h */
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -686,33 +686,29 @@ AllLocalsAliased(StaticBlockObject &obj)
             return false;
     return true;
 }
 #endif
 
 static bool
 ComputeAliasedSlots(ExclusiveContext *cx, BytecodeEmitter *bce, Handle<StaticBlockObject *> blockObj)
 {
+    if (blockObj->slotCount() == 0)
+        return true;
+
     uint32_t depthPlusFixed = blockObj->stackDepth();
     if (!AdjustBlockSlot(cx, bce, &depthPlusFixed))
         return false;
 
     for (unsigned i = 0; i < blockObj->slotCount(); i++) {
-        Definition *dn = blockObj->maybeDefinitionParseNode(i);
-
-        /* Beware the empty destructuring dummy. */
-        if (!dn) {
-            blockObj->setAliased(i, bce->sc->allLocalsAliased());
-            continue;
-        }
+        Definition *dn = blockObj->definitionParseNode(i);
 
         JS_ASSERT(dn->isDefn());
         if (!dn->pn_cookie.set(bce->parser->tokenStream, dn->pn_cookie.level(),
-                               dn->frameSlot() + depthPlusFixed))
-        {
+                               dn->frameSlot() + depthPlusFixed)) {
             return false;
         }
 
 #ifdef DEBUG
         for (ParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
             JS_ASSERT(pnu->pn_lexdef == dn);
             JS_ASSERT(!(pnu->pn_dflags & PND_BOUND));
             JS_ASSERT(pnu->pn_cookie.isFree());
@@ -3411,17 +3407,16 @@ EmitVariables(ExclusiveContext *cx, Byte
              * here and initialize the name.
              */
             if (pn2->pn_left->isKind(PNK_NAME)) {
                 pn3 = pn2->pn_right;
                 pn2 = pn2->pn_left;
                 goto do_name;
             }
 
-            ptrdiff_t stackDepthBefore = bce->stackDepth;
             JSOp op = JSOP_POP;
             if (pn->pn_count == 1) {
                 /*
                  * If this is the only destructuring assignment in the list,
                  * try to optimize to a group assignment.  If we're in a let
                  * head, pass JSOP_POP rather than the pseudo-prolog JSOP_NOP
                  * in pn->pn_op, to suppress a second (and misplaced) 'let'.
                  */
@@ -3442,24 +3437,16 @@ EmitVariables(ExclusiveContext *cx, Byte
                     return false;
 
                 if (!EmitTree(cx, bce, pn2->pn_right))
                     return false;
 
                 if (!EmitDestructuringOps(cx, bce, pn3, isLet))
                     return false;
             }
-            ptrdiff_t stackDepthAfter = bce->stackDepth;
-
-            /* Give let ([] = x) a slot (see CheckDestructuring). */
-            JS_ASSERT(stackDepthBefore <= stackDepthAfter);
-            if (isLet && stackDepthBefore == stackDepthAfter) {
-                if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
-                    return false;
-            }
 
             /* If we are not initializing, nothing to pop. */
             if (emitOption != InitializeVars) {
                 if (next)
                     continue;
                 break;
             }
             goto emit_note_pop;
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -2811,20 +2811,18 @@ LexicalLookup(ContextT *ct, HandleAtom a
 
         // Skip statements that do not introduce a new scope
         if (!stmt->isBlockScope)
             continue;
 
         StaticBlockObject &blockObj = stmt->staticBlock();
         Shape *shape = blockObj.nativeLookup(ct->sc->context, id);
         if (shape) {
-            JS_ASSERT(shape->hasShortID());
-
             if (slotp)
-                *slotp = blockObj.stackDepth() + shape->shortid();
+                *slotp = blockObj.stackDepth() + blockObj.shapeToIndex(*shape);
             return stmt;
         }
     }
 
     if (slotp)
         *slotp = -1;
     return stmt;
 }
@@ -3061,17 +3059,16 @@ Parser<FullParseHandler>::checkDestructu
 
     if (left->isKind(PNK_ARRAYCOMP)) {
         report(ParseError, false, left, JSMSG_ARRAY_COMP_LEFTSIDE);
         return false;
     }
 
     Rooted<StaticBlockObject *> blockObj(context);
     blockObj = data && data->binder == bindLet ? data->let.blockObj.get() : nullptr;
-    uint32_t blockCountBefore = blockObj ? blockObj->slotCount() : 0;
 
     if (left->isKind(PNK_ARRAY)) {
         for (ParseNode *pn = left->pn_head; pn; pn = pn->pn_next) {
             if (!pn->isKind(PNK_ELISION)) {
                 if (pn->isKind(PNK_ARRAY) || pn->isKind(PNK_OBJECT)) {
                     ok = checkDestructuring(data, pn, false);
                 } else {
                     if (data) {
@@ -3116,48 +3113,16 @@ Parser<FullParseHandler>::checkDestructu
                 }
                 ok = checkAndMarkAsAssignmentLhs(expr, KeyedDestructuringAssignment);
             }
             if (!ok)
                 return false;
         }
     }
 
-    /*
-     * The catch/finally handler implementation in the interpreter assumes
-     * that any operation that introduces a new scope (like a "let" or "with"
-     * block) increases the stack depth. This way, it is possible to restore
-     * the scope chain based on stack depth of the handler alone. "let" with
-     * an empty destructuring pattern like in
-     *
-     *   let [] = 1;
-     *
-     * would violate this assumption as the there would be no let locals to
-     * store on the stack.
-     *
-     * Furthermore, the decompiler needs an abstract stack location to store
-     * the decompilation of each let block/expr initializer. E.g., given:
-     *
-     *   let (x = 1, [[]] = b, y = 3, {a:[]} = c) { ... }
-     *
-     * four slots are needed.
-     *
-     * To satisfy both constraints, we push a dummy slot (and add a
-     * corresponding dummy property to the block object) for each initializer
-     * that doesn't introduce at least one binding.
-     */
-    if (toplevel && blockObj && blockCountBefore == blockObj->slotCount()) {
-        bool redeclared;
-        RootedId id(context, INT_TO_JSID(blockCountBefore));
-        if (!StaticBlockObject::addVar(context, blockObj, id, blockCountBefore, &redeclared))
-            return false;
-        JS_ASSERT(!redeclared);
-        JS_ASSERT(blockObj->slotCount() == blockCountBefore + 1);
-    }
-
     return true;
 }
 
 template <>
 bool
 Parser<SyntaxParseHandler>::checkDestructuring(BindData<SyntaxParseHandler> *data,
                                                Node left, bool toplevel)
 {
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug951346.js
@@ -0,0 +1,3 @@
+
+setObjectMetadataCallback(function(obj) {});
+eval(uneval({'-1':true}));
--- a/js/src/jit/AsmJSLink.cpp
+++ b/js/src/jit/AsmJSLink.cpp
@@ -655,17 +655,17 @@ CreateExportObject(JSContext *cx, Handle
 
         RootedFunction fun(cx, NewExportedFunction(cx, func, moduleObj, i));
         if (!fun)
             return nullptr;
 
         JS_ASSERT(func.maybeFieldName() != nullptr);
         RootedId id(cx, NameToId(func.maybeFieldName()));
         RootedValue val(cx, ObjectValue(*fun));
-        if (!DefineNativeProperty(cx, obj, id, val, nullptr, nullptr, JSPROP_ENUMERATE, 0, 0))
+        if (!DefineNativeProperty(cx, obj, id, val, nullptr, nullptr, JSPROP_ENUMERATE, 0))
             return nullptr;
     }
 
     return obj;
 }
 
 static const unsigned MODULE_FUN_SLOT = 0;
 
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -7246,17 +7246,17 @@ DoSetPropFallback(JSContext *cx, Baselin
     if (!obj)
         return false;
     RootedShape oldShape(cx, obj->lastProperty());
     uint32_t oldSlots = obj->numDynamicSlots();
 
     if (op == JSOP_INITPROP) {
         MOZ_ASSERT(name != cx->names().proto, "should have used JSOP_MUTATEPROTO");
         MOZ_ASSERT(obj->is<JSObject>());
-        if (!DefineNativeProperty(cx, obj, id, rhs, nullptr, nullptr, JSPROP_ENUMERATE, 0, 0, 0))
+        if (!DefineNativeProperty(cx, obj, id, rhs, nullptr, nullptr, JSPROP_ENUMERATE, 0))
             return false;
     } else if (op == JSOP_SETNAME || op == JSOP_SETGNAME) {
         if (!SetNameOperation(cx, script, pc, obj, rhs))
             return false;
     } else if (op == JSOP_SETALIASEDVAR) {
         obj->as<ScopeObject>().setAliasedVar(cx, pc, name, rhs);
     } else {
         MOZ_ASSERT(op == JSOP_SETPROP);
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -2046,17 +2046,17 @@ AnalyzePoppedThis(JSContext *cx, types::
         if (!types::AddClearDefiniteGetterSetterForPrototypeChain(cx, type, id)) {
             // The prototype chain already contains a getter/setter for this
             // property, or type information is too imprecise.
             return true;
         }
 
         DebugOnly<unsigned> slotSpan = baseobj->slotSpan();
         if (!DefineNativeProperty(cx, baseobj, id, UndefinedHandleValue, nullptr, nullptr,
-                                  JSPROP_ENUMERATE, 0, 0))
+                                  JSPROP_ENUMERATE, 0))
         {
             return false;
         }
         JS_ASSERT(baseobj->slotSpan() != slotSpan);
         JS_ASSERT(!baseobj->inDictionaryMode());
 
         Vector<MResumePoint *> callerResumePoints(cx);
         MBasicBlock *block = ins->block();
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -935,20 +935,17 @@ EmitGetterCall(JSContext *cx, MacroAssem
 
         // JSPropertyOp: bool fn(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp)
 
         // Push args on stack first so we can take pointers to make handles.
         masm.Push(UndefinedValue());
         masm.movePtr(StackPointer, argVpReg);
 
         // push canonical jsid from shape instead of propertyname.
-        RootedId propId(cx);
-        if (!shape->getUserId(cx, &propId))
-            return false;
-        masm.Push(propId, scratchReg);
+        masm.Push(shape->propid(), scratchReg);
         masm.movePtr(StackPointer, argIdReg);
 
         masm.Push(object);
         masm.movePtr(StackPointer, argObjReg);
 
         masm.loadJSContext(argJSContextReg);
 
         if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
@@ -2342,20 +2339,17 @@ GenerateCallSetter(JSContext *cx, IonScr
             masm.Push(value.value());
         else
             masm.Push(value.reg());
         masm.movePtr(StackPointer, argVpReg);
 
         masm.move32(Imm32(strict ? 1 : 0), argStrictReg);
 
         // push canonical jsid from shape instead of propertyname.
-        RootedId propId(cx);
-        if (!shape->getUserId(cx, &propId))
-            return false;
-        masm.Push(propId, argIdReg);
+        masm.Push(shape->propid(), argIdReg);
         masm.movePtr(StackPointer, argIdReg);
 
         masm.Push(object);
         masm.movePtr(StackPointer, argObjReg);
 
         masm.loadJSContext(argJSContextReg);
 
         if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -221,17 +221,17 @@ bool
 InitProp(JSContext *cx, HandleObject obj, HandlePropertyName name, HandleValue value)
 {
     // Copy the incoming value. This may be overwritten; the return value is discarded.
     RootedValue rval(cx, value);
     RootedId id(cx, NameToId(name));
 
     MOZ_ASSERT(name != cx->names().proto,
                "__proto__ should have been handled by JSOP_MUTATEPROTO");
-    return DefineNativeProperty(cx, obj, id, rval, nullptr, nullptr, JSPROP_ENUMERATE, 0, 0, 0);
+    return DefineNativeProperty(cx, obj, id, rval, nullptr, nullptr, JSPROP_ENUMERATE, 0, 0);
 }
 
 template<bool Equal>
 bool
 LooselyEqual(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
 {
     if (!js::LooselyEqual(cx, lhs, rhs, res))
         return false;
--- a/js/src/jsapi-tests/testIntTypesABI.cpp
+++ b/js/src/jsapi-tests/testIntTypesABI.cpp
@@ -32,16 +32,17 @@
 #include "js/RootingAPI.h"
 #include "js/SliceBudget.h"
 #include "js/StructuredClone.h"
 #include "js/Tracer.h"
 #include "js/TypeDecls.h"
 #include "js/Utility.h"
 #include "js/Value.h"
 #include "js/Vector.h"
+#include "js/WeakMapPtr.h"
 #include "jsapi-tests/tests.h"
 
 /*
  * Verify that our public (and intended to be public, versus being that way
  * because we haven't made them private yet) headers don't define
  * {u,}int{8,16,32,64} or JS{Ui,I}nt{8,16,32,64} types.  If any do, they will
  * assuredly conflict with a corresponding typedef below mapping to a *struct*.
  *
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -2923,17 +2923,17 @@ SetterWrapper(JSStrictPropertyOp setter)
     ret.op = setter;
     ret.info = nullptr;
     return ret;
 }
 
 static bool
 DefinePropertyById(JSContext *cx, HandleObject obj, HandleId id, HandleValue value,
                    const JSPropertyOpWrapper &get, const JSStrictPropertyOpWrapper &set,
-                   unsigned attrs, unsigned flags, int tinyid)
+                   unsigned attrs, unsigned flags)
 {
     PropertyOp getter = get.op;
     StrictPropertyOp setter = set.op;
     /*
      * JSPROP_READONLY has no meaning when accessors are involved. Ideally we'd
      * throw if this happens, but we've accepted it for long enough that it's
      * not worth trying to make callers change their ways. Just flip it off on
      * its way through the API layer so that we can enforce this internally.
@@ -2991,82 +2991,79 @@ DefinePropertyById(JSContext *cx, Handle
                             (attrs & JSPROP_GETTER)
                             ? JS_FUNC_TO_DATA_PTR(JSObject *, getter)
                             : nullptr,
                             (attrs & JSPROP_SETTER)
                             ? JS_FUNC_TO_DATA_PTR(JSObject *, setter)
                             : nullptr);
 
     JSAutoResolveFlags rf(cx, 0);
-    if (flags != 0 && obj->isNative()) {
-        return DefineNativeProperty(cx, obj, id, value, getter, setter,
-                                    attrs, flags, tinyid);
-    }
+    if (flags != 0 && obj->isNative())
+        return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs, flags);
     return JSObject::defineGeneric(cx, obj, id, value, getter, setter, attrs);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefinePropertyById(JSContext *cx, JSObject *objArg, jsid idArg, jsval valueArg,
                       JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs)
 {
     RootedObject obj(cx, objArg);
     RootedId id(cx, idArg);
     RootedValue value(cx, valueArg);
-    return DefinePropertyById(cx, obj, id, value, GetterWrapper(getter),
-                              SetterWrapper(setter), attrs, 0, 0);
+    return DefinePropertyById(cx, obj, id, value, GetterWrapper(getter), SetterWrapper(setter),
+                              attrs, 0);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineElement(JSContext *cx, JSObject *objArg, uint32_t index, jsval valueArg,
                  JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs)
 {
     RootedObject obj(cx, objArg);
     RootedValue value(cx, valueArg);
     AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     RootedId id(cx);
     if (!IndexToId(cx, index, &id))
         return false;
-    return DefinePropertyById(cx, obj, id, value, GetterWrapper(getter),
-                              SetterWrapper(setter), attrs, 0, 0);
+    return DefinePropertyById(cx, obj, id, value, GetterWrapper(getter), SetterWrapper(setter),
+                              attrs, 0);
 }
 
 static bool
 DefineProperty(JSContext *cx, HandleObject obj, const char *name, HandleValue value,
                const JSPropertyOpWrapper &getter, const JSStrictPropertyOpWrapper &setter,
-               unsigned attrs, unsigned flags, int tinyid)
+               unsigned attrs, unsigned flags)
 {
     AutoRooterGetterSetter gsRoot(cx, attrs, const_cast<JSPropertyOp *>(&getter.op),
                                   const_cast<JSStrictPropertyOp *>(&setter.op));
 
     RootedId id(cx);
     if (attrs & JSPROP_INDEX) {
         id.set(INT_TO_JSID(intptr_t(name)));
         attrs &= ~JSPROP_INDEX;
     } else {
         JSAtom *atom = Atomize(cx, name, strlen(name));
         if (!atom)
             return false;
         id = AtomToId(atom);
     }
 
-    return DefinePropertyById(cx, obj, id, value, getter, setter, attrs, flags, tinyid);
+    return DefinePropertyById(cx, obj, id, value, getter, setter, attrs, flags);
 }
 
 
 static bool
 DefineSelfHostedProperty(JSContext *cx,
                          HandleObject obj,
                          const char *name,
                          const char *getterName,
                          const char *setterName,
                          unsigned attrs,
-                         unsigned flags,
-                         int tinyid)
+                         unsigned flags)
 {
     RootedAtom nameAtom(cx, Atomize(cx, name, strlen(name)));
     if (!nameAtom)
         return false;
 
     RootedAtom getterNameAtom(cx, Atomize(cx, getterName, strlen(getterName)));
     if (!getterNameAtom)
         return false;
@@ -3095,51 +3092,51 @@ DefineSelfHostedProperty(JSContext *cx,
         }
         JS_ASSERT(setterValue.isObject() && setterValue.toObject().is<JSFunction>());
         setterFunc = &getterValue.toObject().as<JSFunction>();
     }
     JSStrictPropertyOp setterOp = JS_DATA_TO_FUNC_PTR(StrictPropertyOp, setterFunc.get());
 
     return DefineProperty(cx, obj, name, JS::UndefinedHandleValue,
                           GetterWrapper(getterOp), SetterWrapper(setterOp),
-                          attrs, flags, tinyid);
+                          attrs, flags);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineProperty(JSContext *cx, JSObject *objArg, const char *name, jsval valueArg,
                   PropertyOp getter, JSStrictPropertyOp setter, unsigned attrs)
 {
     RootedObject obj(cx, objArg);
     RootedValue value(cx, valueArg);
-    return DefineProperty(cx, obj, name, value, GetterWrapper(getter),
-                          SetterWrapper(setter), attrs, 0, 0);
+    return DefineProperty(cx, obj, name, value, GetterWrapper(getter), SetterWrapper(setter),
+                          attrs, 0);
 }
 
 static bool
 DefineUCProperty(JSContext *cx, HandleObject obj, const jschar *name, size_t namelen,
                  const Value &value_, PropertyOp getter, StrictPropertyOp setter, unsigned attrs,
-                 unsigned flags, int tinyid)
+                 unsigned flags)
 {
     RootedValue value(cx, value_);
     AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
     JSAtom *atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
     if (!atom)
         return false;
     RootedId id(cx, AtomToId(atom));
-    return DefinePropertyById(cx, obj, id, value, GetterWrapper(getter),
-                              SetterWrapper(setter), attrs, flags, tinyid);
+    return DefinePropertyById(cx, obj, id, value, GetterWrapper(getter), SetterWrapper(setter),
+                              attrs, flags);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineUCProperty(JSContext *cx, JSObject *objArg, const jschar *name, size_t namelen,
                     jsval valueArg, JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs)
 {
     RootedObject obj(cx, objArg);
     RootedValue value(cx, valueArg);
-    return DefineUCProperty(cx, obj, name, namelen, value, getter, setter, attrs, 0, 0);
+    return DefineUCProperty(cx, obj, name, namelen, value, getter, setter, attrs, 0);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineOwnProperty(JSContext *cx, JSObject *objArg, jsid idArg, jsval descriptorArg, bool *bp)
 {
     RootedObject obj(cx, objArg);
     RootedId id(cx, idArg);
     RootedValue descriptor(cx, descriptorArg);
@@ -3164,19 +3161,20 @@ JS_DefineObject(JSContext *cx, JSObject 
     if (!clasp)
         clasp = &JSObject::class_;    /* default class is Object */
 
     RootedObject nobj(cx, NewObjectWithClassProto(cx, clasp, proto, obj));
     if (!nobj)
         return nullptr;
 
     RootedValue nobjValue(cx, ObjectValue(*nobj));
-    if (!DefineProperty(cx, obj, name, nobjValue, GetterWrapper(nullptr),
-                        SetterWrapper(nullptr), attrs, 0, 0))
+    if (!DefineProperty(cx, obj, name, nobjValue, GetterWrapper(nullptr), SetterWrapper(nullptr),
+                        attrs, 0)) {
         return nullptr;
+    }
 
     return nobj;
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineConstDoubles(JSContext *cx, JSObject *objArg, const JSConstDoubleSpec *cds)
 {
     RootedObject obj(cx, objArg);
@@ -3187,17 +3185,17 @@ JS_DefineConstDoubles(JSContext *cx, JSO
     CHECK_REQUEST(cx);
     JSPropertyOpWrapper noget = GetterWrapper(nullptr);
     JSStrictPropertyOpWrapper noset = SetterWrapper(nullptr);
     for (ok = true; cds->name; cds++) {
         RootedValue value(cx, DoubleValue(cds->dval));
         attrs = cds->flags;
         if (!attrs)
             attrs = JSPROP_READONLY | JSPROP_PERMANENT;
-        ok = DefineProperty(cx, obj, cds->name, value, noget, noset, attrs, 0, 0);
+        ok = DefineProperty(cx, obj, cds->name, value, noget, noset, attrs, 0);
         if (!ok)
             break;
     }
     return ok;
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineProperties(JSContext *cx, JSObject *objArg, const JSPropertySpec *ps)
@@ -3210,18 +3208,17 @@ JS_DefineProperties(JSContext *cx, JSObj
             // getter.
             JS_ASSERT(ps->getter.propertyOp.op);
             // If you do not have a self-hosted getter, you should not have a
             // self-hosted setter. This is the closest approximation to that
             // assertion we can have with our setup.
             JS_ASSERT_IF(ps->setter.propertyOp.info, ps->setter.propertyOp.op);
 
             ok = DefineProperty(cx, obj, ps->name, JS::UndefinedHandleValue,
-                                ps->getter.propertyOp, ps->setter.propertyOp,
-                                ps->flags, Shape::HAS_SHORTID, ps->tinyid);
+                                ps->getter.propertyOp, ps->setter.propertyOp, ps->flags, 0);
         } else {
             // If you have self-hosted getter/setter, you can't have a
             // native one.
             JS_ASSERT(!ps->getter.propertyOp.op && !ps->setter.propertyOp.op);
             JS_ASSERT(ps->flags & JSPROP_GETTER);
             /*
              * During creation of the self-hosting global, we ignore all
              * self-hosted properties, as that means we're currently setting up
@@ -3230,18 +3227,17 @@ JS_DefineProperties(JSContext *cx, JSObj
              * self-hosting global itself, right now.
              */
             if (cx->runtime()->isSelfHostingGlobal(cx->global()))
                 continue;
 
             ok = DefineSelfHostedProperty(cx, obj, ps->name,
                                           ps->getter.selfHosted.funname,
                                           ps->setter.selfHosted.funname,
-                                          ps->flags, Shape::HAS_SHORTID,
-                                          ps->tinyid);
+                                          ps->flags, 0);
         }
         if (!ok)
             break;
     }
     return ok;
 }
 
 static bool
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -925,18 +925,16 @@ class AutoIdRooter : private AutoGCRoote
 #define JSPROP_GETTER           0x10    /* property holds getter function */
 #define JSPROP_SETTER           0x20    /* property holds setter function */
 #define JSPROP_SHARED           0x40    /* don't allocate a value slot for this
                                            property; don't copy the property on
                                            set of the same-named property in an
                                            object that delegates to a prototype
                                            containing this property */
 #define JSPROP_INDEX            0x80    /* name is actually (int) index */
-#define JSPROP_SHORTID         0x100    /* set in JS_DefineProperty attrs
-                                           if getters/setters use a shortid */
 
 #define JSFUN_STUB_GSOPS       0x200    /* use JS_PropertyStub getter/setter
                                            instead of defaulting to class gsops
                                            for property holding function */
 
 #define JSFUN_CONSTRUCTOR      0x400    /* native that can be called as a ctor */
 
 
@@ -2814,23 +2812,22 @@ JS_LookupPropertyWithFlags(JSContext *cx
 
 extern JS_PUBLIC_API(bool)
 JS_LookupPropertyWithFlagsById(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
                                unsigned flags, JS::MutableHandleObject objp, JS::MutableHandleValue vp);
 
 struct JSPropertyDescriptor {
     JSObject           *obj;
     unsigned           attrs;
-    unsigned           shortid;
     JSPropertyOp       getter;
     JSStrictPropertyOp setter;
     JS::Value          value;
 
-    JSPropertyDescriptor() : obj(nullptr), attrs(0), shortid(0), getter(nullptr),
-                             setter(nullptr), value(JSVAL_VOID)
+    JSPropertyDescriptor()
+      : obj(nullptr), attrs(0), getter(nullptr), setter(nullptr), value(JSVAL_VOID)
     {}
 
     void trace(JSTracer *trc);
 };
 
 namespace JS {
 
 template <typename Outer>
@@ -2843,24 +2840,22 @@ class PropertyDescriptorOperations
     bool isReadonly() const { return desc()->attrs & JSPROP_READONLY; }
     bool isPermanent() const { return desc()->attrs & JSPROP_PERMANENT; }
     bool hasNativeAccessors() const { return desc()->attrs & JSPROP_NATIVE_ACCESSORS; }
     bool hasGetterObject() const { return desc()->attrs & JSPROP_GETTER; }
     bool hasSetterObject() const { return desc()->attrs & JSPROP_SETTER; }
     bool hasGetterOrSetterObject() const { return desc()->attrs & (JSPROP_GETTER | JSPROP_SETTER); }
     bool isShared() const { return desc()->attrs & JSPROP_SHARED; }
     bool isIndex() const { return desc()->attrs & JSPROP_INDEX; }
-    bool hasShortId() const { return desc()->attrs & JSPROP_SHORTID; }
     bool hasAttributes(unsigned attrs) const { return desc()->attrs & attrs; }
 
     JS::HandleObject object() const {
         return JS::HandleObject::fromMarkedLocation(&desc()->obj);
     }
     unsigned attributes() const { return desc()->attrs; }
-    unsigned shortid() const { return desc()->shortid; }
     JSPropertyOp getter() const { return desc()->getter; }
     JSStrictPropertyOp setter() const { return desc()->setter; }
     JS::HandleObject getterObject() const {
         MOZ_ASSERT(hasGetterObject());
         return JS::HandleObject::fromMarkedLocation(
                 reinterpret_cast<JSObject *const *>(&desc()->getter));
     }
     JS::HandleObject setterObject() const {
@@ -2878,17 +2873,16 @@ class MutablePropertyDescriptorOperation
 {
     JSPropertyDescriptor * desc() { return static_cast<Outer*>(this)->extractMutable(); }
 
   public:
 
     void clear() {
         object().set(nullptr);
         setAttributes(0);
-        setShortId(0);
         setGetter(nullptr);
         setSetter(nullptr);
         value().setUndefined();
     }
 
     JS::MutableHandleObject object() {
         return JS::MutableHandleObject::fromMarkedLocation(&desc()->obj);
     }
@@ -2897,17 +2891,16 @@ class MutablePropertyDescriptorOperation
     JSStrictPropertyOp &setter() { return desc()->setter; }
     JS::MutableHandleValue value() {
         return JS::MutableHandleValue::fromMarkedLocation(&desc()->value);
     }
 
     void setEnumerable() { desc()->attrs |= JSPROP_ENUMERATE; }
     void setAttributes(unsigned attrs) { desc()->attrs = attrs; }
 
-    void setShortId(unsigned id) { desc()->shortid = id; }
     void setGetter(JSPropertyOp op) { desc()->getter = op; }
     void setSetter(JSStrictPropertyOp op) { desc()->setter = op; }
     void setGetterObject(JSObject *obj) { desc()->getter = reinterpret_cast<JSPropertyOp>(obj); }
     void setSetterObject(JSObject *obj) { desc()->setter = reinterpret_cast<JSStrictPropertyOp>(obj); }
 };
 
 } /* namespace JS */
 
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -770,18 +770,17 @@ js::WouldDefinePastNonwritableLength(Thr
         return true;
 
     // XXX include the index and maybe array length in the error message
     return JS_ReportErrorFlagsAndNumber(ncx, flags, js_GetErrorMessage, nullptr,
                                         JSMSG_CANT_DEFINE_PAST_ARRAY_LENGTH);
 }
 
 static bool
-array_addProperty(JSContext *cx, HandleObject obj, HandleId id,
-                  MutableHandleValue vp)
+array_addProperty(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp)
 {
     Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
 
     uint32_t index;
     if (!js_IdIsIndex(id, &index))
         return true;
 
     uint32_t length = arr->length();
@@ -855,17 +854,17 @@ AddLengthProperty(ExclusiveContext *cx, 
      * The shared emptyObjectElements singleton cannot be used for slow arrays,
      * as accesses to 'length' will use the elements header.
      */
 
     RootedId lengthId(cx, NameToId(cx->names().length));
     JS_ASSERT(!obj->nativeLookup(cx, lengthId));
 
     return JSObject::addProperty(cx, obj, lengthId, array_length_getter, array_length_setter,
-                                 SHAPE_INVALID_SLOT, JSPROP_PERMANENT | JSPROP_SHARED, 0, 0,
+                                 SHAPE_INVALID_SLOT, JSPROP_PERMANENT | JSPROP_SHARED, 0,
                                  /* allowDictionary = */ false);
 }
 
 #if JS_HAS_TOSOURCE
 MOZ_ALWAYS_INLINE bool
 IsArray(HandleValue v)
 {
     return v.isObject() && v.toObject().is<ArrayObject>();
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -321,32 +321,27 @@ CallJSDeletePropertyOp(JSContext *cx, JS
     JS_CHECK_RECURSION(cx, return false);
 
     assertSameCompartment(cx, receiver, id);
     return op(cx, receiver, id, succeeded);
 }
 
 inline bool
 CallSetter(JSContext *cx, HandleObject obj, HandleId id, StrictPropertyOp op, unsigned attrs,
-           unsigned shortid, bool strict, MutableHandleValue vp)
+           bool strict, MutableHandleValue vp)
 {
     if (attrs & JSPROP_SETTER) {
         RootedValue opv(cx, CastAsObjectJsval(op));
         return InvokeGetterOrSetter(cx, obj, opv, 1, vp.address(), vp);
     }
 
     if (attrs & JSPROP_GETTER)
         return js_ReportGetterOnlyAssignment(cx, strict);
 
-    if (!(attrs & JSPROP_SHORTID))
-        return CallJSPropertyOpSetter(cx, op, obj, id, strict, vp);
-
-    RootedId nid(cx, INT_TO_JSID(shortid));
-
-    return CallJSPropertyOpSetter(cx, op, obj, nid, strict, vp);
+    return CallJSPropertyOpSetter(cx, op, obj, id, strict, vp);
 }
 
 inline uintptr_t
 GetNativeStackLimit(ThreadSafeContext *cx)
 {
     StackKind kind;
     if (cx->isJSContext()) {
         kind = cx->asJSContext()->runningWithTrustedPrincipals()
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -305,17 +305,17 @@ js::fun_resolve(JSContext *cx, HandleObj
             uint16_t length = fun->hasScript() ? fun->nonLazyScript()->funLength() :
                 fun->nargs() - fun->hasRest();
             v.setInt32(length);
         } else {
             v.setString(fun->atom() == nullptr ? cx->runtime()->emptyString : fun->atom());
         }
 
         if (!DefineNativeProperty(cx, fun, id, v, JS_PropertyStub, JS_StrictPropertyStub,
-                                  JSPROP_PERMANENT | JSPROP_READONLY, 0, 0)) {
+                                  JSPROP_PERMANENT | JSPROP_READONLY, 0)) {
             return false;
         }
         objp.set(fun);
         return true;
     }
 
     for (unsigned i = 0; i < ArrayLength(poisonPillProps); i++) {
         const uint16_t offset = poisonPillProps[i];
@@ -334,20 +334,18 @@ js::fun_resolve(JSContext *cx, HandleObj
                 getter = CastAsPropertyOp(throwTypeError);
                 setter = CastAsStrictPropertyOp(throwTypeError);
                 attrs |= JSPROP_GETTER | JSPROP_SETTER;
             } else {
                 getter = fun_getProperty;
                 setter = JS_StrictPropertyStub;
             }
 
-            if (!DefineNativeProperty(cx, fun, id, UndefinedHandleValue, getter, setter,
-                                      attrs, 0, 0)) {
+            if (!DefineNativeProperty(cx, fun, id, UndefinedHandleValue, getter, setter, attrs, 0))
                 return false;
-            }
             objp.set(fun);
             return true;
         }
     }
 
     return true;
 }
 
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -2598,17 +2598,22 @@ TypeCompartment::fixObjectType(Exclusive
     }
 
     /*
      * Use the same type object for all singleton/JSON objects with the same
      * base shape, i.e. the same fields written in the same order.
      */
     JS_ASSERT(obj->is<JSObject>());
 
-    if (obj->slotSpan() == 0 || obj->inDictionaryMode() || !obj->hasEmptyElements())
+    /*
+     * Exclude some objects we can't readily associate common types for based on their
+     * shape. Objects with metadata are excluded so that the metadata does not need to
+     * be included in the table lookup (the metadata object might be in the nursery).
+     */
+    if (obj->slotSpan() == 0 || obj->inDictionaryMode() || !obj->hasEmptyElements() || obj->getMetadata())
         return;
 
     Vector<IdValuePair> properties(cx);
     if (!properties.resize(obj->slotSpan())) {
         cx->compartment()->types.setPendingNukeTypes(cx);
         return;
     }
 
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -1238,20 +1238,20 @@ js_InitNumberClass(JSContext *cx, Handle
         return nullptr;
 
     RootedValue valueNaN(cx, cx->runtime()->NaNValue);
     RootedValue valueInfinity(cx, cx->runtime()->positiveInfinityValue);
 
     /* ES5 15.1.1.1, 15.1.1.2 */
     if (!DefineNativeProperty(cx, global, cx->names().NaN, valueNaN,
                               JS_PropertyStub, JS_StrictPropertyStub,
-                              JSPROP_PERMANENT | JSPROP_READONLY, 0, 0) ||
+                              JSPROP_PERMANENT | JSPROP_READONLY, 0) ||
         !DefineNativeProperty(cx, global, cx->names().Infinity, valueInfinity,
                               JS_PropertyStub, JS_StrictPropertyStub,
-                              JSPROP_PERMANENT | JSPROP_READONLY, 0, 0))
+                              JSPROP_PERMANENT | JSPROP_READONLY, 0))
     {
         return nullptr;
     }
 
     if (!DefineConstructorAndPrototype(cx, global, JSProto_Number, ctor, numberProto))
         return nullptr;
 
     return numberProto;
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -2055,20 +2055,18 @@ js::XDRObjectLiteral(XDRState<mode> *xdr
                 if (mode == XDR_DECODE)
                     id = INT_TO_JSID(int32_t(indexVal));
             }
 
             if (!xdr->codeConstValue(&tmpValue))
                 return false;
 
             if (mode == XDR_DECODE) {
-                if (!DefineNativeProperty(cx, obj, id, tmpValue, NULL, NULL,
-                                          JSPROP_ENUMERATE, 0, 0)) {
+                if (!DefineNativeProperty(cx, obj, id, tmpValue, NULL, NULL, JSPROP_ENUMERATE, 0))
                     return false;
-                }
             }
         }
 
         JS_ASSERT_IF(mode == XDR_DECODE, !obj->inDictionaryMode());
     }
 
     if (mode == XDR_DECODE) {
         if (isArray)
@@ -2423,17 +2421,17 @@ DefineStandardSlot(JSContext *cx, Handle
          */
         JS_ASSERT(obj->is<GlobalObject>());
         JS_ASSERT(obj->isNative());
 
         if (!obj->nativeLookup(cx, id)) {
             obj->as<GlobalObject>().setConstructorPropertySlot(key, v);
 
             uint32_t slot = GlobalObject::constructorPropertySlot(key);
-            if (!JSObject::addProperty(cx, obj, id, JS_PropertyStub, JS_StrictPropertyStub, slot, attrs, 0, 0))
+            if (!JSObject::addProperty(cx, obj, id, JS_PropertyStub, JS_StrictPropertyStub, slot, attrs, 0))
                 return false;
 
             named = true;
             return true;
         }
     }
 
     named = JSObject::defineGeneric(cx, obj, id,
@@ -3454,17 +3452,17 @@ PurgeScopeChain(ExclusiveContext *cx, JS
         return PurgeScopeChainHelper(cx, obj, id);
     return true;
 }
 
 bool
 baseops::DefineGeneric(ExclusiveContext *cx, HandleObject obj, HandleId id, HandleValue value,
                        PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
 {
-    return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs, 0, 0);
+    return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs, 0);
 }
 
 /* static */ bool
 JSObject::defineGeneric(ExclusiveContext *cx, HandleObject obj,
                         HandleId id, HandleValue value,
                         JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs)
 {
     JS_ASSERT(!(attrs & JSPROP_NATIVE_ACCESSORS));
@@ -3497,25 +3495,25 @@ JSObject::defineSpecial(ExclusiveContext
 
 bool
 baseops::DefineElement(ExclusiveContext *cx, HandleObject obj, uint32_t index, HandleValue value,
                        PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
 {
     RootedId id(cx);
     if (index <= JSID_INT_MAX) {
         id = INT_TO_JSID(index);
-        return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs, 0, 0);
+        return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs, 0);
     }
 
     AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
 
     if (!IndexToId(cx, index, &id))
         return false;
 
-    return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs, 0, 0);
+    return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs, 0);
 }
 
 /* static */ bool
 JSObject::defineElement(ExclusiveContext *cx, HandleObject obj,
                         uint32_t index, HandleValue value,
                         JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs)
 {
     js::DefineElementOp op = obj->getOps()->defineElement;
@@ -3528,27 +3526,27 @@ JSObject::defineElement(ExclusiveContext
 }
 
 Shape *
 JSObject::addDataProperty(ExclusiveContext *cx, jsid idArg, uint32_t slot, unsigned attrs)
 {
     JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
     RootedObject self(cx, this);
     RootedId id(cx, idArg);
-    return addProperty(cx, self, id, nullptr, nullptr, slot, attrs, 0, 0);
+    return addProperty(cx, self, id, nullptr, nullptr, slot, attrs, 0);
 }
 
 Shape *
 JSObject::addDataProperty(ExclusiveContext *cx, HandlePropertyName name,
                           uint32_t slot, unsigned attrs)
 {
     JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
     RootedObject self(cx, this);
     RootedId id(cx, NameToId(name));
-    return addProperty(cx, self, id, nullptr, nullptr, slot, attrs, 0, 0);
+    return addProperty(cx, self, id, nullptr, nullptr, slot, attrs, 0);
 }
 
 /*
  * Backward compatibility requires allowing addProperty hooks to mutate the
  * nominal initial value of a slotful property, while GC safety wants that
  * value to be stored before the call-out through the hook.  Optimize to do
  * both while saving cycles for classes that stub their addProperty hook.
  */
@@ -3661,18 +3659,18 @@ UpdateShapeTypeAndValue(typename Executi
     return true;
 }
 
 template <ExecutionMode mode>
 static inline bool
 DefinePropertyOrElement(typename ExecutionModeTraits<mode>::ExclusiveContextType cx,
                         HandleObject obj, HandleId id,
                         PropertyOp getter, StrictPropertyOp setter,
-                        unsigned attrs, unsigned flags, int shortid,
-                        HandleValue value, bool callSetterAfterwards, bool setterIsStrict)
+                        unsigned attrs, unsigned flags, HandleValue value,
+                        bool callSetterAfterwards, bool setterIsStrict)
 {
     /* Use dense storage for new indexed properties where possible. */
     if (JSID_IS_INT(id) &&
         getter == JS_PropertyStub &&
         setter == JS_StrictPropertyStub &&
         attrs == JSPROP_ENUMERATE &&
         (!obj->isIndexed() || !obj->nativeContainsPure(id)))
     {
@@ -3722,18 +3720,17 @@ DefinePropertyOrElement(typename Executi
             if (definesPast)
                 return true;
         }
     }
 
     AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
 
     RootedShape shape(cx, JSObject::putProperty<mode>(cx, obj, id, getter, setter,
-                                                      SHAPE_INVALID_SLOT,
-                                                      attrs, flags, shortid));
+                                                      SHAPE_INVALID_SLOT, attrs, flags));
     if (!shape)
         return false;
 
     if (!UpdateShapeTypeAndValue<mode>(cx, obj, shape, value))
         return false;
 
     /*
      * Clear any existing dense index after adding a sparse indexed property,
@@ -3770,17 +3767,17 @@ DefinePropertyOrElement(typename Executi
 
 static bool
 NativeLookupOwnProperty(ExclusiveContext *cx, HandleObject obj, HandleId id, unsigned flags,
                         MutableHandle<Shape*> shapep);
 
 bool
 js::DefineNativeProperty(ExclusiveContext *cx, HandleObject obj, HandleId id, HandleValue value,
                          PropertyOp getter, StrictPropertyOp setter, unsigned attrs,
-                         unsigned flags, int shortid, unsigned defineHow /* = 0 */)
+                         unsigned flags, unsigned defineHow /* = 0 */)
 {
     JS_ASSERT((defineHow & ~DNP_DONT_PURGE) == 0);
     JS_ASSERT(!(attrs & JSPROP_NATIVE_ACCESSORS));
 
     AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
 
     /*
      * If defining a getter or setter, we must check for its counterpart and
@@ -3832,18 +3829,17 @@ js::DefineNativeProperty(ExclusiveContex
     const Class *clasp = obj->getClass();
     if (!getter && !(attrs & JSPROP_GETTER))
         getter = clasp->getProperty;
     if (!setter && !(attrs & JSPROP_SETTER))
         setter = clasp->setProperty;
 
     if (!shape) {
         return DefinePropertyOrElement<SequentialExecution>(cx, obj, id, getter, setter,
-                                                            attrs, flags, shortid, value,
-                                                            false, false);
+                                                            attrs, flags, value, false, false);
     }
 
     JS_ALWAYS_TRUE(UpdateShapeTypeAndValue<SequentialExecution>(cx, obj, shape, value));
 
     return CallAddPropertyHook<SequentialExecution>(cx, clasp, obj, shape, value);
 }
 
 /*
@@ -4853,18 +4849,17 @@ baseops::SetPropertyHelper(typename Exec
 
                 JSContext *cx = cxArg->asJSContext();
                 Rooted<PropertyDescriptor> pd(cx);
                 if (!Proxy::getPropertyDescriptor(cx, pobj, id, &pd, JSRESOLVE_ASSIGNING))
                     return false;
 
                 if ((pd.attributes() & (JSPROP_SHARED | JSPROP_SHADOWABLE)) == JSPROP_SHARED) {
                     return !pd.setter() ||
-                           CallSetter(cx, receiver, id, pd.setter(), pd.attributes(),
-                                      pd.shortid(), strict, vp);
+                           CallSetter(cx, receiver, id, pd.setter(), pd.attributes(), strict, vp);
                 }
 
                 if (pd.isReadonly()) {
                     if (strict)
                         return JSObject::reportReadOnly(cx, id, JSREPORT_ERROR);
                     if (cx->options().extraWarnings())
                         return JSObject::reportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING);
                     return true;
@@ -4887,17 +4882,16 @@ baseops::SetPropertyHelper(typename Exec
     }
 
     /*
      * Now either shape is null, meaning id was not found in obj or one of its
      * prototypes; or shape is non-null, meaning id was found directly in pobj.
      */
     unsigned attrs = JSPROP_ENUMERATE;
     unsigned flags = 0;
-    int shortid = 0;
     const Class *clasp = obj->getClass();
     PropertyOp getter = clasp->getProperty;
     StrictPropertyOp setter = clasp->setProperty;
 
     if (IsImplicitDenseElement(shape)) {
         /* ES5 8.12.4 [[Put]] step 2, for a dense data property on pobj. */
         if (pobj != obj)
             shape = nullptr;
@@ -4951,28 +4945,18 @@ baseops::SetPropertyHelper(typename Exec
             /*
              * Preserve attrs except JSPROP_SHARED, getter, and setter when
              * shadowing any property that has no slot (is shared). We must
              * clear the shared attribute for the shadowing shape so that the
              * property in obj that it defines has a slot to retain the value
              * being set, in case the setter simply cannot operate on instances
              * of obj's class by storing the value in some class-specific
              * location.
-             *
-             * A subset of slotless shared properties is the set of properties
-             * with shortids, which must be preserved too. An old API requires
-             * that the property's getter and setter receive the shortid, not
-             * id, when they are called on the shadowing property that we are
-             * about to create in obj.
              */
             if (!shape->hasSlot()) {
-                if (shape->hasShortID()) {
-                    flags = Shape::HAS_SHORTID;
-                    shortid = shape->shortid();
-                }
                 attrs &= ~JSPROP_SHARED;
                 getter = shape->getter();
                 setter = shape->setter();
             } else {
                 /* Restore attrs to the ECMA default for new properties. */
                 attrs = JSPROP_ENUMERATE;
             }
 
@@ -5038,17 +5022,17 @@ baseops::SetPropertyHelper(typename Exec
             JSContext *cx = cxArg->asJSContext();
 
             /* Purge the property cache of now-shadowed id in obj's scope chain. */
             if (!PurgeScopeChain(cx, obj, id))
                 return false;
         }
 
         return DefinePropertyOrElement<mode>(cxArg, obj, id, getter, setter,
-                                             attrs, flags, shortid, vp, true, strict);
+                                             attrs, flags, vp, true, strict);
     }
 
     return NativeSet<mode>(cxArg, obj, receiver, shape, strict, vp);
 }
 
 template bool
 baseops::SetPropertyHelper<SequentialExecution>(JSContext *cx, HandleObject obj,
                                                 HandleObject receiver,
@@ -5141,21 +5125,18 @@ baseops::DeleteGeneric(JSContext *cx, Ha
         return js_SuppressDeletedProperty(cx, obj, id);
     }
 
     if (!shape->configurable()) {
         *succeeded = false;
         return true;
     }
 
-    RootedId userid(cx);
-    if (!shape->getUserId(cx, &userid))
-        return false;
-
-    if (!CallJSDeletePropertyOp(cx, obj->getClass()->delProperty, obj, userid, succeeded))
+    RootedId propid(cx, shape->propid());
+    if (!CallJSDeletePropertyOp(cx, obj->getClass()->delProperty, obj, propid, succeeded))
         return false;
     if (!succeeded)
         return true;
 
     return obj->removeProperty(cx, id) && js_SuppressDeletedProperty(cx, obj, id);
 }
 
 bool
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -886,56 +886,55 @@ class JSObject : public js::ObjectImpl
      * 1. getter and setter must be normalized based on flags (see jsscope.cpp).
      * 2. Checks for non-extensibility must be done by callers.
      */
     template <js::ExecutionMode mode>
     static js::Shape *
     addPropertyInternal(typename js::ExecutionModeTraits<mode>::ExclusiveContextType cx,
                         JS::HandleObject obj, JS::HandleId id,
                         JSPropertyOp getter, JSStrictPropertyOp setter,
-                        uint32_t slot, unsigned attrs,
-                        unsigned flags, int shortid, js::Shape **spp,
+                        uint32_t slot, unsigned attrs, unsigned flags, js::Shape **spp,
                         bool allowDictionary);
 
   private:
     struct TradeGutsReserved;
     static bool ReserveForTradeGuts(JSContext *cx, JSObject *a, JSObject *b,
                                     TradeGutsReserved &reserved);
 
     static void TradeGuts(JSContext *cx, JSObject *a, JSObject *b,
                           TradeGutsReserved &reserved);
 
   public:
     /* Add a property whose id is not yet in this scope. */
     static js::Shape *addProperty(js::ExclusiveContext *cx, JS::HandleObject, JS::HandleId id,
                                   JSPropertyOp getter, JSStrictPropertyOp setter,
                                   uint32_t slot, unsigned attrs, unsigned flags,
-                                  int shortid, bool allowDictionary = true);
+                                  bool allowDictionary = true);
 
     /* Add a data property whose id is not yet in this scope. */
     js::Shape *addDataProperty(js::ExclusiveContext *cx,
                                jsid id_, uint32_t slot, unsigned attrs);
     js::Shape *addDataProperty(js::ExclusiveContext *cx, js::HandlePropertyName name,
                                uint32_t slot, unsigned attrs);
 
     /* Add or overwrite a property for id in this scope. */
     template <js::ExecutionMode mode>
     static js::Shape *
     putProperty(typename js::ExecutionModeTraits<mode>::ExclusiveContextType cx,
                 JS::HandleObject obj, JS::HandleId id,
                 JSPropertyOp getter, JSStrictPropertyOp setter,
                 uint32_t slot, unsigned attrs,
-                unsigned flags, int shortid);
+                unsigned flags);
     template <js::ExecutionMode mode>
     static inline js::Shape *
     putProperty(typename js::ExecutionModeTraits<mode>::ExclusiveContextType cx,
                 JS::HandleObject obj, js::PropertyName *name,
                 JSPropertyOp getter, JSStrictPropertyOp setter,
                 uint32_t slot, unsigned attrs,
-                unsigned flags, int shortid);
+                unsigned flags);
 
     /* Change the given property into a sibling with the same id in this scope. */
     template <js::ExecutionMode mode>
     static js::Shape *
     changeProperty(typename js::ExecutionModeTraits<mode>::ExclusiveContextType cx,
                    js::HandleObject obj, js::HandleShape shape, unsigned attrs, unsigned mask,
                    JSPropertyOp getter, JSStrictPropertyOp setter);
 
@@ -1415,30 +1414,30 @@ CreateThis(JSContext *cx, const js::Clas
 
 extern JSObject *
 CloneObject(JSContext *cx, HandleObject obj, Handle<js::TaggedProto> proto, HandleObject parent);
 
 extern JSObject *
 DeepCloneObjectLiteral(JSContext *cx, HandleObject obj, NewObjectKind newKind = GenericObject);
 
 /*
- * Flags for the defineHow parameter of js_DefineNativeProperty.
+ * Flags for the defineHow parameter of DefineNativeProperty.
  */
 const unsigned DNP_DONT_PURGE   = 1;   /* suppress js_PurgeScopeChain */
 const unsigned DNP_UNQUALIFIED  = 2;   /* Unqualified property set.  Only used in
                                        the defineHow argument of
                                        js_SetPropertyHelper. */
 
 /*
  * Return successfully added or changed shape or nullptr on error.
  */
 extern bool
 DefineNativeProperty(ExclusiveContext *cx, HandleObject obj, HandleId id, HandleValue value,
                      PropertyOp getter, StrictPropertyOp setter, unsigned attrs,
-                     unsigned flags, int shortid, unsigned defineHow = 0);
+                     unsigned flags, unsigned defineHow = 0);
 
 /*
  * Specialized subroutine that allows caller to preset JSRESOLVE_* flags.
  */
 extern bool
 LookupPropertyWithFlags(ExclusiveContext *cx, HandleObject obj, HandleId id, unsigned flags,
                         js::MutableHandleObject objp, js::MutableHandleShape propp);
 
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -1090,21 +1090,20 @@ NewObjectMetadata(ExclusiveContext *cxAr
         }
     }
     return true;
 }
 
 inline bool
 DefineNativeProperty(ExclusiveContext *cx, HandleObject obj, PropertyName *name, HandleValue value,
                      PropertyOp getter, StrictPropertyOp setter, unsigned attrs,
-                     unsigned flags, int shortid, unsigned defineHow = 0)
+                     unsigned flags, unsigned defineHow = 0)
 {
     Rooted<jsid> id(cx, NameToId(name));
-    return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs, flags,
-                                shortid, defineHow);
+    return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs, flags, defineHow);
 }
 
 inline bool
 LookupPropertyWithFlags(ExclusiveContext *cx, HandleObject obj, PropertyName *name, unsigned flags,
                         js::MutableHandleObject objp, js::MutableHandleShape propp)
 {
     Rooted<jsid> id(cx, NameToId(name));
     return LookupPropertyWithFlags(cx, obj, id, flags, objp, propp);
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -643,17 +643,17 @@ js_Stringify(JSContext *cx, MutableHandl
     /* Step 9. */
     RootedObject wrapper(cx, NewBuiltinClassInstance(cx, &JSObject::class_));
     if (!wrapper)
         return false;
 
     /* Step 10. */
     RootedId emptyId(cx, NameToId(cx->names().empty));
     if (!DefineNativeProperty(cx, wrapper, emptyId, vp, JS_PropertyStub, JS_StrictPropertyStub,
-                              JSPROP_ENUMERATE, 0, 0))
+                              JSPROP_ENUMERATE, 0))
     {
         return false;
     }
 
     /* Step 11. */
     StringifyContext scx(cx, sb, gap, replacer, propertyList);
     if (!PreprocessValue(cx, wrapper, HandleId(emptyId), vp, &scx))
         return false;
--- a/js/src/jsonparser.cpp
+++ b/js/src/jsonparser.cpp
@@ -589,20 +589,18 @@ JSONParser::createFinishedObject(Propert
         return nullptr;
 
     RootedId propid(cx);
     RootedValue value(cx);
 
     for (size_t i = 0; i < properties.length(); i++) {
         propid = properties[i].id;
         value = properties[i].value;
-        if (!DefineNativeProperty(cx, obj, propid, value,
-                                  JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE,
-                                  0, 0))
-        {
+        if (!DefineNativeProperty(cx, obj, propid, value, JS_PropertyStub, JS_StrictPropertyStub,
+                                  JSPROP_ENUMERATE, 0)) {
             return nullptr;
         }
     }
 
     /*
      * Try to assign a new type to the object with type information for its
      * properties, and update the initializer type object cache with this
      * object's final shape.
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -828,36 +828,38 @@ ToDisassemblySource(JSContext *cx, Handl
             return false;
         bytes->initBytes(source);
         return true;
     }
 
     if (!JSVAL_IS_PRIMITIVE(v)) {
         JSObject *obj = JSVAL_TO_OBJECT(v);
         if (obj->is<BlockObject>()) {
+            Rooted<BlockObject*> block(cx, &obj->as<BlockObject>());
             char *source = JS_sprintf_append(nullptr, "depth %d {",
-                                             obj->as<BlockObject>().stackDepth());
+                                             block->stackDepth());
             if (!source)
                 return false;
 
-            Shape::Range<CanGC> r(cx, obj->lastProperty());
+            Shape::Range<CanGC> r(cx, block->lastProperty());
 
             while (!r.empty()) {
                 Rooted<Shape*> shape(cx, &r.front());
                 JSAtom *atom = JSID_IS_INT(shape->propid())
                                ? cx->names().empty
                                : JSID_TO_ATOM(shape->propid());
 
                 JSAutoByteString bytes;
                 if (!AtomToPrintableString(cx, atom, &bytes))
                     return false;
 
                 r.popFront();
                 source = JS_sprintf_append(source, "%s: %d%s",
-                                           bytes.ptr(), shape->shortid(),
+                                           bytes.ptr(),
+                                           block->shapeToIndex(*shape),
                                            !r.empty() ? ", " : "");
                 if (!source)
                     return false;
             }
 
             source = JS_sprintf_append(source, "}");
             if (!source)
                 return false;
@@ -1644,20 +1646,20 @@ JSAtom *
 ExpressionDecompiler::findLetVar(jsbytecode *pc, uint32_t depth)
 {
     for (JSObject *chain = script->getStaticScope(pc); chain; chain = chain->getParent()) {
         if (!chain->is<StaticBlockObject>())
             continue;
         StaticBlockObject &block = chain->as<StaticBlockObject>();
         uint32_t blockDepth = block.stackDepth();
         uint32_t blockCount = block.slotCount();
-        if (uint32_t(depth - blockDepth) < uint32_t(blockCount)) {
+        if (depth - blockDepth < blockCount) {
             for (Shape::Range<NoGC> r(block.lastProperty()); !r.empty(); r.popFront()) {
                 const Shape &shape = r.front();
-                if (shape.shortid() == int(depth - blockDepth))
+                if (block.shapeToIndex(shape) == depth - blockDepth)
                     return JSID_TO_ATOM(shape.propid());
             }
         }
     }
     return nullptr;
 }
 
 JSAtom *
--- a/js/src/jspropertytree.cpp
+++ b/js/src/jspropertytree.cpp
@@ -329,23 +329,20 @@ Shape::dump(JSContext *cx, FILE *fp) con
         fputs(") ", fp);
     }
 
     fprintf(fp, "flags %x ", flags);
     if (flags) {
         int first = 1;
         fputs("(", fp);
 #define DUMP_FLAG(name, display) if (flags & name) fputs(&(" " #display)[first], fp), first = 0
-        DUMP_FLAG(HAS_SHORTID, has_shortid);
         DUMP_FLAG(IN_DICTIONARY, in_dictionary);
 #undef  DUMP_FLAG
         fputs(") ", fp);
     }
-
-    fprintf(fp, "shortid %d\n", maybeShortid());
 }
 
 void
 Shape::dumpSubtree(JSContext *cx, int level, FILE *fp) const
 {
     if (!parent) {
         JS_ASSERT(level == 0);
         JS_ASSERT(JSID_IS_EMPTY(propid_));
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -144,20 +144,16 @@ BaseProxyHandler::get(JSContext *cx, Han
     if (desc.hasGetterObject())
         return InvokeGetterOrSetter(cx, receiver, ObjectValue(*desc.getterObject()),
                                     0, nullptr, vp);
     if (!desc.isShared())
         vp.set(desc.value());
     else
         vp.setUndefined();
 
-    if (desc.hasShortId()) {
-        RootedId id(cx, INT_TO_JSID(desc.shortid()));
-        return CallJSPropertyOp(cx, desc.getter(), receiver, id, vp);
-    }
     return CallJSPropertyOp(cx, desc.getter(), receiver, id, vp);
 }
 
 bool
 BaseProxyHandler::set(JSContext *cx, HandleObject proxy, HandleObject receiver,
                       HandleId id, bool strict, MutableHandleValue vp)
 {
     assertEnteredPolicy(cx, proxy, id, SET);
@@ -171,17 +167,17 @@ BaseProxyHandler::set(JSContext *cx, Han
         if (desc.isReadonly())
             return strict ? Throw(cx, id, JSMSG_CANT_REDEFINE_PROP) : true;
         if (!desc.setter()) {
             // Be wary of the odd explicit undefined setter case possible through
             // Object.defineProperty.
             if (!desc.hasSetterObject())
                 desc.setSetter(JS_StrictPropertyStub);
         } else if (desc.hasSetterObject() || desc.setter() != JS_StrictPropertyStub) {
-            if (!CallSetter(cx, receiver, id, desc.setter(), desc.attributes(), desc.shortid(), strict, vp))
+            if (!CallSetter(cx, receiver, id, desc.setter(), desc.attributes(), strict, vp))
                 return false;
             if (!proxy->is<ProxyObject>() || proxy->as<ProxyObject>().handler() != this)
                 return true;
             if (desc.isShared())
                 return true;
         }
         if (!desc.getter()) {
             // Same as above for the null setter case.
@@ -198,17 +194,17 @@ BaseProxyHandler::set(JSContext *cx, Han
         if (desc.isReadonly())
             return strict ? Throw(cx, id, JSMSG_CANT_REDEFINE_PROP) : true;
         if (!desc.setter()) {
             // Be wary of the odd explicit undefined setter case possible through
             // Object.defineProperty.
             if (!desc.hasSetterObject())
                 desc.setSetter(JS_StrictPropertyStub);
         } else if (desc.hasSetterObject() || desc.setter() != JS_StrictPropertyStub) {
-            if (!CallSetter(cx, receiver, id, desc.setter(), desc.attributes(), desc.shortid(), strict, vp))
+            if (!CallSetter(cx, receiver, id, desc.setter(), desc.attributes(), strict, vp))
                 return false;
             if (!proxy->is<ProxyObject>() || proxy->as<ProxyObject>().handler() != this)
                 return true;
             if (desc.isShared())
                 return true;
         }
         if (!desc.getter()) {
             // Same as above for the null setter case.
@@ -217,17 +213,16 @@ BaseProxyHandler::set(JSContext *cx, Han
         }
         desc.value().set(vp.get());
         return defineProperty(cx, receiver, id, &desc);
     }
 
     desc.object().set(receiver);
     desc.value().set(vp.get());
     desc.setAttributes(JSPROP_ENUMERATE);
-    desc.setShortId(0);
     desc.setGetter(nullptr);
     desc.setSetter(nullptr); // Pick up the class getter/setter.
     return defineProperty(cx, receiver, id, &desc);
 }
 
 bool
 BaseProxyHandler::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props)
 {
@@ -703,21 +698,19 @@ ParsePropertyDescriptorObject(JSContext 
     AutoPropDescArrayRooter descs(cx);
     PropDesc *d = descs.append();
     if (!d || !d->initialize(cx, v))
         return false;
     if (complete)
         d->complete();
     desc.object().set(obj);
     desc.value().set(d->hasValue() ? d->value() : UndefinedValue());
-    JS_ASSERT(!(d->attributes() & JSPROP_SHORTID));
     desc.setAttributes(d->attributes());
     desc.setGetter(d->getter());
     desc.setSetter(d->setter());
-    desc.setShortId(0);
     return true;
 }
 
 static bool
 IndicatePropertyNotFound(MutableHandle<PropertyDescriptor> desc)
 {
     desc.object().set(nullptr);
     return true;
@@ -2844,20 +2837,19 @@ js::proxy_LookupSpecial(JSContext *cx, H
 
 bool
 js::proxy_DefineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue value,
                         PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
 {
     Rooted<PropertyDescriptor> desc(cx);
     desc.object().set(obj);
     desc.value().set(value);
-    desc.setAttributes(attrs & (~JSPROP_SHORTID));
+    desc.setAttributes(attrs);
     desc.setGetter(getter);
     desc.setSetter(setter);
-    desc.setShortId(0);
     return Proxy::defineProperty(cx, obj, id, &desc);
 }
 
 bool
 js::proxy_DefineProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, HandleValue value,
                          PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
 {
     Rooted<jsid> id(cx, NameToId(name));
@@ -2960,17 +2952,17 @@ js::proxy_GetGenericAttributes(JSContext
 
 bool
 js::proxy_SetGenericAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp)
 {
     /* Lookup the current property descriptor so we have setter/getter/value. */
     Rooted<PropertyDescriptor> desc(cx);
     if (!Proxy::getOwnPropertyDescriptor(cx, obj, id, &desc, JSRESOLVE_ASSIGNING))
         return false;
-    desc.setAttributes(*attrsp & (~JSPROP_SHORTID));
+    desc.setAttributes(*attrsp);
     return Proxy::defineProperty(cx, obj, id, &desc);
 }
 
 static bool
 proxy_DeleteGeneric(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded)
 {
     bool deleted;
     if (!Proxy::delete_(cx, obj, id, &deleted))
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -138,17 +138,17 @@ Bindings::initWithTemporaryStorage(Exclu
 
         UnownedBaseShape *base = BaseShape::getUnowned(cx, stackBase);
         if (!base)
             return false;
 
         unsigned attrs = JSPROP_PERMANENT |
                          JSPROP_ENUMERATE |
                          (bi->kind() == CONSTANT ? JSPROP_READONLY : 0);
-        StackShape child(base, NameToId(bi->name()), slot, attrs, 0, 0);
+        StackShape child(base, NameToId(bi->name()), slot, attrs, 0);
 
         shape = cx->compartment()->propertyTree.getChild(cx, shape, child);
         if (!shape)
             return false;
 
         JS_ASSERT(slot < nslots);
         slot++;
     }
--- a/js/src/jsweakmap.h
+++ b/js/src/jsweakmap.h
@@ -80,17 +80,18 @@ class WeakMapBase {
 
     // Remove entries whose keys are dead from all weak maps in a compartment marked as
     // live in this garbage collection.
     static void sweepCompartment(JSCompartment *c);
 
     // Trace all delayed weak map bindings. Used by the cycle collector.
     static void traceAllMappings(WeakMapTracer *tracer);
 
-    void check() { JS_ASSERT(next == WeakMapNotInList); }
+    bool isInList() { return next != WeakMapNotInList; }
+    void check() { JS_ASSERT(!isInList()); }
 
     // Remove everything from the weak map list for a compartment.
     static void resetCompartmentWeakMapList(JSCompartment *c);
 
     // Save the live weak map list for a compartment, appending the data to a vector.
     static bool saveCompartmentWeakMapList(JSCompartment *c, WeakMapVector &vector);
 
     // Restore live weak map lists for multiple compartments from a vector.
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -83,16 +83,17 @@ EXPORTS.js += [
     '../public/RootingAPI.h',
     '../public/SliceBudget.h',
     '../public/StructuredClone.h',
     '../public/Tracer.h',
     '../public/TypeDecls.h',
     '../public/Utility.h',
     '../public/Value.h',
     '../public/Vector.h',
+    '../public/WeakMapPtr.h',
 ]
 
 UNIFIED_SOURCES += [
     'assembler/jit/ExecutableAllocator.cpp',
     'builtin/Eval.cpp',
     'builtin/Intl.cpp',
     'builtin/MapObject.cpp',
     'builtin/Object.cpp',
@@ -184,16 +185,17 @@ UNIFIED_SOURCES += [
     'vm/Stack.cpp',
     'vm/String.cpp',
     'vm/StringBuffer.cpp',
     'vm/StructuredClone.cpp',
     'vm/ThreadPool.cpp',
     'vm/TypedArrayObject.cpp',
     'vm/Unicode.cpp',
     'vm/Value.cpp',
+    'vm/WeakMapPtr.cpp',
     'vm/Xdr.cpp',
     'yarr/PageBlock.cpp',
     'yarr/YarrCanonicalizeUCS2.cpp',
     'yarr/YarrInterpreter.cpp',
     'yarr/YarrPattern.cpp',
     'yarr/YarrSyntaxChecker.cpp',
 ]
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -2896,17 +2896,16 @@ ShapeOf(JSContext *cx, unsigned argc, JS
  * non-native referent may be simplified to data properties.
  */
 static bool
 CopyProperty(JSContext *cx, HandleObject obj, HandleObject referent, HandleId id,
              unsigned lookupFlags, MutableHandleObject objp)
 {
     RootedShape shape(cx);
     Rooted<PropertyDescriptor> desc(cx);
-    unsigned propFlags = 0;
     RootedObject obj2(cx);
 
     objp.set(nullptr);
     if (referent->isNative()) {
         if (!LookupPropertyWithFlags(cx, referent, id, lookupFlags, &obj2, &shape))
             return false;
         if (obj2 != referent)
             return true;
@@ -2919,18 +2918,16 @@ CopyProperty(JSContext *cx, HandleObject
 
         desc.setAttributes(shape->attributes());
         desc.setGetter(shape->getter());
         if (!desc.getter() && !desc.hasGetterObject())
             desc.setGetter(JS_PropertyStub);
         desc.setSetter(shape->setter());
         if (!desc.setter() && !desc.hasSetterObject())
             desc.setSetter(JS_StrictPropertyStub);
-        desc.setShortId(shape->shortid());
-        propFlags = shape->getFlags();
     } else if (referent->is<ProxyObject>()) {
         if (!Proxy::getOwnPropertyDescriptor(cx, referent, id, &desc, 0))
             return false;
         if (!desc.object())
             return true;
     } else {
         if (!JSObject::lookupGeneric(cx, referent, id, objp, &shape))
             return false;
@@ -2941,22 +2938,21 @@ CopyProperty(JSContext *cx, HandleObject
             !JSObject::getGenericAttributes(cx, referent, id, &desc.attributesRef()))
         {
             return false;
         }
         desc.value().set(value);
         desc.attributesRef() &= JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT;
         desc.setGetter(JS_PropertyStub);
         desc.setSetter(JS_StrictPropertyStub);
-        desc.setShortId(0);
     }
 
     objp.set(obj);
     return DefineNativeProperty(cx, obj, id, desc.value(), desc.getter(), desc.setter(),
-                                desc.attributes(), propFlags, desc.shortid());
+                                desc.attributes(), 0);
 }
 
 static bool
 resolver_resolve(JSContext *cx, HandleObject obj, HandleId id, unsigned flags,
                  MutableHandleObject objp)
 {
     jsval v = JS_GetReservedSlot(obj, 0);
     Rooted<JSObject*> vobj(cx, &v.toObject());
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -867,17 +867,17 @@ Debugger::newCompletionValue(JSContext *
         MOZ_ASSUME_UNREACHABLE("bad status passed to Debugger::newCompletionValue");
     }
 
     /* Common tail for JSTRAP_RETURN and JSTRAP_THROW. */
     RootedObject obj(cx, NewBuiltinClassInstance(cx, &JSObject::class_));
     if (!obj ||
         !wrapDebuggeeValue(cx, &value) ||
         !DefineNativeProperty(cx, obj, key, value, JS_PropertyStub, JS_StrictPropertyStub,
-                              JSPROP_ENUMERATE, 0, 0))
+                              JSPROP_ENUMERATE, 0))
     {
         return false;
     }
 
     result.setObject(*obj);
     return true;
 }
 
@@ -4263,34 +4263,34 @@ DebuggerFrame_getArguments(JSContext *cx
             return false;
         SetReservedSlot(argsobj, JSSLOT_DEBUGARGUMENTS_FRAME, ObjectValue(*thisobj));
 
         JS_ASSERT(frame.numActualArgs() <= 0x7fffffff);
         unsigned fargc = frame.numActualArgs();
         RootedValue fargcVal(cx, Int32Value(fargc));
         if (!DefineNativeProperty(cx, argsobj, cx->names().length,
                                   fargcVal, nullptr, nullptr,
-                                  JSPROP_PERMANENT | JSPROP_READONLY, 0, 0))
+                                  JSPROP_PERMANENT | JSPROP_READONLY, 0))
         {
             return false;
         }
 
         Rooted<jsid> id(cx);
         for (unsigned i = 0; i < fargc; i++) {
             RootedFunction getobj(cx);
             getobj = NewFunction(cx, js::NullPtr(), DebuggerArguments_getArg, 0,
                                  JSFunction::NATIVE_FUN, global, js::NullPtr(),
                                  JSFunction::ExtendedFinalizeKind);
             if (!getobj)
                 return false;
             id = INT_TO_JSID(i);
             if (!getobj ||
                 !DefineNativeProperty(cx, argsobj, id, UndefinedHandleValue,
                                       JS_DATA_TO_FUNC_PTR(PropertyOp, getobj.get()), nullptr,
-                                      JSPROP_ENUMERATE | JSPROP_SHARED | JSPROP_GETTER, 0, 0))
+                                      JSPROP_ENUMERATE | JSPROP_SHARED | JSPROP_GETTER, 0))
             {
                 return false;
             }
             getobj->setExtendedSlot(0, Int32Value(i));
         }
     } else {
         argsobj = nullptr;
     }
@@ -4578,17 +4578,17 @@ DebuggerGenericEval(JSContext *cx, const
         env = NewObjectWithGivenProto(cx, &JSObject::class_, nullptr, env);
         if (!env)
             return false;
         RootedId id(cx);
         for (size_t i = 0; i < keys.length(); i++) {
             id = keys[i];
             MutableHandleValue val = values.handleAt(i);
             if (!cx->compartment()->wrap(cx, val) ||
-                !DefineNativeProperty(cx, env, id, val, nullptr, nullptr, 0, 0, 0))
+                !DefineNativeProperty(cx, env, id, val, nullptr, nullptr, 0, 0))
             {
                 return false;
             }
         }
     }
 
     /* Run the code and produce the completion value. */
     RootedValue rval(cx);
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -787,17 +787,17 @@ bool
 GlobalObject::addIntrinsicValue(JSContext *cx, HandleId id, HandleValue value)
 {
     RootedObject holder(cx, intrinsicsHolder());
 
     uint32_t slot = holder->slotSpan();
     RootedShape last(cx, holder->lastProperty());
     Rooted<UnownedBaseShape*> base(cx, last->base()->unowned());
 
-    StackShape child(base, id, slot, 0, 0, 0);
+    StackShape child(base, id, slot, 0, 0);
     RootedShape shape(cx, cx->compartment()->propertyTree.getChild(cx, last, child));
     if (!shape)
         return false;
 
     if (!JSObject::setLastProperty(cx, holder, shape))
         return false;
 
     holder->setSlot(shape->slot(), value);
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -3145,17 +3145,17 @@ CASE(JSOP_INITPROP)
     obj = &REGS.sp[-2].toObject();
     JS_ASSERT(obj->is<JSObject>());
 
     PropertyName *name = script->getName(REGS.pc);
 
     RootedId &id = rootId0;
     id = NameToId(name);
 
-    if (!DefineNativeProperty(cx, obj, id, rval, nullptr, nullptr, JSPROP_ENUMERATE, 0, 0, 0))
+    if (!DefineNativeProperty(cx, obj, id, rval, nullptr, nullptr, JSPROP_ENUMERATE, 0, 0))
         goto error;
 
     REGS.sp--;
 }
 END_CASE(JSOP_INITPROP);
 
 CASE(JSOP_INITELEM)
 {
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -865,27 +865,34 @@ JSRuntime::clearUsedByExclusiveThread(Zo
     JS_ASSERT(zone->usedByExclusiveThread);
     zone->usedByExclusiveThread = false;
     numExclusiveThreads--;
 }
 
 bool
 js::CurrentThreadCanAccessRuntime(JSRuntime *rt)
 {
-    DebugOnly<PerThreadData *> pt = js::TlsPerThreadData.get();
-    JS_ASSERT(pt && pt->associatedWith(rt));
     return rt->ownerThread_ == PR_GetCurrentThread() || InExclusiveParallelSection();
 }
 
 bool
 js::CurrentThreadCanAccessZone(Zone *zone)
 {
-    DebugOnly<PerThreadData *> pt = js::TlsPerThreadData.get();
-    JS_ASSERT(pt && pt->associatedWith(zone->runtime_));
-    return !InParallelSection() || InExclusiveParallelSection();
+    if (CurrentThreadCanAccessRuntime(zone->runtime_))
+        return true;
+    if (InParallelSection()) {
+        DebugOnly<PerThreadData *> pt = js::TlsPerThreadData.get();
+        JS_ASSERT(pt && pt->associatedWith(zone->runtime_));
+        return true;
+    }
+
+    // Only zones in use by an exclusive thread can be used off the main thread
+    // or outside of PJS. We don't keep track of which thread owns such zones
+    // though, so this check is imperfect.
+    return zone->usedByExclusiveThread;
 }
 
 #else // JS_THREADSAFE
 
 bool
 js::CurrentThreadCanAccessRuntime(JSRuntime *rt)
 {
     return true;
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -320,18 +320,17 @@ DeclEnvObject::createTemplateObject(JSCo
     if (!obj)
         return nullptr;
 
     // Assign a fixed slot to a property with the same name as the lambda.
     Rooted<jsid> id(cx, AtomToId(fun->atom()));
     const Class *clasp = obj->getClass();
     unsigned attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY;
     if (!JSObject::putProperty<SequentialExecution>(cx, obj, id, clasp->getProperty,
-                                                    clasp->setProperty, lambdaSlot(), attrs, 0, 0))
-    {
+                                                    clasp->setProperty, lambdaSlot(), attrs, 0)) {
         return nullptr;
     }
 
     JS_ASSERT(!obj->hasDynamicSlots());
     return &obj->as<DeclEnvObject>();
 }
 
 DeclEnvObject *
@@ -715,39 +714,41 @@ StaticBlockObject::create(ExclusiveConte
 
     return &obj->as<StaticBlockObject>();
 }
 
 /* static */ Shape *
 StaticBlockObject::addVar(ExclusiveContext *cx, Handle<StaticBlockObject*> block, HandleId id,
                           unsigned index, bool *redeclared)
 {
-    JS_ASSERT(JSID_IS_ATOM(id) || (JSID_IS_INT(id) && JSID_TO_INT(id) == (int)index));
+    JS_ASSERT(JSID_IS_ATOM(id));
     JS_ASSERT(index < VAR_INDEX_LIMIT);
 
     *redeclared = false;
 
     /* Inline JSObject::addProperty in order to trap the redefinition case. */
     Shape **spp;
     if (Shape::search(cx, block->lastProperty(), id, &spp, true)) {
         *redeclared = true;
         return nullptr;
     }
 
     /*
      * Don't convert this object to dictionary mode so that we can clone the
      * block's shape later.
      */
     uint32_t slot = JSSLOT_FREE(&BlockObject::class_) + index;
-    return JSObject::addPropertyInternal<SequentialExecution>(
-        cx, block, id,
-        /* getter = */ nullptr, /* setter = */ nullptr,
-        slot, JSPROP_ENUMERATE | JSPROP_PERMANENT,
-        Shape::HAS_SHORTID, index, spp,
-        /* allowDictionary = */ false);
+    return JSObject::addPropertyInternal<SequentialExecution>(cx, block, id,
+                                                              /* getter = */ nullptr,
+                                                              /* setter = */ nullptr,
+                                                              slot,
+                                                              JSPROP_ENUMERATE | JSPROP_PERMANENT,
+                                                              /* attrs = */ 0,
+                                                              spp,
+                                                              /* allowDictionary = */ false);
 }
 
 const Class BlockObject::class_ = {
     "Block",
     JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_HAS_RESERVED_SLOTS(BlockObject::RESERVED_SLOTS) |
     JSCLASS_IS_ANONYMOUS,
     JS_PropertyStub,         /* addProperty */
@@ -824,32 +825,30 @@ js::XDRStaticBlockObject(XDRState<mode> 
             JS_ASSERT(aliased == 0 || aliased == 1);
             obj->setAliased(i, !!aliased);
         }
     } else {
         AutoShapeVector shapes(cx);
         if (!shapes.growBy(count))
             return false;
 
-        for (Shape::Range<NoGC> r(obj->lastProperty()); !r.empty(); r.popFront()) {
-            Shape *shape = &r.front();
-            shapes[shape->shortid()] = shape;
-        }
+        for (Shape::Range<NoGC> r(obj->lastProperty()); !r.empty(); r.popFront())
+            shapes[obj->shapeToIndex(r.front())] = &r.front();
 
         /*
          * XDR the block object's properties. We know that there are 'count'
          * properties to XDR, stored as id/shortid pairs.
          */
         RootedShape shape(cx);
         RootedId propid(cx);
         RootedAtom atom(cx);
         for (unsigned i = 0; i < count; i++) {
             shape = shapes[i];
             JS_ASSERT(shape->hasDefaultGetter());
-            JS_ASSERT(unsigned(shape->shortid()) == i);
+            JS_ASSERT(obj->shapeToIndex(*shape) == i);
 
             propid = shape->propid();
             JS_ASSERT(JSID_IS_ATOM(propid) || JSID_IS_INT(propid));
 
             /* The empty string indicates an int id. */
             atom = JSID_IS_ATOM(propid)
                    ? JSID_TO_ATOM(propid)
                    : cx->runtime()->emptyString;
@@ -881,22 +880,23 @@ CloneStaticBlockObject(JSContext *cx, Ha
 
     clone->initEnclosingNestedScope(enclosingScope);
     clone->setStackDepth(srcBlock->stackDepth());
 
     /* Shape::Range is reverse order, so build a list in forward order. */
     AutoShapeVector shapes(cx);
     if (!shapes.growBy(srcBlock->slotCount()))
         return nullptr;
+
     for (Shape::Range<NoGC> r(srcBlock->lastProperty()); !r.empty(); r.popFront())
-        shapes[r.front().shortid()] = &r.front();
+        shapes[srcBlock->shapeToIndex(r.front())] = &r.front();
 
     for (Shape **p = shapes.begin(); p != shapes.end(); ++p) {
         RootedId id(cx, (*p)->propid());
-        unsigned i = (*p)->shortid();
+        unsigned i = srcBlock->shapeToIndex(**p);
 
         bool redeclared;
         if (!StaticBlockObject::addVar(cx, clone, id, i, &redeclared)) {
             JS_ASSERT(!redeclared);
             return nullptr;
         }
 
         clone->setAliased(i, srcBlock->isAliased(i));
@@ -1254,17 +1254,17 @@ class DebugScopeProxy : public BaseProxy
 
         /* Handle unaliased let and catch bindings at block scope. */
         if (scope->is<ClonedBlockObject>()) {
             Rooted<ClonedBlockObject *> block(cx, &scope->as<ClonedBlockObject>());
             Shape *shape = block->lastProperty()->search(cx, id);
             if (!shape)
                 return false;
 
-            unsigned i = shape->shortid();
+            unsigned i = block->shapeToIndex(*shape);
             if (block->staticBlock().isAliased(i))
                 return false;
 
             if (maybeLiveScope) {
                 AbstractFramePtr frame = maybeLiveScope->frame();
                 JSScript *script = frame.script();
                 uint32_t local = block->slotToLocalIndex(script->bindings, shape->slot());
                 if (action == GET)
@@ -1376,28 +1376,26 @@ class DebugScopeProxy : public BaseProxy
         RootedArgumentsObject maybeArgsObj(cx);
         if (!checkForMissingArguments(cx, id, *scope, maybeArgsObj.address()))
             return false;
 
         if (maybeArgsObj) {
             desc.object().set(debugScope);
             desc.setAttributes(JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT);
             desc.value().setObject(*maybeArgsObj);
-            desc.setShortId(0);
             desc.setGetter(nullptr);
             desc.setSetter(nullptr);
             return true;
         }
 
         RootedValue v(cx);
         if (handleUnaliasedAccess(cx, debugScope, scope, id, GET, &v)) {
             desc.object().set(debugScope);
             desc.setAttributes(JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT);
             desc.value().set(v);
-            desc.setShortId(0);
             desc.setGetter(nullptr);
             desc.setSetter(nullptr);
             return true;
         }
 
         return JS_GetOwnPropertyDescriptorById(cx, scope, id, flags, desc);
     }
 
--- a/js/src/vm/ScopeObject.h
+++ b/js/src/vm/ScopeObject.h
@@ -419,16 +419,26 @@ class BlockObject : public NestedScopeOb
         JS_ASSERT(slot < RESERVED_SLOTS + slotCount());
         return bindings.numVars() + stackDepth() + (slot - RESERVED_SLOTS);
     }
 
     uint32_t localIndexToSlot(const Bindings &bindings, uint32_t i) {
         return RESERVED_SLOTS + (i - (bindings.numVars() + stackDepth()));
     }
 
+    /*
+     * Return the index (in the range [0, slotCount()) corresponding to the
+     * given shape of a block object.
+     */
+    uint32_t shapeToIndex(const Shape &shape) {
+        uint32_t slot = shape.slot();
+        JS_ASSERT(slot - RESERVED_SLOTS < slotCount());
+        return slot - RESERVED_SLOTS;
+    }
+
   protected:
     /* Blocks contain an object slot for each slot i: 0 <= i < slotCount. */
     const Value &slotValue(unsigned i) {
         return getSlotRef(RESERVED_SLOTS + i);
     }
 
     void setSlotValue(unsigned i, const Value &v) {
         setSlot(RESERVED_SLOTS + i, v);
@@ -485,20 +495,19 @@ class StaticBlockObject : public BlockOb
      * Frontend compilation temporarily uses the object's slots to link
      * a let var to its associated Definition parse node.
      */
     void setDefinitionParseNode(unsigned i, frontend::Definition *def) {
         JS_ASSERT(slotValue(i).isUndefined());
         setSlotValue(i, PrivateValue(def));
     }
 
-    frontend::Definition *maybeDefinitionParseNode(unsigned i) {
+    frontend::Definition *definitionParseNode(unsigned i) {
         Value v = slotValue(i);
-        return v.isUndefined() ? nullptr
-                               : reinterpret_cast<frontend::Definition *>(v.toPrivate());
+        return reinterpret_cast<frontend::Definition *>(v.toPrivate());
     }
 
     /*
      * While ScopeCoordinate can generally reference up to 2^24 slots, block objects have an
      * additional limitation that all slot indices must be storable as uint16_t short-ids in the
      * associated Shape. If we could remove the block dependencies on shape->shortid, we could
      * remove INDEX_LIMIT.
      */
--- a/js/src/vm/Shape-inl.h
+++ b/js/src/vm/Shape-inl.h
@@ -30,55 +30,28 @@ StackBaseShape::StackBaseShape(ThreadSaf
     parent(parent),
     metadata(metadata),
     rawGetter(nullptr),
     rawSetter(nullptr),
     compartment(cx->compartment_)
 {}
 
 inline bool
-Shape::getUserId(JSContext *cx, MutableHandleId idp) const
-{
-    const Shape *self = this;
-#ifdef DEBUG
-    {
-        SkipRoot skip(cx, &self);
-        MaybeCheckStackRoots(cx);
-    }
-#endif
-    if (self->hasShortID()) {
-        int16_t id = self->shortid();
-        if (id < 0) {
-            RootedValue v(cx, Int32Value(id));
-            return ValueToId<CanGC>(cx, v, idp);
-        }
-        idp.set(INT_TO_JSID(id));
-    } else {
-        idp.set(self->propid());
-    }
-    return true;
-}
-
-inline bool
 Shape::get(JSContext* cx, HandleObject receiver, JSObject* obj, JSObject *pobj,
            MutableHandleValue vp)
 {
     JS_ASSERT(!hasDefaultGetter());
 
     if (hasGetterValue()) {
         Value fval = getterValue();
         return InvokeGetterOrSetter(cx, receiver, fval, 0, 0, vp);
     }
 
-    Rooted<Shape *> self(cx, this);
-    RootedId id(cx);
-    if (!self->getUserId(cx, &id))
-        return false;
-
-    return CallJSPropertyOp(cx, self->getterOp(), receiver, id, vp);
+    RootedId id(cx, propid());
+    return CallJSPropertyOp(cx, getterOp(), receiver, id, vp);
 }
 
 inline Shape *
 Shape::search(ExclusiveContext *cx, jsid id)
 {
     Shape **_;
     return search(cx, this, id, &_);
 }
@@ -119,31 +92,28 @@ Shape::set(JSContext* cx, HandleObject o
     if (attrs & JSPROP_SETTER) {
         Value fval = setterValue();
         return InvokeGetterOrSetter(cx, receiver, fval, 1, vp.address(), vp);
     }
 
     if (attrs & JSPROP_GETTER)
         return js_ReportGetterOnlyAssignment(cx, strict);
 
-    Rooted<Shape *> self(cx, this);
-    RootedId id(cx);
-    if (!self->getUserId(cx, &id))
-        return false;
+    RootedId id(cx, propid());
 
     /*
      * |with (it) color='red';| ends up here.
      * Avoid exposing the With object to native setters.
      */
     if (obj->is<DynamicWithObject>()) {
         RootedObject nobj(cx, &obj->as<DynamicWithObject>().object());
-        return CallJSPropertyOpSetter(cx, self->setterOp(), nobj, id, strict, vp);
+        return CallJSPropertyOpSetter(cx, setterOp(), nobj, id, strict, vp);
     }
 
-    return CallJSPropertyOpSetter(cx, self->setterOp(), obj, id, strict, vp);
+    return CallJSPropertyOpSetter(cx, setterOp(), obj, id, strict, vp);
 }
 
 /* static */ inline Shape *
 Shape::search(ExclusiveContext *cx, Shape *start, jsid id, Shape ***pspp, bool adding)
 {
     if (start->inDictionary()) {
         *pspp = start->table().search(id, adding);
         return SHAPE_FETCH(*pspp);
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -513,17 +513,17 @@ NormalizeGetterAndSetter(JSObject *obj,
 
     return true;
 }
 
 /* static */ Shape *
 JSObject::addProperty(ExclusiveContext *cx, HandleObject obj, HandleId id,
                       PropertyOp getter, StrictPropertyOp setter,
                       uint32_t slot, unsigned attrs,
-                      unsigned flags, int shortid, bool allowDictionary)
+                      unsigned flags, bool allowDictionary)
 {
     JS_ASSERT(!JSID_IS_VOID(id));
 
     bool extensible;
     if (!JSObject::isExtensible(cx, obj, &extensible))
         return nullptr;
     if (!extensible) {
         if (cx->isJSContext())
@@ -533,17 +533,17 @@ JSObject::addProperty(ExclusiveContext *
 
     NormalizeGetterAndSetter(obj, id, attrs, flags, getter, setter);
 
     Shape **spp = nullptr;
     if (obj->inDictionaryMode())
         spp = obj->lastProperty()->table().search(id, true);
 
     return addPropertyInternal<SequentialExecution>(cx, obj, id, getter, setter, slot, attrs,
-                                                    flags, shortid, spp, allowDictionary);
+                                                    flags, spp, allowDictionary);
 }
 
 static bool
 ShouldConvertToDictionary(JSObject *obj)
 {
     /*
      * Use a lower limit if this object is likely a hashmap (SETELEM was used
      * to set properties).
@@ -564,17 +564,17 @@ GetOrLookupUnownedBaseShape(typename Exe
 }
 
 template <ExecutionMode mode>
 /* static */ Shape *
 JSObject::addPropertyInternal(typename ExecutionModeTraits<mode>::ExclusiveContextType cx,
                               HandleObject obj, HandleId id,
                               PropertyOp getter, StrictPropertyOp setter,
                               uint32_t slot, unsigned attrs,
-                              unsigned flags, int shortid, Shape **spp,
+                              unsigned flags, Shape **spp,
                               bool allowDictionary)
 {
     JS_ASSERT(cx->isThreadLocal(obj));
     JS_ASSERT_IF(!allowDictionary, !obj->inDictionaryMode());
 
     AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
 
     /*
@@ -625,17 +625,17 @@ JSObject::addPropertyInternal(typename E
             base.updateGetterSetter(attrs, getter, setter);
             if (indexed)
                 base.flags |= BaseShape::INDEXED;
             nbase = GetOrLookupUnownedBaseShape<mode>(cx, base);
             if (!nbase)
                 return nullptr;
         }
 
-        StackShape child(nbase, id, slot, attrs, flags, shortid);
+        StackShape child(nbase, id, slot, attrs, flags);
         shape = getOrLookupChildProperty<mode>(cx, obj, last, child);
     }
 
     if (shape) {
         JS_ASSERT(shape == obj->lastProperty());
 
         if (table) {
             /* Store the tree node pointer in the table entry for id. */
@@ -655,24 +655,24 @@ JSObject::addPropertyInternal(typename E
     return nullptr;
 }
 
 template /* static */ Shape *
 JSObject::addPropertyInternal<SequentialExecution>(ExclusiveContext *cx,
                                                    HandleObject obj, HandleId id,
                                                    PropertyOp getter, StrictPropertyOp setter,
                                                    uint32_t slot, unsigned attrs,
-                                                   unsigned flags, int shortid, Shape **spp,
+                                                   unsigned flags, Shape **spp,
                                                    bool allowDictionary);
 template /* static */ Shape *
 JSObject::addPropertyInternal<ParallelExecution>(ForkJoinContext *cx,
                                                  HandleObject obj, HandleId id,
                                                  PropertyOp getter, StrictPropertyOp setter,
                                                  uint32_t slot, unsigned attrs,
-                                                 unsigned flags, int shortid, Shape **spp,
+                                                 unsigned flags, Shape **spp,
                                                  bool allowDictionary);
 
 JSObject *
 js::NewReshapedObject(JSContext *cx, HandleTypeObject type, JSObject *parent,
                       gc::AllocKind allocKind, HandleShape shape, NewObjectKind newKind)
 {
     RootedObject res(cx, NewObjectWithType(cx, type, parent, allocKind, newKind));
     if (!res)
@@ -709,17 +709,17 @@ js::NewReshapedObject(JSContext *cx, Han
         if (indexed) {
             StackBaseShape base(nbase);
             base.flags |= BaseShape::INDEXED;
             nbase = GetOrLookupUnownedBaseShape<SequentialExecution>(cx, base);
             if (!nbase)
                 return nullptr;
         }
 
-        StackShape child(nbase, id, i, JSPROP_ENUMERATE, 0, 0);
+        StackShape child(nbase, id, i, JSPROP_ENUMERATE, 0);
         newShape = cx->compartment()->propertyTree.getChild(cx, newShape, child);
         if (!newShape)
             return nullptr;
         if (!JSObject::setLastProperty(cx, res, newShape))
             return nullptr;
     }
 
     return res;
@@ -751,18 +751,17 @@ CheckCanChangeAttrs(ThreadSafeContext *c
     return true;
 }
 
 template <ExecutionMode mode>
 /* static */ Shape *
 JSObject::putProperty(typename ExecutionModeTraits<mode>::ExclusiveContextType cx,
                       HandleObject obj, HandleId id,
                       PropertyOp getter, StrictPropertyOp setter,
-                      uint32_t slot, unsigned attrs,
-                      unsigned flags, int shortid)
+                      uint32_t slot, unsigned attrs, unsigned flags)
 {
     JS_ASSERT(cx->isThreadLocal(obj));
     JS_ASSERT(!JSID_IS_VOID(id));
 
 #ifdef DEBUG
     if (obj->is<ArrayObject>()) {
         ArrayObject *arr = &obj->as<ArrayObject>();
         uint32_t index;
@@ -809,17 +808,17 @@ JSObject::putProperty(typename Execution
 
         if (!extensible) {
             if (cx->isJSContext())
                 obj->reportNotExtensible(cx->asJSContext());
             return nullptr;
         }
 
         return addPropertyInternal<mode>(cx, obj, id, getter, setter, slot, attrs, flags,
-                                         shortid, spp, true);
+                                         spp, true);
     }
 
     /* Property exists: search must have returned a valid *spp. */
     JS_ASSERT_IF(spp, !SHAPE_IS_REMOVED(*spp));
 
     if (!CheckCanChangeAttrs(cx, obj, shape, &attrs))
         return nullptr;
 
@@ -845,17 +844,17 @@ JSObject::putProperty(typename Execution
         if (!nbase)
             return nullptr;
     }
 
     /*
      * Now that we've possibly preserved slot, check whether all members match.
      * If so, this is a redundant "put" and we can return without more work.
      */
-    if (shape->matchesParamsAfterId(nbase, slot, attrs, flags, shortid))
+    if (shape->matchesParamsAfterId(nbase, slot, attrs, flags))
         return shape;
 
     /*
      * Overwriting a non-last property requires switching to dictionary mode.
      * The shape tree is shared immutable, and we can't removeProperty and then
      * addPropertyInternal because a failure under add would lose data.
      */
     if (shape != obj->lastProperty() && !obj->inDictionaryMode()) {
@@ -890,33 +889,32 @@ JSObject::putProperty(typename Execution
         if (updateLast)
             shape->base()->adoptUnowned(nbase);
         else
             shape->base_ = nbase;
 
         shape->setSlot(slot);
         shape->attrs = uint8_t(attrs);
         shape->flags = flags | Shape::IN_DICTIONARY;
-        shape->shortid_ = int16_t(shortid);
     } else {
         /*
          * Updating the last property in a non-dictionary-mode object. Find an
          * alternate shared child of the last property's previous shape.
          */
         StackBaseShape base(obj->lastProperty()->base());
         base.updateGetterSetter(attrs, getter, setter);
 
         UnownedBaseShape *nbase = GetOrLookupUnownedBaseShape<mode>(cx, base);
         if (!nbase)
             return nullptr;
 
         JS_ASSERT(shape == obj->lastProperty());
 
         /* Find or create a property tree node labeled by our arguments. */
-        StackShape child(nbase, id, slot, attrs, flags, shortid);
+        StackShape child(nbase, id, slot, attrs, flags);
         RootedShape parent(cx, shape->parent);
         Shape *newShape = getOrLookupChildProperty<mode>(cx, obj, parent, child);
 
         if (!newShape) {
             obj->checkShapeConsistency();
             return nullptr;
         }
 
@@ -942,23 +940,23 @@ JSObject::putProperty(typename Execution
     return shape;
 }
 
 template /* static */ Shape *
 JSObject::putProperty<SequentialExecution>(ExclusiveContext *cx,
                                            HandleObject obj, HandleId id,
                                            PropertyOp getter, StrictPropertyOp setter,
                                            uint32_t slot, unsigned attrs,
-                                           unsigned flags, int shortid);
+                                           unsigned flags);
 template /* static */ Shape *
 JSObject::putProperty<ParallelExecution>(ForkJoinContext *cx,
                                          HandleObject obj, HandleId id,
                                          PropertyOp getter, StrictPropertyOp setter,
                                          uint32_t slot, unsigned attrs,
-                                         unsigned flags, int shortid);
+                                         unsigned flags);
 
 template <ExecutionMode mode>
 /* static */ Shape *
 JSObject::changeProperty(typename ExecutionModeTraits<mode>::ExclusiveContextType cx,
                          HandleObject obj, HandleShape shape, unsigned attrs,
                          unsigned mask, PropertyOp getter, StrictPropertyOp setter)
 {
     JS_ASSERT(cx->isThreadLocal(obj));
@@ -991,18 +989,17 @@ JSObject::changeProperty(typename Execut
     /*
      * Let JSObject::putProperty handle this |overwriting| case, including
      * the conservation of shape->slot (if it's valid). We must not call
      * removeProperty because it will free an allocated shape->slot, and
      * putProperty won't re-allocate it.
      */
     RootedId propid(cx, shape->propid());
     Shape *newShape = putProperty<mode>(cx, obj, propid, getter, setter,
-                                        shape->maybeSlot(), attrs, shape->flags,
-                                        shape->maybeShortid());
+                                        shape->maybeSlot(), attrs, shape->flags);
 
     obj->checkShapeConsistency();
     return newShape;
 }
 
 template /* static */ Shape *
 JSObject::changeProperty<SequentialExecution>(ExclusiveContext *cx,
                                               HandleObject obj, HandleShape shape,
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -915,17 +915,16 @@ class Shape : public gc::BarrieredCell<S
          * other shapes.
          */
         SLOT_MASK              = JS_BIT(24) - 1
     } JS_ENUM_FOOTER(SlotInfo);
 
     uint32_t            slotInfo;       /* mask of above info */
     uint8_t             attrs;          /* attributes, see jsapi.h JSPROP_* */
     uint8_t             flags;          /* flags, see below for defines */
-    int16_t             shortid_;       /* tinyid, or local arg/var index */
 
     HeapPtrShape        parent;        /* parent node, reverse for..in order */
     /* kids is valid when !inDictionary(), listp is valid when inDictionary(). */
     union {
         KidsPointer kids;       /* null, single child, or a tagged ptr
                                    to many-kids data structure */
         HeapPtrShape *listp;    /* dictionary list starting at shape_
                                    has a double-indirect back pointer,
@@ -1078,27 +1077,19 @@ class Shape : public gc::BarrieredCell<S
      * Whether this shape has a valid slot value. This may be true even if
      * !hasSlot() (see SlotInfo comment above), and may be false even if
      * hasSlot() if the shape is being constructed and has not had a slot
      * assigned yet. After construction, hasSlot() implies !hasMissingSlot().
      */
     bool hasMissingSlot() const { return maybeSlot() == SHAPE_INVALID_SLOT; }
 
   public:
-    /* Public bits stored in shape->flags. */
-    enum {
-        HAS_SHORTID     = 0x40,
-        PUBLIC_FLAGS    = HAS_SHORTID
-    };
-
     bool inDictionary() const {
         return (flags & IN_DICTIONARY) != 0;
     }
-    unsigned getFlags() const { return flags & PUBLIC_FLAGS; }
-    bool hasShortID() const { return (flags & HAS_SHORTID) != 0; }
 
     PropertyOp getter() const { return base()->rawGetter; }
     bool hasDefaultGetter() const {return !base()->rawGetter; }
     PropertyOp getterOp() const { JS_ASSERT(!hasGetterValue()); return base()->rawGetter; }
     JSObject *getterObject() const { JS_ASSERT(hasGetterValue()); return base()->getterObj; }
 
     // Per ES5, decode null getterObj as the undefined value, which encodes as null.
     Value getterValue() const {
@@ -1128,30 +1119,26 @@ class Shape : public gc::BarrieredCell<S
                ? ObjectValue(*base()->setterObj)
                : UndefinedValue();
     }
 
     void update(PropertyOp getter, StrictPropertyOp setter, uint8_t attrs);
 
     bool matches(const Shape *other) const {
         return propid_.get() == other->propid_.get() &&
-               matchesParamsAfterId(other->base(), other->maybeSlot(), other->attrs, other->flags,
-                                    other->shortid_);
+               matchesParamsAfterId(other->base(), other->maybeSlot(), other->attrs, other->flags);
     }
 
     inline bool matches(const StackShape &other) const;
 
-    bool matchesParamsAfterId(BaseShape *base, uint32_t aslot, unsigned aattrs, unsigned aflags,
-                              int ashortid) const
+    bool matchesParamsAfterId(BaseShape *base, uint32_t aslot, unsigned aattrs, unsigned aflags) const
     {
         return base->unowned() == this->base()->unowned() &&
                maybeSlot() == aslot &&
-               attrs == aattrs &&
-               ((flags ^ aflags) & PUBLIC_FLAGS) == 0 &&
-               shortid_ == ashortid;
+               attrs == aattrs;
     }
 
     bool get(JSContext* cx, HandleObject receiver, JSObject *obj, JSObject *pobj, MutableHandleValue vp);
     bool set(JSContext* cx, HandleObject obj, HandleObject receiver, bool strict, MutableHandleValue vp);
 
     BaseShape *base() const { return base_.get(); }
 
     bool hasSlot() const {
@@ -1210,25 +1197,16 @@ class Shape : public gc::BarrieredCell<S
         return propid_;
     }
     EncapsulatedId &propidRef() { JS_ASSERT(!JSID_IS_VOID(propid_)); return propid_; }
     jsid propidRaw() const {
         // Return the actual jsid, not an internal reference.
         return propid();
     }
 
-    int16_t shortid() const { JS_ASSERT(hasShortID()); return maybeShortid(); }
-    int16_t maybeShortid() const { return shortid_; }
-
-    /*
-     * If SHORTID is set in shape->flags, we use shape->shortid rather
-     * than id when calling shape's getter or setter.
-     */
-    inline bool getUserId(JSContext *cx, MutableHandleId idp) const;
-
     uint8_t attributes() const { return attrs; }
     bool configurable() const { return (attrs & JSPROP_PERMANENT) == 0; }
     bool enumerable() const { return (attrs & JSPROP_ENUMERATE) != 0; }
     bool writable() const {
         return (attrs & JSPROP_READONLY) == 0;
     }
     bool hasGetterValue() const { return attrs & JSPROP_GETTER; }
     bool hasSetterValue() const { return attrs & JSPROP_SETTER; }
@@ -1469,39 +1447,36 @@ typedef HashSet<InitialShapeEntry, Initi
 struct StackShape
 {
     /* For performance, StackShape only roots when absolutely necessary. */
     UnownedBaseShape *base;
     jsid             propid;
     uint32_t         slot_;
     uint8_t          attrs;
     uint8_t          flags;
-    int16_t          shortid;
 
     explicit StackShape(UnownedBaseShape *base, jsid propid, uint32_t slot,
-                        unsigned attrs, unsigned flags, int shortid)
+                        unsigned attrs, unsigned flags)
       : base(base),
         propid(propid),
         slot_(slot),
         attrs(uint8_t(attrs)),
-        flags(uint8_t(flags)),
-        shortid(int16_t(shortid))
+        flags(uint8_t(flags))
     {
         JS_ASSERT(base);
         JS_ASSERT(!JSID_IS_VOID(propid));
         JS_ASSERT(slot <= SHAPE_INVALID_SLOT);
     }
 
     StackShape(Shape *shape)
       : base(shape->base()->unowned()),
         propid(shape->propidRef()),
         slot_(shape->maybeSlot()),
         attrs(shape->attrs),
-        flags(shape->flags),
-        shortid(shape->shortid_)
+        flags(shape->flags)
     {}
 
     bool hasSlot() const { return (attrs & JSPROP_SHARED) == 0; }
     bool hasMissingSlot() const { return maybeSlot() == SHAPE_INVALID_SLOT; }
 
     uint32_t slot() const { JS_ASSERT(hasSlot() && !hasMissingSlot()); return slot_; }
     uint32_t maybeSlot() const { return slot_; }
 
@@ -1514,19 +1489,17 @@ struct StackShape
         JS_ASSERT(slot <= SHAPE_INVALID_SLOT);
         slot_ = slot;
     }
 
     HashNumber hash() const {
         HashNumber hash = uintptr_t(base);
 
         /* Accumulate from least to most random so the low bits are most random. */
-        hash = mozilla::RotateLeft(hash, 4) ^ (flags & Shape::PUBLIC_FLAGS);
         hash = mozilla::RotateLeft(hash, 4) ^ attrs;
-        hash = mozilla::RotateLeft(hash, 4) ^ shortid;
         hash = mozilla::RotateLeft(hash, 4) ^ slot_;
         hash = mozilla::RotateLeft(hash, 4) ^ JSID_BITS(propid);
         return hash;
     }
 
     // For RootedGeneric<StackShape*>
     static inline js::ThingRootKind rootKind() { return js::THING_ROOT_CUSTOM; }
     void trace(JSTracer *trc);
@@ -1593,30 +1566,28 @@ namespace js {
 
 inline
 Shape::Shape(const StackShape &other, uint32_t nfixed)
   : base_(other.base),
     propid_(other.propid),
     slotInfo(other.maybeSlot() | (nfixed << FIXED_SLOTS_SHIFT)),
     attrs(other.attrs),
     flags(other.flags),
-    shortid_(other.shortid),
     parent(nullptr)
 {
     kids.setNull();
 }
 
 inline
 Shape::Shape(UnownedBaseShape *base, uint32_t nfixed)
   : base_(base),
     propid_(JSID_EMPTY),
     slotInfo(SHAPE_INVALID_SLOT | (nfixed << FIXED_SLOTS_SHIFT)),
     attrs(JSPROP_SHARED),
     flags(0),
-    shortid_(0),
     parent(nullptr)
 {
     JS_ASSERT(base);
     kids.setNull();
 }
 
 inline Shape *
 Shape::searchLinear(jsid id)
@@ -1656,17 +1627,17 @@ Shape::searchNoHashify(Shape *start, jsi
 
     return start->searchLinear(id);
 }
 
 inline bool
 Shape::matches(const StackShape &other) const
 {
     return propid_.get() == other.propid &&
-           matchesParamsAfterId(other.base, other.slot_, other.attrs, other.flags, other.shortid);
+           matchesParamsAfterId(other.base, other.slot_, other.attrs, other.flags);
 }
 
 template<> struct RootKind<Shape *> : SpecificRootKind<Shape *, THING_ROOT_SHAPE> {};
 template<> struct RootKind<BaseShape *> : SpecificRootKind<BaseShape *, THING_ROOT_BASE_SHAPE> {};
 
 // Property lookup hooks on objects are required to return a non-nullptr shape
 // to signify that the property has been found. For cases where the property is
 // not actually represented by a Shape, use a dummy value. This includes all
new file mode 100644
--- /dev/null
+++ b/js/src/vm/WeakMapPtr.cpp
@@ -0,0 +1,114 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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 "js/WeakMapPtr.h"
+
+#include "jsweakmap.h"
+
+//
+// Machinery for the externally-linkable JS::WeakMapPtr, which wraps js::WeakMap
+// for a few public data types.
+//
+
+using namespace js;
+
+namespace {
+
+template<typename T>
+struct DataType
+{
+};
+
+template<>
+struct DataType<JSObject*>
+{
+    typedef EncapsulatedPtrObject Encapsulated;
+    static JSObject *NullValue() { return nullptr; }
+};
+
+template<>
+struct DataType<JS::Value>
+{
+    typedef EncapsulatedValue Encapsulated;
+    static JS::Value NullValue() { return JS::UndefinedValue(); }
+};
+
+template <typename K, typename V>
+struct Utils
+{
+    typedef typename DataType<K>::Encapsulated KeyType;
+    typedef typename DataType<V>::Encapsulated ValueType;
+    typedef WeakMap<KeyType, ValueType> Type;
+    typedef Type* PtrType;
+    static PtrType cast(void *ptr) { return static_cast<PtrType>(ptr); }
+};
+
+} /* namespace */
+
+template <typename K, typename V>
+void
+JS::WeakMapPtr<K, V>::destroy()
+{
+    MOZ_ASSERT(initialized());
+    auto map = Utils<K, V>::cast(ptr);
+    // If this destruction happens mid-GC, we might be in the compartment's list
+    // of known live weakmaps. If we are, remove ourselves before deleting.
+    if (map->isInList())
+        WeakMapBase::removeWeakMapFromList(map);
+    map->check();
+    js_delete(map);
+    ptr = nullptr;
+}
+
+template <typename K, typename V>
+bool
+JS::WeakMapPtr<K, V>::init(JSContext *cx)
+{
+    MOZ_ASSERT(!initialized());
+    typename Utils<K, V>::PtrType map = cx->runtime()->new_<typename Utils<K,V>::Type>(cx);
+    if (!map || !map->init())
+        return false;
+    ptr = map;
+    return true;
+}
+
+template <typename K, typename V>
+void
+JS::WeakMapPtr<K, V>::trace(JSTracer *trc)
+{
+    MOZ_ASSERT(initialized());
+    return Utils<K, V>::cast(ptr)->trace(trc);
+}
+
+template <typename K, typename V>
+V
+JS::WeakMapPtr<K, V>::lookup(const K &key)
+{
+    MOZ_ASSERT(initialized());
+    typename Utils<K, V>::Type::Ptr result = Utils<K, V>::cast(ptr)->lookup(key);
+    if (!result)
+        return DataType<V>::NullValue();
+    return result->value();
+}
+
+template <typename K, typename V>
+bool
+JS::WeakMapPtr<K, V>::put(const K &key, const V &value)
+{
+    MOZ_ASSERT(initialized());
+    return Utils<K, V>::cast(ptr)->put(key, value);
+}
+
+//
+// Supported specializations of JS::WeakMap:
+//
+
+template class JS::WeakMapPtr<JSObject*, JSObject*>;
+
+#ifdef DEBUG
+// Nobody's using this at the moment, but we want to make sure it compiles.
+template class JS::WeakMapPtr<JSObject*, JS::Value>;
+#endif
--- a/js/xpconnect/src/XPCWrappedNative.cpp
+++ b/js/xpconnect/src/XPCWrappedNative.cpp
@@ -854,20 +854,16 @@ XPCWrappedNative::Init(HandleObject pare
     return FinishInit();
 }
 
 bool
 XPCWrappedNative::FinishInit()
 {
     AutoJSContext cx;
 
-    // For all WNs, we want to make sure that the expando chain slot starts out
-    // as null.
-    JS_SetReservedSlot(mFlatJSObject, WN_XRAYEXPANDOCHAIN_SLOT, JSVAL_NULL);
-
     // This reference will be released when mFlatJSObject is finalized.
     // Since this reference will push the refcount to 2 it will also root
     // mFlatJSObject;
     MOZ_ASSERT(1 == mRefCnt, "unexpected refcount value");
     NS_ADDREF(this);
 
     if (mScriptableInfo && mScriptableInfo->GetFlags().WantCreate() &&
         NS_FAILED(mScriptableInfo->GetCallback()->Create(this, cx,
@@ -1181,17 +1177,16 @@ XPCWrappedNative::ReparentWrapperIfFound
                                                         aNewParent);
             if (!propertyHolder)
                 return NS_ERROR_OUT_OF_MEMORY;
             if (!JS_CopyPropertiesFrom(cx, propertyHolder, flat))
                 return NS_ERROR_FAILURE;
 
             // Expandos from other compartments are attached to the target JS object.
             // Copy them over, and let the old ones die a natural death.
-            SetWNExpandoChain(newobj, nullptr);
             if (!XrayUtils::CloneExpandoChain(cx, newobj, flat))
                 return NS_ERROR_FAILURE;
 
             // We've set up |newobj|, so we make it own the WN by nulling out
             // the private of |flat|.
             //
             // NB: It's important to do this _after_ copying the properties to
             // propertyHolder. Otherwise, an object with |foo.x === foo| will
--- a/js/xpconnect/src/XPCWrappedNativeScope.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeScope.cpp
@@ -297,16 +297,19 @@ XPCWrappedNativeScope::~XPCWrappedNative
     // with the scope but just in case.
     if (mComponents)
         mComponents->mScope = nullptr;
 
     // XXX we should assert that we are dead or that xpconnect has shutdown
     // XXX might not want to do this at xpconnect shutdown time???
     mComponents = nullptr;
 
+    if (mXrayExpandos.initialized())
+        mXrayExpandos.destroy();
+
     JSRuntime *rt = XPCJSRuntime::Get()->Runtime();
     mXBLScope.finalize(rt);
     mGlobalJSObject.finalize(rt);
 }
 
 static PLDHashOperator
 WrappedNativeJSGCThingTracer(PLDHashTable *table, PLDHashEntryHdr *hdr,
                              uint32_t number, void *arg)
@@ -594,16 +597,37 @@ WNProtoRemover(PLDHashTable *table, PLDH
 
 void
 XPCWrappedNativeScope::RemoveWrappedNativeProtos()
 {
     mWrappedNativeProtoMap->Enumerate(WNProtoRemover,
                                       GetRuntime()->GetDetachedWrappedNativeProtoMap());
 }
 
+JSObject *
+XPCWrappedNativeScope::GetExpandoChain(JSObject *target)
+{
+    MOZ_ASSERT(GetObjectScope(target) == this);
+    if (!mXrayExpandos.initialized())
+        return nullptr;
+    return mXrayExpandos.lookup(target);
+}
+
+bool
+XPCWrappedNativeScope::SetExpandoChain(JSContext *cx, HandleObject target,
+                                       HandleObject chain)
+{
+    MOZ_ASSERT(GetObjectScope(target) == this);
+    MOZ_ASSERT(js::IsObjectInContextCompartment(target, cx));
+    MOZ_ASSERT_IF(chain, GetObjectScope(chain) == this);
+    if (!mXrayExpandos.initialized() && !mXrayExpandos.init(cx))
+        return false;
+    return mXrayExpandos.put(target, chain);
+}
+
 /***************************************************************************/
 
 // static
 void
 XPCWrappedNativeScope::DebugDumpAllScopes(int16_t depth)
 {
 #ifdef DEBUG
     depth-- ;
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -85,16 +85,17 @@
 
 #include <math.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "xpcpublic.h"
 #include "js/Tracer.h"
+#include "js/WeakMapPtr.h"
 #include "pldhash.h"
 #include "nscore.h"
 #include "nsXPCOM.h"
 #include "nsAutoPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsDebug.h"
 #include "nsISupports.h"
 #include "nsIServiceManager.h"
@@ -207,51 +208,32 @@ extern const char XPC_XPCONNECT_CONTRACT
         result = (char*) nsMemory::Clone(src,                                 \
                                          sizeof(char)*(strlen(src)+1));       \
     else                                                                      \
         result = nullptr;                                                      \
     *dest = result;                                                           \
     return (result || !src) ? NS_OK : NS_ERROR_OUT_OF_MEMORY
 
 
-#define WRAPPER_SLOTS (JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | \
-                       JSCLASS_HAS_RESERVED_SLOTS(1))
+#define WRAPPER_SLOTS (JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS )
 
 #define INVALID_OBJECT ((JSObject *)1)
 
-// NB: This slot isn't actually reserved for us on globals, because SpiderMonkey
-// uses the first N slots on globals internally. The fact that we use it for
-// wrapped global objects is totally broken. But due to a happy coincidence, the
-// JS engine never uses that slot. This still needs fixing though. See bug 760095.
-#define WN_XRAYEXPANDOCHAIN_SLOT 0
-
 // If IS_WN_CLASS for the JSClass of an object is true, the object is a
 // wrappednative wrapper, holding the XPCWrappedNative in its private slot.
 static inline bool IS_WN_CLASS(const js::Class* clazz)
 {
     return clazz->ext.isWrappedNative;
 }
 
 static inline bool IS_WN_REFLECTOR(JSObject *obj)
 {
     return IS_WN_CLASS(js::GetObjectClass(obj));
 }
 
-inline void SetWNExpandoChain(JSObject *obj, JSObject *chain)
-{
-    MOZ_ASSERT(IS_WN_REFLECTOR(obj));
-    JS_SetReservedSlot(obj, WN_XRAYEXPANDOCHAIN_SLOT, JS::ObjectOrNullValue(chain));
-}
-
-inline JSObject* GetWNExpandoChain(JSObject *obj)
-{
-    MOZ_ASSERT(IS_WN_REFLECTOR(obj));
-    return JS_GetReservedSlot(obj, WN_XRAYEXPANDOCHAIN_SLOT).toObjectOrNull();
-}
-
 /***************************************************************************
 ****************************************************************************
 *
 * Core runtime and context classes...
 *
 ****************************************************************************
 ***************************************************************************/
 
@@ -1032,29 +1014,37 @@ public:
     GetGlobalJSObjectPreserveColor() const {return mGlobalJSObject;}
 
     nsIPrincipal*
     GetPrincipal() const {
         JSCompartment *c = js::GetObjectCompartment(mGlobalJSObject);
         return nsJSPrincipals::get(JS_GetCompartmentPrincipals(c));
     }
 
+    JSObject*
+    GetExpandoChain(JSObject *target);
+
+    bool
+    SetExpandoChain(JSContext *cx, JS::HandleObject target, JS::HandleObject chain);
+
     void RemoveWrappedNativeProtos();
 
     static void
     SystemIsBeingShutDown();
 
     static void
     TraceWrappedNativesInAllScopes(JSTracer* trc, XPCJSRuntime* rt);
 
     void TraceSelf(JSTracer *trc) {
         MOZ_ASSERT(mGlobalJSObject);
         mGlobalJSObject.trace(trc, "XPCWrappedNativeScope::mGlobalJSObject");
         if (mXBLScope)
             mXBLScope.trace(trc, "XPCWrappedNativeScope::mXBLScope");
+        if (mXrayExpandos.initialized())
+            mXrayExpandos.trace(trc);
     }
 
     static void
     SuspectAllWrappers(XPCJSRuntime* rt, nsCycleCollectionNoteRootCallback &cb);
 
     static void
     StartFinalizationPhaseOfGC(JSFreeOp *fop, XPCJSRuntime* rt);
 
@@ -1164,16 +1154,18 @@ private:
     // EnsureXBLScope() decides whether it needs to be created or not.
     // This reference is wrapped into the compartment of mGlobalJSObject.
     JS::ObjectPtr                    mXBLScope;
 
     XPCContext*                      mContext;
 
     nsAutoPtr<DOMExpandoSet> mDOMExpandoSet;
 
+    JS::WeakMapPtr<JSObject*, JSObject*> mXrayExpandos;
+
     bool mIsXBLScope;
 
     // For remote XUL domains, we run all XBL in the content scope for compat
     // reasons (though we sometimes pref this off for automation). We separately
     // track the result of this decision (mAllowXBLScope), from the decision
     // of whether to actually _use_ an XBL scope (mUseXBLScope), which depends
     // on the type of global and whether the compartment is system principal
     // or not.
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -164,18 +164,23 @@ public:
                                HandleObject consumer);
     JSObject* ensureExpandoObject(JSContext *cx, HandleObject wrapper,
                                   HandleObject target);
 
     JSObject* getHolder(JSObject *wrapper);
     JSObject* ensureHolder(JSContext *cx, HandleObject wrapper);
     virtual JSObject* createHolder(JSContext *cx, JSObject *wrapper) = 0;
 
-    virtual JSObject* getExpandoChain(JSObject *obj) = 0;
-    virtual void setExpandoChain(JSObject *obj, JSObject *chain) = 0;
+    JSObject* getExpandoChain(JSObject *obj) {
+      return GetObjectScope(obj)->GetExpandoChain(obj);
+    }
+
+    bool setExpandoChain(JSContext *cx, HandleObject obj, HandleObject chain) {
+      return GetObjectScope(obj)->SetExpandoChain(cx, obj, chain);
+    }
     bool cloneExpandoChain(JSContext *cx, HandleObject dst, HandleObject src);
 
 private:
     bool expandoObjectMatchesConsumer(JSContext *cx, HandleObject expandoObject,
                                       nsIPrincipal *consumerOrigin,
                                       HandleObject exclusiveGlobal);
     JSObject* getExpandoObjectInternal(JSContext *cx, HandleObject target,
                                        nsIPrincipal *origin,
@@ -216,22 +221,16 @@ public:
         return XPCWrappedNative::Get(getTargetObject(wrapper));
     }
 
     virtual void preserveWrapper(JSObject *target);
 
     typedef ResolvingId ResolvingIdImpl;
 
     virtual JSObject* createHolder(JSContext *cx, JSObject *wrapper);
-    virtual JSObject* getExpandoChain(JSObject *obj) {
-        return GetWNExpandoChain(obj);
-    }
-    virtual void setExpandoChain(JSObject *obj, JSObject *chain) {
-        SetWNExpandoChain(obj, chain);
-    }
 
     static XPCWrappedNativeXrayTraits singleton;
 };
 
 class DOMXrayTraits : public XrayTraits
 {
 public:
     static const XrayType Type = XrayForDOMObject;
@@ -258,23 +257,16 @@ public:
     }
 
     typedef ResolvingIdDummy ResolvingIdImpl;
 
     virtual void preserveWrapper(JSObject *target);
 
     virtual JSObject* createHolder(JSContext *cx, JSObject *wrapper);
 
-    virtual JSObject* getExpandoChain(JSObject *obj) {
-        return mozilla::dom::GetXrayExpandoChain(obj);
-    }
-    virtual void setExpandoChain(JSObject *obj, JSObject *chain) {
-        mozilla::dom::SetXrayExpandoChain(obj, chain);
-    }
-
     static DOMXrayTraits singleton;
 };
 
 XPCWrappedNativeXrayTraits XPCWrappedNativeXrayTraits::singleton;
 DOMXrayTraits DOMXrayTraits::singleton;
 
 XrayTraits*
 GetXrayTraits(JSObject *obj)
@@ -435,17 +427,17 @@ XrayTraits::attachExpandoObject(JSContex
     // the wrapper. This keeps our expandos alive even if the Xray wrapper gets
     // collected.
     RootedObject chain(cx, getExpandoChain(target));
     if (!chain)
         preserveWrapper(target);
 
     // Insert it at the front of the chain.
     JS_SetReservedSlot(expandoObject, JSSLOT_EXPANDO_NEXT, OBJECT_TO_JSVAL(chain));
-    setExpandoChain(target, expandoObject);
+    setExpandoChain(cx, target, expandoObject);
 
     return expandoObject;
 }
 
 JSObject *
 XrayTraits::ensureExpandoObject(JSContext *cx, HandleObject wrapper,
                                 HandleObject target)
 {
@@ -760,17 +752,16 @@ XPCWrappedNativeXrayTraits::resolveNativ
         /* Not found */
         return resolveDOMCollectionProperty(cx, wrapper, holder, id, desc, flags);
     }
 
     desc.object().set(holder);
     desc.setAttributes(JSPROP_ENUMERATE);
     desc.setGetter(nullptr);
     desc.setSetter(nullptr);
-    desc.setShortId(0);
     desc.value().set(JSVAL_VOID);
 
     RootedValue fval(cx, JSVAL_VOID);
     if (member->IsConstant()) {
         if (!member->GetConstantValue(ccx, iface, desc.value().address())) {
             JS_ReportError(cx, "Failed to convert constant native property to JS value");
             return false;
         }
@@ -1456,17 +1447,16 @@ XrayWrapper<Base, Traits>::getPropertyDe
     // of the wrapper subsumes that of the wrappee.
     XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance();
     if (AccessCheck::wrapperSubsumes(wrapper) &&
         id == rt->GetStringID(XPCJSRuntime::IDX_WRAPPED_JSOBJECT)) {
         desc.object().set(wrapper);
         desc.setAttributes(JSPROP_ENUMERATE|JSPROP_SHARED);
         desc.setGetter(wrappedJSObject_getter);
         desc.setSetter(nullptr);
-        desc.setShortId(0);
         desc.value().set(JSVAL_VOID);
         return true;
     }
 
     // Ordering is important here.
     //
     // We first need to call resolveOwnProperty, even before checking the holder,
     // because there might be a new dynamic |own| property that appears and
@@ -1532,17 +1522,16 @@ XrayWrapper<Base, Traits>::getPropertyDe
         JSFunction *toString = JS_NewFunction(cx, XrayToString, 0, 0, holder, "toString");
         if (!toString)
             return false;
 
         desc.object().set(wrapper);
         desc.setAttributes(0);
         desc.setGetter(nullptr);
         desc.setSetter(nullptr);
-        desc.setShortId(0);
         desc.value().setObject(*JS_GetFunctionObject(toString));
     }
 
     // If we're a special scope for in-content XBL, our script expects to see
     // the bound XBL methods and attributes when accessing content. However,
     // these members are implemented in content via custom-spliced prototypes,
     // and thus aren't visible through Xray wrappers unless we handle them
     // explicitly. So we check if we're running in such a scope, and if so,
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -971,21 +971,18 @@ public:
   virtual void FireOrClearDelayedEvents(bool aFireEvents) = 0;
 
   /**
    * When this shell is disconnected from its containing docshell, we
    * lose our container pointer.  However, we'd still like to be able to target
    * user events at the docshell's parent.  This pointer allows us to do that.
    * It should not be used for any other purpose.
    */
-  void SetForwardingContainer(const mozilla::WeakPtr<nsDocShell> &aContainer)
-  {
-    mForwardingContainer = aContainer;
-  }
-  
+  void SetForwardingContainer(const mozilla::WeakPtr<nsDocShell> &aContainer);
+
   /**
    * Render the document into an arbitrary gfxContext
    * Designed for getting a picture of a document or a piece of a document
    * Note that callers will generally want to call FlushPendingNotifications
    * to get an up-to-date view of the document
    * @param aRect is the region to capture into the offscreen buffer, in the
    * root frame's coordinate system (if aIgnoreViewportScrolling is false)
    * or in the root scrolled frame's coordinate system
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -2859,16 +2859,22 @@ nsIPresShell::PostRecreateFramesFor(Elem
 void
 nsIPresShell::RestyleForAnimation(Element* aElement, nsRestyleHint aHint)
 {
   mPresContext->RestyleManager()->PostAnimationRestyleEvent(aElement, aHint,
                                                             NS_STYLE_HINT_NONE);
 }
 
 void
+nsIPresShell::SetForwardingContainer(const WeakPtr<nsDocShell> &aContainer)
+{
+  mForwardingContainer = aContainer;
+}
+
+void
 PresShell::ClearFrameRefs(nsIFrame* aFrame)
 {
   mPresContext->EventStateManager()->ClearFrameRefs(aFrame);
 
   nsWeakFrame* weakFrame = mWeakFrames;
   while (weakFrame) {
     nsWeakFrame* prev = weakFrame->GetPreviousWeakFrame();
     if (weakFrame->GetFrame() == aFrame) {
--- a/layout/ipc/RenderFrameParent.cpp
+++ b/layout/ipc/RenderFrameParent.cpp
@@ -1128,16 +1128,21 @@ RenderFrameParent::ZoomToRect(uint32_t a
                                      aRect);
   }
 }
 
 void
 RenderFrameParent::ContentReceivedTouch(const ScrollableLayerGuid& aGuid,
                                         bool aPreventDefault)
 {
+  if (aGuid.mLayersId != mLayersId) {
+    // Guard against bad data from hijacked child processes
+    NS_ERROR("Unexpected layers id in ContentReceivedTouch; dropping message...");
+    return;
+  }
   if (GetApzcTreeManager()) {
     GetApzcTreeManager()->ContentReceivedTouch(aGuid, aPreventDefault);
   }
 }
 
 void
 RenderFrameParent::UpdateZoomConstraints(uint32_t aPresShellId,
                                          ViewID aViewId,
new file mode 100644
--- /dev/null
+++ b/mfbt/RefCountType.h
@@ -0,0 +1,32 @@
+/* -*- 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/. */
+
+#ifndef mozilla_RefCountType_h
+#define mozilla_RefCountType_h
+
+/**
+ * MozRefCountType is Mozilla's reference count type.
+ *
+ * This is the return type for AddRef() and Release() in nsISupports.
+ * IUnknown of COM returns an unsigned long from equivalent functions.
+ *
+ * We use the same type to represent the refcount of RefCounted objects
+ * as well, in order to be able to use the leak detection facilities
+ * that are implemented by XPCOM.
+ *
+ * The following ifdef exists to maintain binary compatibility with
+ * IUnknown, the base interface in Microsoft COM.
+ *
+ * Note that this type is not in the mozilla namespace so that it is
+ * usable for both C and C++ code.
+ */
+#ifdef XP_WIN
+typedef unsigned long MozRefCountType;
+#else
+typedef uint32_t MozRefCountType;
+#endif
+
+#endif
--- a/mfbt/RefPtr.h
+++ b/mfbt/RefPtr.h
@@ -7,16 +7,17 @@
 /* Helpers for defining and using refcounted objects. */
 
 #ifndef mozilla_RefPtr_h
 #define mozilla_RefPtr_h
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/Attributes.h"
+#include "mozilla/RefCountType.h"
 #include "mozilla/TypeTraits.h"
 
 namespace mozilla {
 
 template<typename T> class RefCounted;
 template<typename T> class RefPtr;
 template<typename T> class TemporaryRef;
 template<typename T> class OutParamRef;
@@ -44,17 +45,17 @@ template<typename T> OutParamRef<T> byRe
  * use-after-destroy (refcount==0xffffdead).
  *
  * Note that when deriving from RefCounted or AtomicRefCounted, you
  * should add MOZ_DECLARE_REFCOUNTED_TYPENAME(ClassName) to the public
  * section of your class, where ClassName is the name of your class.
  */
 namespace detail {
 #ifdef DEBUG
-static const int DEAD = 0xffffdead;
+static const MozRefCountType DEAD = 0xffffdead;
 #endif
 
 // This is used WeakPtr.h as well as this file.
 enum RefCountAtomicity
 {
   AtomicRefCount,
   NonAtomicRefCount
 };
@@ -68,41 +69,40 @@ class RefCounted
     RefCounted() : refCnt(0) { }
     ~RefCounted() {
       MOZ_ASSERT(refCnt == detail::DEAD);
     }
 
   public:
     // Compatibility with nsRefPtr.
     void AddRef() const {
-      MOZ_ASSERT(refCnt >= 0);
       ++refCnt;
     }
 
     void Release() const {
       MOZ_ASSERT(refCnt > 0);
       if (0 == --refCnt) {
 #ifdef DEBUG
         refCnt = detail::DEAD;
 #endif
         delete static_cast<const T*>(this);
       }
     }
 
     // Compatibility with wtf::RefPtr.
     void ref() { AddRef(); }
     void deref() { Release(); }
-    int refCount() const { return refCnt; }
+    MozRefCountType refCount() const { return refCnt; }
     bool hasOneRef() const {
       MOZ_ASSERT(refCnt > 0);
       return refCnt == 1;
     }
 
   private:
-    mutable typename Conditional<Atomicity == AtomicRefCount, Atomic<int>, int>::Type refCnt;
+    mutable typename Conditional<Atomicity == AtomicRefCount, Atomic<MozRefCountType>, MozRefCountType>::Type refCnt;
 };
 
 #define MOZ_DECLARE_REFCOUNTED_TYPENAME(T) \
   const char* typeName() const { return #T; }
 
 }
 
 template<typename T>
--- a/mfbt/moz.build
+++ b/mfbt/moz.build
@@ -40,16 +40,17 @@ EXPORTS.mozilla = [
     'Move.h',
     'MSIntTypes.h',
     'NullPtr.h',
     'NumericLimits.h',
     'PodOperations.h',
     'Poison.h',
     'Range.h',
     'RangedPtr.h',
+    'RefCountType.h',
     'ReentrancyGuard.h',
     'RefPtr.h',
     'RollingMean.h',
     'Scoped.h',
     'SHA1.h',
     'SplayTree.h',
     'TemplateLib.h',
     'ThreadLocal.h',
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -19,16 +19,18 @@ import org.mozilla.gecko.gfx.BitmapUtils
 import org.mozilla.gecko.gfx.GeckoLayerClient;
 import org.mozilla.gecko.gfx.ImmutableViewportMetrics;
 import org.mozilla.gecko.gfx.LayerMarginsAnimator;
 import org.mozilla.gecko.health.BrowserHealthRecorder;
 import org.mozilla.gecko.health.BrowserHealthReporter;
 import org.mozilla.gecko.health.HealthRecorder;
 import org.mozilla.gecko.health.SessionInformation;
 import org.mozilla.gecko.home.BrowserSearch;
+import org.mozilla.gecko.home.HomeBanner;
+import org.mozilla.gecko.home.HomeConfigInvalidator;
 import org.mozilla.gecko.home.HomePager;
 import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
 import org.mozilla.gecko.home.SearchEngine;
 import org.mozilla.gecko.menu.GeckoMenu;
 import org.mozilla.gecko.preferences.GeckoPreferences;
 import org.mozilla.gecko.prompts.Prompt;
 import org.mozilla.gecko.sync.setup.SyncAccounts;
 import org.mozilla.gecko.toolbar.AutocompleteHandler;
@@ -1007,17 +1009,17 @@ abstract public class BrowserApp extends
     public boolean hasTabsSideBar() {
         return (mTabsPanel != null && mTabsPanel.isSideBar());
     }
 
     private void updateSideBarState() {
         if (mMainLayoutAnimator != null)
             mMainLayoutAnimator.stop();
 
-        boolean isSideBar = (HardwareUtils.isTablet() && mOrientation == Configuration.ORIENTATION_LANDSCAPE);
+        boolean isSideBar = (HardwareUtils.isTablet() && getOrientation() == Configuration.ORIENTATION_LANDSCAPE);
         final int sidebarWidth = getResources().getDimensionPixelSize(R.dimen.tabs_sidebar_width);
 
         ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) mTabsPanel.getLayoutParams();
         lp.width = (isSideBar ? sidebarWidth : ViewGroup.LayoutParams.FILL_PARENT);
         mTabsPanel.requestLayout();
 
         final boolean sidebarIsShown = (isSideBar && mTabsPanel.isShown());
         final int mainLayoutScrollX = (sidebarIsShown ? -sidebarWidth : 0);
@@ -1416,17 +1418,18 @@ abstract public class BrowserApp extends
         }
 
         Tabs.getInstance().loadUrl(url, searchEngine, -1, flags);
 
         mBrowserToolbar.cancelEdit();
     }
 
     private boolean isHomePagerVisible() {
-        return (mHomePager != null && mHomePager.isVisible());
+        return (mHomePager != null && mHomePager.isLoaded()
+            && mHomePagerContainer != null && mHomePagerContainer.getVisibility() == View.VISIBLE);
     }
 
     /* Favicon stuff. */
     private static OnFaviconLoadedListener sFaviconLoadedListener = new OnFaviconLoadedListener() {
         @Override
         public void onFaviconLoaded(String pageUrl, String faviconURL, Bitmap favicon) {
             // If we failed to load a favicon, we use the default favicon instead.
             Tabs.getInstance()
@@ -1636,19 +1639,17 @@ abstract public class BrowserApp extends
             hideHomePager();
         }
     }
 
     @Override
     public void onLocaleReady(final String locale) {
         super.onLocaleReady(locale);
 
-        if (mHomePager != null) {
-            mHomePager.invalidate(getSupportLoaderManager(), getSupportFragmentManager());
-        }
+        HomeConfigInvalidator.getInstance().onLocaleReady(locale);
 
         if (mMenu != null) {
             mMenu.clear();
             onCreateOptionsMenu(mMenu);
         }
     }
 
     private void showHomePager(String pageId) {
@@ -1673,19 +1674,23 @@ abstract public class BrowserApp extends
         // onMetricsChanged callback still works.
         if (isDynamicToolbarEnabled() && mLayerView != null) {
             mLayerView.getLayerMarginsAnimator().showMargins(true);
         }
 
         if (mHomePager == null) {
             final ViewStub homePagerStub = (ViewStub) findViewById(R.id.home_pager_stub);
             mHomePager = (HomePager) homePagerStub.inflate();
+
+            HomeBanner homeBanner = (HomeBanner) findViewById(R.id.home_banner);
+            mHomePager.setBanner(homeBanner);
         }
 
-        mHomePager.show(getSupportLoaderManager(),
+        mHomePagerContainer.setVisibility(View.VISIBLE);
+        mHomePager.load(getSupportLoaderManager(),
                         getSupportFragmentManager(),
                         pageId, animator);
 
         // Hide the web content so it cannot be focused by screen readers.
         hideWebContentOnPropertyAnimationEnd(animator);
     }
 
     private void hideWebContentOnPropertyAnimationEnd(final PropertyAnimator animator) {
@@ -1735,19 +1740,20 @@ abstract public class BrowserApp extends
             return;
         }
 
         // Prevent race in hiding web content - see declaration for more info.
         mHideWebContentOnAnimationEnd = false;
 
         // Display the previously hidden web content (which prevented screen reader access).
         mLayerView.setVisibility(View.VISIBLE);
+        mHomePagerContainer.setVisibility(View.GONE);
 
         if (mHomePager != null) {
-            mHomePager.hide();
+            mHomePager.unload();
         }
 
         mBrowserToolbar.setNextFocusDownId(R.id.layer_view);
 
         // Refresh toolbar height to possibly restore the toolbar padding
         refreshToolbarHeight();
     }
 
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -178,17 +178,16 @@ public abstract class GeckoApp
     public View getView() { return mGeckoLayout; }
     private View mCameraView;
     private OrientationEventListener mCameraOrientationEventListener;
     public List<GeckoAppShell.AppStateListener> mAppStateListeners;
     private static GeckoApp sAppContext;
     protected MenuPanel mMenuPanel;
     protected Menu mMenu;
     protected GeckoProfile mProfile;
-    public static int mOrientation;
     protected boolean mIsRestoringActivity;
 
     private ContactService mContactService;
     private PromptService mPromptService;
     private TextSelection mTextSelection;
 
     protected DoorHangerPopup mDoorHangerPopup;
     protected FormAssistPopup mFormAssistPopup;
@@ -420,18 +419,22 @@ public abstract class GeckoApp
             return onCreateOptionsMenu(menu);
         }
 
         return super.onCreatePanelMenu(featureId, menu);
     }
 
     @Override
     public boolean onPreparePanel(int featureId, View view, Menu menu) {
-        if (Build.VERSION.SDK_INT >= 11 && featureId == Window.FEATURE_OPTIONS_PANEL)
+        if (Build.VERSION.SDK_INT >= 11 && featureId == Window.FEATURE_OPTIONS_PANEL) {
+            if (menu instanceof GeckoMenu) {
+                ((GeckoMenu) menu).refresh();
+            }
             return onPrepareOptionsMenu(menu);
+        }
 
         return super.onPreparePanel(featureId, view, menu);
     }
 
     @Override
     public boolean onMenuOpened(int featureId, Menu menu) {
         // exit full-screen mode whenever the menu is opened
         if (mLayerView != null && mLayerView.isFullScreen()) {
@@ -932,17 +935,17 @@ public abstract class GeckoApp
             }
         });
 
         FrameLayout decor = (FrameLayout)getWindow().getDecorView();
         decor.removeView(mFullScreenPluginContainer);
 
         mFullScreenPluginView = null;
 
-        GeckoScreenOrientationListener.getInstance().unlockScreenOrientation();
+        GeckoScreenOrientation.getInstance().unlock();
         setFullScreen(false);
     }
 
     public void removePluginView(final View view, final boolean isFullScreen) {
         ThreadUtils.postToUiThread(new Runnable() {
             @Override
             public void run() {
                 Tabs tabs = Tabs.getInstance();
@@ -1243,17 +1246,17 @@ public abstract class GeckoApp
         } else if (savedInstanceState != null) {
             // Bug 896992 - This intent has already been handled; reset the intent.
             setIntent(new Intent(Intent.ACTION_MAIN));
         }
 
 
         super.onCreate(savedInstanceState);
 
-        mOrientation = getResources().getConfiguration().orientation;
+        GeckoScreenOrientation.getInstance().update(getResources().getConfiguration().orientation);
 
         setContentView(getLayout());
 
         // Set up Gecko layout.
         mGeckoLayout = (RelativeLayout) findViewById(R.id.gecko_layout);
         mMainLayout = (RelativeLayout) findViewById(R.id.main_layout);
 
         // Determine whether we should restore tabs.
@@ -1915,32 +1918,32 @@ public abstract class GeckoApp
             uri = intent.getStringExtra("args");
             if (uri != null && uri.startsWith("--url=")) {
                 uri.replace("--url=", "");
             }
         }
         return uri;
     }
 
+    protected int getOrientation() {
+        return GeckoScreenOrientation.getInstance().getAndroidOrientation();
+    }
+
     @Override
     public void onResume()
     {
         // After an onPause, the activity is back in the foreground.
         // Undo whatever we did in onPause.
         super.onResume();
 
         int newOrientation = getResources().getConfiguration().orientation;
-
-        if (mOrientation != newOrientation) {
-            mOrientation = newOrientation;
+        if (GeckoScreenOrientation.getInstance().update(newOrientation)) {
             refreshChrome();
         }
 
-        GeckoScreenOrientationListener.getInstance().start();
-
         // User may have enabled/disabled accessibility.
         GeckoAccessibility.updateAccessibilitySettings(this);
 
         if (mAppStateListeners != null) {
             for (GeckoAppShell.AppStateListener listener: mAppStateListeners) {
                 listener.onResume();
             }
         }
@@ -2006,18 +2009,16 @@ public abstract class GeckoApp
 
                 // In theory, the first browser session will not run long enough that we need to
                 // prune during it and we'd rather run it when the browser is inactive so we wait
                 // until here to register the prune service.
                 GeckoPreferences.broadcastHealthReportPrune(context);
             }
         });
 
-        GeckoScreenOrientationListener.getInstance().stop();
-
         if (mAppStateListeners != null) {
             for(GeckoAppShell.AppStateListener listener: mAppStateListeners) {
                 listener.onPause();
             }
         }
 
         super.onPause();
     }
@@ -2146,24 +2147,26 @@ public abstract class GeckoApp
             file.delete();
         }
     }
 
     @Override
     public void onConfigurationChanged(Configuration newConfig) {
         Log.d(LOGTAG, "onConfigurationChanged: " + newConfig.locale);
         LocaleManager.correctLocale(getResources(), newConfig);
-        super.onConfigurationChanged(newConfig);
-
-        if (mOrientation != newConfig.orientation) {
-            mOrientation = newConfig.orientation;
+
+        // onConfigurationChanged is not called for 180 degree orientation changes,
+        // we will miss such rotations and the screen orientation will not be
+        // updated.
+        if (GeckoScreenOrientation.getInstance().update(newConfig.orientation)) {
             if (mFormAssistPopup != null)
                 mFormAssistPopup.hide();
             refreshChrome();
         }
+        super.onConfigurationChanged(newConfig);
     }
 
     public String getContentProcessName() {
         return AppConstants.MOZ_CHILD_PROCESS_NAME;
     }
 
     public void addEnvToIntent(Intent intent) {
         Map<String,String> envMap = System.getenv();
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -2566,37 +2566,37 @@ public class GeckoAppShell
     }
 
     public static byte[] decodeBase64(String s, int flags) {
         return decodeBase64(s.getBytes(), flags);
     }
 
     @WrapElementForJNI(stubName = "GetScreenOrientationWrapper")
     public static short getScreenOrientation() {
-        return GeckoScreenOrientationListener.getInstance().getScreenOrientation();
+        return GeckoScreenOrientation.getInstance().getScreenOrientation().value;
     }
 
     @WrapElementForJNI
     public static void enableScreenOrientationNotifications() {
-        GeckoScreenOrientationListener.getInstance().enableNotifications();
+        GeckoScreenOrientation.getInstance().enableNotifications();
     }
 
     @WrapElementForJNI
     public static void disableScreenOrientationNotifications() {
-        GeckoScreenOrientationListener.getInstance().disableNotifications();
+        GeckoScreenOrientation.getInstance().disableNotifications();
     }
 
     @WrapElementForJNI
     public static void lockScreenOrientation(int aOrientation) {
-        GeckoScreenOrientationListener.getInstance().lockScreenOrientation(aOrientation);
+        GeckoScreenOrientation.getInstance().lock(aOrientation);
     }
 
     @WrapElementForJNI
     public static void unlockScreenOrientation() {
-        GeckoScreenOrientationListener.getInstance().unlockScreenOrientation();
+        GeckoScreenOrientation.getInstance().unlock();
     }
 
     @WrapElementForJNI
     public static boolean pumpMessageLoop() {
         Handler geckoHandler = ThreadUtils.sGeckoHandler;
         Message msg = getNextMessageFromQueue(ThreadUtils.sGeckoQueue);
 
         if (msg == null)
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/GeckoScreenOrientation.java
@@ -0,0 +1,376 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
+ * 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/. */
+
+package org.mozilla.gecko;
+
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.util.Log;
+import android.view.Surface;
+import android.app.Activity;
+
+import java.util.Arrays;
+import java.util.List;
+
+/*
+ * Updates, locks and unlocks the screen orientation.
+ *
+ * Note: Replaces the OnOrientationChangeListener to avoid redundant rotation
+ * event handling.
+ */
+public class GeckoScreenOrientation {
+    private static final String LOGTAG = "GeckoScreenOrientation";
+
+    // Make sure that any change in dom/base/ScreenOrientation.h happens here too.
+    public enum ScreenOrientation {
+        NONE(0),
+        PORTRAIT_PRIMARY(1 << 0),
+        PORTRAIT_SECONDARY(1 << 1),
+        LANDSCAPE_PRIMARY(1 << 2),
+        LANDSCAPE_SECONDARY(1 << 3),
+        DEFAULT(1 << 4);
+
+        public final short value;
+
+        private ScreenOrientation(int value) {
+            this.value = (short)value;
+        }
+
+        public static ScreenOrientation get(short value) {
+            switch (value) {
+                case (1 << 0): return PORTRAIT_PRIMARY;
+                case (1 << 1): return PORTRAIT_SECONDARY;
+                case (1 << 2): return LANDSCAPE_PRIMARY;
+                case (1 << 3): return LANDSCAPE_SECONDARY;
+                case (1 << 4): return DEFAULT;
+                default: return NONE;
+            }
+        }
+    }
+
+    // Singleton instance.
+    private static GeckoScreenOrientation sInstance = null;
+    // Default screen orientation, used for initialization and unlocking.
+    private static final ScreenOrientation DEFAULT_SCREEN_ORIENTATION = ScreenOrientation.DEFAULT;
+    // Default rotation, used when device rotation is unknown.
+    private static final int DEFAULT_ROTATION = Surface.ROTATION_0;
+    // Default orientation, used if screen orientation is unspecified.
+    private ScreenOrientation mDefaultScreenOrientation;
+    // Last updated screen orientation.
+    private ScreenOrientation mScreenOrientation;
+    // Whether the update should notify Gecko about screen orientation changes.
+    private boolean mShouldNotify = true;
+    // Configuration screen orientation preference path.
+    private static final String DEFAULT_SCREEN_ORIENTATION_PREF = "app.orientation.default";
+
+    public GeckoScreenOrientation() {
+        PrefsHelper.getPref(DEFAULT_SCREEN_ORIENTATION_PREF, new PrefsHelper.PrefHandlerBase() {
+            @Override public void prefValue(String pref, String value) {
+                // Read and update the configuration default preference.
+                mDefaultScreenOrientation = screenOrientationFromArrayString(value);
+                setRequestedOrientation(mDefaultScreenOrientation);
+            }
+        });
+
+        mDefaultScreenOrientation = DEFAULT_SCREEN_ORIENTATION;
+        update();
+    }
+
+    public static GeckoScreenOrientation getInstance() {
+        if (sInstance == null) {
+            sInstance = new GeckoScreenOrientation();
+        }
+        return sInstance;
+    }
+
+    /*
+     * Enable Gecko screen orientation events on update.
+     */
+    public void enableNotifications() {
+        update();
+        mShouldNotify = true;
+    }
+
+    /*
+     * Disable Gecko screen orientation events on update.
+     */
+    public void disableNotifications() {
+        mShouldNotify = false;
+    }
+
+    /*
+     * Update screen orientation.
+     * Retrieve orientation and rotation via GeckoAppShell.
+     *
+     * @return Whether the screen orientation has changed.
+     */
+    public boolean update() {
+        Activity activity = GeckoAppShell.getGeckoInterface().getActivity();
+        if (activity == null) {
+            return false;
+        }
+        Configuration config = activity.getResources().getConfiguration();
+        return update(config.orientation);
+    }
+
+    /*
+     * Update screen orientation given the android orientation.
+     * Retrieve rotation via GeckoAppShell.
+     *
+     * @param aAndroidOrientation
+     *        Android screen orientation from Configuration.orientation.
+     *
+     * @return Whether the screen orientation has changed.
+     */
+    public boolean update(int aAndroidOrientation) {
+        return update(getScreenOrientation(aAndroidOrientation, getRotation()));
+    }
+
+    /*
+     * Update screen orientation given the screen orientation.
+     *
+     * @param aScreenOrientation
+     *        Gecko screen orientation based on android orientation and rotation.
+     *
+     * @return Whether the screen orientation has changed.
+     */
+    public boolean update(ScreenOrientation aScreenOrientation) {
+        if (mScreenOrientation == aScreenOrientation) {
+            return false;
+        }
+        mScreenOrientation = aScreenOrientation;
+        Log.d(LOGTAG, "updating to new orientation " + mScreenOrientation);
+        if (mShouldNotify) {
+            GeckoAppShell.sendEventToGecko(GeckoEvent.createScreenOrientationEvent(mScreenOrientation.value));
+        }
+        return true;
+    }
+
+    /*
+     * @return The Android orientation (Configuration.orientation).
+     */
+    public int getAndroidOrientation() {
+        return screenOrientationToAndroidOrientation(getScreenOrientation());
+    }
+
+    /*
+     * @return The Gecko screen orientation derived from Android orientation and
+     *         rotation.
+     */
+    public ScreenOrientation getScreenOrientation() {
+        return mScreenOrientation;
+    }
+
+    /*
+     * Lock screen orientation given the Android orientation.
+     * Retrieve rotation via GeckoAppShell.
+     *
+     * @param aAndroidOrientation
+     *        The Android orientation provided by Configuration.orientation.
+     */
+    public void lock(int aAndroidOrientation) {
+        lock(getScreenOrientation(aAndroidOrientation, getRotation()));
+    }
+
+    /*
+     * Lock screen orientation given the Gecko screen orientation.
+     * Retrieve rotation via GeckoAppShell.
+     *
+     * @param aScreenOrientation
+     *        Gecko screen orientation derived from Android orientation and
+     *        rotation.
+     *
+     * @return Whether the locking was successful.
+     */
+    public boolean lock(ScreenOrientation aScreenOrientation) {
+        Log.d(LOGTAG, "locking to " + aScreenOrientation);
+        update(aScreenOrientation);
+        return setRequestedOrientation(aScreenOrientation);
+    }
+
+    /*
+     * Unlock and update screen orientation.
+     *
+     * @return Whether the unlocking was successful.
+     */
+    public boolean unlock() {
+        Log.d(LOGTAG, "unlocking");
+        setRequestedOrientation(mDefaultScreenOrientation);
+        return update();
+    }
+
+    /*
+     * Set the given requested orientation for the current activity.
+     * This is essentially an unlock without an update.
+     *
+     * @param aScreenOrientation
+     *        Gecko screen orientation.
+     *
+     * @return Whether the requested orientation was set. This can only fail if
+     *         the current activity cannot be retrieved vie GeckoAppShell.
+     *
+     */
+    private boolean setRequestedOrientation(ScreenOrientation aScreenOrientation) {
+        int activityOrientation = screenOrientationToActivityInfoOrientation(aScreenOrientation);
+        Activity activity = GeckoAppShell.getGeckoInterface().getActivity();
+        if (activity == null) {
+            Log.w(LOGTAG, "setRequestOrientation: failed to get activity");
+        }
+        if (activity.getRequestedOrientation() == activityOrientation) {
+            return false;
+        }
+        activity.setRequestedOrientation(activityOrientation);
+        return true;
+    }
+
+    /*
+     * Combine the Android orientation and rotation to the Gecko orientation.
+     *
+     * @param aAndroidOrientation
+     *        Android orientation from Configuration.orientation.
+     * @param aRotation
+     *        Device rotation from Display.getRotation().
+     *
+     * @return Gecko screen orientation.
+     */
+    private ScreenOrientation getScreenOrientation(int aAndroidOrientation, int aRotation) {
+        boolean isPrimary = aRotation == Surface.ROTATION_0 || aRotation == Surface.ROTATION_90;
+        if (aAndroidOrientation == Configuration.ORIENTATION_PORTRAIT) {
+            if (isPrimary) {
+                // Non-rotated portrait device or landscape device rotated
+                // to primary portrait mode counter-clockwise.
+                return ScreenOrientation.PORTRAIT_PRIMARY;
+            }
+            return ScreenOrientation.PORTRAIT_SECONDARY;
+        }
+        if (aAndroidOrientation == Configuration.ORIENTATION_LANDSCAPE) {
+            if (isPrimary) {
+                // Non-rotated landscape device or portrait device rotated
+                // to primary landscape mode counter-clockwise.
+                return ScreenOrientation.LANDSCAPE_PRIMARY;
+            }
+            return ScreenOrientation.LANDSCAPE_SECONDARY;
+        }
+        return ScreenOrientation.NONE;
+    }
+
+    /*
+     * @return Device rotation from Display.getRotation().
+     */
+    private int getRotation() {
+        Activity activity = GeckoAppShell.getGeckoInterface().getActivity();
+        if (activity == null) {
+            Log.w(LOGTAG, "getRotation: failed to get activity");
+            return DEFAULT_ROTATION;
+        }
+        return activity.getWindowManager().getDefaultDisplay().getRotation();
+    }
+
+    /*
+     * Retrieve the screen orientation from an array string.
+     *
+     * @param aArray
+     *        String containing comma-delimited strings.
+     *
+     * @return First parsed Gecko screen orientation.
+     */
+    public static ScreenOrientation screenOrientationFromArrayString(String aArray) {
+        List<String> orientations = Arrays.asList(aArray.split(","));
+        if (orientations.size() == 0) {
+            // If nothing is listed, return default.
+            Log.w(LOGTAG, "screenOrientationFromArrayString: no orientation in string");
+            return DEFAULT_SCREEN_ORIENTATION;
+        }
+
+        // We don't support multiple orientations yet. To avoid developer
+        // confusion, just take the first one listed.
+        return screenOrientationFromString(orientations.get(0));
+    }
+
+    /*
+     * Retrieve the scren orientation from a string.
+     *
+     * @param aStr
+     *        String hopefully containing a screen orientation name.
+     * @return Gecko screen orientation if matched, DEFAULT_SCREEN_ORIENTATION
+     *         otherwise.
+     */
+    public static ScreenOrientation screenOrientationFromString(String aStr) {
+        if ("portrait".equals(aStr)) {
+            return ScreenOrientation.PORTRAIT_PRIMARY;
+        }
+        else if ("landscape".equals(aStr)) {
+            return ScreenOrientation.LANDSCAPE_PRIMARY;
+        }
+        else if ("portrait-primary".equals(aStr)) {
+            return ScreenOrientation.PORTRAIT_PRIMARY;
+        }
+        else if ("portrait-secondary".equals(aStr)) {
+            return ScreenOrientation.PORTRAIT_SECONDARY;
+        }
+        else if ("landscape-primary".equals(aStr)) {
+            return ScreenOrientation.LANDSCAPE_PRIMARY;
+        }
+        else if ("landscape-secondary".equals(aStr)) {
+            return ScreenOrientation.LANDSCAPE_SECONDARY;
+        }
+        Log.w(LOGTAG, "screenOrientationFromString: unknown orientation string");
+        return DEFAULT_SCREEN_ORIENTATION;
+    }
+
+    /*
+     * Convert Gecko screen orientation to Android orientation.
+     *
+     * @param aScreenOrientation
+     *        Gecko screen orientation.
+     * @return Android orientation. This conversion is lossy, the Android
+     *         orientation does not differentiate between primary and secondary
+     *         orientations.
+     */
+    public static int screenOrientationToAndroidOrientation(ScreenOrientation aScreenOrientation) {
+        switch (aScreenOrientation) {
+            case PORTRAIT_PRIMARY:
+            case PORTRAIT_SECONDARY:
+                return Configuration.ORIENTATION_PORTRAIT;
+            case LANDSCAPE_PRIMARY:
+            case LANDSCAPE_SECONDARY:
+                return Configuration.ORIENTATION_LANDSCAPE;
+            case NONE:
+            case DEFAULT:
+            default:
+                return Configuration.ORIENTATION_UNDEFINED;
+        }
+    }
+
+
+    /*
+     * Convert Gecko screen orientation to Android ActivityInfo orientation.
+     * This is yet another orientation used by Android, but it's more detailed
+     * than the Android orientation.
+     * It is required for screen orientation locking and unlocking.
+     *
+     * @param aScreenOrientation
+     *        Gecko screen orientation.
+     * @return Android ActivityInfo orientation.
+     */
+    public static int screenOrientationToActivityInfoOrientation(ScreenOrientation aScreenOrientation) {
+        switch (aScreenOrientation) {
+            case PORTRAIT_PRIMARY:
+                return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+            case PORTRAIT_SECONDARY:
+                return ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
+            case LANDSCAPE_PRIMARY:
+                return ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+            case LANDSCAPE_SECONDARY:
+                return ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
+            case DEFAULT:
+            case NONE:
+                return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+            default:
+                return ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
+        }
+    }
+}
deleted file mode 100644
--- a/mobile/android/base/GeckoScreenOrientationListener.java
+++ /dev/null
@@ -1,229 +0,0 @@
-/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
- * 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/. */
-
-package org.mozilla.gecko;
-
-import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.util.Log;
-import android.view.OrientationEventListener;
-import android.view.Surface;
-
-import java.util.Arrays;
-import java.util.List;
-
-import android.app.Activity;
-
-public class GeckoScreenOrientationListener {
-    private static final String LOGTAG = "GeckoScreenOrientationListener";
-
-    static class OrientationEventListenerImpl extends OrientationEventListener {
-        public OrientationEventListenerImpl(Context c) {
-            super(c);
-        }
-
-        @Override
-        public void onOrientationChanged(int aOrientation) {
-            GeckoScreenOrientationListener.getInstance().updateScreenOrientation(aOrientation);
-        }
-    }
-
-    static private GeckoScreenOrientationListener sInstance = null;
-
-    // Make sure that any change in dom/base/ScreenOrientation.h happens here too.
-    static public final short eScreenOrientation_None               = 0;
-    static public final short eScreenOrientation_PortraitPrimary    = 1; // PR_BIT(0)
-    static public final short eScreenOrientation_PortraitSecondary  = 2; // PR_BIT(1)
-    static public final short eScreenOrientation_LandscapePrimary   = 4; // PR_BIT(2)
-    static public final short eScreenOrientation_LandscapeSecondary = 8; // PR_BIT(3)
-    static public final short eScreenOrientation_Default            = 16;// PR_BIT(4)
-
-    static private final short DEFAULT_ORIENTATION = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-
-    private short mOrientation;
-    private OrientationEventListenerImpl mListener = null;
-
-    // Whether the listener should be listening to changes.
-    private boolean mShouldBeListening = false;
-    // Whether the listener should notify Gecko that a change happened.
-    private boolean mShouldNotify      = false;
-    // The default orientation to use if nothing is specified
-    private short mDefaultOrientation;
-
-    private static final String DEFAULT_ORIENTATION_PREF = "app.orientation.default";
-
-    private GeckoScreenOrientationListener() {
-        mListener = new OrientationEventListenerImpl(GeckoAppShell.getContext());
-
-        PrefsHelper.getPref(DEFAULT_ORIENTATION_PREF, new PrefsHelper.PrefHandlerBase() {
-            @Override public void prefValue(String pref, String value) {
-                mDefaultOrientation = orientationFromStringArray(value);
-                unlockScreenOrientation();
-            }
-
-            @Override
-            public boolean isObserver() {
-                return true;
-            }
-        });
-
-        mDefaultOrientation = DEFAULT_ORIENTATION;
-    }
-
-    public static GeckoScreenOrientationListener getInstance() {
-        if (sInstance == null) {
-            sInstance = new GeckoScreenOrientationListener();
-        }
-
-        return sInstance;
-    }
-
-    public void start() {
-        mShouldBeListening = true;
-        updateScreenOrientation();
-
-        if (mShouldNotify) {
-            startListening();
-        }
-    }
-
-    public void stop() {
-        mShouldBeListening = false;
-
-        if (mShouldNotify) {
-            stopListening();
-        }
-    }
-
-    public void enableNotifications() {
-        updateScreenOrientation();
-        mShouldNotify = true;
-
-        if (mShouldBeListening) {
-            startListening();
-        }
-    }
-
-    public void disableNotifications() {
-        mShouldNotify = false;
-
-        if (mShouldBeListening) {
-            stopListening();
-        }
-    }
-
-    private void startListening() {
-        mListener.enable();
-    }
-
-    private void stopListening() {
-        mListener.disable();
-    }
-
-    private short orientationFromStringArray(String val) {
-        List<String> orientations = Arrays.asList(val.split(","));
-        // if nothing is listed, return unspecified
-        if (orientations.size() == 0)
-            return DEFAULT_ORIENTATION;
-
-        // we dont' support multiple orientations yet. To avoid developer confusion,
-        // just take the first one listed
-        return orientationFromString(orientations.get(0));
-    }
-
-    private short orientationFromString(String val) {
-        if ("portrait".equals(val))
-            return (short)ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT;
-        else if ("landscape".equals(val))
-            return (short)ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
-        else if ("portrait-primary".equals(val))
-            return (short)ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
-        else if ("portrait-secondary".equals(val))
-            return (short)ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
-        else if ("landscape-primary".equals(val))
-            return (short)ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
-        else if ("landscape-secondary".equals(val))
-            return (short)ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
-        return DEFAULT_ORIENTATION;
-    }
-
-    private void updateScreenOrientation() {
-        Context context = GeckoAppShell.getContext();
-        int rotation = mDefaultOrientation;
-        if (context instanceof Activity) {
-            rotation = ((Activity)context).getWindowManager().getDefaultDisplay().getRotation();
-        }
-        updateScreenOrientation(rotation * 90);
-    }
-
-    private void updateScreenOrientation(int aOrientation) {
-        short previousOrientation = mOrientation;
-
-        if (aOrientation >= 315  || aOrientation < 45) {
-            mOrientation = eScreenOrientation_PortraitPrimary;
-        } else if (aOrientation >= 45 && aOrientation < 135) {
-            mOrientation = eScreenOrientation_LandscapePrimary;
-        } else if (aOrientation >= 135 && aOrientation < 225) {
-            mOrientation = eScreenOrientation_PortraitSecondary;
-        } else if (aOrientation >= 225 && aOrientation < 315) {
-            mOrientation = eScreenOrientation_LandscapeSecondary;
-        } else {
-            Log.e(LOGTAG, "Unexpected value received! (" + aOrientation + ")");
-            return;
-        }
-
-        if (mShouldNotify && mOrientation != previousOrientation) {
-            GeckoAppShell.sendEventToGecko(GeckoEvent.createScreenOrientationEvent(mOrientation));
-        }
-    }
-
-    public short getScreenOrientation() {
-        return mOrientation;
-    }
-
-    public void lockScreenOrientation(int aOrientation) {
-        int orientation = 0;
-
-        switch (aOrientation) {
-        case eScreenOrientation_PortraitPrimary:
-            orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
-            break;
-        case eScreenOrientation_PortraitSecondary:
-            orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
-            break;
-        case eScreenOrientation_PortraitPrimary | eScreenOrientation_PortraitSecondary:
-            orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT;
-            break;
-        case eScreenOrientation_LandscapePrimary:
-            orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
-            break;
-        case eScreenOrientation_LandscapeSecondary:
-            orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
-            break;
-        case eScreenOrientation_LandscapePrimary | eScreenOrientation_LandscapeSecondary:
-            orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
-            break;
-        case eScreenOrientation_Default:
-            orientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
-            break;
-        default:
-            Log.e(LOGTAG, "Unexpected value received! (" + aOrientation + ")");
-            return;
-        }
-        if (GeckoAppShell.getContext() instanceof Activity)
-            ((Activity)GeckoAppShell.getContext()).setRequestedOrientation(orientation);
-        updateScreenOrientation();
-    }
-
-    public void unlockScreenOrientation() {
-        if (!(GeckoAppShell.getContext() instanceof Activity))
-            return;
-        if (((Activity)GeckoAppShell.getContext()).getRequestedOrientation() == mDefaultOrientation)
-            return;
-
-        ((Activity)GeckoAppShell.getContext()).setRequestedOrientation(mDefaultOrientation);
-        updateScreenOrientation();
-    }
-}
--- a/mobile/android/base/LocaleManager.java
+++ b/mobile/android/base/LocaleManager.java
@@ -230,21 +230,21 @@ public class LocaleManager {
         final long t1 = android.os.SystemClock.uptimeMillis();
         final String localeCode = getPersistedLocale();
         if (localeCode == null) {
             return null;
         }
 
         // Note that we don't tell Gecko about this. We notify Gecko when the
         // locale is set, not when we update Java.
-        updateLocale(localeCode);
+        final String resultant = updateLocale(localeCode);
 
         final long t2 = android.os.SystemClock.uptimeMillis();
         Log.i(LOG_TAG, "Locale read and update took: " + (t2 - t1) + "ms.");
-        return localeCode;
+        return resultant;
     }
 
     /**
      * Returns the set locale if it changed.
      *
      * Always persists and notifies Gecko.
      */
     public static String setSelectedLocale(String localeCode) {
--- a/mobile/android/base/TabsTray.java
+++ b/mobile/android/base/TabsTray.java
@@ -197,21 +197,21 @@ public class TabsTray extends TwoWayView
 
             notifyDataSetChanged(); // Be sure to call this whenever mTabs changes.
             updateSelectedPosition();
         }
 
         // Updates the selected position in the list so that it will be scrolled to the right place.
         private void updateSelectedPosition() {
             int selected = getPositionForTab(Tabs.getInstance().getSelectedTab());
+            updateSelectedStyle(selected);
+
             if (selected != -1) {
                 TabsTray.this.setSelection(selected);
             }
-
-            updateSelectedStyle(selected);
         }
 
         /**
          * Updates the selected/unselected style for the tabs.
          *
          * @param selected position of the selected tab
          */
         private void updateSelectedStyle(int selected) {
--- a/mobile/android/base/home/HomeBanner.java
+++ b/mobile/android/base/home/HomeBanner.java
@@ -1,15 +1,19 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * 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/. */
 
 package org.mozilla.gecko.home;
 
+import org.mozilla.gecko.animation.PropertyAnimator;
+import org.mozilla.gecko.animation.PropertyAnimator.Property;
+import org.mozilla.gecko.animation.PropertyAnimator.PropertyAnimationListener;
+import org.mozilla.gecko.animation.ViewHelper;
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.GeckoEvent;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.gfx.BitmapUtils;
 import org.mozilla.gecko.util.GeckoEventListener;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import org.json.JSONException;
@@ -18,26 +22,43 @@ import org.json.JSONObject;
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.text.Html;
 import android.text.Spanned;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.LayoutInflater;
+import android.view.MotionEvent;
 import android.view.View;
 import android.widget.ImageButton;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
 public class HomeBanner extends LinearLayout
                         implements GeckoEventListener {
     private static final String LOGTAG = "GeckoHomeBanner";
 
+    // Used for tracking scroll length
+    private float mTouchY = -1;
+
+    // Used to detect for upwards scroll to push banner all the way up
+    private boolean mSnapBannerToTop;
+
+    // Tracks if the banner has been enabled by HomePager to avoid race conditions.
+    private boolean mEnabled = false;
+
+    // The user is currently swiping between HomePager pages
+    private boolean mScrollingPages = false;
+
+    // Tracks whether the user swiped the banner down, preventing us from autoshowing when the user
+    // switches back to the default page.
+    private boolean mUserSwipedDown = false;
+
     public HomeBanner(Context context) {
         this(context, null);
     }
 
     public HomeBanner(Context context, AttributeSet attrs) {
         super(context, attrs);
 
         LayoutInflater.from(context).inflate(R.layout.home_banner, this);
@@ -77,36 +98,37 @@ public class HomeBanner extends LinearLa
 
     @Override
     public void onDetachedFromWindow() {
         super.onDetachedFromWindow();
 
         GeckoAppShell.getEventDispatcher().unregisterEventListener("HomeBanner:Data", this);
      }
 
-    public boolean isDismissed() {
-        return (getVisibility() == View.GONE);
-    }
+     public void setScrollingPages(boolean scrollingPages) {
+         mScrollingPages = scrollingPages;
+     }
 
     @Override
     public void handleMessage(String event, JSONObject message) {
         try {
             // Store the current message id to pass back to JS in the view's OnClickListener.
             setTag(message.getString("id"));
 
             // Display styled text from an HTML string.
             final Spanned text = Html.fromHtml(message.getString("text"));
             final TextView textView = (TextView) findViewById(R.id.text);
 
             // Update the banner message on the UI thread.
             ThreadUtils.postToUiThread(new Runnable() {
                 @Override
                 public void run() {
                     textView.setText(text);
-                    setVisibility(View.VISIBLE);
+                    setVisibility(VISIBLE);
+                    animateUp();
                 }
             });
         } catch (JSONException e) {
             Log.e(LOGTAG, "Exception handling " + event + " message", e);
             return;
         }
 
         final String iconURI = message.optString("iconURI");
@@ -132,9 +154,107 @@ public class HomeBanner extends LinearLa
                     @Override
                     public void run() {
                         iconView.setImageDrawable(d);
                     }
                 });
             }
         });
     }
+
+    public void setEnabled(boolean enabled) {
+        // No need to animate if not changing
+        if (mEnabled == enabled) {
+            return;
+        }
+
+        mEnabled = enabled;
+        if (enabled) {
+            animateUp();
+        } else {
+            animateDown();
+        }
+    }
+
+    private void animateUp() {
+        // Check to make sure that message has been received and the banner has been enabled.
+        // Necessary to avoid race conditions between show() and handleMessage() calls.
+        TextView textView = (TextView) findViewById(R.id.text);
+        if (!mEnabled || TextUtils.isEmpty(textView.getText()) || mUserSwipedDown) {
+            return;
+        }
+
+        // No need to animate if already translated.
+        if (ViewHelper.getTranslationY(this) == 0) {
+            return;
+        }
+
+        final PropertyAnimator animator = new PropertyAnimator(100);
+        animator.attach(this, Property.TRANSLATION_Y, 0);
+        animator.start();
+    }
+
+    private void animateDown() {
+        // No need to animate if already translated or gone.
+        if (ViewHelper.getTranslationY(this) == getHeight()) {
+            return;
+        }
+
+        final PropertyAnimator animator = new PropertyAnimator(100);
+        animator.attach(this, Property.TRANSLATION_Y, getHeight());
+        animator.start();
+    }
+
+    public void handleHomeTouch(MotionEvent event) {
+        if (!mEnabled || getVisibility() == GONE || mScrollingPages) {
+            return;
+        }
+
+        switch (event.getActionMasked()) {
+            case MotionEvent.ACTION_DOWN: {
+                // Track the beginning of the touch
+                mTouchY = event.getRawY();
+                break;
+            }
+
+            case MotionEvent.ACTION_MOVE: {
+                final float curY = event.getRawY();
+                final float delta = mTouchY - curY;
+                mSnapBannerToTop = delta <= 0.0f;
+
+                final float height = getHeight();
+                float newTranslationY = ViewHelper.getTranslationY(this) + delta;
+
+                // Clamp the values to be between 0 and height.
+                if (newTranslationY < 0.0f) {
+                    newTranslationY = 0.0f;
+                } else if (newTranslationY > height) {
+                    newTranslationY = height;
+                }
+
+                // Don't change this value if it wasn't a significant movement
+                if (delta >= 10 || delta <= -10) {
+                    mUserSwipedDown = newTranslationY == height;
+                }
+
+                ViewHelper.setTranslationY(this, newTranslationY);
+                mTouchY = curY;
+                break;
+            }
+
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL: {
+                mTouchY = -1;
+                final float y = ViewHelper.getTranslationY(this);
+                final float height = getHeight();
+                if (y > 0.0f && y < height) {
+                    if (mSnapBannerToTop) {
+                        animateUp();
+                    } else {
+                        animateDown();
+                        mUserSwipedDown = true;
+                    }
+                }
+                break;
+            }
+        }
+    }
 }
--- a/mobile/android/base/home/HomeConfig.java
+++ b/mobile/android/base/home/HomeConfig.java
@@ -694,16 +694,17 @@ public final class HomeConfig {
 
     public interface OnChangeListener {
         public void onChange();
     }
 
     public interface HomeConfigBackend {
         public List<PanelConfig> load();
         public void save(List<PanelConfig> entries);
+        public String getLocale();
         public void setOnChangeListener(OnChangeListener listener);
     }
 
     // UUIDs used to create PanelConfigs for default built-in panels
     private static final String TOP_SITES_PANEL_ID = "4becc86b-41eb-429a-a042-88fe8b5a094e";
     private static final String BOOKMARKS_PANEL_ID = "7f6d419a-cd6c-4e34-b26f-f68b1b551907";
     private static final String READING_LIST_PANEL_ID = "20f4549a-64ad-4c32-93e4-1dcef792733b";
     private static final String HISTORY_PANEL_ID = "f134bf20-11f7-4867-ab8b-e8e705d7fbe8";
@@ -713,16 +714,20 @@ public final class HomeConfig {
     public HomeConfig(HomeConfigBackend backend) {
         mBackend = backend;
     }
 
     public List<PanelConfig> load() {
         return mBackend.load();
     }
 
+    public String getLocale() {
+        return mBackend.getLocale();
+    }
+
     public void save(List<PanelConfig> panelConfigs) {
         mBackend.save(panelConfigs);
     }
 
     public void setOnChangeListener(OnChangeListener listener) {
         mBackend.setOnChangeListener(listener);
     }
 
--- a/mobile/android/base/home/HomeConfigInvalidator.java
+++ b/mobile/android/base/home/HomeConfigInvalidator.java
@@ -12,16 +12,17 @@ import org.mozilla.gecko.home.PanelManag
 import org.mozilla.gecko.home.PanelManager.RequestCallback;
 import org.mozilla.gecko.util.GeckoEventListener;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import static org.mozilla.gecko.home.HomeConfig.createBuiltinPanelConfig;
 
 import android.content.Context;
 import android.os.Handler;
+import android.text.TextUtils;
 import android.util.Log;
 
 import org.json.JSONException;
 import org.json.JSONObject;
 
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
@@ -33,32 +34,43 @@ public class HomeConfigInvalidator imple
     public static final String LOGTAG = "HomeConfigInvalidator";
 
     private static final HomeConfigInvalidator sInstance = new HomeConfigInvalidator();
 
     private static final int INVALIDATION_DELAY_MSEC = 500;
     private static final int PANEL_INFO_TIMEOUT_MSEC = 1000;
 
     private static final String EVENT_HOMEPANELS_INSTALL = "HomePanels:Install";
-    private static final String EVENT_HOMEPANELS_REMOVE = "HomePanels:Remove";
-    private static final String EVENT_HOMEPANELS_REFRESH = "HomePanels:Refresh";
+    private static final String EVENT_HOMEPANELS_UNINSTALL = "HomePanels:Uninstall";
+    private static final String EVENT_HOMEPANELS_UPDATE = "HomePanels:Update";
 
     private static final String JSON_KEY_PANEL = "panel";
+    private static final String JSON_KEY_PANEL_ID = "id";
 
     private enum ChangeType {
-        REMOVE,
+        UNINSTALL,
         INSTALL,
+        UPDATE,
         REFRESH
     }
 
+    private enum InvalidationMode {
+        DELAYED,
+        IMMEDIATE
+    }
+
     private static class ConfigChange {
         private final ChangeType type;
-        private final PanelConfig target;
+        private final Object target;
 
-        public ConfigChange(ChangeType type, PanelConfig target) {
+        public ConfigChange(ChangeType type) {
+            this(type, null);
+        }
+
+        public ConfigChange(ChangeType type, Object target) {
             this.type = type;
             this.target = target;
         }
     }
 
     private Context mContext;
     private HomeConfig mHomeConfig;
 
@@ -69,88 +81,111 @@ public class HomeConfigInvalidator imple
         return sInstance;
     }
 
     public void init(Context context) {
         mContext = context;
         mHomeConfig = HomeConfig.getDefault(context);
 
         GeckoAppShell.getEventDispatcher().registerEventListener(EVENT_HOMEPANELS_INSTALL, this);
-        GeckoAppShell.getEventDispatcher().registerEventListener(EVENT_HOMEPANELS_REMOVE, this);
-        GeckoAppShell.getEventDispatcher().registerEventListener(EVENT_HOMEPANELS_REFRESH, this);
+        GeckoAppShell.getEventDispatcher().registerEventListener(EVENT_HOMEPANELS_UNINSTALL, this);
+        GeckoAppShell.getEventDispatcher().registerEventListener(EVENT_HOMEPANELS_UPDATE, this);
     }
 
-    public void refreshAll() {
-        handlePanelRefresh(null);
+    public void onLocaleReady(final String locale) {
+        ThreadUtils.getBackgroundHandler().post(new Runnable() {
+            @Override
+            public void run() {
+                final String configLocale = mHomeConfig.getLocale();
+                if (configLocale == null || !configLocale.equals(locale)) {
+                    handleLocaleChange();
+                }
+            }
+        });
     }
 
     @Override
     public void handleMessage(String event, JSONObject message) {
         try {
-            final JSONObject json = message.getJSONObject(JSON_KEY_PANEL);
-            final PanelConfig panelConfig = new PanelConfig(json);
-
             if (event.equals(EVENT_HOMEPANELS_INSTALL)) {
                 Log.d(LOGTAG, EVENT_HOMEPANELS_INSTALL);
-                handlePanelInstall(panelConfig);
-            } else if (event.equals(EVENT_HOMEPANELS_REMOVE)) {
-                Log.d(LOGTAG, EVENT_HOMEPANELS_REMOVE);
-                handlePanelRemove(panelConfig);
-            } else if (event.equals(EVENT_HOMEPANELS_REFRESH)) {
-                Log.d(LOGTAG, EVENT_HOMEPANELS_REFRESH);
-                handlePanelRefresh(panelConfig);
+                handlePanelInstall(createPanelConfigFromMessage(message));
+            } else if (event.equals(EVENT_HOMEPANELS_UNINSTALL)) {
+                Log.d(LOGTAG, EVENT_HOMEPANELS_UNINSTALL);
+                final String panelId = message.getString(JSON_KEY_PANEL_ID);
+                handlePanelUninstall(panelId);
+            } else if (event.equals(EVENT_HOMEPANELS_UPDATE)) {
+                Log.d(LOGTAG, EVENT_HOMEPANELS_UPDATE);
+                handlePanelUpdate(createPanelConfigFromMessage(message));
             }
         } catch (Exception e) {
             Log.e(LOGTAG, "Failed to handle event " + event, e);
         }
     }
 
+    private PanelConfig createPanelConfigFromMessage(JSONObject message) throws JSONException {
+        final JSONObject json = message.getJSONObject(JSON_KEY_PANEL);
+        return new PanelConfig(json);
+    }
+
     /**
      * Runs in the gecko thread.
      */
     private void handlePanelInstall(PanelConfig panelConfig) {
         mPendingChanges.offer(new ConfigChange(ChangeType.INSTALL, panelConfig));
         Log.d(LOGTAG, "handlePanelInstall: " + mPendingChanges.size());
 
-        scheduleInvalidation();
+        scheduleInvalidation(InvalidationMode.DELAYED);
+    }
+
+    /**
+     * Runs in the gecko thread.
+     */
+    private void handlePanelUninstall(String panelId) {
+        mPendingChanges.offer(new ConfigChange(ChangeType.UNINSTALL, panelId));
+        Log.d(LOGTAG, "handlePanelUninstall: " + mPendingChanges.size());
+
+        scheduleInvalidation(InvalidationMode.DELAYED);
     }
 
     /**
      * Runs in the gecko thread.
      */
-    private void handlePanelRemove(PanelConfig panelConfig) {
-        mPendingChanges.offer(new ConfigChange(ChangeType.REMOVE, panelConfig));
-        Log.d(LOGTAG, "handlePanelRemove: " + mPendingChanges.size());
+    private void handlePanelUpdate(PanelConfig panelConfig) {
+        mPendingChanges.offer(new ConfigChange(ChangeType.UPDATE, panelConfig));
+        Log.d(LOGTAG, "handlePanelUpdate: " + mPendingChanges.size());
 
-        scheduleInvalidation();
+        scheduleInvalidation(InvalidationMode.DELAYED);
     }
 
     /**
-     * Schedules a panel refresh in HomeConfig. Runs in the gecko thread.
-     *
-     * @param panelConfig the target PanelConfig instance or NULL to refresh
-     *                    all HomeConfig entries.
+     * Runs in the background thread.
      */
-    private void handlePanelRefresh(PanelConfig panelConfig) {
-        mPendingChanges.offer(new ConfigChange(ChangeType.REFRESH, panelConfig));
-        Log.d(LOGTAG, "handlePanelRefresh: " + mPendingChanges.size());
+    private void handleLocaleChange() {
+        mPendingChanges.offer(new ConfigChange(ChangeType.REFRESH));
+        Log.d(LOGTAG, "handleLocaleChange: " + mPendingChanges.size());
 
-        scheduleInvalidation();
+        scheduleInvalidation(InvalidationMode.IMMEDIATE);
     }
 
     /**
      * Runs in the gecko or main thread.
      */
-    private void scheduleInvalidation() {
+    private void scheduleInvalidation(InvalidationMode mode) {
         final Handler handler = ThreadUtils.getBackgroundHandler();
 
         handler.removeCallbacks(mInvalidationRunnable);
-        handler.postDelayed(mInvalidationRunnable, INVALIDATION_DELAY_MSEC);
 
-        Log.d(LOGTAG, "scheduleInvalidation: scheduled new invalidation");
+        if (mode == InvalidationMode.IMMEDIATE) {
+            handler.post(mInvalidationRunnable);
+        } else {
+            handler.postDelayed(mInvalidationRunnable, INVALIDATION_DELAY_MSEC);
+        }
+
+        Log.d(LOGTAG, "scheduleInvalidation: scheduled new invalidation: " + mode);
     }
 
     /**
      * Replace an element if a matching PanelConfig is
      * present in the given list.
      */
     private boolean replacePanelConfig(List<PanelConfig> panelConfigs, PanelConfig panelConfig) {
         final int index = panelConfigs.indexOf(panelConfig);
@@ -159,53 +194,69 @@ public class HomeConfigInvalidator imple
             Log.d(LOGTAG, "executePendingChanges: replaced position " + index + " with " + panelConfig.getId());
 
             return true;
         }
 
         return false;
     }
 
+    private PanelConfig findPanelConfigWithId(List<PanelConfig> panelConfigs, String panelId) {
+        for (PanelConfig panelConfig : panelConfigs) {
+            if (panelConfig.getId().equals(panelId)) {
+                return panelConfig;
+            }
+        }
+
+        return null;
+    }
+
     /**
      * Runs in the background thread.
      */
     private List<PanelConfig> executePendingChanges(List<PanelConfig> panelConfigs) {
-        boolean shouldRefreshAll = false;
+        boolean shouldRefresh = false;
 
         while (!mPendingChanges.isEmpty()) {
             final ConfigChange pendingChange = mPendingChanges.poll();
-            final PanelConfig panelConfig = pendingChange.target;
 
             switch (pendingChange.type) {
-                case REMOVE:
-                    if (panelConfigs.remove(panelConfig)) {
+                case UNINSTALL: {
+                    final String panelId = (String) pendingChange.target;
+                    final PanelConfig panelConfig = findPanelConfigWithId(panelConfigs, panelId);
+                    if (panelConfig != null && panelConfigs.remove(panelConfig)) {
                         Log.d(LOGTAG, "executePendingChanges: removed panel " + panelConfig.getId());
                     }
                     break;
+                }
 
-                case INSTALL:
+                case INSTALL: {
+                    final PanelConfig panelConfig = (PanelConfig) pendingChange.target;
                     if (!replacePanelConfig(panelConfigs, panelConfig)) {
                         panelConfigs.add(panelConfig);
                         Log.d(LOGTAG, "executePendingChanges: added panel " + panelConfig.getId());
                     }
                     break;
+                }
 
-                case REFRESH:
-                    if (panelConfig != null) {
-                        if (!replacePanelConfig(panelConfigs, panelConfig)) {
-                            Log.w(LOGTAG, "Tried to refresh non-existing panel " + panelConfig.getId());
-                        }
-                    } else {
-                        shouldRefreshAll = true;
+                case UPDATE: {
+                    final PanelConfig panelConfig = (PanelConfig) pendingChange.target;
+                    if (!replacePanelConfig(panelConfigs, panelConfig)) {
+                        Log.w(LOGTAG, "Tried to update non-existing panel " + panelConfig.getId());
                     }
                     break;
+                }
+
+                case REFRESH: {
+                    shouldRefresh = true;
+                }
             }
         }
 
-        if (shouldRefreshAll) {
+        if (shouldRefresh) {
             return executeRefresh(panelConfigs);
         } else {
             return panelConfigs;
         }
     }
 
     /**
      * Runs in the background thread.
--- a/mobile/android/base/home/HomeConfigPrefsBackend.java
+++ b/mobile/android/base/home/HomeConfigPrefsBackend.java
@@ -24,21 +24,23 @@ import android.content.SharedPreferences
 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
 import android.preference.PreferenceManager;
 import android.text.TextUtils;
 import android.util.Log;
 
 import java.util.ArrayList;
 import java.util.EnumSet;
 import java.util.List;
+import java.util.Locale;
 
 class HomeConfigPrefsBackend implements HomeConfigBackend {
     private static final String LOGTAG = "GeckoHomeConfigBackend";
 
-    private static final String PREFS_KEY = "home_panels";
+    private static final String PREFS_CONFIG_KEY = "home_panels";
+    private static final String PREFS_LOCALE_KEY = "home_locale";
 
     private final Context mContext;
     private PrefsListener mPrefsListener;
     private OnChangeListener mChangeListener;
 
     public HomeConfigPrefsBackend(Context context) {
         mContext = context;
     }
@@ -99,17 +101,17 @@ class HomeConfigPrefsBackend implements 
         }
 
         return panelConfigs;
     }
 
     @Override
     public List<PanelConfig> load() {
         final SharedPreferences prefs = getSharedPreferences();
-        final String jsonString = prefs.getString(PREFS_KEY, null);
+        final String jsonString = prefs.getString(PREFS_CONFIG_KEY, null);
 
         final List<PanelConfig> panelConfigs;
         if (TextUtils.isEmpty(jsonString)) {
             panelConfigs = loadDefaultConfig();
         } else {
             panelConfigs = loadConfigFromString(jsonString);
         }
 
@@ -130,21 +132,47 @@ class HomeConfigPrefsBackend implements 
                 Log.e(LOGTAG, "Exception converting PanelConfig to JSON", e);
             }
         }
 
         final SharedPreferences prefs = getSharedPreferences();
         final SharedPreferences.Editor editor = prefs.edit();
 
         final String jsonString = jsonPanelConfigs.toString();
-        editor.putString(PREFS_KEY, jsonString);
+        editor.putString(PREFS_CONFIG_KEY, jsonString);
+        editor.putString(PREFS_LOCALE_KEY, Locale.getDefault().toString());
         editor.commit();
     }
 
     @Override
+    public String getLocale() {
+        final SharedPreferences prefs = getSharedPreferences();
+
+        String locale = prefs.getString(PREFS_LOCALE_KEY, null);
+        if (locale == null) {
+            // Initialize config with the current locale
+            final String currentLocale = Locale.getDefault().toString();
+
+            final SharedPreferences.Editor editor = prefs.edit();
+            editor.putString(PREFS_LOCALE_KEY, currentLocale);
+            editor.commit();
+
+            // If the user has saved HomeConfig before, return null this
+            // one time to trigger a refresh and ensure we use the
+            // correct locale for the saved state. For more context,
+            // see HomeConfigInvalidator.onLocaleReady().
+            if (!prefs.contains(PREFS_CONFIG_KEY)) {
+                locale = currentLocale;
+            }
+        }
+
+        return locale;
+    }
+
+    @Override
     public void setOnChangeListener(OnChangeListener listener) {
         final SharedPreferences prefs = getSharedPreferences();
 
         if (mChangeListener != null) {
             prefs.unregisterOnSharedPreferenceChangeListener(mPrefsListener);
             mPrefsListener = null;
         }
 
@@ -154,14 +182,14 @@ class HomeConfigPrefsBackend implements 
             mPrefsListener = new PrefsListener();
             prefs.registerOnSharedPreferenceChangeListener(mPrefsListener);
         }
     }
 
     private class PrefsListener implements OnSharedPreferenceChangeListener {
         @Override
         public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
-            if (TextUtils.equals(key, PREFS_KEY)) {
+            if (TextUtils.equals(key, PREFS_CONFIG_KEY)) {
                 mChangeListener.onChange();
             }
         }
     }
 }
--- a/mobile/android/base/home/HomePager.java
+++ b/mobile/android/base/home/HomePager.java
@@ -36,27 +36,26 @@ import java.util.List;
 public class HomePager extends ViewPager {
 
     private static final int LOADER_ID_CONFIG = 0;
 
     private final Context mContext;
     private volatile boolean mLoaded;
     private Decor mDecor;
     private View mTabStrip;
+    private HomeBanner mHomeBanner;
+    private int mDefaultPageIndex = -1;
 
     private final OnAddPanelListener mAddPanelListener;
 
     private final HomeConfig mConfig;
     private ConfigLoaderCallbacks mConfigLoaderCallbacks;
 
     private String mInitialPanelId;
 
-    // Whether or not we need to restart the loader when we show the HomePager.
-    private boolean mRestartLoader;
-
     // Cached original ViewPager background.
     private final Drawable mOriginalBackground;
 
     // This is mostly used by UI tests to easily fetch
     // specific list views at runtime.
     static final String LIST_TAG_HISTORY = "history";
     static final String LIST_TAG_BOOKMARKS = "bookmarks";
     static final String LIST_TAG_READING_LIST = "reading_list";
@@ -123,112 +122,62 @@ public class HomePager extends ViewPager
         //  We can call HomePager.requestFocus to steal focus from the URL bar and drop the soft
         //  keyboard. However, if there are no focusable views (e.g. an empty reading list), the
         //  URL bar will be refocused. Therefore, we make the HomePager container focusable to
         //  ensure there is always a focusable view. This would ordinarily be done via an XML
         //  attribute, but it is not working properly.
         setFocusableInTouchMode(true);
 
         mOriginalBackground = getBackground();
+        setOnPageChangeListener(new PageChangeListener());
     }
 
     @Override
     public void addView(View child, int index, ViewGroup.LayoutParams params) {
         if (child instanceof Decor) {
             ((ViewPager.LayoutParams) params).isDecor = true;
             mDecor = (Decor) child;
             mTabStrip = child;
 
             mDecor.setOnTitleClickListener(new OnTitleClickListener() {
                 @Override
                 public void onTitleClicked(int index) {
                     setCurrentItem(index, true);
                 }
             });
-
-            setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
-                @Override
-                public void onPageSelected(int position) {
-                    mDecor.onPageSelected(position);
-                }
-
-                @Override
-                public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
-                    mDecor.onPageScrolled(position, positionOffset, positionOffsetPixels);
-                }
-
-                @Override
-                public void onPageScrollStateChanged(int state) { }
-            });
         } else if (child instanceof HomePagerTabStrip) {
             mTabStrip = child;
         }
 
         super.addView(child, index, params);
     }
 
     /**
-     * Invalidates the current configuration, redisplaying the HomePager if necessary.
-     */
-    public void invalidate(LoaderManager lm, FragmentManager fm) {
-        // We need to restart the loader to load the new strings.
-        mRestartLoader = true;
-
-        // If the HomePager is currently visible, redisplay it with the new strings.
-        if (isVisible()) {
-            redisplay(lm, fm);
-        }
-    }
-
-    private void redisplay(LoaderManager lm, FragmentManager fm) {
-        final HomeAdapter adapter = (HomeAdapter) getAdapter();
-
-        // If mInitialPanelId is non-null, this means the HomePager hasn't
-        // finished loading its config yet. Simply re-show() with the
-        // current target panel.
-        final String currentPanelId;
-        if (mInitialPanelId != null) {
-            currentPanelId = mInitialPanelId;
-        } else {
-            currentPanelId = adapter.getPanelIdAtPosition(getCurrentItem());
-        }
-
-        show(lm, fm, currentPanelId, null);
-    }
-
-    /**
      * Loads and initializes the pager.
      *
      * @param fm FragmentManager for the adapter
      */
-    public void show(LoaderManager lm, FragmentManager fm, String panelId, PropertyAnimator animator) {
+    public void load(LoaderManager lm, FragmentManager fm, String panelId, PropertyAnimator animator) {
         mLoaded = true;
         mInitialPanelId = panelId;
 
         // Only animate on post-HC devices, when a non-null animator is given
         final boolean shouldAnimate = (animator != null && Build.VERSION.SDK_INT >= 11);
 
         final HomeAdapter adapter = new HomeAdapter(mContext, fm);
         adapter.setOnAddPanelListener(mAddPanelListener);
         adapter.setCanLoadHint(!shouldAnimate);
         setAdapter(adapter);
 
-        setVisibility(VISIBLE);
-
         // Don't show the tabs strip until we have the
         // list of panels in place.
         mTabStrip.setVisibility(View.INVISIBLE);
 
-        // Load list of panels from configuration. Restart the loader if necessary.
-        if (mRestartLoader) {
-            lm.restartLoader(LOADER_ID_CONFIG, null, mConfigLoaderCallbacks);
-            mRestartLoader = false;
-        } else {
-            lm.initLoader(LOADER_ID_CONFIG, null, mConfigLoaderCallbacks);
-        }
+        // Load list of panels from configuration
+        lm.initLoader(LOADER_ID_CONFIG, null, mConfigLoaderCallbacks);
 
         if (shouldAnimate) {
             animator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() {
                 @Override
                 public void onPropertyAnimationStart() {
                     setLayerType(View.LAYER_TYPE_HARDWARE, null);
                 }
 
@@ -243,33 +192,32 @@ public class HomePager extends ViewPager
 
             animator.attach(this,
                             PropertyAnimator.Property.ALPHA,
                             1.0f);
         }
     }
 
     /**
-     * Hides the pager and removes all child fragments.
+     * Removes all child fragments to free memory.
      */
-    public void hide() {
+    public void unload() {
         mLoaded = false;
-        setVisibility(GONE);
         setAdapter(null);
     }
 
     /**
-     * Determines whether the pager is visible.
+     * Determines whether the pager is loaded.
      *
      * Unlike getVisibility(), this method does not need to be called on the UI
      * thread.
      *
-     * @return Whether the pager and its fragments are being displayed
+     * @return Whether the pager and its fragments are loaded
      */
-    public boolean isVisible() {
+    public boolean isLoaded() {
         return mLoaded;
     }
 
     @Override
     public void setCurrentItem(int item, boolean smoothScroll) {
         super.setCurrentItem(item, smoothScroll);
 
         if (mDecor != null) {
@@ -282,16 +230,29 @@ public class HomePager extends ViewPager
         if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
             // Drop the soft keyboard by stealing focus from the URL bar.
             requestFocus();
         }
 
         return super.onInterceptTouchEvent(event);
     }
 
+    public void setBanner(HomeBanner banner) {
+        mHomeBanner = banner;
+    }
+
+    @Override
+    public boolean dispatchTouchEvent(MotionEvent event) {
+        if (mHomeBanner != null) {
+            mHomeBanner.handleHomeTouch(event);
+        }
+
+        return super.dispatchTouchEvent(event);
+    }
+
     private void updateUiFromPanelConfigs(List<PanelConfig> panelConfigs) {
         // We only care about the adapter if HomePager is currently
         // loaded, which means it's visible in the activity.
         if (!mLoaded) {
             return;
         }
 
         if (mDecor != null) {
@@ -328,26 +289,27 @@ public class HomePager extends ViewPager
             setBackgroundDrawable(mOriginalBackground);
         }
 
         // Re-install the adapter with the final state
         // in the pager.
         setAdapter(adapter);
 
         // Use the default panel as defined in the HomePager's configuration
-        // if the initial panel wasn't explicitly set by the show() caller,
+        // if the initial panel wasn't explicitly set by the load() caller,
         // or if the initial panel is not found in the adapter.
         final int itemPosition = (mInitialPanelId == null) ? -1 : adapter.getItemPosition(mInitialPanelId);
         if (itemPosition > -1) {
             setCurrentItem(itemPosition, false);
             mInitialPanelId = null;
         } else {
             for (int i = 0; i < count; i++) {
                 final PanelConfig panelConfig = enabledPanels.get(i);
                 if (panelConfig.isDefault()) {
+                    mDefaultPageIndex = i;
                     setCurrentItem(i, false);
                     break;
                 }
             }
         }
     }
 
     private class ConfigLoaderCallbacks implements LoaderCallbacks<List<PanelConfig>> {
@@ -360,9 +322,36 @@ public class HomePager extends ViewPager
         public void onLoadFinished(Loader<List<PanelConfig>> loader, List<PanelConfig> panelConfigs) {
             updateUiFromPanelConfigs(panelConfigs);
         }
 
         @Override
         public void onLoaderReset(Loader<List<PanelConfig>> loader) {
         }
     }
+
+    private class PageChangeListener implements ViewPager.OnPageChangeListener {
+        @Override
+        public void onPageSelected(int position) {
+            if (mDecor != null) {
+                mDecor.onPageSelected(position);
+            }
+
+            if (mHomeBanner != null) {
+                mHomeBanner.setEnabled(position == mDefaultPageIndex);
+            }
+        }
+
+        @Override
+        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+            if (mDecor != null) {
+                mDecor.onPageScrolled(position, positionOffset, positionOffsetPixels);
+            }
+
+            if (mHomeBanner != null) {
+                mHomeBanner.setScrollingPages(positionOffsetPixels != 0);
+            }
+        }
+
+        @Override
+        public void onPageScrollStateChanged(int state) { }
+    }
 }
--- a/mobile/android/base/home/TopSitesPanel.java
+++ b/mobile/android/base/home/TopSitesPanel.java
@@ -82,25 +82,16 @@ public class TopSitesPanel extends HomeF
     private TopSitesGridAdapter mGridAdapter;
 
     // List of top sites
     private HomeListView mList;
 
     // Grid of top sites
     private TopSitesGridView mGrid;
 
-    // Banner to show snippets.
-    private HomeBanner mBanner;
-
-    // Raw Y value of the last event that happened on the list view.
-    private float mListTouchY = -1;
-
-    // Scrolling direction of the banner.
-    private boolean mSnapBannerToTop;
-
     // Callbacks used for the search and favicon cursor loaders
     private CursorLoaderCallbacks mCursorLoaderCallbacks;
 
     // Callback for thumbnail loader
     private ThumbnailsLoaderCallbacks mThumbnailsLoaderCallbacks;
 
     // Listener for editing pinned sites.
     private EditPinnedSiteListener mEditPinnedSiteListener;
@@ -221,25 +212,16 @@ public class TopSitesPanel extends HomeF
             }
         });
 
         mGrid.setOnUrlOpenListener(mUrlOpenListener);
         mGrid.setOnEditPinnedSiteListener(mEditPinnedSiteListener);
 
         registerForContextMenu(mList);
         registerForContextMenu(mGrid);
-
-        mBanner = (HomeBanner) view.findViewById(R.id.home_banner);
-        mList.setOnTouchListener(new OnTouchListener() {
-            @Override
-            public boolean onTouch(View v, MotionEvent event) {
-                TopSitesPanel.this.handleListTouchEvent(event);
-                return false;
-            }
-        });
     }
 
     @Override
     public void onDestroyView() {
         super.onDestroyView();
 
         // Discard any additional item clicks on the list
         // as the panel is getting destroyed (see bug 930160).
@@ -468,70 +450,16 @@ public class TopSitesPanel extends HomeF
                 @Override
                 public void run() {
                     BrowserDB.pinSite(context.getContentResolver(), url, title, position);
                 }
             });
         }
     }
 
-    private void handleListTouchEvent(MotionEvent event) {
-        // Ignore the event if the banner is hidden for this session.
-        if (mBanner.isDismissed()) {
-            return;
-        }
-
-        switch (event.getActionMasked()) {
-            case MotionEvent.ACTION_DOWN: {
-                mListTouchY = event.getRawY();
-                break;
-            }
-
-            case MotionEvent.ACTION_MOVE: {
-                // There is a chance that we won't receive ACTION_DOWN, if the touch event
-                // actually started on the Grid instead of the List. Treat this as first event.
-                if (mListTouchY == -1) {
-                    mListTouchY = event.getRawY();
-                    return;
-                }
-
-                final float curY = event.getRawY();
-                final float delta = mListTouchY - curY;
-                mSnapBannerToTop = (delta > 0.0f) ? false : true;
-
-                final float height = mBanner.getHeight();
-                float newTranslationY = ViewHelper.getTranslationY(mBanner) + delta;
-
-                // Clamp the values to be between 0 and height.
-                if (newTranslationY < 0.0f) {
-                    newTranslationY = 0.0f;
-                } else if (newTranslationY > height) {
-                    newTranslationY = height;
-                }
-
-                ViewHelper.setTranslationY(mBanner, newTranslationY);
-                mListTouchY = curY;
-                break;
-            }
-
-            case MotionEvent.ACTION_UP:
-            case MotionEvent.ACTION_CANCEL: {
-                mListTouchY = -1;
-                final float y = ViewHelper.getTranslationY(mBanner);
-                final float height = mBanner.getHeight();
-                if (y > 0.0f && y < height) {
-                    final PropertyAnimator animator = new PropertyAnimator(100);
-                    animator.attach(mBanner, Property.TRANSLATION_Y, mSnapBannerToTop ? 0 : height);
-                    animator.start();
-                }
-                break;
-            }
-        }
-    }
-
     private void updateUiFromCursor(Cursor c) {
         mList.setHeaderDividersEnabled(c != null && c.getCount() > mMaxGridEntries);
     }
 
     private static class TopSitesLoader extends SimpleCursorLoader {
         // Max number of search results
         private static final int SEARCH_LIMIT = 30;
         private int mMaxGridEntries;
--- a/mobile/android/base/menu/GeckoMenu.java
+++ b/mobile/android/base/menu/GeckoMenu.java
@@ -21,16 +21,17 @@ import android.view.View;
 import android.view.ViewGroup;
 import android.widget.AdapterView;
 import android.widget.BaseAdapter;
 import android.widget.LinearLayout;
 import android.widget.ListView;
 
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
 public class GeckoMenu extends ListView 
                        implements Menu,
                                   AdapterView.OnItemClickListener,
                                   GeckoMenuItem.OnShowAsActionChangedListener {
     private static final String LOGTAG = "GeckoMenu";
@@ -631,16 +632,28 @@ public class GeckoMenu extends ListView
             if (indexOfChild(actionItem) != -1) {
                 LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) actionItem.getLayoutParams();
                 mWeightSum -= params.weight;
                 removeView(actionItem);
             }
         }
     }
 
+    public void refresh() {
+        for (Iterator<GeckoMenuItem> i = mPrimaryActionItems.keySet().iterator(); i.hasNext();) {
+            GeckoMenuItem item = i.next();
+            item.refreshIfChanged();
+        }
+
+        for (Iterator<GeckoMenuItem> i = mSecondaryActionItems.keySet().iterator(); i.hasNext();) {
+            GeckoMenuItem item = i.next();
+            item.refreshIfChanged();
+        }
+    }
+
     // Adapter to bind menu items to the list.
     private class MenuItemsAdapter extends BaseAdapter {
         private static final int VIEW_TYPE_DEFAULT = 0;
         private static final int VIEW_TYPE_ACTION_MODE = 1;
 
         private List<GeckoMenuItem> mItems;
 
         public MenuItemsAdapter() {
--- a/mobile/android/base/menu/GeckoMenuItem.java
+++ b/mobile/android/base/menu/GeckoMenuItem.java
@@ -217,16 +217,27 @@ public class GeckoMenuItem implements Me
                 }
             });
         }
 
         mShowAsActionChangedListener.onShowAsActionChanged(this);
         return this;
     }
 
+    public void refreshIfChanged() {
+        if (mActionProvider == null)
+            return;
+
+        if (mActionProvider instanceof GeckoActionProvider) {
+            if (((GeckoActionProvider) mActionProvider).hasChanged()) {
+                mShowAsActionChangedListener.onShowAsActionChanged(GeckoMenuItem.this);
+            }
+        }
+    }
+
     @Override
     public MenuItem setActionView(int resId) {
         return this;
     }
 
     @Override
     public MenuItem setActionView(View view) {
         return this;
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -149,17 +149,17 @@ gbjar.sources += [
     'GeckoEditable.java',
     'GeckoEvent.java',
     'GeckoHalDefines.java',
     'GeckoInputConnection.java',
     'GeckoJavaSampler.java',
     'GeckoMessageReceiver.java',
     'GeckoNetworkManager.java',
     'GeckoProfile.java',
-    'GeckoScreenOrientationListener.java',
+    'GeckoScreenOrientation.java',
     'GeckoSmsManager.java',
     'GeckoThread.java',
     'GeckoUpdateReceiver.java',
     'GeckoView.java',
     'GeckoViewChrome.java',
     'GeckoViewContent.java',
     'gfx/Axis.java',
     'gfx/BitmapUtils.java',
--- a/mobile/android/base/resources/layout-large-v11/home_pager.xml
+++ b/mobile/android/base/resources/layout-large-v11/home_pager.xml
@@ -6,18 +6,17 @@
 <!-- This file is used to include the home pager in gecko app
      layout based on screen size -->
 
 <org.mozilla.gecko.home.HomePager xmlns:android="http://schemas.android.com/apk/res/android"
                                   xmlns:gecko="http://schemas.android.com/apk/res-auto"
                                   android:id="@+id/home_pager"
                                   android:layout_width="fill_parent"
                                   android:layout_height="fill_parent"
-                                  android:background="@android:color/white"
-                                  android:visibility="gone">
+                                  android:background="@android:color/white">
 
     <org.mozilla.gecko.home.TabMenuStrip android:layout_width="fill_parent"
                                          android:layout_height="32dip"
                                          android:background="@color/background_light"
                                          android:layout_gravity="top"
                                          gecko:strip="@drawable/home_tab_menu_strip"/>
 
 </org.mozilla.gecko.home.HomePager>
--- a/mobile/android/base/resources/layout/gecko_app.xml
+++ b/mobile/android/base/resources/layout/gecko_app.xml
@@ -30,16 +30,27 @@
                          android:layout_width="fill_parent"
                          android:layout_height="fill_parent">
 
                 <ViewStub android:id="@+id/home_pager_stub"
                           android:layout="@layout/home_pager"
                           android:layout_width="fill_parent"
                           android:layout_height="fill_parent"/>
 
+                <org.mozilla.gecko.home.HomeBanner android:id="@+id/home_banner"
+                                                   style="@style/Widget.HomeBanner"
+                                                   android:layout_width="fill_parent"
+                                                   android:layout_height="@dimen/home_banner_height"
+                                                   android:background="@drawable/home_banner"
+                                                   android:layout_gravity="bottom"
+                                                   android:gravity="center_vertical"
+                                                   android:visibility="gone"
+                                                   android:clickable="true"
+                                                   android:focusable="true"
+                                                   android:translationY="@dimen/home_banner_height"/>
 
             </FrameLayout>
 
         </RelativeLayout>
 
         <org.mozilla.gecko.FindInPageBar android:id="@+id/find_in_page"
                                          android:layout_width="fill_parent"
                                          android:layout_height="wrap_content"
--- a/mobile/android/base/resources/layout/home_pager.xml
+++ b/mobile/android/base/resources/layout/home_pager.xml
@@ -6,18 +6,17 @@
 <!-- This file is used to include the home pager in gecko app
      layout based on screen size -->
 
 <org.mozilla.gecko.home.HomePager xmlns:android="http://schemas.android.com/apk/res/android"
                                   xmlns:gecko="http://schemas.android.com/apk/res-auto"
                                   android:id="@+id/home_pager"
                                   android:layout_width="fill_parent"
                                   android:layout_height="fill_parent"
-                                  android:background="@android:color/white"
-                                  android:visibility="gone">
+                                  android:background="@android:color/white">
 
     <org.mozilla.gecko.home.HomePagerTabStrip android:layout_width="fill_parent"
                                               android:layout_height="32dip"
                                               android:layout_gravity="top"
                                               android:gravity="bottom"
                                               android:background="@color/background_light"
                                               gecko:tabIndicatorColor="@color/text_color_highlight"
                                               android:textAppearance="@style/TextAppearance.Widget.HomePagerTabStrip"/>
--- a/mobile/android/base/resources/layout/home_top_sites_panel.xml
+++ b/mobile/android/base/resources/layout/home_top_sites_panel.xml
@@ -1,28 +1,10 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!-- 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/. -->
 
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-             android:layout_width="fill_parent"
-             android:layout_height="fill_parent"
-             android:orientation="vertical">
-
-    <org.mozilla.gecko.home.HomeListView
-            android:id="@+id/list"
-            style="@style/Widget.TopSitesListView"
-            android:layout_width="fill_parent"
-            android:layout_height="fill_parent"/>
-
-    <org.mozilla.gecko.home.HomeBanner android:id="@+id/home_banner"
-                                       style="@style/Widget.HomeBanner"
-                                       android:layout_width="fill_parent"
-                                       android:layout_height="@dimen/home_banner_height"
-                                       android:background="@drawable/home_banner"
-                                       android:layout_gravity="bottom"
-                                       android:gravity="center_vertical"
-                                       android:visibility="gone"
-                                       android:clickable="true"
-                                       android:focusable="true"/>
-
-</FrameLayout>
+<org.mozilla.gecko.home.HomeListView xmlns:android="http://schemas.android.com/apk/res/android"
+                                     android:id="@+id/list"
+                                     style="@style/Widget.TopSitesListView"
+                                     android:layout_width="fill_parent"
+                                     android:layout_height="fill_parent"/>
--- a/mobile/android/base/tests/BaseTest.java
+++ b/mobile/android/base/tests/BaseTest.java
@@ -478,22 +478,22 @@ abstract class BaseTest extends Activity
                 mSolo.clickOnText("(^More$|^Tools$)");
             }
             waitForText(itemName);
             mSolo.clickOnText(itemName);
         }
     }
 
     public final void verifyHomePagerHidden() {
-        final View homePagerView = mSolo.getView(R.id.home_pager);
+        final View homePagerContainer = mSolo.getView(R.id.home_pager_container);
 
         boolean rc = waitForCondition(new Condition() {
             @Override
             public boolean isSatisfied() {
-                return homePagerView.getVisibility() != View.VISIBLE;
+                return homePagerContainer.getVisibility() != View.VISIBLE;
             }
         }, MAX_WAIT_HOME_PAGER_HIDDEN_MS);
 
         if (!rc) {
             mAsserter.ok(rc, "Verify HomePager is hidden", "HomePager is hidden");
         }
     }
 
--- a/mobile/android/base/tests/components/AboutHomeComponent.java
+++ b/mobile/android/base/tests/components/AboutHomeComponent.java
@@ -54,16 +54,20 @@ public class AboutHomeComponent extends 
     // The percentage of the panel to swipe between 0 and 1. This value was set through
     // testing: 0.55f was tested on try and fails on armv6 devices.
     private static final float SWIPE_PERCENTAGE = 0.70f;
 
     public AboutHomeComponent(final UITestContext testContext) {
         super(testContext);
     }
 
+    private View getHomePagerContainer() {
+        return mSolo.getView(R.id.home_pager_container);
+    }
+
     private ViewPager getHomePagerView() {
         return (ViewPager) mSolo.getView(R.id.home_pager);
     }
 
     private View getHomeBannerView() {
         return mSolo.getView(R.id.home_banner);
     }
 
@@ -72,36 +76,42 @@ public class AboutHomeComponent extends 
 
         final int expectedPanelIndex = getPanelIndexForDevice(expectedPanel.ordinal());
         assertEquals("The current HomePager panel is " + expectedPanel,
                      expectedPanelIndex, getHomePagerView().getCurrentItem());
         return this;
     }
 
     public AboutHomeComponent assertNotVisible() {
-        assertFalse("The HomePager is not visible",
-                    getHomePagerView().getVisibility() == View.VISIBLE);
+        assertTrue("The HomePager is not visible",
+                    getHomePagerContainer().getVisibility() != View.VISIBLE ||
+                    getHomePagerView().getVisibility() != View.VISIBLE);
         return this;
     }
 
     public AboutHomeComponent assertVisible() {
-        assertEquals("The HomePager is visible",
-                     View.VISIBLE, getHomePagerView().getVisibility());
+        assertTrue("The HomePager is visible",
+                    getHomePagerContainer().getVisibility() == View.VISIBLE &&
+                    getHomePagerView().getVisibility() == View.VISIBLE);
         return this;
     }
 
     public AboutHomeComponent assertBannerNotVisible() {
-        assertFalse("The HomeBanner is not visible",
-                    getHomeBannerView().getVisibility() == View.VISIBLE);
+        View banner = getHomeBannerView();
+        assertTrue("The HomeBanner is not visible",
+                    getHomePagerContainer().getVisibility() != View.VISIBLE ||
+                    banner.getVisibility() != View.VISIBLE ||
+                    banner.getTranslationY() == banner.getHeight());
         return this;
     }
 
     public AboutHomeComponent assertBannerVisible() {
-        assertEquals("The HomeBanner is visible",
-                     View.VISIBLE, getHomeBannerView().getVisibility());
+        assertTrue("The HomeBanner is visible",
+                    getHomePagerContainer().getVisibility() == View.VISIBLE &&
+                    getHomeBannerView().getVisibility() == View.VISIBLE);
         return this;
     }
 
     public AboutHomeComponent assertBannerText(String text) {
         assertBannerVisible();
 
         final TextView textView = (TextView) getHomeBannerView().findViewById(R.id.text);
         assertEquals("The correct HomeBanner text is shown",
--- a/mobile/android/base/tests/testHomeBanner.java
+++ b/mobile/android/base/tests/testHomeBanner.java
@@ -17,17 +17,19 @@ public class testHomeBanner extends UITe
         GeckoHelper.blockForReady();
 
         // Make sure the banner is not visible to start.
         mAboutHome.assertVisible()
                   .assertBannerNotVisible();
 
         // These test methods depend on being run in this order.
         addBannerTest();
-        removeBannerTest();
+        // TODO: API doesn't actually support this but it used to work due to how the banner was
+        // part of TopSitesPanel's lifecycle
+        // removeBannerTest();
 
         // Make sure to test dismissing the banner after everything else, since dismissing
         // the banner will prevent it from showing up again.
         dismissBannerTest();
     }
 
     /**
      * Adds a banner message, verifies that it appears when it should, and verifies that
@@ -36,33 +38,31 @@ public class testHomeBanner extends UITe
      * Note: This test does not remove the message after it is done.
      */
     private void addBannerTest() {
         addBannerMessage();
 
         // Load about:home again, and make sure the onshown handler is called.
         Actions.EventExpecter eventExpecter = getActions().expectGeckoEvent("TestHomeBanner:MessageShown");
         NavigationHelper.enterAndLoadUrl("about:home");
-        eventExpecter.blockForEvent();
+        // TODO: Add shown event passing from Java: bug 974723
+        // eventExpecter.blockForEvent();
 
         // Verify that the banner is visible with the correct text.
         mAboutHome.assertBannerText(TEXT);
 
         // Test to make sure the onclick handler is called.
         eventExpecter = getActions().expectGeckoEvent("TestHomeBanner:MessageClicked");
         mAboutHome.clickOnBanner();
         eventExpecter.blockForEvent();
 
         // Verify that the banner isn't visible after navigating away from about:home.
         NavigationHelper.enterAndLoadUrl("about:firefox");
 
-        // AboutHomeComponent calls mSolo.getView, which will fail an assertion if the
-        // view is not present, so we need to use findViewById in this case.
-        final View banner = getActivity().findViewById(R.id.home_banner);
-        assertTrue("The HomeBanner is not visible", banner == null || banner.getVisibility() != View.VISIBLE);
+        mAboutHome.assertBannerNotVisible();
     }
 
 
     /**
      * Removes a banner message, and verifies that it no longer appears on about:home.
      *
      * Note: This test expects for a message to have been added before it runs.
      */
--- a/mobile/android/base/widget/ActivityChooserModel.java
+++ b/mobile/android/base/widget/ActivityChooserModel.java
@@ -314,16 +314,18 @@ public class ActivityChooserModel extend
      */
     private boolean mHistoricalRecordsChanged = true;
 
     /**
      * Flag whether to reload the activities for the current intent.
      */
     private boolean mReloadActivities = false;
 
+    private long mLastChanged = 0;
+
     /**
      * Policy for controlling how the model handles chosen activities.
      */
     private OnChooseActivityListener mActivityChoserModelPolicy;
 
     /**
      * Gets the data model backed by the contents of the provided file with historical data.
      * Note that only one data model is backed by a given file, thus multiple calls with
@@ -740,16 +742,17 @@ public class ActivityChooserModel extend
             mActivities.clear();
             List<ResolveInfo> resolveInfos = mContext.getPackageManager()
                     .queryIntentActivities(mIntent, 0);
             final int resolveInfoCount = resolveInfos.size();
             for (int i = 0; i < resolveInfoCount; i++) {
                 ResolveInfo resolveInfo = resolveInfos.get(i);
                 mActivities.add(new ActivityResolveInfo(resolveInfo));
             }
+            mLastChanged = System.currentTimeMillis();
             return true;
         }
         return false;
     }
 
     /**
      * Reads the historical data if necessary which is it has
      * changed, there is a history file, and there is not persist
@@ -1215,12 +1218,16 @@ public class ActivityChooserModel extend
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
             if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
                 String packageName = intent.getData().getSchemeSpecificPart();
                 removeHistoricalRecordsForPackage(packageName);
             }
 
             mReloadActivities = true;
+            mLastChanged = System.currentTimeMillis();
         }
     }
+
+    public long getLastChanged() {
+        return mLastChanged;
+    }
 }
-
--- a/mobile/android/base/widget/GeckoActionProvider.java
+++ b/mobile/android/base/widget/GeckoActionProvider.java
@@ -16,16 +16,17 @@ import android.view.ActionProvider;
 import android.view.MenuItem;
 import android.view.MenuItem.OnMenuItemClickListener;
 import android.view.SubMenu;
 import android.view.View;
 import android.view.View.OnClickListener;
 
 public class GeckoActionProvider extends ActionProvider {
     private static int MAX_HISTORY_SIZE = 2;
+    private long mLastChanged = 0;
 
     /**
      * A listener to know when a target was selected.
      * When setting a provider, the activity can listen to this,
      * to close the menu.
      */
     public interface OnTargetSelectedListener {
         public void onTargetSelected();
@@ -74,16 +75,24 @@ public class GeckoActionProvider extends
 
         return view;
     }
 
     public View getView() {
         return onCreateActionView();
     }
 
+    public boolean hasChanged() {
+        ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mHistoryFileName);
+        long lastChanged = dataModel.getLastChanged();
+        boolean ret = lastChanged != mLastChanged;
+        mLastChanged = lastChanged;
+        return ret;
+    }
+
     @Override
     public boolean hasSubMenu() {
         return true;
     }
 
     @Override
     public void onPrepareSubMenu(SubMenu subMenu) {
         // Clear since the order of items may change.
--- a/mobile/android/modules/HelperApps.jsm
+++ b/mobile/android/modules/HelperApps.jsm
@@ -37,19 +37,21 @@ var HelperApps =  {
   get defaultHttpHandlers() {
     delete this.defaultHttpHandlers;
     this.defaultHttpHandlers = this.getAppsForProtocol("http");
     return this.defaultHttpHandlers;
   },
 
   get defaultHtmlHandlers() {
     delete this.defaultHtmlHandlers;
-    let handlers = this.getAppsForUri(Services.io.newURI("http://www.example.com/index.html", null, null));
+    this.defaultHtmlHandlers = {};
+    let handlers = this.getAppsForUri(Services.io.newURI("http://www.example.com/index.html", null, null), {
+      filterHtml: false
+    });
 
-    this.defaultHtmlHandlers = {};
     handlers.forEach(function(app) {
       this.defaultHtmlHandlers[app.name] = app;
     }, this);
     return this.defaultHtmlHandlers;
   },
 
   get protoSvc() {
     delete this.protoSvc;
--- a/mobile/android/modules/Home.jsm
+++ b/mobile/android/modules/Home.jsm
@@ -205,58 +205,56 @@ let HomePanels = (function () {
     for (let key in obj) {
       if (obj[key] == value) {
         return true;
       }
     }
     return false;
   };
 
+  let _assertPanelExists = function(id) {
+    if (!(id in _panels)) {
+      throw "Home.panels: Panel doesn't exist: id = " + id;
+    }
+  };
+
   return Object.freeze({
     // Valid layouts for a panel.
     Layout: Object.freeze({
       FRAME: "frame"
     }),
 
     // Valid types of views for a dataset.
     View: Object.freeze({
       LIST: "list",
       GRID: "grid"
     }),
 
-    // Valid actions for a panel.
-    Action: Object.freeze({
-      INSTALL: "install",
-      REFRESH: "refresh"
-    }),
-
     // Valid item types for a panel view.
     Item: Object.freeze({
       ARTICLE: "article",
       IMAGE: "image"
     }),
 
     // Valid item handlers for a panel view.
     ItemHandler: Object.freeze({
       BROWSER: "browser",
       INTENT: "intent"
     }),
 
-    add: function(options) {
+    register: function(options) {
       let panel = new Panel(options);
-      if (!panel.id || !panel.title) {
-        throw "Home.panels: Can't create a home panel without an id and title!";
+
+      // Bail if the panel already exists
+      if (panel.id in _panels) {
+        throw "Home.panels: Panel already exists: id = " + panel.id;
       }
 
-      let action = options.action;
-
-      // Bail if the panel already exists, except when we're refreshing
-      // an existing panel instance.
-      if (panel.id in _panels && action != this.Action.REFRESH) {
-        throw "Home.panels: Panel already exists: id = " + panel.id;
+      if (!panel.id || !panel.title) {
+        throw "Home.panels: Can't create a home panel without an id and title!";
       }
 
       if (!_valueExists(this.Layout, panel.layout)) {
         throw "Home.panels: Invalid layout for panel: panel.id = " + panel.id + ", panel.layout =" + panel.layout;
       }
 
       for (let view of panel.views) {
         if (!_valueExists(this.View, view.type)) {
@@ -283,51 +281,48 @@ let HomePanels = (function () {
         }
 
         if (!view.dataset) {
           throw "Home.panels: No dataset provided for view: panel.id = " + panel.id + ", view.type = " + view.type;
         }
       }
 
       _panels[panel.id] = panel;
-
-      if (action) {
-        let messageType;
-
-        switch(action) {
-          case this.Action.INSTALL:
-            messageType = "HomePanels:Install";
-            break;
+    },
 
-          case this.Action.REFRESH:
-            messageType = "HomePanels:Refresh";
-            break;
+    unregister: function(id) {
+      _assertPanelExists(id);
 
-          default:
-            throw "Home.panels: Invalid action for panel: panel.id = " + panel.id + ", action = " + action;
-        }
-
-        sendMessageToJava({
-          type: messageType,
-          panel: _panelToJSON(panel)
-        });
-      }
+      delete _panels[id];
     },
 
-    remove: function(id) {
-      if (!(id in _panels)) {
-        throw "Home.panels: Panel doesn't exist: id = " + id;
-      }
-
-      let panel = _panels[id];
-      delete _panels[id];
+    install: function(id) {
+      _assertPanelExists(id);
 
       sendMessageToJava({
-        type: "HomePanels:Remove",
-        panel: _panelToJSON(panel)
+        type: "HomePanels:Install",
+        panel: _panelToJSON(_panels[id])
+      });
+    },
+
+    uninstall: function(id) {
+      _assertPanelExists(id);
+
+      sendMessageToJava({
+        type: "HomePanels:Uninstall",
+        id: id
+      });
+    },
+
+    update: function(id) {
+      _assertPanelExists(id);
+
+      sendMessageToJava({
+        type: "HomePanels:Update",
+        panel: _panelToJSON(_panels[id])
       });
     }
   });
 })();
 
 // Public API
 this.Home = Object.freeze({
   banner: HomeBanner,
--- a/mozglue/linker/ElfLoader.h
+++ b/mozglue/linker/ElfLoader.h
@@ -158,17 +158,17 @@ public:
       mozilla::AtomicRefCounted<LibHandle>::Release();
     }
     return ret;
   }
 
   /**
    * Returns the number of direct references
    */
-  int DirectRefCount()
+  MozRefCountType DirectRefCount()
   {
     return directRefCnt;
   }
 
   /**
    * Returns the complete size of the file or stream behind the library
    * handle.
    */
@@ -205,17 +205,17 @@ protected:
    * to do this without RTTI)
    */
   friend class ElfLoader;
   friend class CustomElf;
   friend class SEGVHandler;
   virtual bool IsSystemElf() const { return false; }
 
 private:
-  int directRefCnt;
+  MozRefCountType directRefCnt;
   char *path;
 
   /* Mappable object keeping the result of GetMappable() */
   mutable mozilla::RefPtr<Mappable> mappable;
 };
 
 /**
  * Specialized RefCounted<LibHandle>::Release. Under normal operation, when
--- a/testing/mozbase/mozprocess/mozprocess/processhandler.py
+++ b/testing/mozbase/mozprocess/mozprocess/processhandler.py
@@ -35,18 +35,18 @@ class ProcessHandlerMixin(object):
 
     :param cmd: command to run. May be a string or a list. If specified as a list, the first element will be interpreted as the command, and all additional elements will be interpreted as arguments to that command.
     :param args: list of arguments to pass to the command (defaults to None). Must not be set when `cmd` is specified as a list.
     :param cwd: working directory for command (defaults to None).
     :param env: is the environment to use for the process (defaults to os.environ).
     :param ignore_children: causes system to ignore child processes when True, defaults to False (which tracks child processes).
     :param kill_on_timeout: when True, the process will be killed when a timeout is reached. When False, the caller is responsible for killing the process. Failure to do so could cause a call to wait() to hang indefinitely. (Defaults to True.)
     :param processOutputLine: function or list of functions to be called for each line of output produced by the process (defaults to None).
-    :param onTimeout: function to be called when the process times out.
-    :param onFinish: function to be called when the process terminates normally without timing out.
+    :param onTimeout: function or list of functions to be called when the process times out.
+    :param onFinish: function or list of functions to be called when the process terminates normally without timing out.
     :param kwargs: additional keyword args to pass directly into Popen.
 
     NOTE: Child processes will be tracked by default.  If for any reason
     we are unable to track child processes and ignore_children is set to False,
     then we will fall back to only tracking the root process.  The fallback
     will be logged.
     """
 
@@ -608,17 +608,21 @@ falling back to not using job objects fo
         if env is None:
             env = os.environ.copy()
         self.env = env
 
         # handlers
         if callable(processOutputLine):
             processOutputLine = [processOutputLine]
         self.processOutputLineHandlers = list(processOutputLine)
+        if callable(onTimeout):
+            onTimeout = [onTimeout]
         self.onTimeoutHandlers = list(onTimeout)
+        if callable(onFinish):
+            onFinish = [onFinish]
         self.onFinishHandlers = list(onFinish)
 
         # It is common for people to pass in the entire array with the cmd and
         # the args together since this is how Popen uses it.  Allow for that.
         if isinstance(self.cmd, list):
             if self.args != None:
                 raise TypeError("cmd and args must not both be lists")
             (self.cmd, self.args) = (self.cmd[0], self.cmd[1:])
--- a/toolkit/components/places/tests/cpp/mock_Link.h
+++ b/toolkit/components/places/tests/cpp/mock_Link.h
@@ -118,19 +118,19 @@ Link::SizeOfExcludingThis(mozilla::Mallo
 
 void
 Link::URLSearchParamsUpdated()
 {
   NS_NOTREACHED("Unexpected call to Link::URLSearchParamsUpdated");
 }
 
 void
-Link::UpdateURLSearchParams()
+Link::URLSearchParamsNeedsUpdates()
 {
-  NS_NOTREACHED("Unexpected call to Link::UpdateURLSearchParams");
+  NS_NOTREACHED("Unexpected call to Link::URLSearchParamsNeedsUpdates");
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(URLSearchParams)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(URLSearchParams)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(URLSearchParams)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(URLSearchParams)
@@ -154,30 +154,29 @@ URLSearchParams::~URLSearchParams()
 
 JSObject*
 URLSearchParams::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
   return nullptr;
 }
 
 void
-URLSearchParams::ParseInput(const nsACString& aInput,
-                            URLSearchParamsObserver* aObserver)
+URLSearchParams::ParseInput(const nsACString& aInput)
 {
   NS_NOTREACHED("Unexpected call to URLSearchParams::ParseInput");
 }
 
 void
-URLSearchParams::AddObserver(URLSearchParamsObserver* aObserver)
+URLSearchParams::CopyFromURLSearchParams(URLSearchParams& aSearchParams)
 {
-  NS_NOTREACHED("Unexpected call to URLSearchParams::SetObserver");
+  NS_NOTREACHED("Unexpected call to URLSearchParams::CopyFromURLSearchParams");
 }
 
 void
-URLSearchParams::RemoveObserver(URLSearchParamsObserver* aObserver)
+URLSearchParams::SetObserver(URLSearchParamsObserver* aObserver)
 {
   NS_NOTREACHED("Unexpected call to URLSearchParams::SetObserver");
 }
 
 void
 URLSearchParams::Serialize(nsAString& aValue) const
 {
   NS_NOTREACHED("Unexpected call to URLSearchParams::Serialize");
@@ -228,17 +227,24 @@ URLSearchParams::Delete(const nsAString&
 
 void
 URLSearchParams::DeleteAll()
 {
   NS_NOTREACHED("Unexpected call to URLSearchParams::DeleteAll");
 }
 
 void
-URLSearchParams::NotifyObservers(URLSearchParamsObserver* aExceptObserver)
+URLSearchParams::NotifyObserver()
 {
-  NS_NOTREACHED("Unexpected call to URLSearchParams::NotifyObservers");
+  NS_NOTREACHED("Unexpected call to URLSearchParams::NotifyObserver");
 }
 
+void
+URLSearchParams::Invalidate()
+{
+  NS_NOTREACHED("Unexpected call to URLSearchParams::Invalidate");
+}
+
+
 } // namespace dom
 } // namespace mozilla
 
 #endif // mock_Link_h__
--- a/widget/cocoa/nsMenuItemIconX.mm
+++ b/widget/cocoa/nsMenuItemIconX.mm
@@ -36,16 +36,17 @@
 #include "imgLoader.h"
 #include "imgRequestProxy.h"
 #include "nsMenuItemX.h"
 #include "gfxImageSurface.h"
 #include "imgIContainer.h"
 #include "nsCocoaUtils.h"
 #include "nsContentUtils.h"
 
+using mozilla::RefPtr;
 static const uint32_t kIconWidth = 16;
 static const uint32_t kIconHeight = 16;
 static const uint32_t kIconBitsPerComponent = 8;
 static const uint32_t kIconComponents = 4;
 static const uint32_t kIconBitsPerPixel = kIconBitsPerComponent *
                                           kIconComponents;
 static const uint32_t kIconBytesPerRow = kIconWidth * kIconBitsPerPixel / 8;
 static const uint32_t kIconBytes = kIconBytesPerRow * kIconHeight;
--- a/xpcom/base/nscore.h
+++ b/xpcom/base/nscore.h
@@ -24,16 +24,18 @@
  */
 #include <stddef.h>
 #include <stdint.h>
 
 #ifdef __cplusplus
 #  include "mozilla/NullPtr.h"
 #endif
 
+#include "mozilla/RefCountType.h"
+
 /* Core XPCOM declarations. */
 
 /*----------------------------------------------------------------------*/
 /* Import/export defines */
 
 /**
  * Using the visibility("hidden") attribute allows the compiler to use
  * PC-relative addressing to call this function.  If a function does not
@@ -292,29 +294,17 @@
 #endif
 
 
 /**
  * Generic XPCOM result data type
  */
 #include "nsError.h"
 
-/**
- * Reference count values
- *
- * This is the return type for AddRef() and Release() in nsISupports.
- * IUnknown of COM returns an unsigned long from equivalent functions.
- * The following ifdef exists to maintain binary compatibility with
- * IUnknown.
- */
-#ifdef XP_WIN
-typedef unsigned long nsrefcnt;
-#else
-typedef uint32_t nsrefcnt;
-#endif
+typedef MozRefCountType nsrefcnt;
 
 /*
  * Use these macros to do 64bit safe pointer conversions.
  */
 
 #define NS_PTR_TO_INT32(x)  ((int32_t)  (intptr_t) (x))
 #define NS_PTR_TO_UINT32(x) ((uint32_t) (intptr_t) (x))
 #define NS_INT32_TO_PTR(x)  ((void *)   (intptr_t) (x))