merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 02 Jul 2015 15:44:14 +0200
changeset 251110 f1b3144fed94901bc63fb7f94d87be62d9dd382b
parent 251109 cb321bafe1e27583c47f70fb41cf5fe0e769024a (current diff)
parent 251068 2f29a1c8f49eefbafa7e1d3232b8a7625c3465e6 (diff)
child 251111 8ee689f2899a8072a8958344edce57d98ca2791f
child 251161 0b901209064cc5c976de5e29332ff749d5be895a
child 251171 9e698abc07a61a3eabb4d03d66505f232d6a63cb
child 251179 b7e20473015a9198cc694285eab677536a7ecc8e
push id61766
push usercbook@mozilla.com
push dateThu, 02 Jul 2015 13:47:40 +0000
treeherdermozilla-inbound@8ee689f2899a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone42.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
mfbt/IteratorTraits.h
--- a/.hgignore
+++ b/.hgignore
@@ -79,8 +79,12 @@ GPATH
 ^browser/components/loop/standalone/content/config\.js$
 ^browser/components/loop/standalone/node_modules/
 
 # Git clone directory for updating web-platform-tests
 ^testing/web-platform/sync/
 
 # Android Gradle artifacts.
 ^mobile/android/gradle/.gradle
+
+# XCode project cruft
+^embedding/ios/GeckoEmbed/GeckoEmbed.xcodeproj/project.xcworkspace/xcuserdata
+^embedding/ios/GeckoEmbed/GeckoEmbed.xcodeproj/xcuserdata
--- a/browser/base/content/test/plugins/browser_pluginnotification.js
+++ b/browser/base/content/test/plugins/browser_pluginnotification.js
@@ -45,16 +45,67 @@ add_task(function* () {
   // Work around for delayed PluginBindingAttached
   yield promiseUpdatePluginBindings(gTestBrowser);
 
   let pluginInfo = yield promiseForPluginInfo("unknown");
   is(pluginInfo.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_UNSUPPORTED,
      "Test 1a, plugin fallback type should be PLUGIN_UNSUPPORTED");
 });
 
+// Test that the doorhanger is shown when the user clicks on the overlay
+// after having previously blocked the plugin.
+add_task(function* () {
+  updateAllTestPlugins(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
+
+  yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_test.html");
+
+  // Work around for delayed PluginBindingAttached
+  yield promiseUpdatePluginBindings(gTestBrowser);
+
+  yield promisePopupNotification("click-to-play-plugins");
+
+  let pluginInfo = yield promiseForPluginInfo("test");
+  ok(!pluginInfo.activated, "Plugin should not be activated");
+
+  // Simulate clicking the "Allow Now" button.
+  let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
+
+  yield promiseForNotificationShown(notification);
+
+  PopupNotifications.panel.firstChild._secondaryButton.click();
+
+  pluginInfo = yield promiseForPluginInfo("test");
+  ok(pluginInfo.activated, "Plugin should be activated");
+
+  // Simulate clicking the "Block" button.
+  yield promiseForNotificationShown(notification);
+
+  PopupNotifications.panel.firstChild._primaryButton.click();
+
+  pluginInfo = yield promiseForPluginInfo("test");
+  ok(!pluginInfo.activated, "Plugin should not be activated");
+
+  // Simulate clicking the overlay
+  yield ContentTask.spawn(gTestBrowser, {}, function* () {
+    let doc = content.document;
+    let plugin = doc.getElementById("test");
+    let bounds = doc.getAnonymousElementByAttribute(plugin, "anonid", "main").getBoundingClientRect();
+    let left = (bounds.left + bounds.right) / 2;
+    let top = (bounds.top + bounds.bottom) / 2;
+    let utils = content.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+                       .getInterface(Components.interfaces.nsIDOMWindowUtils);
+    utils.sendMouseEvent("mousedown", left, top, 0, 1, 0, false, 0, 0);
+    utils.sendMouseEvent("mouseup", left, top, 0, 1, 0, false, 0, 0);
+  });
+
+  ok(!notification.dismissed, "A plugin notification should be shown.");
+
+  clearAllPluginPermissions();
+});
+
 // Tests that going back will reshow the notification for click-to-play plugins
 add_task(function* () {
   updateAllTestPlugins(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
 
   yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_test.html");
 
   // Work around for delayed PluginBindingAttached
   yield promiseUpdatePluginBindings(gTestBrowser);
--- a/browser/modules/PluginContent.jsm
+++ b/browser/modules/PluginContent.jsm
@@ -718,22 +718,25 @@ PluginContent.prototype = {
 
     let pluginFound = false;
     for (let plugin of plugins) {
       plugin.QueryInterface(Ci.nsIObjectLoadingContent);
       if (!this.isKnownPlugin(plugin)) {
         continue;
       }
       if (pluginInfo.permissionString == pluginHost.getPermissionStringForType(plugin.actualType)) {
+        let overlay = this.getPluginUI(plugin, "main");
         pluginFound = true;
         if (newState == "block") {
+          if (overlay) {
+            overlay.addEventListener("click", this, true);
+          }
           plugin.reload(true);
         } else {
           if (this.canActivatePlugin(plugin)) {
-            let overlay = this.getPluginUI(plugin, "main");
             if (overlay) {
               overlay.removeEventListener("click", this, true);
             }
             plugin.playPlugin();
           }
         }
       }
     }
--- a/db/sqlite3/src/moz.build
+++ b/db/sqlite3/src/moz.build
@@ -48,16 +48,20 @@ DEFINES['SQLITE_MAX_SCHEMA_RETRY'] = 25
 # because we don't support Win9x.
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     DEFINES['SQLITE_WIN32_GETVERSIONEX'] = 0
 
 # -DSQLITE_ENABLE_LOCKING_STYLE=1 to help with AFP folders
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
     DEFINES['SQLITE_ENABLE_LOCKING_STYLE'] = 1
 
+# sqlite defaults this to on on __APPLE_ but it breaks on newer iOS SDKs
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'uikit':
+    DEFINES['SQLITE_ENABLE_LOCKING_STYLE'] = 0
+
 # Turn on SQLite's assertions in debug builds.
 if CONFIG['MOZ_DEBUG']:
     DEFINES['SQLITE_DEBUG'] = 1
 
 if CONFIG['OS_TARGET'] == 'Android':
     # default to user readable only to fit Android security model
     DEFINES['SQLITE_DEFAULT_FILE_PERMISSIONS'] = '0600'
 
--- a/dom/activities/ActivitiesService.jsm
+++ b/dom/activities/ActivitiesService.jsm
@@ -29,17 +29,17 @@ XPCOMUtils.defineLazyServiceGetter(this,
 
 this.EXPORTED_SYMBOLS = [];
 
 function debug(aMsg) {
   //dump("-- ActivitiesService.jsm " + Date.now() + " " + aMsg + "\n");
 }
 
 const DB_NAME    = "activities";
-const DB_VERSION = 1;
+const DB_VERSION = 2;
 const STORE_NAME = "activities";
 
 function ActivitiesDb() {
 
 }
 
 ActivitiesDb.prototype = {
   __proto__: IndexedDBHelper.prototype,
@@ -58,38 +58,109 @@ ActivitiesDb.prototype = {
    *  manifest:            String
    *  name:                String
    *  icon:                String
    *  description:         jsval
    * }
    */
   upgradeSchema: function actdb_upgradeSchema(aTransaction, aDb, aOldVersion, aNewVersion) {
     debug("Upgrade schema " + aOldVersion + " -> " + aNewVersion);
+
+    let self = this;
+
+    function upgrade(currentVersion) {
+      let next = upgrade.bind(self, currentVersion + 1);
+      switch (currentVersion) {
+        case 0:
+          self.createSchema(aDb, next);
+          break;
+        case 1:
+          self.upgradeSchemaVersion2(aDb, aTransaction, next);
+          break;
+      }
+    }
+
+    upgrade(aOldVersion);
+  },
+
+  createSchema: function(aDb, aNext) {
     let objectStore = aDb.createObjectStore(STORE_NAME, { keyPath: "id" });
 
     // indexes
     objectStore.createIndex("name", "name", { unique: false });
     objectStore.createIndex("manifest", "manifest", { unique: false });
 
     debug("Created object stores and indexes");
+
+    aNext();
+  },
+
+  upgradeSchemaVersion2: function(aDb, aTransaction, aNext) {
+    debug("Upgrading DB to version 2");
+
+    // In order to be able to have multiple activities with same name
+    // but different descriptions, we need to update the keypath from
+    // a hash made from {manifest, name} to a hash made from {manifest,
+    // name, description}.
+    //
+    // Unfortunately, updating the keypath is not allowed by IDB, so we
+    // need to remove and recreate the activities object store.
+
+    let activities = [];
+    let objectStore = aTransaction.objectStore(STORE_NAME);
+    objectStore.openCursor().onsuccess = (event) => {
+      let cursor = event.target.result;
+      if (!cursor) {
+        aDb.deleteObjectStore(STORE_NAME);
+
+        let objectStore = aDb.createObjectStore(STORE_NAME, { keyPath: "id" });
+
+        // indexes
+        objectStore.createIndex("name", "name", { unique: false });
+        objectStore.createIndex("manifest", "manifest", { unique: false });
+
+        this.add(activities, () => {
+          debug("DB upgraded to version 2");
+          aNext();
+        }, () => {
+          dump("Error upgrading DB to version 2 " + error + "\n");
+        });
+        return;
+      }
+
+      let activity = cursor.value;
+      debug("Upgrading activity " + JSON.stringify(activity));
+      activity.id = this.createId(activity);
+      activities.push(activity);
+      cursor.continue();
+    };
   },
 
   // unique ids made of (uri, action)
   createId: function actdb_createId(aObject) {
     let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
                       .createInstance(Ci.nsIScriptableUnicodeConverter);
     converter.charset = "UTF-8";
 
     let hasher = Cc["@mozilla.org/security/hash;1"]
                    .createInstance(Ci.nsICryptoHash);
     hasher.init(hasher.SHA1);
 
     // add uri and action to the hash
-    ["manifest", "name"].forEach(function(aProp) {
-      let data = converter.convertToByteArray(aObject[aProp], {});
+    ["manifest", "name", "description"].forEach(function(aProp) {
+      if (!aObject[aProp]) {
+        return;
+      }
+
+      let property = aObject[aProp];
+      if (aProp == "description") {
+        property = JSON.stringify(aObject[aProp]);
+      }
+
+      let data = converter.convertToByteArray(property, {});
       hasher.update(data, data.length);
     });
 
     return hasher.finish(true);
   },
 
   // Add all the activities carried in the |aObjects| array.
   add: function actdb_add(aObjects, aSuccess, aError) {
@@ -105,26 +176,27 @@ ActivitiesDb.prototype = {
         debug("Going to add " + JSON.stringify(object));
         store.put(object);
       }, this);
     }.bind(this), aSuccess, aError);
   },
 
   // Remove all the activities carried in the |aObjects| array.
   remove: function actdb_remove(aObjects) {
-    this.newTxn("readwrite", STORE_NAME, function (txn, store) {
-      aObjects.forEach(function (aObject) {
+    this.newTxn("readwrite", STORE_NAME, (txn, store) => {
+      aObjects.forEach((aObject) => {
         let object = {
           manifest: aObject.manifest,
-          name: aObject.name
+          name: aObject.name,
+          description: aObject.description
         };
         debug("Going to remove " + JSON.stringify(object));
         store.delete(this.createId(object));
-      }, this);
-    }.bind(this), function() {}, function() {});
+      });
+    }, function() {}, function() {});
   },
 
   // Remove all activities associated with the given |aManifest| URL.
   removeAll: function actdb_removeAll(aManifest) {
     this.newTxn("readwrite", STORE_NAME, function (txn, store) {
       let index = store.index("manifest");
       let request = index.mozGetAll(aManifest);
       request.onsuccess = function manifestActivities(aEvent) {
@@ -242,18 +314,18 @@ let Activities = {
               "result": aResult
             });
             break;
           }
           case Ci.nsIActivityUIGlueCallback.WEBAPPS_ACTIVITY: {
             debug("Activity choice: " + aResult);
 
             // We have no matching activity registered, let's fire an error.
-            // Don't do this check until we have passed to UIGlue so the glue can choose to launch
-            // its own activity if needed.
+            // Don't do this check until we have passed to UIGlue so the glue
+            // can choose to launch its own activity if needed.
             if (aResults.options.length === 0) {
               self.trySendAndCleanup(aMsg.id, "Activity:FireError", {
                 "id": aMsg.id,
                 "error": "NO_PROVIDER"
               });
               return;
             }
 
new file mode 100644
--- /dev/null
+++ b/dom/activities/tests/mochi/common.js
@@ -0,0 +1,37 @@
+const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+const ACTIVITY_GLUE_CID = Components.ID("{f4cfbe10-a106-4cd1-b04e-0d2a6aac138b}");
+const SYS_MSG_GLUE_CID = Components.ID("{b0b6b9af-bc4e-4200-bffe-fb7691065ec9}");
+
+const gRootUrl = "http://test/chrome/dom/activities/tests/mochi/";
+
+function registerComponent(aObject, aDescription, aContract, aCid) {
+  info("Registering " + aCid);
+
+  var componentManager =
+    Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+  componentManager.registerFactory(aCid, aDescription, aContract, aObject);
+
+  // Keep the id on the object so we can unregister later.
+  aObject.cid = aCid;
+}
+
+function unregisterComponent(aObject) {
+  info("Unregistering " + aObject.cid);
+  var componentManager =
+    Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+  componentManager.unregisterFactory(aObject.cid, aObject);
+}
+
+function cbError(aEvent) {
+  ok(false, "Error callback invoked " +
+            aEvent.target.error.name + " " + aEvent.target.error.message);
+  finish();
+}
+
+function unexpectedSuccess(aMsg) {
+  return function() {
+    ok(false, "Should not have succeeded: " + aMsg);
+    finish();
+  }
+}
--- a/dom/activities/tests/mochi/manifest.webapp
+++ b/dom/activities/tests/mochi/manifest.webapp
@@ -1,6 +1,17 @@
 {
   "name": "Random app",
   "activities": {
-    "import-app": { "blob": { "required": true } }
+    "import-app": { "blob": { "required": true } },
+    "bug1176712": [{
+      "filters": {
+        "type": "type1"
+      },
+      "href": "href1"
+    }, {
+      "filters": {
+        "type": "type2"
+      },
+      "href": "href2"
+    }]
   }
 }
--- a/dom/activities/tests/mochi/mochitest.ini
+++ b/dom/activities/tests/mochi/mochitest.ini
@@ -1,9 +1,11 @@
 [DEFAULT]
 skip-if = e10s
 support-files =
+  common.js
   system.webapp
   system.webapp^headers^
   manifest.webapp
   manifest.webapp^headers^
 
 [test_dev_mode_activity.html]
+[test_same_name_multiple_filters.html]
--- a/dom/activities/tests/mochi/test_dev_mode_activity.html
+++ b/dom/activities/tests/mochi/test_dev_mode_activity.html
@@ -4,16 +4,18 @@
 https://bugzilla.mozilla.org/show_bug.cgi?id={1123846}
 -->
 <head>
   <title>Test for Bug {1123846}</title>
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript"
           src="chrome://mochikit/content/chrome-harness.js"></script>
+  <script type="application/javascript"
+          src="http://test/chrome/dom/activities/tests/mochi/common.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
 </head>
 <body>
 
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id={1123846}">Mozilla Bug {1123846}</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 
@@ -28,48 +30,32 @@ https://bugzilla.mozilla.org/show_bug.cg
   *
   * We test the following:
   * 1) No dev mode, no system app installed (failure).
   * 2) No dev mode, system app installed (failure).
   * 3) No dev mode, system app and other app installed (failure).
   * 4) Dev mode, system app and other app installed (success, only system app returned).
   */
 
-const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
-
-var gRootUrl = "http://test/chrome/dom/activities/tests/mochi/";
 var gGenerator = runTest();
 
 function go() {
   SpecialPowers.pushPermissions(
     [{ "type": "webapps-manage", "allow": 1, "context": document },
      { "type": "browser", "allow": 1, "context": document },
      { "type": "embed-apps", "allow": 1, "context": document }],
     function() {
       SpecialPowers.pushPrefEnv(
         {'set': [["dom.mozBrowserFramesEnabled", true],
                  ["dom.sysmsg.enabled", true],
                  ["dom.apps.developer_mode", false],
                  ["dom.activities.developer_mode_only", "import-app"]]},
         continueTest) });
 }
 
-function cbError(aEvent) {
-  ok(false, "Error callback invoked " +
-            aEvent.target.error.name + " " + aEvent.target.error.message);
-  finish();
-}
-
-function unexpectedSuccess(aMsg) {
-  return function() {
-    ok(false, "Should not have succeeded: " + aMsg);
-    finish();
-  }
-}
-
 SimpleTest.waitForExplicitFinish();
 
 var systemAppUrl = gRootUrl + "system.webapp";
 var otherAppUrl = gRootUrl + "manifest.webapp";
 
 function installApp(aUrl) {
   var request = navigator.mozApps.install(aUrl, { });
   request.onerror = cbError;
@@ -87,38 +73,16 @@ function installOtherApp() {
 
 function uninstall(aApp) {
   info("Uninstalling " + (aApp ? aApp.manifestURL : "NO APP!!"));
   var request = navigator.mozApps.mgmt.uninstall(aApp);
   request.onerror = cbError;
   request.onsuccess = continueTest;
 }
 
-function registerComponent(aObject, aDescription, aContract) {
-  var uuidGenerator = Cc["@mozilla.org/uuid-generator;1"]
-                        .getService(Ci.nsIUUIDGenerator);
-  var cid = uuidGenerator.generateUUID();
-
-  info("Registering " + cid);
-
-  var componentManager =
-    Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
-  componentManager.registerFactory(cid, aDescription, aContract, aObject);
-
-  // Keep the id on the object so we can unregister later.
-  aObject.cid = cid;
-}
-
-function unregisterComponent(aObject) {
-  info("Unregistering " + aObject.cid);
-  var componentManager =
-    Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
-  componentManager.unregisterFactory(aObject.cid, aObject);
-}
-
 var ActivityGlue = {
   // nsISupports implementation.
   QueryInterface: function(iid) {
     if (iid.equals(Ci.nsISupports) ||
         iid.equals(Ci.nsIFactory) ||
         iid.equals(Ci.nsIActivityUIGlue)) {
       return this;
     }
@@ -152,32 +116,37 @@ var SystemMessageGlue = {
 
   // nsIFactory implementation.
   createInstance: function(outer, iid) {
     return this.QueryInterface(iid);
   },
 
   // nsISystemMessageGlue implementation.
   openApp(pageURL, manifestURL, type, target, showApp, onlyShowApp, extra) {
-    // We should only try to open a page in the sytem app.
+    // We should only try to open a page in the system app.
     is(manifestURL, systemAppUrl, "Opening a page in the system app.");
   }
 };
 
 registerComponent(ActivityGlue,
                   "Activity Glue",
-                  "@mozilla.org/dom/activities/ui-glue;1");
+                  "@mozilla.org/dom/activities/ui-glue;1",
+                  ACTIVITY_GLUE_CID);
 
 registerComponent(SystemMessageGlue,
                   "System Message Glue",
-                  "@mozilla.org/dom/messages/system-message-glue;1");
+                  "@mozilla.org/dom/messages/system-message-glue;1",
+                  SYS_MSG_GLUE_CID);
 
 function finish() {
   unregisterComponent(ActivityGlue);
   unregisterComponent(SystemMessageGlue);
+  obsService.removeObserver(continueTest, "new-activity-registered-success");
+  obsService.removeObserver(continueTest, "new-activity-registered-failure");
+
   SimpleTest.finish();
 }
 
 function continueTest() {
   try {
     gGenerator.next();
   } catch (e if e instanceof StopIteration) {
     finish();
new file mode 100644
--- /dev/null
+++ b/dom/activities/tests/mochi/test_same_name_multiple_filters.html
@@ -0,0 +1,222 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id={1176712}
+-->
+<head>
+  <title>Test for Bug {1176712}</title>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/chrome-harness.js"></script>
+  <script type="application/javascript"
+          src="http://test/chrome/dom/activities/tests/mochi/common.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+</head>
+<body>
+
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id={1176712}">Mozilla Bug {1176712}</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="application/javascript;version=1.7">
+
+var gGenerator = runTest();
+
+function go() {
+  SpecialPowers.pushPermissions(
+    [{ "type": "webapps-manage", "allow": 1, "context": document },
+     { "type": "browser", "allow": 1, "context": document },
+     { "type": "embed-apps", "allow": 1, "context": document }],
+    function() {
+      SpecialPowers.pushPrefEnv(
+        {'set': [["dom.mozBrowserFramesEnabled", true],
+                 ["dom.sysmsg.enabled", true]]},
+        continueTest) });
+}
+
+SimpleTest.waitForExplicitFinish();
+
+function installApp(aUrl) {
+  var request = navigator.mozApps.install(aUrl, { });
+  request.onerror = cbError;
+  request.onsuccess = continueTest;
+  return request;
+}
+
+function uninstall(aApp) {
+  info("Uninstalling " + (aApp ? aApp.manifestURL : "NO APP!!"));
+  var request = navigator.mozApps.mgmt.uninstall(aApp);
+  request.onerror = cbError;
+  request.onsuccess = continueTest;
+}
+
+var ActivityGlue = {
+  // nsISupports implementation.
+  QueryInterface: function(iid) {
+    if (iid.equals(Ci.nsISupports) ||
+        iid.equals(Ci.nsIFactory) ||
+        iid.equals(Ci.nsIActivityUIGlue)) {
+      return this;
+    }
+
+    throw Cr.NS_ERROR_NO_INTERFACE;
+  },
+
+  // nsIFactory implementation.
+  createInstance: function(outer, iid) {
+    return this.QueryInterface(iid);
+  },
+
+  // nsIActivityUIGlue implementation.
+  chooseActivity: function(aOptions, aActivities, aCallback) {
+    aCallback.handleEvent(Ci.nsIActivityUIGlueCallback.WEBAPPS_ACTIVITY,
+                          aActivities.length == 1 ? 0 : -1);
+  }
+};
+
+var SystemMessageGlue = {
+  // nsISupports implementation.
+  QueryInterface: function(iid) {
+    if (iid.equals(Ci.nsISupports) ||
+        iid.equals(Ci.nsIFactory) ||
+        iid.equals(Ci.nsISystemMessageGlue)) {
+      return this;
+    }
+
+    throw Cr.NS_ERROR_NO_INTERFACE;
+  },
+
+  // nsIFactory implementation.
+  createInstance: function(outer, iid) {
+    return this.QueryInterface(iid);
+  },
+
+  // nsISystemMessageGlue implementation.
+  openApp(pageURL, manifestURL, type, target, showApp, onlyShowApp, extra) {
+  }
+};
+
+registerComponent(ActivityGlue,
+                  "Activity Glue",
+                  "@mozilla.org/dom/activities/ui-glue;1",
+                  ACTIVITY_GLUE_CID);
+
+registerComponent(SystemMessageGlue,
+                  "System Message Glue",
+                  "@mozilla.org/dom/messages/system-message-glue;1",
+                  SYS_MSG_GLUE_CID);
+
+function finish() {
+  unregisterComponent(ActivityGlue);
+  unregisterComponent(SystemMessageGlue);
+  SimpleTest.finish();
+}
+
+function continueTest() {
+  try {
+    gGenerator.next();
+  } catch (e if e instanceof StopIteration) {
+    finish();
+  }
+}
+
+function runTest() {
+  SpecialPowers.setAllAppsLaunchable(true);
+
+  SpecialPowers.autoConfirmAppInstall(continueTest);
+  yield undefined;
+
+  SpecialPowers.autoConfirmAppUninstall(continueTest);
+  yield undefined;
+
+  // Check how many apps we are starting with.
+  var request = navigator.mozApps.mgmt.getAll();
+  request.onerror = cbError;
+  request.onsuccess = continueTest;
+  yield undefined;
+  var initialAppsCount = request.result.length;
+  info("Starting with " + initialAppsCount + " apps installed.");
+
+  // Before app installed
+
+  var activity = new MozActivity({
+    name: "bug1176712",
+    data: {
+      type: "type1"
+    }
+  });
+  activity.onsuccess = unexpectedSuccess("Shouldn't launch unregistered activity");
+  activity.onerror = () => {
+    ok(activity.error.name == "NO_PROVIDER", "Expected NO_PROVIDER");
+    continueTest();
+  };
+  yield undefined;
+
+  var activity = new MozActivity({
+    name: "bug1176712",
+    data: {
+      type: "type2"
+    }
+  });
+  activity.onsuccess = unexpectedSuccess("Shouldn't launch unregistered activity");
+  activity.onerror = () => {
+    ok(activity.error.name == "NO_PROVIDER", "Expected NO_PROVIDER");
+    continueTest();
+  };
+  yield undefined;
+
+  var request = installApp(gRootUrl + "manifest.webapp");
+  yield undefined;
+  var app = request.result;
+  ok(app, "App installed");
+
+  // After app installed
+
+  var activity = new MozActivity({
+    name: "bug1176712",
+    data: {
+      type: "type1"
+    }
+  });
+  activity.onsuccess = function() {
+    ok(true, "Activity launch succeed");
+    continueTest();
+  }
+  activity.onerror = cbError;
+  yield undefined;
+
+  var activity = new MozActivity({
+    name: "bug1176712",
+    data: {
+      type: "type2"
+    }
+  });
+  activity.onsuccess = function() {
+    ok(true, "Activity launch succeed");
+    continueTest();
+  }
+  activity.onerror = cbError;
+  yield undefined;
+
+  // Cleanup
+  uninstall(app);
+  yield undefined;
+
+  // Check that we restored the app registry.
+  request = navigator.mozApps.mgmt.getAll();
+  request.onerror = cbError;
+  request.onsuccess = continueTest;
+  yield undefined;
+
+  is(request.result.length, initialAppsCount, "All apps are uninstalled.");
+}
+
+addLoadEvent(go);
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/apps/PermissionsTable.jsm
+++ b/dom/apps/PermissionsTable.jsm
@@ -537,16 +537,22 @@ this.PermissionsTable =  { geolocation: 
                              privileged: DENY_ACTION,
                              certified: ALLOW_ACTION
                            },
                            "inputport": {
                              app: DENY_ACTION,
                              trusted: DENY_ACTION,
                              privileged: DENY_ACTION,
                              certified: ALLOW_ACTION
+                           },
+                           "external-app": {
+                             app: DENY_ACTION,
+                             trusted: DENY_ACTION,
+                             privileged: ALLOW_ACTION,
+                             certified: ALLOW_ACTION
                            }
                          };
 
 /**
  * Append access modes to the permission name as suffixes.
  *   e.g. permission name 'contacts' with ['read', 'write'] =
  *   ['contacts-read', contacts-write']
  * @param string aPermName
--- a/dom/apps/Webapps.jsm
+++ b/dom/apps/Webapps.jsm
@@ -985,69 +985,80 @@ this.DOMApplicationRegistry = {
     for (let entryPoint in aManifest.entry_points) {
       this._registerInterAppConnectionsForEntryPoint(aManifest, aApp,
                                                      entryPoint);
     }
   },
 
   // |aEntryPoint| is either the entry_point name or the null in which case we
   // use the root of the manifest.
-  _createActivitiesToRegister: function(aManifest, aApp, aEntryPoint, aRunUpdate) {
+  _createActivitiesToRegister: function(aManifest, aApp, aEntryPoint,
+                                        aRunUpdate, aUninstall) {
     let activitiesToRegister = [];
     let root = aManifest;
     if (aEntryPoint && aManifest.entry_points[aEntryPoint]) {
       root = aManifest.entry_points[aEntryPoint];
     }
 
     if (!root || !root.activities) {
       return activitiesToRegister;
     }
 
     let manifest = new ManifestHelper(aManifest, aApp.origin, aApp.manifestURL);
     for (let activity in root.activities) {
-      let description = root.activities[activity];
-      let href = description.href;
-      if (!href) {
-        href = manifest.launch_path;
-      }
-
-      try {
-        href = manifest.resolveURL(href);
-      } catch (e) {
-        debug("Activity href (" + href + ") is invalid, skipping. " +
-              "Error is: " + e);
-        continue;
-      }
-
-      // Make a copy of the description object since we don't want to modify
-      // the manifest itself, but need to register with a resolved URI.
-      let newDesc = {};
-      for (let prop in description) {
-        newDesc[prop] = description[prop];
+      let entry = root.activities[activity];
+      if (!Array.isArray(entry)) {
+        entry = [entry];
       }
-      newDesc.href = href;
-
-      debug('_createActivitiesToRegister: ' + aApp.manifestURL + ', activity ' +
-          activity + ', description.href is ' + newDesc.href);
-
-      if (aRunUpdate) {
-        activitiesToRegister.push({ "manifest": aApp.manifestURL,
-                                    "name": activity,
-                                    "icon": manifest.iconURLForSize(128),
-                                    "description": newDesc });
-      }
-
-      let launchPathURI = Services.io.newURI(href, null, null);
-      let manifestURI = Services.io.newURI(aApp.manifestURL, null, null);
-
-      if (SystemMessagePermissionsChecker
-            .isSystemMessagePermittedToRegister("activity",
-                                                aApp.manifestURL,
-                                                aManifest)) {
-        msgmgr.registerPage("activity", launchPathURI, manifestURI);
+      for (let i = 0; i < entry.length; i++) {
+        let description = entry[i];
+        let href = description.href;
+        if (!href) {
+          href = manifest.launch_path;
+        }
+
+        try {
+          href = manifest.resolveURL(href);
+        } catch (e) {
+          debug("Activity href (" + href + ") is invalid, skipping. " +
+                "Error is: " + e);
+          continue;
+        }
+
+        // Make a copy of the description object since we don't want to modify
+        // the manifest itself, but need to register with a resolved URI.
+        let newDesc = {};
+        for (let prop in description) {
+          newDesc[prop] = description[prop];
+        }
+        newDesc.href = href;
+
+        debug('_createActivitiesToRegister: ' + aApp.manifestURL + ', activity ' +
+            activity + ', description.href is ' + newDesc.href);
+
+        if (aRunUpdate || aUninstall) {
+          activitiesToRegister.push({ "manifest": aApp.manifestURL,
+                                      "name": activity,
+                                      "icon": manifest.iconURLForSize(128),
+                                      "description": newDesc });
+        }
+
+        if (aUninstall) {
+          continue;
+        }
+
+        let launchPathURI = Services.io.newURI(href, null, null);
+        let manifestURI = Services.io.newURI(aApp.manifestURL, null, null);
+
+        if (SystemMessagePermissionsChecker
+              .isSystemMessagePermittedToRegister("activity",
+                                                  aApp.manifestURL,
+                                                  aManifest)) {
+          msgmgr.registerPage("activity", launchPathURI, manifestURI);
+        }
       }
     }
     return activitiesToRegister;
   },
 
   // |aAppsToRegister| contains an array of apps to be registered, where
   // each element is an object in the format of {manifest: foo, app: bar}.
   _registerActivitiesForApps: function(aAppsToRegister, aRunUpdate) {
@@ -1083,56 +1094,34 @@ this.DOMApplicationRegistry = {
   },
 
   // Better to directly use |_registerActivitiesForApps()| if we have
   // multiple apps to be registered for activities.
   _registerActivities: function(aManifest, aApp, aRunUpdate) {
     this._registerActivitiesForApps([{ manifest: aManifest, app: aApp }], aRunUpdate);
   },
 
-  // |aEntryPoint| is either the entry_point name or the null in which case we
-  // use the root of the manifest.
-  _createActivitiesToUnregister: function(aManifest, aApp, aEntryPoint) {
-    let activitiesToUnregister = [];
-    let root = aManifest;
-    if (aEntryPoint && aManifest.entry_points[aEntryPoint]) {
-      root = aManifest.entry_points[aEntryPoint];
-    }
-
-    if (!root.activities) {
-      return activitiesToUnregister;
-    }
-
-    for (let activity in root.activities) {
-      let description = root.activities[activity];
-      activitiesToUnregister.push({ "manifest": aApp.manifestURL,
-                                    "name": activity,
-                                    "description": description });
-    }
-    return activitiesToUnregister;
-  },
-
   // |aAppsToUnregister| contains an array of apps to be unregistered, where
   // each element is an object in the format of {manifest: foo, app: bar}.
   _unregisterActivitiesForApps: function(aAppsToUnregister) {
     // Collect the activities to be unregistered for root and entry_points.
     let activitiesToUnregister = [];
     aAppsToUnregister.forEach(function (aApp) {
       let manifest = aApp.manifest;
       let app = aApp.app;
       activitiesToUnregister.push.apply(activitiesToUnregister,
-        this._createActivitiesToUnregister(manifest, app, null));
+        this._createActivitiesToRegister(manifest, app, null, false, true));
 
       if (!manifest.entry_points) {
         return;
       }
 
       for (let entryPoint in manifest.entry_points) {
         activitiesToUnregister.push.apply(activitiesToUnregister,
-          this._createActivitiesToUnregister(manifest, app, entryPoint));
+          this._createActivitiesToRegister(manifest, app, entryPoint, false, true));
       }
     }, this);
 
     // Send the array carrying all the activities to be unregistered.
     cpmm.sendAsyncMessage("Activities:Unregister", activitiesToUnregister);
   },
 
   // Better to directly use |_unregisterActivitiesForApps()| if we have
--- a/dom/base/WindowNamedPropertiesHandler.cpp
+++ b/dom/base/WindowNamedPropertiesHandler.cpp
@@ -108,19 +108,17 @@ WindowNamedPropertiesHandler::getOwnProp
     if (childWin && ShouldExposeChildWindow(str, childWin)) {
       // We found a subframe of the right name. Shadowing via |var foo| in
       // global scope is still allowed, since |var| only looks up |own|
       // properties. But unqualified shadowing will fail, per-spec.
       JS::Rooted<JS::Value> v(aCx);
       if (!WrapObject(aCx, childWin, &v)) {
         return false;
       }
-      aDesc.object().set(aProxy);
-      aDesc.value().set(v);
-      aDesc.setAttributes(JSPROP_ENUMERATE);
+      FillPropertyDescriptor(aDesc, aProxy, 0, v);
       return true;
     }
   }
 
   // The rest of this function is for HTML documents only.
   nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(win->GetExtantDoc());
   if (!htmlDoc) {
     return true;
@@ -128,35 +126,31 @@ WindowNamedPropertiesHandler::getOwnProp
   nsHTMLDocument* document = static_cast<nsHTMLDocument*>(htmlDoc.get());
 
   Element* element = document->GetElementById(str);
   if (element) {
     JS::Rooted<JS::Value> v(aCx);
     if (!WrapObject(aCx, element, &v)) {
       return false;
     }
-    aDesc.object().set(aProxy);
-    aDesc.value().set(v);
-    aDesc.setAttributes(JSPROP_ENUMERATE);
+    FillPropertyDescriptor(aDesc, aProxy, 0, v);
     return true;
   }
 
   nsWrapperCache* cache;
   nsISupports* result = document->ResolveName(str, &cache);
   if (!result) {
     return true;
   }
 
   JS::Rooted<JS::Value> v(aCx);
   if (!WrapObject(aCx, result, cache, nullptr, &v)) {
     return false;
   }
-  aDesc.object().set(aProxy);
-  aDesc.value().set(v);
-  aDesc.setAttributes(JSPROP_ENUMERATE);
+  FillPropertyDescriptor(aDesc, aProxy, 0, v);
   return true;
 }
 
 bool
 WindowNamedPropertiesHandler::defineProperty(JSContext* aCx,
                                              JS::Handle<JSObject*> aProxy,
                                              JS::Handle<jsid> aId,
                                              JS::Handle<JSPropertyDescriptor> aDesc,
@@ -169,16 +163,21 @@ WindowNamedPropertiesHandler::defineProp
 }
 
 bool
 WindowNamedPropertiesHandler::ownPropNames(JSContext* aCx,
                                            JS::Handle<JSObject*> aProxy,
                                            unsigned flags,
                                            JS::AutoIdVector& aProps) const
 {
+  if (!(flags & JSITER_HIDDEN)) {
+    // None of our named properties are enumerable.
+    return true;
+  }
+
   // Grab the DOM window.
   nsGlobalWindow* win = xpc::WindowOrNull(JS_GetGlobalForObject(aCx, aProxy));
   nsTArray<nsString> names;
   win->GetSupportedNames(names);
   // Filter out the ones we wouldn't expose from getOwnPropertyDescriptor.
   // We iterate backwards so we can remove things from the list easily.
   for (size_t i = names.Length(); i > 0; ) {
     --i; // Now we're pointing at the next name we want to look at
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -345,17 +345,17 @@ nsDOMClassInfo::GetNative(nsIXPConnectWr
 {
   return wrapper ? wrapper->Native() : static_cast<nsISupports*>(js::GetObjectPrivate(obj));
 }
 
 nsresult
 nsDOMClassInfo::DefineStaticJSVals(JSContext *cx)
 {
 #define SET_JSID_TO_STRING(_id, _cx, _str)                                    \
-  if (JSString *str = ::JS_InternString(_cx, _str))                           \
+  if (JSString *str = ::JS_AtomizeAndPinString(_cx, _str))                             \
       _id = INTERNED_STRING_TO_JSID(_cx, str);                                \
   else                                                                        \
       return NS_ERROR_OUT_OF_MEMORY;
 
   SET_JSID_TO_STRING(sConstructor_id,     cx, "constructor");
   SET_JSID_TO_STRING(sWrappedJSObject_id, cx, "wrappedJSObject");
 
   return NS_OK;
@@ -1719,23 +1719,20 @@ GetXPCProto(nsIXPConnect *aXPConnect, JS
 
     ci = NS_GetDOMClassInfoInstance(ci_id);
   }
   else {
     ci = nsDOMClassInfo::GetClassInfoInstance(aNameStruct->mData);
   }
   NS_ENSURE_TRUE(ci, NS_ERROR_UNEXPECTED);
 
-  nsCOMPtr<nsIXPConnectJSObjectHolder> proto_holder;
   nsresult rv =
-    aXPConnect->GetWrappedNativePrototype(cx, aWin->GetGlobalJSObject(), ci,
-                                          getter_AddRefs(proto_holder));
+    aXPConnect->GetWrappedNativePrototype(cx, aWin->GetGlobalJSObject(), ci, aProto.address());
   NS_ENSURE_SUCCESS(rv, rv);
 
-  aProto.set(proto_holder->GetJSObject());
   return JS_WrapObject(cx, aProto) ? NS_OK : NS_ERROR_FAILURE;
 }
 
 // Either ci_data must be non-null or name_struct must be non-null and of type
 // eTypeClassProto.
 static nsresult
 ResolvePrototype(nsIXPConnect *aXPConnect, nsGlobalWindow *aWin, JSContext *cx,
                  JS::Handle<JSObject*> obj, const char16_t *name,
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -361,16 +361,17 @@ GK_ATOM(equalsize, "equalsize")
 GK_ATOM(error, "error")
 GK_ATOM(even, "even")
 GK_ATOM(event, "event")
 GK_ATOM(events, "events")
 GK_ATOM(excludeResultPrefixes, "exclude-result-prefixes")
 GK_ATOM(excludes, "excludes")
 GK_ATOM(expr, "expr")
 GK_ATOM(expectingSystemMessage, "expecting-system-message")
+GK_ATOM(extapp, "extapp")
 GK_ATOM(extends, "extends")
 GK_ATOM(extensionElementPrefixes, "extension-element-prefixes")
 GK_ATOM(face, "face")
 GK_ATOM(fallback, "fallback")
 GK_ATOM(_false, "false")
 GK_ATOM(farthest, "farthest")
 GK_ATOM(field, "field")
 GK_ATOM(fieldset, "fieldset")
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -776,8 +776,9 @@ skip-if = true # bug 1107443 - code for 
 skip-if = buildapp == 'mulet' || buildapp == 'b2g'
 [test_bug1118689.html]
 skip-if = buildapp == 'mulet' || buildapp == 'b2g'
 [test_integer_attr_with_leading_zero.html]
 [test_getAttribute_after_createAttribute.html]
 [test_script_loader_crossorigin_data_url.html]
 [test_file_negative_date.html]
 [test_nonascii_blob_url.html]
+[test_window_element_enumeration.html]
--- a/dom/base/test/test_sendQueryContentAndSelectionSetEvent.html
+++ b/dom/base/test/test_sendQueryContentAndSelectionSetEvent.html
@@ -16,17 +16,17 @@
 
 var editor = document.getElementById("editor");
 
 SimpleTest.waitForExplicitFinish();
 
 const kIsWin = navigator.platform.indexOf("Win") == 0;
 const kIsMac = navigator.platform.indexOf("Mac") == 0;
 
-const kLineBreak = kIsWin ? "\r\n" : kIsMac ? "\r" : "\n";
+const kLineBreak = kIsWin ? "\r\n" : "\n";
 
 var gUtils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                    .getInterface(Components.interfaces.nsIDOMWindowUtils);
 
 function escape(aStr)
 {
   var result = aStr.replace("\n", "\\n");
   return result.replace("\r", "\\r");
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_window_element_enumeration.html
@@ -0,0 +1,70 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=959992
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 959992</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=959992">Mozilla Bug 959992</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <img id="one" name="two">
+  <div id="three" name="four"></div>
+  <div id=""></div>
+  <img name="">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 959992 **/
+    var names1 = Object.getOwnPropertyNames(window);
+    var names2 = [];
+    var gsp = Object.getPrototypeOf(Window.prototype);
+    var names3 = Object.getOwnPropertyNames(gsp);
+    for (var i in window) {
+      names2.push(i);
+    }
+
+    is(names1.indexOf(""), -1,
+       "Element with empty id/name should not be in our own prop list");
+    is(names2.indexOf(""), -1,
+       "Element with empty id/name name should not be in our enumeration list");
+    is(names3.indexOf(""), -1,
+       "Element with empty id/name should not be in GSP own prop list");
+
+    is(names1.indexOf("one"), -1,
+       "<img> with id should not be in our own prop list");
+    is(names2.indexOf("one"), -1,
+       "<img> with id should not be in our enumeration list");
+    isnot(names3.indexOf("one"), -1,
+          "<img> with id should be in GSP own prop list");
+
+    is(names1.indexOf("two"), -1,
+       "<img> with name should not be in our own prop list");
+    is(names2.indexOf("two"), -1,
+       "<img> with name should not be in our enumeration list");
+    isnot(names3.indexOf("two"), -1,
+          "<img> with name should be in GSP own prop list");
+
+    is(names1.indexOf("three"), -1,
+       "<div> with id should not be in our own prop list");
+    is(names2.indexOf("three"), -1,
+       "<div> with id should not be in our enumeration list");
+    todo_isnot(names3.indexOf("three"), -1,
+               "<div> with id should be in GSP own prop list");
+
+    is(names1.indexOf("four"), -1,
+       "<div> with name should not be in our own prop list");
+    is(names2.indexOf("four"), -1,
+       "<div> with name should not be in our enumeration list");
+    is(names3.indexOf("four"), -1,
+       "<div> with name should not be in GSP own prop list");
+</script>
+</pre>
+</body>
+</html>
--- a/dom/base/test/test_window_named_frame_enumeration.html
+++ b/dom/base/test/test_window_named_frame_enumeration.html
@@ -24,36 +24,36 @@ https://bugzilla.mozilla.org/show_bug.cg
     is(names1.indexOf(""), -1,
        "Frame with no name or empty name should not be in our own prop list");
     is(names2.indexOf(""), -1,
        "Frame with no name or empty name should not be in our enumeration list");
     is(names3.indexOf(""), -1,
        "Frame with no name or empty name should not be in GSP own prop list");
     is(names1.indexOf("x"), -1,
        "Frame with about:blank loaded should not be in our own prop list");
-    isnot(names2.indexOf("x"), -1,
-          "Frame with about:blank loaded should be in our enumeration list");
+    is(names2.indexOf("x"), -1,
+       "Frame with about:blank loaded should not be in our enumeration list");
     isnot(names3.indexOf("x"), -1,
           "Frame with about:blank loaded should be in GSP own prop list");
     is(names1.indexOf("y"), -1,
        "Frame with same-origin loaded should not be in our own prop list");
-    isnot(names2.indexOf("y"), -1,
-          "Frame with same-origin loaded should be in our enumeration list");
+    is(names2.indexOf("y"), -1,
+       "Frame with same-origin loaded should not be in our enumeration list");
     isnot(names3.indexOf("y"), -1,
           "Frame with same-origin loaded should be in GSP own prop list");
     is(names1.indexOf("z"), -1,
        "Frame with cross-origin loaded should not be in our own prop list");
-    isnot(names2.indexOf("z"), -1,
-          "Frame with cross-origin loaded should be in our enumeration list");
+    is(names2.indexOf("z"), -1,
+       "Frame with cross-origin loaded should not be in our enumeration list");
     isnot(names3.indexOf("z"), -1,
-          "Frame with cross-origin loaded should be in GSPown prop list");
+          "Frame with cross-origin loaded should be in GSP own prop list");
     is(names1.indexOf("sameorigin"), -1,
           "Frame with same-origin changed name should not be in our own prop list");
-    isnot(names2.indexOf("sameorigin"), -1,
-          "Frame with same-origin changed name should be in our enumeration list");
+    is(names2.indexOf("sameorigin"), -1,
+       "Frame with same-origin changed name should not be in our enumeration list");
     isnot(names3.indexOf("sameorigin"), -1,
           "Frame with same-origin changed name should be in GSP own prop list");
     is(names1.indexOf("crossorigin"), -1,
        "Frame with cross-origin changed name should not be in our own prop list");
     is(names2.indexOf("crossorigin"), -1,
        "Frame with cross-origin changed name should not be in our enumeration list");
     is(names3.indexOf("crossorigin"), -1,
        "Frame with cross-origin changed name should not be in GSP own prop list");
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -553,17 +553,17 @@ CreateInterfaceObject(JSContext* cx, JS:
     if (!JS_DefineProperty(cx, constructor, "length", ctorNargs,
                            JSPROP_READONLY)) {
       return nullptr;
     }
 
     // Might as well intern, since we're going to need an atomized
     // version of name anyway when we stick our constructor on the
     // global.
-    JS::Rooted<JSString*> nameStr(cx, JS_InternString(cx, name));
+    JS::Rooted<JSString*> nameStr(cx, JS_AtomizeAndPinString(cx, name));
     if (!nameStr) {
       return nullptr;
     }
 
     if (!JS_DefineProperty(cx, constructor, "name", nameStr, JSPROP_READONLY)) {
       return nullptr;
     }
   }
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -1726,19 +1726,19 @@ struct GetCallbackFromCallbackObjectHelp
 template<class T>
 inline JSObject*
 GetCallbackFromCallbackObject(T& aObj)
 {
   return GetCallbackFromCallbackObjectHelper<T>::Get(aObj);
 }
 
 static inline bool
-InternJSString(JSContext* cx, jsid& id, const char* chars)
+AtomizeAndPinJSString(JSContext* cx, jsid& id, const char* chars)
 {
-  if (JSString *str = ::JS_InternString(cx, chars)) {
+  if (JSString *str = ::JS_AtomizeAndPinString(cx, chars)) {
     id = INTERNED_STRING_TO_JSID(cx, str);
     return true;
   }
   return false;
 }
 
 // Spec needs a name property
 template <typename Spec>
@@ -2374,17 +2374,17 @@ IdEquals(jsid id, const char* string)
   return JSID_IS_STRING(id) &&
          JS_FlatStringEqualsAscii(JSID_TO_FLAT_STRING(id), string);
 }
 
 inline bool
 AddStringToIDVector(JSContext* cx, JS::AutoIdVector& vector, const char* name)
 {
   return vector.growBy(1) &&
-         InternJSString(cx, *(vector[vector.length() - 1]).address(), name);
+         AtomizeAndPinJSString(cx, *(vector[vector.length() - 1]).address(), name);
 }
 
 // Implementation of the bits that XrayWrapper needs
 
 /**
  * This resolves operations, attributes and constants of the interfaces for obj.
  *
  * wrapper is the Xray JS object.
@@ -3053,40 +3053,40 @@ CreateGlobal(JSContext* aCx, T* aNative,
     NS_WARNING("Failed to set proto");
     return nullptr;
   }
 
   return proto;
 }
 
 /*
- * Holds a jsid that is initialized to an interned string, with conversion to
- * Handle<jsid>.
+ * Holds a jsid that is initialized to a pinned string, with automatic
+ * conversion to Handle<jsid>, as it is held live forever by pinning.
  */
-class InternedStringId
+class PinnedStringId
 {
   jsid id;
 
  public:
-  InternedStringId() : id(JSID_VOID) {}
+  PinnedStringId() : id(JSID_VOID) {}
 
   bool init(JSContext *cx, const char *string) {
-    JSString* str = JS_InternString(cx, string);
+    JSString* str = JS_AtomizeAndPinString(cx, string);
     if (!str)
       return false;
     id = INTERNED_STRING_TO_JSID(cx, str);
     return true;
   }
 
   operator const jsid& () {
     return id;
   }
 
   operator JS::Handle<jsid> () {
-    /* This is safe because we have interned the string. */
+    /* This is safe because we have pinned the string. */
     return JS::Handle<jsid>::fromMarkedLocation(&id);
   }
 };
 
 bool
 GenericBindingGetter(JSContext* cx, unsigned argc, JS::Value* vp);
 
 bool
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -12295,17 +12295,17 @@ class CGResolveSystemBinding(CGAbstractM
             return "s%s_id" % name
         jsidNames = [descNameToId(desc.name) for desc in descriptors]
         jsidDecls = CGList(CGGeneric("static jsid %s;\n" % name)
                            for name in jsidNames)
 
         jsidInits = CGList(
             (CGIfWrapper(
                 CGGeneric("return false;\n"),
-                '!InternJSString(aCx, %s, "%s")' %
+                '!AtomizeAndPinJSString(aCx, %s, "%s")' %
                 (descNameToId(desc.name), desc.interface.identifier.name))
              for desc in descriptors),
             "\n")
         jsidInits.append(CGGeneric("idsInited = true;\n"))
         jsidInits = CGIfWrapper(jsidInits, "!idsInited")
         jsidInits = CGList([CGGeneric("static bool idsInited = false;\n"),
                             jsidInits])
 
@@ -12471,105 +12471,108 @@ class ForwardDeclarationBuilder:
         cg = CGList(decls, "\n")
         if not atTopLevel and len(decls) + len(self.decls) > 1:
             cg = CGWrapper(cg, pre='\n', post='\n')
         return cg
 
     def build(self):
         return self._build(atTopLevel=True)
 
+    def forwardDeclareForType(self, t, config, workerness='both'):
+        t = t.unroll()
+        if t.isGeckoInterface():
+            name = t.inner.identifier.name
+            # Find and add the non-worker implementation, if any.
+            if workerness != 'workeronly':
+                try:
+                    desc = config.getDescriptor(name, False)
+                    self.add(desc.nativeType)
+                except NoSuchDescriptorError:
+                    pass
+            # Find and add the worker implementation, if any.
+            if workerness != 'mainthreadonly':
+                try:
+                    desc = config.getDescriptor(name, True)
+                    self.add(desc.nativeType)
+                except NoSuchDescriptorError:
+                    pass
+        # Note: Spidermonkey interfaces are typedefs, so can't be
+        # forward-declared
+        elif t.isCallback():
+            self.addInMozillaDom(t.callback.identifier.name)
+        elif t.isDictionary():
+            self.addInMozillaDom(t.inner.identifier.name, isStruct=True)
+        elif t.isCallbackInterface():
+            self.addInMozillaDom(t.inner.identifier.name)
+        elif t.isUnion():
+            # Forward declare both the owning and non-owning version,
+            # since we don't know which one we might want
+            self.addInMozillaDom(CGUnionStruct.unionTypeName(t, False))
+            self.addInMozillaDom(CGUnionStruct.unionTypeName(t, True))
+        elif t.isMozMap():
+            self.forwardDeclareForType(t.inner, config, workerness)
+        # Don't need to do anything for void, primitive, string, any or object.
+        # There may be some other cases we are missing.
+
 
 class CGForwardDeclarations(CGWrapper):
     """
     Code generate the forward declarations for a header file.
     additionalDeclarations is a list of tuples containing a classname and a
     boolean. If the boolean is true we will declare a struct, otherwise we'll
     declare a class.
     """
     def __init__(self, config, descriptors, mainCallbacks, workerCallbacks,
                  dictionaries, callbackInterfaces, additionalDeclarations=[]):
         builder = ForwardDeclarationBuilder()
 
-        def forwardDeclareForType(t, workerness='both'):
-            t = t.unroll()
-            if t.isGeckoInterface():
-                name = t.inner.identifier.name
-                # Find and add the non-worker implementation, if any.
-                if workerness != 'workeronly':
-                    try:
-                        desc = config.getDescriptor(name, False)
-                        builder.add(desc.nativeType)
-                    except NoSuchDescriptorError:
-                        pass
-                # Find and add the worker implementation, if any.
-                if workerness != 'mainthreadonly':
-                    try:
-                        desc = config.getDescriptor(name, True)
-                        builder.add(desc.nativeType)
-                    except NoSuchDescriptorError:
-                        pass
-            # Note: Spidermonkey interfaces are typedefs, so can't be
-            # forward-declared
-            elif t.isCallback():
-                builder.addInMozillaDom(t.callback.identifier.name)
-            elif t.isDictionary():
-                builder.addInMozillaDom(t.inner.identifier.name, isStruct=True)
-            elif t.isCallbackInterface():
-                builder.addInMozillaDom(t.inner.identifier.name)
-            elif t.isUnion():
-                # Forward declare both the owning and non-owning version,
-                # since we don't know which one we might want
-                builder.addInMozillaDom(CGUnionStruct.unionTypeName(t, False))
-                builder.addInMozillaDom(CGUnionStruct.unionTypeName(t, True))
-            elif t.isMozMap():
-                forwardDeclareForType(t.inner, workerness)
-            # Don't need to do anything for void, primitive, string, any or object.
-            # There may be some other cases we are missing.
-
         # Needed for at least Wrap.
         for d in descriptors:
             builder.add(d.nativeType)
             # If we're an interface and we have a maplike/setlike declaration,
             # we'll have helper functions exposed to the native side of our
             # bindings, which will need to show up in the header. If either of
             # our key/value types are interfaces, they'll be passed as
             # arguments to helper functions, and they'll need to be forward
             # declared in the header.
             if d.interface.maplikeOrSetlike:
-                forwardDeclareForType(d.interface.maplikeOrSetlike.keyType)
-                forwardDeclareForType(d.interface.maplikeOrSetlike.valueType)
+                builder.forwardDeclareForType(d.interface.maplikeOrSetlike.keyType,
+                                              config)
+                builder.forwardDeclareForType(d.interface.maplikeOrSetlike.valueType,
+                                              config)
 
         # We just about always need NativePropertyHooks
         builder.addInMozillaDom("NativePropertyHooks", isStruct=True)
         builder.addInMozillaDom("ProtoAndIfaceCache")
         # Add the atoms cache type, even if we don't need it.
         for d in descriptors:
             builder.add(d.nativeType + "Atoms", isStruct=True)
 
         for callback in mainCallbacks:
             builder.addInMozillaDom(callback.identifier.name)
             for t in getTypesFromCallback(callback):
-                forwardDeclareForType(t, workerness='mainthreadonly')
+                builder.forwardDeclareForType(t, config,
+                                              workerness='mainthreadonly')
 
         for callback in workerCallbacks:
             builder.addInMozillaDom(callback.identifier.name)
             for t in getTypesFromCallback(callback):
-                forwardDeclareForType(t, workerness='workeronly')
+                builder.forwardDeclareForType(t, config, workerness='workeronly')
 
         for d in callbackInterfaces:
             builder.add(d.nativeType)
             builder.add(d.nativeType + "Atoms", isStruct=True)
             for t in getTypesFromDescriptor(d):
-                forwardDeclareForType(t)
+                builder.forwardDeclareForType(t, config)
 
         for d in dictionaries:
             if len(d.members) > 0:
                 builder.addInMozillaDom(d.identifier.name + "Atoms", isStruct=True)
             for t in getTypesFromDictionary(d):
-                forwardDeclareForType(t)
+                builder.forwardDeclareForType(t, config)
 
         for className, isStruct in additionalDeclarations:
             builder.add(className, isStruct=isStruct)
 
         CGWrapper.__init__(self, builder.build())
 
 
 class CGBindingRoot(CGThing):
@@ -13399,20 +13402,23 @@ class CGBindingImplClass(CGClass):
                      [FakeArgument(BuiltinTypes[IDLBuiltinType.Types.domstring],
                                    FakeMember(),
                                    name="aName")]),
                     { "infallible": True }))
 
         wrapArgs = [Argument('JSContext*', 'aCx'),
                     Argument('JS::Handle<JSObject*>', 'aGivenProto')]
         if not descriptor.wrapperCache:
+            wrapReturnType = "bool"
             wrapArgs.append(Argument('JS::MutableHandle<JSObject*>',
                                      'aReflector'))
+        else:
+            wrapReturnType = "JSObject*"
         self.methodDecls.insert(0,
-                                ClassMethod(wrapMethodName, "JSObject*",
+                                ClassMethod(wrapMethodName, wrapReturnType,
                                             wrapArgs, virtual=descriptor.wrapperCache,
                                             breakAfterReturnDecl=" ",
                                             override=descriptor.wrapperCache,
                                             body=self.getWrapObjectBody()))
         if wantGetParent:
             self.methodDecls.insert(0,
                                     ClassMethod("GetParentObject",
                                                 self.getGetParentObjectReturnType(),
@@ -13521,31 +13527,34 @@ class CGExampleClass(CGBindingImplClass)
                   NS_INTERFACE_MAP_ENTRY(nsISupports)
                 NS_INTERFACE_MAP_END
 
                 """)
 
         if self.descriptor.wrapperCache:
             reflectorArg = ""
             reflectorPassArg = ""
+            returnType = "JSObject*"
         else:
             reflectorArg = ", JS::MutableHandle<JSObject*> aReflector"
             reflectorPassArg = ", aReflector"
+            returnType = "bool"
         classImpl = ccImpl + ctordtor + "\n" + dedent("""
-            JSObject*
+            ${returnType}
             ${nativeType}::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto${reflectorArg})
             {
               return ${ifaceName}Binding::Wrap(aCx, this, aGivenProto${reflectorPassArg});
             }
 
             """)
         return string.Template(classImpl).substitute(
             ifaceName=self.descriptor.name,
             nativeType=self.nativeLeafName(self.descriptor),
             parentType=self.nativeLeafName(self.parentDesc) if self.parentIface else "",
+            returnType=returnType,
             reflectorArg=reflectorArg,
             reflectorPassArg=reflectorPassArg)
 
     @staticmethod
     def nativeLeafName(descriptor):
         return descriptor.nativeType.split('::')[-1]
 
 
@@ -13559,25 +13568,42 @@ class CGExampleRoot(CGThing):
         # Let's assume we're not doing workers stuff
         descriptor = config.getDescriptor(interfaceName, False)
 
         self.root = CGWrapper(CGExampleClass(descriptor),
                               pre="\n", post="\n")
 
         self.root = CGNamespace.build(["mozilla", "dom"], self.root)
 
-        self.root = CGList([CGClassForwardDeclare("JSContext", isStruct=True),
+        builder = ForwardDeclarationBuilder()
+        for member in descriptor.interface.members:
+            if not member.isAttr() and not member.isMethod():
+                continue
+            if member.isStatic():
+                builder.addInMozillaDom("GlobalObject")
+            if member.isAttr():
+                builder.forwardDeclareForType(member.type, config)
+            else:
+                assert member.isMethod()
+                for sig in member.signatures():
+                    builder.forwardDeclareForType(sig[0], config)
+                    for arg in sig[1]:
+                        builder.forwardDeclareForType(arg.type, config)
+
+        self.root = CGList([builder.build(),
                             self.root], "\n")
 
         # Throw in our #includes
         self.root = CGHeaders([], [], [], [],
                               ["nsWrapperCache.h",
                                "nsCycleCollectionParticipant.h",
                                "mozilla/Attributes.h",
-                               "mozilla/ErrorResult.h"],
+                               "mozilla/ErrorResult.h",
+                               "mozilla/dom/BindingDeclarations.h",
+                               "js/TypeDecls.h"],
                               ["mozilla/dom/%s.h" % interfaceName,
                                ("mozilla/dom/%s" %
                                 CGHeaders.getDeclarationFilename(descriptor.interface))], "", self.root)
 
         # And now some include guards
         self.root = CGIncludeGuard(interfaceName, self.root)
 
         # And our license block comes before everything else
@@ -14636,17 +14662,20 @@ class CallbackSetter(CallbackAccessor):
 
 class CGJSImplInitOperation(CallbackOperationBase):
     """
     Codegen the __Init() method used to pass along constructor arguments for JS-implemented WebIDL.
     """
     def __init__(self, sig, descriptor):
         assert sig in descriptor.interface.ctor().signatures()
         CallbackOperationBase.__init__(self, (BuiltinTypes[IDLBuiltinType.Types.void], sig[1]),
-                                       "__init", "__Init", descriptor, False, True)
+                                       "__init", "__Init", descriptor,
+                                       singleOperation=False,
+                                       rethrowContentException=True,
+                                       typedArraysAreStructs=True)
 
     def getPrettyName(self):
         return "__init"
 
 
 def getMaplikeOrSetlikeErrorReturn(helperImpl):
     """
     Generate return values based on whether a maplike or setlike generated
@@ -15151,17 +15180,17 @@ class GlobalGenRoots():
         # Atom enum
         dictionaries = config.dictionaries
 
         structs = []
 
         def memberToAtomCacheMember(binaryNameFor, m):
             binaryMemberName = binaryNameFor(m.identifier.name)
             return ClassMember(CGDictionary.makeIdName(binaryMemberName),
-                               "InternedStringId", visibility="public")
+                               "PinnedStringId", visibility="public")
         def buildAtomCacheStructure(idlobj, binaryNameFor, members):
             classMembers = [memberToAtomCacheMember(binaryNameFor, m)
                             for m in members]
             structName = idlobj.identifier.name + "Atoms"
             return (structName,
                     CGWrapper(CGClass(structName,
                                       bases=None,
                                       isStruct=True,
@@ -15197,17 +15226,17 @@ class GlobalGenRoots():
 
         structs = CGList(generatedStructs + [mainStruct])
 
         # Wrap all of that in our namespaces.
         curr = CGNamespace.build(['mozilla', 'dom'],
                                  CGWrapper(structs, pre='\n'))
         curr = CGWrapper(curr, post='\n')
 
-        # Add include statement for InternedStringId.
+        # Add include statement for PinnedStringId.
         declareIncludes = ['mozilla/dom/BindingUtils.h']
         curr = CGHeaders([], [], [], [], declareIncludes, [], 'GeneratedAtomList',
                          curr)
 
         # Add include guards.
         curr = CGIncludeGuard('GeneratedAtomList', curr)
 
         # Add the auto-generated comment.
@@ -15374,17 +15403,17 @@ class GlobalGenRoots():
         # Add the includes
         defineIncludes = [CGHeaders.getDeclarationFilename(desc.interface)
                           for desc in config.getDescriptors(hasInterfaceObject=True,
                                                             register=True,
                                                             isExposedInSystemGlobals=True,
                                                             skipGen=False)]
         defineIncludes.append("nsThreadUtils.h") # For NS_IsMainThread
         defineIncludes.append("js/Id.h") # For jsid
-        defineIncludes.append("mozilla/dom/BindingUtils.h") # InternJSString
+        defineIncludes.append("mozilla/dom/BindingUtils.h") # AtomizeAndPinJSString
 
         curr = CGHeaders([], [], [], [], [], defineIncludes,
                          'ResolveSystemBinding', curr)
 
         # Add include guards.
         curr = CGIncludeGuard('ResolveSystemBinding', curr)
 
         # Done.
--- a/dom/bindings/DOMJSProxyHandler.cpp
+++ b/dom/bindings/DOMJSProxyHandler.cpp
@@ -20,17 +20,17 @@ using namespace JS;
 namespace mozilla {
 namespace dom {
 
 jsid s_length_id = JSID_VOID;
 
 bool
 DefineStaticJSVals(JSContext* cx)
 {
-  return InternJSString(cx, s_length_id, "length");
+  return AtomizeAndPinJSString(cx, s_length_id, "length");
 }
 
 const char DOMProxyHandler::family = 0;
 
 js::DOMProxyShadowsResult
 DOMProxyShadows(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id)
 {
   JS::Rooted<JSObject*> expando(cx, DOMProxyHandler::GetExpandoObject(proxy));
--- a/dom/bindings/test/TestBindingHeader.h
+++ b/dom/bindings/test/TestBindingHeader.h
@@ -129,16 +129,22 @@ public:
     Constructor(const GlobalObject&, TestInterface*, ErrorResult&);
   static
   already_AddRefed<TestInterface>
     Constructor(const GlobalObject&, uint32_t, IndirectlyImplementedInterface&, ErrorResult&);
 
   static
   already_AddRefed<TestInterface>
     Constructor(const GlobalObject&, Date&, ErrorResult&);
+  static
+  already_AddRefed<TestInterface>
+    Constructor(const GlobalObject&, const ArrayBuffer&, ErrorResult&);
+  static
+  already_AddRefed<TestInterface>
+    Constructor(const GlobalObject&, const Uint8Array&, ErrorResult&);
   /*  static
   already_AddRefed<TestInterface>
     Constructor(const GlobalObject&, uint32_t, uint32_t,
                 const TestInterfaceOrOnlyForUseInConstructor&, ErrorResult&);
   */
 
   static
   already_AddRefed<TestInterface> Test(const GlobalObject&, ErrorResult&);
--- a/dom/bindings/test/TestCodeGen.webidl
+++ b/dom/bindings/test/TestCodeGen.webidl
@@ -124,16 +124,18 @@ interface OnlyForUseInConstructor {
 };
 
 [Constructor,
  Constructor(DOMString str),
  Constructor(unsigned long num, boolean? boolArg),
  Constructor(TestInterface? iface),
  Constructor(long arg1, IndirectlyImplementedInterface iface),
  Constructor(Date arg1),
+ Constructor(ArrayBuffer arrayBuf),
+ Constructor(Uint8Array typedArr),
  // Constructor(long arg1, long arg2, (TestInterface or OnlyForUseInConstructor) arg3),
  AvailableIn=CertifiedApps,
  NamedConstructor=Test,
  NamedConstructor=Test(DOMString str),
  NamedConstructor=Test2(DictForConstructor dict, any any1, object obj1,
                         object? obj2, sequence<Dict> seq, optional any any2,
                         optional object obj3, optional object? obj4),
  NamedConstructor=Test3((long or MozMap<any>) arg1)
--- a/dom/bindings/test/TestExampleGen.webidl
+++ b/dom/bindings/test/TestExampleGen.webidl
@@ -3,16 +3,19 @@
  * 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/.
  */
 [Constructor,
  Constructor(DOMString str),
  Constructor(unsigned long num, boolean? boolArg),
  Constructor(TestInterface? iface),
  Constructor(long arg1, IndirectlyImplementedInterface iface),
+ Constructor(Date arg1),
+ Constructor(ArrayBuffer arrayBuf),
+ Constructor(Uint8Array typedArr),
  // Constructor(long arg1, long arg2, (TestInterface or OnlyForUseInConstructor) arg3),
  NamedConstructor=Example,
  NamedConstructor=Example(DOMString str),
  NamedConstructor=Example2(DictForConstructor dict, any any1, object obj1,
                            object? obj2, sequence<Dict> seq, optional any any2,
                            optional object obj3, optional object? obj4),
  NamedConstructor=Example2((long or MozMap<any>) arg1)
  ]
--- a/dom/bindings/test/TestJSImplGen.webidl
+++ b/dom/bindings/test/TestJSImplGen.webidl
@@ -18,17 +18,19 @@ enum MyTestEnum {
 // We don't support multiple constructors (bug 869268) or named constructors
 // for JS-implemented WebIDL.
 [Constructor(DOMString str, unsigned long num, boolean? boolArg,
              TestInterface? iface, long arg1,
              DictForConstructor dict, any any1,
              object obj1,
              object? obj2, sequence<Dict> seq, optional any any2,
              optional object obj3,
-             optional object? obj4),
+             optional object? obj4,
+             Uint8Array typedArr,
+             ArrayBuffer arrayBuf),
  JSImplementation="@mozilla.org/test-js-impl-interface;1"]
 interface TestJSImplInterface {
   // Integer types
   // XXXbz add tests for throwing versions of all the integer stuff
   readonly attribute byte readonlyByte;
   attribute byte writableByte;
   void passByte(byte arg);
   byte receiveByte();
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -1126,18 +1126,22 @@ WebGLContext::GetFramebufferAttachmentPa
     }
 
     if (!ValidateFramebufferAttachment(fb, attachment,
                                        "getFramebufferAttachmentParameter"))
     {
         return JS::NullValue();
     }
 
-    if (IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers))
+    if (IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers) &&
+        attachment >= LOCAL_GL_COLOR_ATTACHMENT0 &&
+        attachment <= LOCAL_GL_COLOR_ATTACHMENT15)
+    {
         fb->EnsureColorAttachPoints(attachment - LOCAL_GL_COLOR_ATTACHMENT0);
+    }
 
     MakeContextCurrent();
 
     const WebGLFramebuffer::AttachPoint& fba = fb->GetAttachPoint(attachment);
 
     if (fba.Renderbuffer()) {
         switch (pname) {
             case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT:
--- a/dom/events/ContentEventHandler.cpp
+++ b/dom/events/ContentEventHandler.cpp
@@ -207,20 +207,17 @@ static bool IsContentBR(nsIContent* aCon
          !aContent->AttrValueIs(kNameSpaceID_None,
                                 nsGkAtoms::mozeditorbogusnode,
                                 nsGkAtoms::_true,
                                 eIgnoreCase);
 }
 
 static void ConvertToNativeNewlines(nsAFlatString& aString)
 {
-#if defined(XP_MACOSX)
-  // XXX Mac OS X doesn't use "\r".
-  aString.ReplaceSubstring(NS_LITERAL_STRING("\n"), NS_LITERAL_STRING("\r"));
-#elif defined(XP_WIN)
+#if defined(XP_WIN)
   aString.ReplaceSubstring(NS_LITERAL_STRING("\n"), NS_LITERAL_STRING("\r\n"));
 #endif
 }
 
 static void AppendString(nsAString& aString, nsIContent* aContent)
 {
   NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
                "aContent is not a text node!");
@@ -329,22 +326,17 @@ GetBRLength(LineBreakType aLineBreakType
 
 /* static */ uint32_t
 ContentEventHandler::GetTextLength(nsIContent* aContent,
                                    LineBreakType aLineBreakType,
                                    uint32_t aMaxLength)
 {
   if (aContent->IsNodeOfType(nsINode::eTEXT)) {
     uint32_t textLengthDifference =
-#if defined(XP_MACOSX)
-      // On Mac, the length of a native newline ("\r") is equal to the length of
-      // the XP newline ("\n"), so the native length is the same as the XP
-      // length.
-      0;
-#elif defined(XP_WIN)
+#if defined(XP_WIN)
       // On Windows, the length of a native newline ("\r\n") is twice the length
       // of the XP newline ("\n"), so XP length is equal to the length of the
       // native offset plus the number of newlines encountered in the string.
       (aLineBreakType == LINE_BREAK_TYPE_NATIVE) ?
         CountNewlinesInXPLength(aContent, aMaxLength) : 0;
 #else
       // On other platforms, the native and XP newlines are the same.
       0;
@@ -359,21 +351,17 @@ ContentEventHandler::GetTextLength(nsICo
   } else if (IsContentBR(aContent)) {
     return GetBRLength(aLineBreakType);
   }
   return 0;
 }
 
 static uint32_t ConvertToXPOffset(nsIContent* aContent, uint32_t aNativeOffset)
 {
-#if defined(XP_MACOSX)
-  // On Mac, the length of a native newline ("\r") is equal to the length of
-  // the XP newline ("\n"), so the native offset is the same as the XP offset.
-  return aNativeOffset;
-#elif defined(XP_WIN)
+#if defined(XP_WIN)
   // On Windows, the length of a native newline ("\r\n") is twice the length of
   // the XP newline ("\n"), so XP offset is equal to the length of the native
   // offset minus the number of newlines encountered in the string.
   return aNativeOffset - CountNewlinesInNativeLength(aContent, aNativeOffset);
 #else
   // On other platforms, the native and XP newlines are the same.
   return aNativeOffset;
 #endif
--- a/dom/events/IMEStateManager.cpp
+++ b/dom/events/IMEStateManager.cpp
@@ -1379,18 +1379,29 @@ IMEStateManager::NotifyIME(const IMENoti
   switch (aNotification.mMessage) {
     case REQUEST_TO_COMMIT_COMPOSITION:
       return composition ?
         composition->RequestToCommit(aWidget, false) : NS_OK;
     case REQUEST_TO_CANCEL_COMPOSITION:
       return composition ?
         composition->RequestToCommit(aWidget, true) : NS_OK;
     case NOTIFY_IME_OF_COMPOSITION_UPDATE:
-      return composition && !isSynthesizedForTests ?
-        aWidget->NotifyIME(aNotification) : NS_OK;
+      if (!aOriginIsRemote && (!composition || isSynthesizedForTests)) {
+        MOZ_LOG(sISMLog, LogLevel::Info,
+          ("ISM:   IMEStateManager::NotifyIME(), FAILED, received content "
+           "change notification from this process but there is no compostion"));
+        return NS_OK;
+      }
+      if (!sRemoteHasFocus && aOriginIsRemote) {
+        MOZ_LOG(sISMLog, LogLevel::Info,
+          ("ISM:   IMEStateManager::NotifyIME(), received content change "
+           "notification from the remote but it's already lost focus"));
+        return NS_OK;
+      }
+      return aWidget->NotifyIME(aNotification);
     default:
       MOZ_CRASH("Unsupported notification");
   }
   MOZ_CRASH(
     "Failed to handle the notification for non-synthesized composition");
   return NS_ERROR_FAILURE;
 }
 
new file mode 100644
--- /dev/null
+++ b/dom/html/HTMLExtAppElement.cpp
@@ -0,0 +1,185 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/HTMLExtAppElement.h"
+#include "mozilla/dom/HTMLUnknownElement.h"
+#include "mozilla/dom/HTMLExtAppElementBinding.h"
+#include "mozilla/dom/ExternalAppEvent.h"
+
+#include "nsGkAtoms.h"
+#include "nsIAtom.h"
+#include "nsIPermissionManager.h"
+#include "nsStyleConsts.h"
+#include "nsRuleData.h"
+
+nsGenericHTMLElement*
+NS_NewHTMLExtAppElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
+                        mozilla::dom::FromParser aFromParser) {
+  // Return HTMLUnknownElement if the document doesn't have the 'external-app' permission.
+  nsCOMPtr<nsIPermissionManager> permissionManager =
+    mozilla::services::GetPermissionManager();
+  nsRefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
+  nsIPrincipal* principal = ni->GetDocument()->NodePrincipal();
+
+  already_AddRefed<mozilla::dom::NodeInfo> aarni = ni.forget();
+
+  if (!permissionManager) {
+    return new HTMLUnknownElement(aarni);
+  }
+
+  uint32_t perm = nsIPermissionManager::UNKNOWN_ACTION;
+  permissionManager->TestExactPermissionFromPrincipal(principal,
+                                                      "external-app",
+                                                      &perm);
+  if (perm != nsIPermissionManager::ALLOW_ACTION) {
+    return new HTMLUnknownElement(aarni);
+  }
+
+  return new HTMLExtAppElement(aarni);
+}
+
+namespace mozilla {
+namespace dom {
+
+HTMLExtAppElement::HTMLExtAppElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
+  : nsGenericHTMLElement(aNodeInfo)
+{
+  mCustomEventDispatch = new nsCustomEventDispatch(this);
+  mCustomPropertyBag = new nsCustomPropertyBag();
+
+  nsCOMPtr<nsIExternalApplication> app =
+    do_CreateInstance(NS_EXTERNALAPP_CONTRACTID);
+  if (app) {
+    nsresult rv = app->Init(OwnerDoc()->GetInnerWindow(), mCustomPropertyBag, mCustomEventDispatch);
+    if (NS_SUCCEEDED(rv)) {
+      mApp = app;
+    }
+  }
+}
+
+HTMLExtAppElement::~HTMLExtAppElement()
+{
+  mCustomEventDispatch->ClearEventTarget();
+}
+
+void
+HTMLExtAppElement::GetCustomProperty(const nsAString& aName, nsString& aReturn)
+{
+  mCustomPropertyBag->GetCustomProperty(aName, aReturn);
+}
+
+void
+HTMLExtAppElement::PostMessage(const nsAString& aMessage, ErrorResult& aRv)
+{
+  if (!mApp) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return;
+  }
+
+  aRv = mApp->PostMessage(aMessage);
+}
+
+NS_IMPL_ADDREF_INHERITED(HTMLExtAppElement, Element)
+NS_IMPL_RELEASE_INHERITED(HTMLExtAppElement, Element)
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLExtAppElement)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLExtAppElement,
+                                                nsGenericHTMLElement)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLExtAppElement,
+                                                  nsGenericHTMLElement)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+// QueryInterface implementation for HTMLExtAppElement
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(HTMLExtAppElement)
+NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)
+
+NS_IMPL_ELEMENT_CLONE(HTMLExtAppElement)
+
+JSObject*
+HTMLExtAppElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
+{
+  return HTMLExtAppElementBinding::Wrap(aCx, this, aGivenProto);
+}
+
+} // namespace dom
+} // namespace mozilla
+
+NS_IMPL_ISUPPORTS(nsCustomPropertyBag, nsICustomPropertyBag)
+
+nsCustomPropertyBag::nsCustomPropertyBag()
+{
+}
+
+nsCustomPropertyBag::~nsCustomPropertyBag()
+{
+}
+
+NS_IMETHODIMP
+nsCustomPropertyBag::SetProperty(const nsAString& aName, const nsAString& aValue)
+{
+  mBag.Put(nsString(aName), new nsString(aValue));
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCustomPropertyBag::RemoveProperty(const nsAString& aName)
+{
+  mBag.Remove(nsString(aName));
+  return NS_OK;
+}
+
+void
+nsCustomPropertyBag::GetCustomProperty(const nsAString& aName, nsString& aReturn)
+{
+  nsString* value;
+  if (!mBag.Get(nsString(aName), &value)) {
+    aReturn.Truncate();
+    return;
+  }
+
+  MOZ_ASSERT(value);
+  aReturn.Assign(*value);
+}
+
+NS_IMPL_ISUPPORTS(nsCustomEventDispatch, nsICustomEventDispatch)
+
+nsCustomEventDispatch::nsCustomEventDispatch(mozilla::dom::EventTarget* aEventTarget)
+  : mEventTarget(aEventTarget)
+{
+  MOZ_ASSERT(mEventTarget);
+}
+
+void
+nsCustomEventDispatch::ClearEventTarget()
+{
+  mEventTarget = nullptr;
+}
+
+nsCustomEventDispatch::~nsCustomEventDispatch()
+{
+}
+
+NS_IMETHODIMP
+nsCustomEventDispatch::DispatchExternalEvent(const nsAString& value)
+{
+  if (!mEventTarget) {
+    return NS_OK;
+  }
+
+  mozilla::dom::ExternalAppEventInit init;
+  init.mData = value;
+
+  nsRefPtr<mozilla::dom::ExternalAppEvent> event =
+    mozilla::dom::ExternalAppEvent::Constructor(mEventTarget,
+                                                NS_LITERAL_STRING("externalappevent"),
+                                                init);
+
+  bool defaultActionEnabled;
+  return mEventTarget->DispatchEvent(event, &defaultActionEnabled);
+}
new file mode 100644
--- /dev/null
+++ b/dom/html/HTMLExtAppElement.h
@@ -0,0 +1,79 @@
+/* -*- 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_dom_HTMLExtAppElement_h
+#define mozilla_dom_HTMLExtAppElement_h
+
+#include "nsGenericHTMLElement.h"
+#include "nsIExternalApplication.h"
+
+class nsCustomEventDispatch;
+class nsCustomPropertyBag;
+
+namespace mozilla {
+namespace dom {
+
+class HTMLExtAppElement final : public nsGenericHTMLElement
+{
+public:
+  explicit HTMLExtAppElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
+
+  // nsISupports
+  NS_DECL_ISUPPORTS_INHERITED
+
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLExtAppElement,
+                                           nsGenericHTMLElement)
+
+  virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override;
+
+  void GetCustomProperty(const nsAString& aName, nsString& aReturn);
+  void PostMessage(const nsAString& aMessage, ErrorResult& aRv);
+
+protected:
+  virtual ~HTMLExtAppElement();
+
+  virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override;
+
+  nsRefPtr<nsCustomEventDispatch> mCustomEventDispatch;
+  nsRefPtr<nsCustomPropertyBag> mCustomPropertyBag;
+  nsCOMPtr<nsIExternalApplication> mApp;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+class nsCustomEventDispatch final : public nsICustomEventDispatch
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSICUSTOMEVENTDISPATCH
+
+  explicit nsCustomEventDispatch(mozilla::dom::EventTarget* aEventTarget);
+  void ClearEventTarget();
+
+private:
+  ~nsCustomEventDispatch();
+
+  // Weak pointer, this object is owned by the event target.
+  mozilla::dom::EventTarget* mEventTarget;
+};
+
+class nsCustomPropertyBag final : public nsICustomPropertyBag
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSICUSTOMPROPERTYBAG
+
+  nsCustomPropertyBag();
+  void GetCustomProperty(const nsAString& aName, nsString& aReturn);
+
+private:
+  ~nsCustomPropertyBag();
+
+  nsClassHashtable<nsStringHashKey, nsString> mBag;
+};
+
+#endif // mozilla_dom_HTMLExtAppElement_h
--- a/dom/html/moz.build
+++ b/dom/html/moz.build
@@ -52,16 +52,17 @@ EXPORTS.mozilla.dom += [
     'HTMLBodyElement.h',
     'HTMLBRElement.h',
     'HTMLButtonElement.h',
     'HTMLCanvasElement.h',
     'HTMLContentElement.h',
     'HTMLDataElement.h',
     'HTMLDataListElement.h',
     'HTMLDivElement.h',
+    'HTMLExtAppElement.h',
     'HTMLFieldSetElement.h',
     'HTMLFontElement.h',
     'HTMLFormControlsCollection.h',
     'HTMLFormElement.h',
     'HTMLFrameElement.h',
     'HTMLFrameSetElement.h',
     'HTMLHeadingElement.h',
     'HTMLHRElement.h',
@@ -129,16 +130,17 @@ UNIFIED_SOURCES += [
     'HTMLBRElement.cpp',
     'HTMLButtonElement.cpp',
     'HTMLCanvasElement.cpp',
     'HTMLContentElement.cpp',
     'HTMLDataElement.cpp',
     'HTMLDataListElement.cpp',
     'HTMLDivElement.cpp',
     'HTMLElement.cpp',
+    'HTMLExtAppElement.cpp',
     'HTMLFieldSetElement.cpp',
     'HTMLFontElement.cpp',
     'HTMLFormControlsCollection.cpp',
     'HTMLFormElement.cpp',
     'HTMLFrameElement.cpp',
     'HTMLFrameSetElement.cpp',
     'HTMLHeadingElement.cpp',
     'HTMLHRElement.cpp',
--- a/dom/html/nsGenericHTMLElement.h
+++ b/dom/html/nsGenericHTMLElement.h
@@ -1737,16 +1737,17 @@ NS_DECLARE_NS_NEW_HTML_ELEMENT(BR)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Body)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Button)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Canvas)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Content)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Mod)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Data)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(DataList)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Div)
+NS_DECLARE_NS_NEW_HTML_ELEMENT(ExtApp)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(FieldSet)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Font)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Form)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Frame)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(FrameSet)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(HR)
 NS_DECLARE_NS_NEW_HTML_ELEMENT_AS_SHARED(Head)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Heading)
--- a/dom/html/test/mochitest.ini
+++ b/dom/html/test/mochitest.ini
@@ -589,9 +589,10 @@ skip-if = buildapp == 'b2g' || e10s
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || e10s #Bug 931116, b2g desktop specific, initial triage
 support-files = file_bug871161-1.html file_bug871161-2.html
 [test_bug1013316.html]
 [test_hash_encoded.html]
 [test_bug1081037.html]
 [test_window_open_close.html]
 skip-if = buildapp == 'b2g' # bug 1129014
 [test_img_complete.html]
-[test_viewport_resize.html]
\ No newline at end of file
+[test_viewport_resize.html]
+[test_extapp.html]
new file mode 100644
--- /dev/null
+++ b/dom/html/test/test_extapp.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test for extapp element being HTMLUnknownElement when permission is not available</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+  assert_true(document.createElement("extapp") instanceof HTMLUnknownElement);
+}, "extapp should be HTMLUnknownElement when external-app permission is not enabled.");
+</script>
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -2095,17 +2095,17 @@ ContentParent::NotifyTabDestroying(PBrow
 
 void
 ContentParent::StartForceKillTimer()
 {
     if (mForceKillTimer || !mIPCOpen) {
         return;
     }
 
-    int32_t timeoutSecs = Preferences::GetInt("dom.ipc.tabs.shutdownTimeoutSecs", 5);
+    int32_t timeoutSecs = Preferences::GetInt("dom.ipc.tabs.shutdownTimeoutSecs", 0);
     if (timeoutSecs > 0) {
         mForceKillTimer = do_CreateInstance("@mozilla.org/timer;1");
         MOZ_ASSERT(mForceKillTimer);
         mForceKillTimer->InitWithFuncCallback(ContentParent::ForceKillTimerCallback,
                                               this,
                                               timeoutSecs * 1000,
                                               nsITimer::TYPE_ONE_SHOT);
     }
--- a/dom/media/DecodedStream.cpp
+++ b/dom/media/DecodedStream.cpp
@@ -188,34 +188,34 @@ OutputStreamData::~OutputStreamData()
 void
 OutputStreamData::Init(DecodedStream* aDecodedStream, ProcessedMediaStream* aStream)
 {
   mStream = aStream;
   mListener = new OutputStreamListener(aDecodedStream, aStream);
   aStream->AddListener(mListener);
 }
 
-DecodedStream::DecodedStream(ReentrantMonitor& aMonitor)
-  : mMonitor(aMonitor)
+DecodedStream::DecodedStream()
+  : mMonitor("DecodedStream::mMonitor")
 {
   //
 }
 
 DecodedStreamData*
 DecodedStream::GetData() const
 {
-  GetReentrantMonitor().AssertCurrentThreadIn();
+  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   return mData.get();
 }
 
 void
 DecodedStream::DestroyData()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  GetReentrantMonitor().AssertCurrentThreadIn();
+  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
 
   // Avoid the redundant blocking to output stream.
   if (!mData) {
     return;
   }
 
   // All streams are having their SourceMediaStream disconnected, so they
   // need to be explicitly blocked again.
@@ -240,17 +240,17 @@ DecodedStream::DestroyData()
 
   mData = nullptr;
 }
 
 void
 DecodedStream::RecreateData(MediaStreamGraph* aGraph)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  GetReentrantMonitor().AssertCurrentThreadIn();
+  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   MOZ_ASSERT((aGraph && !mData && OutputStreams().IsEmpty()) || // first time
              (!aGraph && mData)); // 2nd time and later
 
   if (!aGraph) {
     aGraph = mData->mStream->Graph();
   }
   auto source = aGraph->CreateSourceStream(nullptr);
   DestroyData();
@@ -295,28 +295,28 @@ DecodedStream::Connect(OutputStreamData*
   // DecodedStream is responsible for controlling blocking.
   aStream->mStream->ChangeExplicitBlockerCount(-1);
 }
 
 void
 DecodedStream::Connect(ProcessedMediaStream* aStream, bool aFinishWhenEnded)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  GetReentrantMonitor().AssertCurrentThreadIn();
+  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
 
   OutputStreamData* os = OutputStreams().AppendElement();
   os->Init(this, aStream);
   Connect(os);
   if (aFinishWhenEnded) {
     // Ensure that aStream finishes the moment mDecodedStream does.
     aStream->SetAutofinish(true);
   }
 }
 
 void
 DecodedStream::SetPlaying(bool aPlaying)
 {
-  GetReentrantMonitor().AssertCurrentThreadIn();
+  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   MOZ_ASSERT(mData);
   mData->SetPlaying(aPlaying);
 }
 
 } // namespace mozilla
--- a/dom/media/DecodedStream.h
+++ b/dom/media/DecodedStream.h
@@ -80,29 +80,37 @@ public:
   nsRefPtr<ProcessedMediaStream> mStream;
   // mPort connects DecodedStreamData::mStream to our mStream.
   nsRefPtr<MediaInputPort> mPort;
   nsRefPtr<OutputStreamListener> mListener;
 };
 
 class DecodedStream {
 public:
-  explicit DecodedStream(ReentrantMonitor& aMonitor);
+  DecodedStream();
   DecodedStreamData* GetData() const;
   void DestroyData();
   void RecreateData(MediaStreamGraph* aGraph);
   nsTArray<OutputStreamData>& OutputStreams();
   ReentrantMonitor& GetReentrantMonitor() const;
   void Connect(ProcessedMediaStream* aStream, bool aFinishWhenEnded);
   void SetPlaying(bool aPlaying);
 
 private:
   void Connect(OutputStreamData* aStream);
 
   UniquePtr<DecodedStreamData> mData;
   // Data about MediaStreams that are being fed by the decoder.
   nsTArray<OutputStreamData> mOutputStreams;
-  ReentrantMonitor& mMonitor;
+
+  // TODO: This is a temp solution to get rid of decoder monitor on the main
+  // thread in MDSM::AddOutputStream and MDSM::RecreateDecodedStream as
+  // required by bug 1146482. DecodedStream needs to release monitor before
+  // calling back into MDSM functions in order to prevent deadlocks.
+  //
+  // Please move all capture-stream related code from MDSM into DecodedStream
+  // and apply "dispatch + mirroring" to get rid of this monitor in the future.
+  mutable ReentrantMonitor mMonitor;
 };
 
 } // namespace mozilla
 
 #endif // DecodedStream_h_
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -162,17 +162,17 @@ void MediaDecoder::UpdateDormantState(bo
 {
   MOZ_ASSERT(NS_IsMainThread());
   GetReentrantMonitor().AssertCurrentThreadIn();
 
   if (!mDecoderStateMachine ||
       mPlayState == PLAY_STATE_SHUTDOWN ||
       !mOwner->GetVideoFrameContainer() ||
       (mOwner->GetMediaElement() && mOwner->GetMediaElement()->IsBeingDestroyed()) ||
-      !mDecoderStateMachine->IsDormantNeeded())
+      !mDormantSupported)
   {
     return;
   }
 
   DECODER_LOG("UpdateDormantState aTimeout=%d aActivity=%d mIsDormant=%d "
               "ownerActive=%d ownerHidden=%d mIsHeuristicDormant=%d mPlayState=%s",
               aDormantTimeout, aActivity, mIsDormant, mOwner->IsActive(),
               mOwner->IsHidden(), mIsHeuristicDormant, PlayStateStr());
@@ -333,16 +333,17 @@ bool MediaDecoder::IsInfinite()
 }
 
 MediaDecoder::MediaDecoder() :
   mWatchManager(this, AbstractThread::MainThread()),
   mBuffered(AbstractThread::MainThread(), TimeIntervals(), "MediaDecoder::mBuffered (Mirror)"),
   mNextFrameStatus(AbstractThread::MainThread(),
                    MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED,
                    "MediaDecoder::mNextFrameStatus (Mirror)"),
+  mDormantSupported(false),
   mDecoderPosition(0),
   mPlaybackPosition(0),
   mLogicalPosition(0.0),
   mCurrentPosition(AbstractThread::MainThread(), 0, "MediaDecoder::mCurrentPosition (Mirror)"),
   mVolume(AbstractThread::MainThread(), 0.0, "MediaDecoder::mVolume (Canonical)"),
   mPlaybackRate(AbstractThread::MainThread(), 1.0, "MediaDecoder::mPlaybackRate (Canonical)"),
   mPreservesPitch(AbstractThread::MainThread(), true, "MediaDecoder::mPreservesPitch (Canonical)"),
   mDuration(std::numeric_limits<double>::quiet_NaN()),
@@ -1148,18 +1149,17 @@ media::TimeIntervals MediaDecoder::GetSe
                             media::TimeUnit::FromSeconds(GetDuration())));
   }
 }
 
 void MediaDecoder::SetFragmentEndTime(double aTime)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mDecoderStateMachine) {
-    ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
-    mDecoderStateMachine->SetFragmentEndTime(static_cast<int64_t>(aTime * USECS_PER_S));
+    mDecoderStateMachine->DispatchSetFragmentEndTime(static_cast<int64_t>(aTime * USECS_PER_S));
   }
 }
 
 void MediaDecoder::Suspend()
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mResource) {
     mResource->Suspend(true);
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -897,16 +897,19 @@ protected:
 
   // NextFrameStatus, mirrored from the state machine.
   Mirror<MediaDecoderOwner::NextFrameStatus> mNextFrameStatus;
 
   /******
    * The following members should be accessed with the decoder lock held.
    ******/
 
+  // Whether the decoder implementation supports dormant mode.
+  bool mDormantSupported;
+
   // Current decoding position in the stream. This is where the decoder
   // is up to consuming the stream. This is not adjusted during decoder
   // seek operations, but it's updated at the end when we start playing
   // back again.
   int64_t mDecoderPosition;
   // Current playback position in the stream. This is (approximately)
   // where we're up to playing back the stream. This is not adjusted
   // during decoder seek operations, but it's updated at the end when we
--- a/dom/media/MediaDecoderReader.cpp
+++ b/dom/media/MediaDecoderReader.cpp
@@ -226,21 +226,21 @@ MediaDecoderReader::DoThrottledNotify()
 
 media::TimeIntervals
 MediaDecoderReader::GetBuffered()
 {
   MOZ_ASSERT(OnTaskQueue());
   NS_ENSURE_TRUE(mStartTime >= 0, media::TimeIntervals());
   AutoPinned<MediaResource> stream(mDecoder->GetResource());
 
-  if (!mDuration.ReadOnWrongThread().isSome()) {
+  if (!mDuration.Ref().isSome()) {
     return TimeIntervals();
   }
 
-  return GetEstimatedBufferedTimeRanges(stream, mDuration.ReadOnWrongThread().ref().ToMicroseconds());
+  return GetEstimatedBufferedTimeRanges(stream, mDuration.Ref().ref().ToMicroseconds());
 }
 
 nsRefPtr<MediaDecoderReader::MetadataPromise>
 MediaDecoderReader::AsyncReadMetadata()
 {
   typedef ReadMetadataFailureReason Reason;
 
   MOZ_ASSERT(OnTaskQueue());
--- a/dom/media/MediaDecoderReader.h
+++ b/dom/media/MediaDecoderReader.h
@@ -93,18 +93,16 @@ public:
   // on failure.
   virtual nsresult Init(MediaDecoderReader* aCloneDonor) = 0;
 
   // True if this reader is waiting media resource allocation
   virtual bool IsWaitingMediaResources() { return false; }
   // True if this reader is waiting for a Content Decryption Module to become
   // available.
   virtual bool IsWaitingOnCDMResource() { return false; }
-  // True when this reader need to become dormant state
-  virtual bool IsDormantNeeded() { return false; }
   // Release media resources they should be released in dormant state
   // The reader can be made usable again by calling ReadMetadata().
   virtual void ReleaseMediaResources() {};
   virtual void SetSharedDecoderManager(SharedDecoderManager* aManager) {}
   // Breaks reference-counted cycles. Called during shutdown.
   // WARNING: If you override this, you must call the base implementation
   // in your override.
   virtual void BreakCycles();
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -232,18 +232,17 @@ MediaDecoderStateMachine::MediaDecoderSt
   mDropVideoUntilNextDiscontinuity(false),
   mDecodeToSeekTarget(false),
   mCurrentTimeBeforeSeek(0),
   mCorruptFrames(30),
   mDisabledHardwareAcceleration(false),
   mDecodingFrozenAtStateDecoding(false),
   mSentLoadedMetadataEvent(false),
   mSentFirstFrameLoadedEvent(false),
-  mSentPlaybackEndedEvent(false),
-  mDecodedStream(mDecoder->GetReentrantMonitor())
+  mSentPlaybackEndedEvent(false)
 {
   MOZ_COUNT_CTOR(MediaDecoderStateMachine);
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
 
   // Dispatch initialization that needs to happen on that task queue.
   nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(this, &MediaDecoderStateMachine::InitializationTask);
   mTaskQueue->Dispatch(r.forget());
 
@@ -361,18 +360,18 @@ void MediaDecoderStateMachine::SendStrea
                                                AudioSegment* aOutput)
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
 
   // This logic has to mimic AudioSink closely to make sure we write
   // the exact same silences
   CheckedInt64 audioWrittenOffset = aStream->mAudioFramesWritten +
-      UsecsToFrames(mInfo.mAudio.mRate, mStreamStartTime);
-  CheckedInt64 frameOffset = UsecsToFrames(mInfo.mAudio.mRate, aAudio->mTime);
+      UsecsToFrames(mStreamStartTime, mInfo.mAudio.mRate);
+  CheckedInt64 frameOffset = UsecsToFrames(aAudio->mTime, mInfo.mAudio.mRate);
 
   if (!audioWrittenOffset.isValid() ||
       !frameOffset.isValid() ||
       // ignore packet that we've already processed
       frameOffset.value() + aAudio->mFrames <= audioWrittenOffset.value()) {
     return;
   }
 
@@ -1279,27 +1278,29 @@ void MediaDecoderStateMachine::StopPlayb
 
   DispatchDecodeTasksIfNeeded();
 }
 
 void MediaDecoderStateMachine::MaybeStartPlayback()
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
+  MOZ_ASSERT(mState == DECODER_STATE_DECODING ||
+             mState == DECODER_STATE_COMPLETED);
+
   if (IsPlaying()) {
     // Logging this case is really spammy - don't do it.
     return;
   }
 
   bool playStatePermits = mPlayState == MediaDecoder::PLAY_STATE_PLAYING;
-  bool decodeStatePermits = mState == DECODER_STATE_DECODING || mState == DECODER_STATE_COMPLETED;
-  if (!playStatePermits || !decodeStatePermits || mIsAudioPrerolling || mIsVideoPrerolling) {
-    DECODER_LOG("Not starting playback [playStatePermits: %d, decodeStatePermits: %d, "
-                "mIsAudioPrerolling: %d, mIsVideoPrerolling: %d]", (int) playStatePermits,
-                (int) decodeStatePermits, (int) mIsAudioPrerolling, (int) mIsVideoPrerolling);
+  if (!playStatePermits || mIsAudioPrerolling || mIsVideoPrerolling) {
+    DECODER_LOG("Not starting playback [playStatePermits: %d, "
+                "mIsAudioPrerolling: %d, mIsVideoPrerolling: %d]",
+                (int) playStatePermits, (int) mIsAudioPrerolling, (int) mIsVideoPrerolling);
     return;
   }
 
   if (mDecoder->CheckDecoderCanOffloadAudio()) {
     DECODER_LOG("Offloading playback");
     return;
   }
 
@@ -1384,21 +1385,16 @@ void MediaDecoderStateMachine::VolumeCha
 {
   MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   if (mAudioSink) {
     mAudioSink->SetVolume(mVolume);
   }
 }
 
-bool MediaDecoderStateMachine::IsRealTime() const
-{
-  return mRealTime;
-}
-
 void MediaDecoderStateMachine::RecomputeDuration()
 {
   MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
 
   TimeUnit duration;
   if (mExplicitDuration.Ref().isSome()) {
     double d = mExplicitDuration.Ref().ref();
@@ -1421,28 +1417,16 @@ void MediaDecoderStateMachine::Recompute
   if (duration < mObservedDuration.Ref()) {
     duration = mObservedDuration;
   }
 
   MOZ_ASSERT(duration.ToMicroseconds() >= 0);
   mDuration = Some(duration);
 }
 
-void MediaDecoderStateMachine::SetFragmentEndTime(int64_t aEndTime)
-{
-  AssertCurrentThreadInMonitor();
-
-  mFragmentEndTime = aEndTime < 0 ? aEndTime : aEndTime;
-}
-
-bool MediaDecoderStateMachine::IsDormantNeeded()
-{
-  return mReader->IsDormantNeeded();
-}
-
 void MediaDecoderStateMachine::SetDormant(bool aDormant)
 {
   MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
 
   if (IsShutdown()) {
     return;
   }
@@ -2332,37 +2316,29 @@ MediaDecoderStateMachine::FinishDecodeFi
   }
 
   // Get potentially updated metadata
   {
     ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
     mReader->ReadUpdatedMetadata(&mInfo);
   }
 
-  nsAutoPtr<MediaInfo> info(new MediaInfo());
-  *info = mInfo;
   if (!mNotifyMetadataBeforeFirstFrame) {
     // If we didn't have duration and/or start time before, we should now.
     EnqueueLoadedMetadataEvent();
   }
   EnqueueFirstFrameLoadedEvent();
 
-  if (mState == DECODER_STATE_DECODING_FIRSTFRAME) {
-    StartDecoding();
-  }
-
-  // For very short media the first frame decode can decode the entire media.
-  // So we need to check if this has occurred, else our decode pipeline won't
-  // run (since it doesn't need to) and we won't detect end of stream.
-  CheckIfDecodeComplete();
-
   if (mQueuedSeek.Exists()) {
     mPendingSeek.Steal(mQueuedSeek);
     SetState(DECODER_STATE_SEEKING);
     ScheduleStateMachine();
+  } else if (mState == DECODER_STATE_DECODING_FIRSTFRAME) {
+    // StartDecoding() will also check if decode is completed.
+    StartDecoding();
   }
 
   return NS_OK;
 }
 
 void
 MediaDecoderStateMachine::SeekCompleted()
 {
@@ -2617,17 +2593,16 @@ nsresult MediaDecoderStateMachine::RunSt
       }
 
       DECODER_LOG("Changed state from BUFFERING to DECODING");
       DECODER_LOG("Buffered for %.3lfs", (now - mBufferingStart).ToSeconds());
       StartDecoding();
 
       // Notify to allow blocked decoder thread to continue
       mDecoder->GetReentrantMonitor().NotifyAll();
-      MaybeStartPlayback();
       NS_ASSERTION(IsStateMachineScheduled(), "Must have timer scheduled");
       return NS_OK;
     }
 
     case DECODER_STATE_SEEKING: {
       if (mPendingSeek.Exists()) {
         InitiateSeek();
       }
@@ -2803,16 +2778,17 @@ int64_t MediaDecoderStateMachine::GetStr
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   MOZ_ASSERT(mStreamStartTime != -1);
   return mStreamStartTime + GetDecodedStream()->GetPosition();
 }
 
 int64_t MediaDecoderStateMachine::GetVideoStreamPosition() const
 {
+  MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
 
   if (!IsPlaying()) {
     return mPlayDuration;
   }
 
   // Time elapsed since we started playing.
   int64_t delta = DurationToUsecs(TimeStamp::Now() - mPlayStartTime);
@@ -3175,16 +3151,17 @@ void MediaDecoderStateMachine::StartBuff
   MediaDecoder::Statistics stats = mDecoder->GetStatistics();
   DECODER_LOG("Playback rate: %.1lfKB/s%s download rate: %.1lfKB/s%s",
               stats.mPlaybackRate/1024, stats.mPlaybackRateReliable ? "" : " (unreliable)",
               stats.mDownloadRate/1024, stats.mDownloadRateReliable ? "" : " (unreliable)");
 }
 
 void MediaDecoderStateMachine::SetPlayStartTime(const TimeStamp& aTimeStamp)
 {
+  MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   mPlayStartTime = aTimeStamp;
 
   if (mAudioSink) {
     mAudioSink->SetPlaying(!mPlayStartTime.IsNull());
   } else if (mAudioCaptured) {
     mDecodedStream.SetPlaying(!mPlayStartTime.IsNull());
   }
@@ -3365,17 +3342,16 @@ uint32_t MediaDecoderStateMachine::GetAm
   AssertCurrentThreadInMonitor();
   return (mReader->IsAsync() && mReader->VideoIsHardwareAccelerated())
     ? std::max<uint32_t>(sVideoQueueHWAccelSize, MIN_VIDEO_QUEUE_SIZE)
     : std::max<uint32_t>(sVideoQueueDefaultSize, MIN_VIDEO_QUEUE_SIZE);
 }
 
 DecodedStreamData* MediaDecoderStateMachine::GetDecodedStream() const
 {
-  AssertCurrentThreadInMonitor();
   return mDecodedStream.GetData();
 }
 
 void MediaDecoderStateMachine::DispatchAudioCaptured()
 {
   nsRefPtr<MediaDecoderStateMachine> self = this;
   nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self] () -> void
   {
@@ -3399,30 +3375,35 @@ void MediaDecoderStateMachine::DispatchA
 }
 
 void MediaDecoderStateMachine::AddOutputStream(ProcessedMediaStream* aStream,
                                                bool aFinishWhenEnded)
 {
   MOZ_ASSERT(NS_IsMainThread());
   DECODER_LOG("AddOutputStream aStream=%p!", aStream);
 
-  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   if (!GetDecodedStream()) {
     RecreateDecodedStream(aStream->Graph());
   }
   mDecodedStream.Connect(aStream, aFinishWhenEnded);
   DispatchAudioCaptured();
 }
 
 void MediaDecoderStateMachine::RecreateDecodedStream(MediaStreamGraph* aGraph)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   mDecodedStream.RecreateData(aGraph);
-  mDecodedStream.SetPlaying(IsPlaying());
+
+  nsRefPtr<MediaDecoderStateMachine> self = this;
+  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self] () -> void
+  {
+    ReentrantMonitorAutoEnter mon(self->mDecoder->GetReentrantMonitor());
+    self->mDecodedStream.SetPlaying(self->IsPlaying());
+  });
+  TaskQueue()->Dispatch(r.forget());
 }
 
 } // namespace mozilla
 
 // avoid redefined macro in unified build
 #undef LOG
 #undef DECODER_LOG
 #undef VERBOSE_LOG
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -140,27 +140,20 @@ public:
     DECODER_STATE_DECODING,
     DECODER_STATE_SEEKING,
     DECODER_STATE_BUFFERING,
     DECODER_STATE_COMPLETED,
     DECODER_STATE_SHUTDOWN,
     DECODER_STATE_ERROR
   };
 
-  State GetState() {
-    AssertCurrentThreadInMonitor();
-    return mState;
-  }
-
   DecodedStreamData* GetDecodedStream() const;
 
   void AddOutputStream(ProcessedMediaStream* aStream, bool aFinishWhenEnded);
 
-  // Check if the decoder needs to become dormant state.
-  bool IsDormantNeeded();
   // Set/Unset dormant state.
   void SetDormant(bool aDormant);
 
 private:
   // Initialization that needs to happen on the task queue. This is the first
   // task that gets run on the task queue, and is dispatched from the MDSM
   // constructor immediately after the task queue is created.
   void InitializationTask();
@@ -179,17 +172,18 @@ public:
   {
     nsCOMPtr<nsIRunnable> runnable =
       NS_NewRunnableMethod(this, &MediaDecoderStateMachine::Shutdown);
     TaskQueue()->Dispatch(runnable.forget());
   }
 
   void FinishShutdown();
 
-  bool IsRealTime() const;
+  // Immutable after construction - may be called on any thread.
+  bool IsRealTime() const { return mRealTime; }
 
   // Functions used by assertions to ensure we're calling things
   // on the appropriate threads.
   bool OnDecodeTaskQueue() const;
   bool OnTaskQueue() const;
 
   // Seeks to the decoder to aTarget asynchronously.
   // Must be called on the state machine thread.
@@ -219,23 +213,25 @@ public:
     nsCOMPtr<nsIRunnable> runnable =
       NS_NewRunnableMethod(this, &MediaDecoderStateMachine::StartBuffering);
     TaskQueue()->Dispatch(runnable.forget());
   }
 
   // This is called on the state machine thread and audio thread.
   // The decoder monitor must be obtained before calling this.
   bool HasAudio() const {
+    MOZ_ASSERT(OnTaskQueue());
     AssertCurrentThreadInMonitor();
     return mInfo.HasAudio();
   }
 
   // This is called on the state machine thread and audio thread.
   // The decoder monitor must be obtained before calling this.
   bool HasVideo() const {
+    MOZ_ASSERT(OnTaskQueue());
     AssertCurrentThreadInMonitor();
     return mInfo.HasVideo();
   }
 
   // Should be called by main thread.
   bool HaveNextFrameData();
 
   // Must be called with the decode monitor held.
@@ -304,28 +300,32 @@ public:
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     mDelayedScheduler.CompleteRequest();
     ScheduleStateMachine();
   }
 
   void NotReached() { MOZ_DIAGNOSTIC_ASSERT(false); }
 
   // Set the media fragment end time. aEndTime is in microseconds.
-  void SetFragmentEndTime(int64_t aEndTime);
+  void DispatchSetFragmentEndTime(int64_t aEndTime)
+  {
+    nsRefPtr<MediaDecoderStateMachine> self = this;
+    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self, aEndTime] () {
+      self->mFragmentEndTime = aEndTime;
+    });
+    TaskQueue()->Dispatch(r.forget());
+  }
 
   // Drop reference to decoder.  Only called during shutdown dance.
   void BreakCycles() {
     MOZ_ASSERT(NS_IsMainThread());
     if (mReader) {
       mReader->BreakCycles();
     }
-    {
-      ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
-      mDecodedStream.DestroyData();
-    }
+    mDecodedStream.DestroyData();
     mDecoder = nullptr;
   }
 
   // Copy queued audio/video data in the reader to any output MediaStreams that
   // need it.
   void SendStreamData();
   void FinishStreamData();
   bool HaveEnoughDecodedAudio(int64_t aAmpleAudioUSecs);
@@ -584,16 +584,17 @@ protected:
   void DispatchDecodeTasksIfNeeded();
 
   // Returns the "media time". This is the absolute time which the media
   // playback has reached. i.e. this returns values in the range
   // [mStartTime, mEndTime], and mStartTime will not be 0 if the media does
   // not start at 0. Note this is different than the "current playback position",
   // which is in the range [0,duration].
   int64_t GetMediaTime() const {
+    MOZ_ASSERT(OnTaskQueue());
     AssertCurrentThreadInMonitor();
     return mCurrentPosition;
   }
 
   // Returns an upper bound on the number of microseconds of audio that is
   // decoded and playable. This is the sum of the number of usecs of audio which
   // is decoded and in the reader's audio queue, and the usecs of unplayed audio
   // which has been pushed to the audio hardware for playback. Note that after
@@ -715,17 +716,17 @@ private:
 
   // Task queue for running the state machine.
   nsRefPtr<MediaTaskQueue> mTaskQueue;
 
   // State-watching manager.
   WatchManager<MediaDecoderStateMachine> mWatchManager;
 
   // True is we are decoding a realtime stream, like a camera stream.
-  bool mRealTime;
+  const bool mRealTime;
 
   // True if we've dispatched a task to run the state machine but the task has
   // yet to run.
   bool mDispatchedStateMachine;
 
   // Class for managing delayed dispatches of the state machine.
   class DelayedScheduler {
   public:
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -68,19 +68,16 @@ MediaFormatReader::MediaFormatReader(Abs
   , mAudio(this, MediaData::AUDIO_DATA, Preferences::GetUint("media.audio-decode-ahead", 2))
   , mVideo(this, MediaData::VIDEO_DATA, Preferences::GetUint("media.video-decode-ahead", 2))
   , mLastReportedNumDecodedFrames(0)
   , mLayersBackendType(layers::LayersBackend::LAYERS_NONE)
   , mInitDone(false)
   , mSeekable(false)
   , mIsEncrypted(false)
   , mTrackDemuxersMayBlock(false)
-#if defined(READER_DORMANT_HEURISTIC)
-  , mDormantEnabled(Preferences::GetBool("media.decoder.heuristic.dormant.enabled", false))
-#endif
 {
   MOZ_ASSERT(aDemuxer);
   MOZ_COUNT_CTOR(MediaFormatReader);
 }
 
 MediaFormatReader::~MediaFormatReader()
 {
   MOZ_COUNT_DTOR(MediaFormatReader);
@@ -1102,19 +1099,17 @@ MediaFormatReader::WaitForData(MediaData
   ScheduleUpdate(trackType);
   return p;
 }
 
 nsresult
 MediaFormatReader::ResetDecode()
 {
   MOZ_ASSERT(OnTaskQueue());
-
   LOGV("");
-  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
 
   mAudio.mSeekRequest.DisconnectIfExists();
   mVideo.mSeekRequest.DisconnectIfExists();
   mSeekPromise.RejectIfExists(NS_OK, __func__);
   mSkipRequest.DisconnectIfExists();
 
   // Do the same for any data wait promises.
   mAudio.mWaitingPromise.RejectIfExists(WaitForDataRejectValue(MediaData::AUDIO_DATA, WaitForDataRejectValue::CANCELED), __func__);
@@ -1431,25 +1426,16 @@ MediaFormatReader::GetBuffered()
     intervals = Move(audioti);
   } else if (HasVideo()) {
     intervals = Move(videoti);
   }
 
   return intervals.Shift(media::TimeUnit::FromMicroseconds(-startTime));
 }
 
-bool MediaFormatReader::IsDormantNeeded()
-{
-#if defined(READER_DORMANT_HEURISTIC)
-  return mDormantEnabled;
-#else
-  return false;
-#endif
-}
-
 void MediaFormatReader::ReleaseMediaResources()
 {
   // Before freeing a video codec, all video buffers needed to be released
   // even from graphics pipeline.
   VideoFrameContainer* container = mDecoder->GetVideoFrameContainer();
   if (container) {
     container->ClearCurrentFrame();
   }
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -11,22 +11,16 @@
 #include "mozilla/Maybe.h"
 #include "MediaDataDemuxer.h"
 #include "MediaDecoderReader.h"
 #include "MediaTaskQueue.h"
 #include "PlatformDecoderModule.h"
 
 namespace mozilla {
 
-#if defined(MOZ_GONK_MEDIACODEC) || defined(XP_WIN) || defined(MOZ_APPLEMEDIA) || defined(MOZ_FFMPEG)
-#define READER_DORMANT_HEURISTIC
-#else
-#undef READER_DORMANT_HEURISTIC
-#endif
-
 class MediaFormatReader final : public MediaDecoderReader
 {
   typedef TrackInfo::TrackType TrackType;
   typedef media::Interval<int64_t> ByteInterval;
 
 public:
   explicit MediaFormatReader(AbstractMediaDecoder* aDecoder,
                              MediaDataDemuxer* aDemuxer,
@@ -73,17 +67,16 @@ public:
   void NotifyDataRemoved() override;
 
   media::TimeIntervals GetBuffered() override;
 
   virtual bool ForceZeroStartTime() const override;
 
   // For Media Resource Management
   void SetIdle() override;
-  bool IsDormantNeeded() override;
   void ReleaseMediaResources() override;
   void SetSharedDecoderManager(SharedDecoderManager* aManager)
     override;
 
   nsresult ResetDecode() override;
 
   nsRefPtr<ShutdownPromise> Shutdown() override;
 
--- a/dom/media/StateMirroring.h
+++ b/dom/media/StateMirroring.h
@@ -162,19 +162,16 @@ private:
     }
 
     operator const T&()
     {
       MOZ_ASSERT(OwnerThread()->IsCurrentThreadIn());
       return mValue;
     }
 
-    // Temporary workaround for naughty code.
-    const T& ReadOnWrongThread() { return mValue; }
-
     void Set(const T& aNewValue)
     {
       MOZ_ASSERT(OwnerThread()->IsCurrentThreadIn());
 
       if (aNewValue == mValue) {
         return;
       }
 
@@ -245,17 +242,16 @@ public:
   void DisconnectAll() { return mImpl->DisconnectAll(); }
 
   // Access to the Impl.
   operator Impl&() { return *mImpl; }
   Impl* operator&() { return mImpl; }
 
   // Access to the T.
   const T& Ref() const { return *mImpl; }
-  const T& ReadOnWrongThread() const { return mImpl->ReadOnWrongThread(); }
   operator const T&() const { return Ref(); }
   void Set(const T& aNewValue) { mImpl->Set(aNewValue); }
   Canonical& operator=(const T& aNewValue) { Set(aNewValue); return *this; }
   Canonical& operator=(const Canonical& aOther) { Set(aOther); return *this; }
   Canonical(const Canonical& aOther) = delete;
 
 private:
   nsRefPtr<Impl> mImpl;
@@ -304,19 +300,16 @@ private:
     }
 
     operator const T&()
     {
       MOZ_ASSERT(OwnerThread()->IsCurrentThreadIn());
       return mValue;
     }
 
-    // Temporary workaround for naughty code.
-    const T& ReadOnWrongThread() { return mValue; }
-
     virtual void UpdateValue(const T& aNewValue) override
     {
       MOZ_ASSERT(OwnerThread()->IsCurrentThreadIn());
       if (mValue != aNewValue) {
         mValue = aNewValue;
         WatchTarget::NotifyWatchers();
       }
     }
@@ -372,17 +365,16 @@ public:
   void DisconnectIfConnected() { mImpl->DisconnectIfConnected(); }
 
   // Access to the Impl<T>.
   operator Impl&() { return *mImpl; }
   Impl* operator&() { return mImpl; }
 
   // Access to the T.
   const T& Ref() const { return *mImpl; }
-  const T& ReadOnWrongThread() const { return mImpl->ReadOnWrongThread(); }
   operator const T&() const { return Ref(); }
 
 private:
   nsRefPtr<Impl> mImpl;
 };
 
 #undef MIRROR_LOG
 
--- a/dom/media/fmp4/MP4Decoder.cpp
+++ b/dom/media/fmp4/MP4Decoder.cpp
@@ -22,16 +22,29 @@
 #ifdef MOZ_WIDGET_ANDROID
 #include "nsIGfxInfo.h"
 #include "AndroidBridge.h"
 #endif
 #include "mozilla/layers/LayersTypes.h"
 
 namespace mozilla {
 
+#if defined(MOZ_GONK_MEDIACODEC) || defined(XP_WIN) || defined(MOZ_APPLEMEDIA) || defined(MOZ_FFMPEG)
+#define MP4_READER_DORMANT_HEURISTIC
+#else
+#undef MP4_READER_DORMANT_HEURISTIC
+#endif
+
+MP4Decoder::MP4Decoder()
+{
+#if defined(MP4_READER_DORMANT_HEURISTIC)
+  mDormantSupported = Preferences::GetBool("media.decoder.heuristic.dormant.enabled", false);
+#endif
+}
+
 MediaDecoderStateMachine* MP4Decoder::CreateStateMachine()
 {
   bool useFormatDecoder =
     Preferences::GetBool("media.format-reader.mp4", true);
   nsRefPtr<MediaDecoderReader> reader = useFormatDecoder ?
     static_cast<MediaDecoderReader*>(new MediaFormatReader(this, new MP4Demuxer(GetResource()))) :
     static_cast<MediaDecoderReader*>(new MP4Reader(this));
 
--- a/dom/media/fmp4/MP4Decoder.h
+++ b/dom/media/fmp4/MP4Decoder.h
@@ -9,16 +9,17 @@
 #include "MediaDecoder.h"
 
 namespace mozilla {
 
 // Decoder that uses a bundled MP4 demuxer and platform decoders to play MP4.
 class MP4Decoder : public MediaDecoder
 {
 public:
+  MP4Decoder();
 
   virtual MediaDecoder* Clone() override {
     if (!IsEnabled()) {
       return nullptr;
     }
     return new MP4Decoder();
   }
 
--- a/dom/media/fmp4/MP4Reader.cpp
+++ b/dom/media/fmp4/MP4Reader.cpp
@@ -151,19 +151,16 @@ MP4Reader::MP4Reader(AbstractMediaDecode
   , mLastReportedNumDecodedFrames(0)
   , mLayersBackendType(layers::LayersBackend::LAYERS_NONE)
   , mDemuxerInitialized(false)
   , mFoundSPSForTelemetry(false)
   , mIsEncrypted(false)
   , mIndexReady(false)
   , mLastSeenEnd(-1)
   , mDemuxerMonitor("MP4 Demuxer")
-#if defined(MP4_READER_DORMANT_HEURISTIC)
-  , mDormantEnabled(Preferences::GetBool("media.decoder.heuristic.dormant.enabled", false))
-#endif
 {
   MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
   MOZ_COUNT_CTOR(MP4Reader);
 }
 
 MP4Reader::~MP4Reader()
 {
   MOZ_COUNT_DTOR(MP4Reader);
@@ -1096,25 +1093,16 @@ MP4Reader::GetBuffered()
         media::TimeUnit::FromMicroseconds(timeRanges[i].start - mStartTime),
         media::TimeUnit::FromMicroseconds(timeRanges[i].end - mStartTime));
     }
   }
 
   return buffered;
 }
 
-bool MP4Reader::IsDormantNeeded()
-{
-#if defined(MP4_READER_DORMANT_HEURISTIC)
-  return mDormantEnabled;
-#else
-  return false;
-#endif
-}
-
 void MP4Reader::ReleaseMediaResources()
 {
   // Before freeing a video codec, all video buffers needed to be released
   // even from graphics pipeline.
   VideoFrameContainer* container = mDecoder->GetVideoFrameContainer();
   if (container) {
     container->ClearCurrentFrame();
   }
--- a/dom/media/fmp4/MP4Reader.h
+++ b/dom/media/fmp4/MP4Reader.h
@@ -18,22 +18,16 @@
 #include "mozilla/Monitor.h"
 
 namespace mozilla {
 
 typedef std::deque<nsRefPtr<MediaRawData>> MediaSampleQueue;
 
 class MP4Stream;
 
-#if defined(MOZ_GONK_MEDIACODEC) || defined(XP_WIN) || defined(MOZ_APPLEMEDIA) || defined(MOZ_FFMPEG)
-#define MP4_READER_DORMANT_HEURISTIC
-#else
-#undef MP4_READER_DORMANT_HEURISTIC
-#endif
-
 class MP4Reader final : public MediaDecoderReader
 {
   typedef TrackInfo::TrackType TrackType;
 
 public:
   explicit MP4Reader(AbstractMediaDecoder* aDecoder, MediaTaskQueue* aBorrowedTaskQueue = nullptr);
 
   virtual ~MP4Reader();
@@ -66,17 +60,16 @@ public:
 protected:
   virtual void NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset) override;
 public:
 
   virtual media::TimeIntervals GetBuffered() override;
 
   // For Media Resource Management
   virtual void SetIdle() override;
-  virtual bool IsDormantNeeded() override;
   virtual void ReleaseMediaResources() override;
   virtual void SetSharedDecoderManager(SharedDecoderManager* aManager)
     override;
 
   virtual nsresult ResetDecode() override;
 
   virtual nsRefPtr<ShutdownPromise> Shutdown() override;
 
@@ -272,17 +265,13 @@ private:
 
   // Synchronized by decoder monitor.
   bool mIsEncrypted;
 
   bool mIndexReady;
   int64_t mLastSeenEnd;
   Monitor mDemuxerMonitor;
   nsRefPtr<SharedDecoderManager> mSharedDecoderManager;
-
-#if defined(MP4_READER_DORMANT_HEURISTIC)
-  const bool mDormantEnabled;
-#endif
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/gstreamer/GStreamerReader.cpp
+++ b/dom/media/gstreamer/GStreamerReader.cpp
@@ -883,19 +883,19 @@ media::TimeIntervals GStreamerReader::Ge
 
 #if GST_VERSION_MAJOR == 0
   GstFormat format = GST_FORMAT_TIME;
 #endif
   AutoPinned<MediaResource> resource(mDecoder->GetResource());
   nsTArray<MediaByteRange> ranges;
   resource->GetCachedRanges(ranges);
 
-  if (resource->IsDataCachedToEndOfResource(0) && mDuration.ReadOnWrongThread().isSome()) {
+  if (resource->IsDataCachedToEndOfResource(0) && mDuration.Ref().isSome()) {
     /* fast path for local or completely cached files */
-    gint64 duration = mDuration.ReadOnWrongThread().ref().ToMicroseconds();
+    gint64 duration = mDuration.Ref().ref().ToMicroseconds();
     LOG(LogLevel::Debug, "complete range [0, %f] for [0, %li]",
         (double) duration / GST_MSECOND, GetDataLength());
     buffered +=
       media::TimeInterval(media::TimeUnit::FromMicroseconds(0),
                           media::TimeUnit::FromMicroseconds(duration));
     return buffered;
   }
 
--- a/dom/media/mediasource/MediaSourceDecoder.h
+++ b/dom/media/mediasource/MediaSourceDecoder.h
@@ -38,16 +38,24 @@ public:
   explicit MediaSourceDecoder(dom::HTMLMediaElement* aElement);
 
   virtual MediaDecoder* Clone() override;
   virtual MediaDecoderStateMachine* CreateStateMachine() override;
   virtual nsresult Load(nsIStreamListener**, MediaDecoder*) override;
   virtual media::TimeIntervals GetSeekable() override;
   media::TimeIntervals GetBuffered() override;
 
+  // We can't do this in the constructor because we don't know what type of
+  // media we're dealing with by that point.
+  void NotifyDormantSupported(bool aSupported)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    mDormantSupported = aSupported;
+  }
+
   virtual void Shutdown() override;
 
   static already_AddRefed<MediaResource> CreateResource(nsIPrincipal* aPrincipal = nullptr);
 
   void AttachMediaSource(dom::MediaSource* aMediaSource);
   void DetachMediaSource();
 
   already_AddRefed<SourceBufferDecoder> CreateSubDecoder(const nsACString& aType,
--- a/dom/media/mediasource/MediaSourceReader.cpp
+++ b/dom/media/mediasource/MediaSourceReader.cpp
@@ -675,27 +675,16 @@ MediaSourceReader::SwitchVideoSource(int
       *aTarget = startTime;
     }
   }
   MSE_DEBUGV("switched decoder to %p (fuzz:%d)",
              mVideoSourceDecoder.get(), usedFuzz);
   return SOURCE_NEW;
 }
 
-bool
-MediaSourceReader::IsDormantNeeded()
-{
-  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
-  if (GetVideoReader()) {
-    return GetVideoReader()->IsDormantNeeded();
-  }
-
-  return false;
-}
-
 void
 MediaSourceReader::ReleaseMediaResources()
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   if (GetVideoReader()) {
     GetVideoReader()->ReleaseMediaResources();
   }
 }
--- a/dom/media/mediasource/MediaSourceReader.h
+++ b/dom/media/mediasource/MediaSourceReader.h
@@ -49,17 +49,16 @@ public:
 
   nsRefPtr<AudioDataPromise> RequestAudioData() override;
   nsRefPtr<VideoDataPromise>
   RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold) override;
 
   virtual size_t SizeOfVideoQueueInFrames() override;
   virtual size_t SizeOfAudioQueueInFrames() override;
 
-  virtual bool IsDormantNeeded() override;
   virtual void ReleaseMediaResources() override;
 
   void OnAudioDecoded(AudioData* aSample);
   void OnAudioNotDecoded(NotDecodedReason aReason);
   void OnVideoDecoded(VideoData* aSample);
   void OnVideoNotDecoded(NotDecodedReason aReason);
 
   void DoVideoSeek();
--- a/dom/media/mediasource/SourceBufferContentManager.cpp
+++ b/dom/media/mediasource/SourceBufferContentManager.cpp
@@ -5,25 +5,43 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "SourceBufferContentManager.h"
 #include "TrackBuffer.h"
 #include "TrackBuffersManager.h"
 
 namespace mozilla {
 
+#if defined(MOZ_GONK_MEDIACODEC) || defined(XP_WIN) || defined(MOZ_APPLEMEDIA) || defined(MOZ_FFMPEG)
+#define MP4_READER_DORMANT_HEURISTIC
+#else
+#undef MP4_READER_DORMANT_HEURISTIC
+#endif
+
 already_AddRefed<SourceBufferContentManager>
 SourceBufferContentManager::CreateManager(dom::SourceBuffer* aParent,
                                           MediaSourceDecoder* aParentDecoder,
                                           const nsACString &aType)
 {
   nsRefPtr<SourceBufferContentManager> manager;
   bool useFormatReader =
     Preferences::GetBool("media.mediasource.format-reader", false);
   if (useFormatReader) {
     manager = new TrackBuffersManager(aParent, aParentDecoder, aType);
   } else {
     manager = new TrackBuffer(aParentDecoder, aType);
   }
+
+  // Now that we know what type we're dealing with, enable dormant as needed.
+#if defined(MP4_READER_DORMANT_HEURISTIC)
+  if (aType.LowerCaseEqualsLiteral("video/mp4") ||
+      aType.LowerCaseEqualsLiteral("audio/mp4") ||
+      useFormatReader)
+  {
+    aParentDecoder->NotifyDormantSupported(Preferences::GetBool("media.decoder.heuristic.dormant.enabled", false));
+  }
+#endif
+
+
   return  manager.forget();
 }
 
 }
--- a/dom/media/omx/MediaCodecReader.h
+++ b/dom/media/omx/MediaCodecReader.h
@@ -57,19 +57,16 @@ class MediaCodecReader : public MediaOmx
 public:
   MediaCodecReader(AbstractMediaDecoder* aDecoder);
   virtual ~MediaCodecReader();
 
   // Initializes the reader, returns NS_OK on success, or NS_ERROR_FAILURE
   // on failure.
   virtual nsresult Init(MediaDecoderReader* aCloneDonor);
 
-  // True when this reader need to become dormant state
-  virtual bool IsDormantNeeded() { return true;}
-
   // Release media resources they should be released in dormant state
   virtual void ReleaseMediaResources();
 
   // Destroys the decoding state. The reader cannot be made usable again.
   // This is different from ReleaseMediaResources() as Shutdown() is
   // irreversible, whereas ReleaseMediaResources() is reversible.
   virtual nsRefPtr<ShutdownPromise> Shutdown();
 
--- a/dom/media/omx/MediaOmxCommonDecoder.cpp
+++ b/dom/media/omx/MediaOmxCommonDecoder.cpp
@@ -24,16 +24,17 @@ extern PRLogModuleInfo* gMediaDecoderLog
 #define DECODER_LOG(type, msg) MOZ_LOG(gMediaDecoderLog, type, msg)
 
 MediaOmxCommonDecoder::MediaOmxCommonDecoder()
   : MediaDecoder()
   , mReader(nullptr)
   , mCanOffloadAudio(false)
   , mFallbackToStateMachine(false)
 {
+  mDormantSupported = true;
   if (!gMediaDecoderLog) {
     gMediaDecoderLog = PR_NewLogModule("MediaDecoder");
   }
 }
 
 MediaOmxCommonDecoder::~MediaOmxCommonDecoder() {}
 
 void
--- a/dom/media/omx/MediaOmxReader.h
+++ b/dom/media/omx/MediaOmxReader.h
@@ -82,17 +82,16 @@ public:
     return mHasAudio;
   }
 
   virtual bool HasVideo()
   {
     return mHasVideo;
   }
 
-  virtual bool IsDormantNeeded() { return true;}
   virtual void ReleaseMediaResources();
 
   virtual nsRefPtr<MediaDecoderReader::MetadataPromise> AsyncReadMetadata() override;
 
   virtual nsRefPtr<SeekPromise>
   Seek(int64_t aTime, int64_t aEndTime) override;
 
   virtual bool IsMediaSeekable() override;
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -1048,17 +1048,17 @@ nsJSObjWrapper::NP_Enumerate(NPObject *n
     if (!JS_IdToValue(cx, ida[i], &v)) {
       PR_Free(*idarray);
       return false;
     }
 
     NPIdentifier id;
     if (v.isString()) {
       JS::Rooted<JSString*> str(cx, v.toString());
-      str = JS_InternJSString(cx, str);
+      str = JS_AtomizeAndPinJSString(cx, str);
       if (!str) {
         PR_Free(*idarray);
         return false;
       }
       id = StringToNPIdentifier(cx, str);
     } else {
       NS_ASSERTION(v.isInt32(),
                    "The element in ida must be either string or int!\n");
@@ -1548,17 +1548,17 @@ CallNPMethodInternal(JSContext *cx, JS::
     }
   } else if (funobj != obj) {
     // A obj.function() style call is made, get the method name from
     // the function object.
 
     if (npobj->_class->invoke) {
       JSFunction *fun = ::JS_GetObjectFunction(funobj);
       JS::Rooted<JSString*> funId(cx, ::JS_GetFunctionId(fun));
-      JSString *name = ::JS_InternJSString(cx, funId);
+      JSString *name = ::JS_AtomizeAndPinJSString(cx, funId);
       NPIdentifier id = StringToNPIdentifier(cx, name);
 
       ok = npobj->_class->invoke(npobj, id, npargs, argc, &v);
     } else {
       ok = false;
 
       msg = "Attempt to call a method on object with no invoke method.";
     }
@@ -1937,76 +1937,16 @@ nsNPObjWrapper::GetNewOrUsed(NPP npp, JS
   ::JS_SetPrivate(obj, npobj);
 
   // The new JSObject now holds on to npobj
   _retainobject(npobj);
 
   return obj;
 }
 
-
-// Struct for passing an NPP and a JSContext to
-// NPObjWrapperPluginDestroyedCallback
-struct NppAndCx
-{
-  NPP npp;
-  JSContext *cx;
-};
-
-static PLDHashOperator
-NPObjWrapperPluginDestroyedCallback(PLDHashTable *table, PLDHashEntryHdr *hdr,
-                                    uint32_t number, void *arg)
-{
-  NPObjWrapperHashEntry *entry = (NPObjWrapperHashEntry *)hdr;
-  NppAndCx *nppcx = reinterpret_cast<NppAndCx *>(arg);
-
-  if (entry->mNpp == nppcx->npp) {
-    // HACK: temporarily hide the hash we're enumerating so that invalidate()
-    // and deallocate() don't touch it.
-    PLDHashTable *tmp = static_cast<PLDHashTable*>(table);
-    sNPObjWrappers = nullptr;
-
-    NPObject *npobj = entry->mNPObj;
-
-    if (npobj->_class && npobj->_class->invalidate) {
-      npobj->_class->invalidate(npobj);
-    }
-
-#ifdef NS_BUILD_REFCNT_LOGGING
-    {
-      int32_t refCnt = npobj->referenceCount;
-      while (refCnt) {
-        --refCnt;
-        NS_LOG_RELEASE(npobj, refCnt, "BrowserNPObject");
-      }
-    }
-#endif
-
-    // Force deallocation of plugin objects since the plugin they came
-    // from is being torn down.
-    if (npobj->_class && npobj->_class->deallocate) {
-      npobj->_class->deallocate(npobj);
-    } else {
-      PR_Free(npobj);
-    }
-
-    ::JS_SetPrivate(entry->mJSObj, nullptr);
-
-    sNPObjWrappers = tmp;
-
-    if (sDelayedReleases && sDelayedReleases->RemoveElement(npobj)) {
-      OnWrapperDestroyed();
-    }
-
-    return PL_DHASH_REMOVE;
-  }
-
-  return PL_DHASH_NEXT;
-}
-
 // static
 void
 nsJSNPRuntime::OnPluginDestroy(NPP npp)
 {
   if (sJSObjWrappersAccessible) {
 
     // Prevent modification of sJSObjWrappers table if we go reentrant.
     sJSObjWrappersAccessible = false;
@@ -2023,23 +1963,61 @@ nsJSNPRuntime::OnPluginDestroy(NPP npp)
 
         e.removeFront();
       }
     }
 
     sJSObjWrappersAccessible = true;
   }
 
-  // Use the safe JSContext here as we're not always able to find the
-  // JSContext associated with the NPP any more.
-  AutoSafeJSContext cx;
   if (sNPObjWrappers) {
-    NppAndCx nppcx = { npp, cx };
-    PL_DHashTableEnumerate(sNPObjWrappers,
-                           NPObjWrapperPluginDestroyedCallback, &nppcx);
+    for (auto i = sNPObjWrappers->RemovingIter(); !i.Done(); i.Next()) {
+      auto entry = static_cast<NPObjWrapperHashEntry*>(i.Get());
+
+      if (entry->mNpp == npp) {
+        // HACK: temporarily hide the table we're enumerating so that
+        // invalidate() and deallocate() don't touch it.
+        PLDHashTable *tmp = sNPObjWrappers;
+        sNPObjWrappers = nullptr;
+
+        NPObject *npobj = entry->mNPObj;
+
+        if (npobj->_class && npobj->_class->invalidate) {
+          npobj->_class->invalidate(npobj);
+        }
+
+#ifdef NS_BUILD_REFCNT_LOGGING
+        {
+          int32_t refCnt = npobj->referenceCount;
+          while (refCnt) {
+            --refCnt;
+            NS_LOG_RELEASE(npobj, refCnt, "BrowserNPObject");
+          }
+        }
+#endif
+
+        // Force deallocation of plugin objects since the plugin they came
+        // from is being torn down.
+        if (npobj->_class && npobj->_class->deallocate) {
+          npobj->_class->deallocate(npobj);
+        } else {
+          PR_Free(npobj);
+        }
+
+        ::JS_SetPrivate(entry->mJSObj, nullptr);
+
+        sNPObjWrappers = tmp;
+
+        if (sDelayedReleases && sDelayedReleases->RemoveElement(npobj)) {
+          OnWrapperDestroyed();
+        }
+
+        i.Remove();
+      }
+    }
   }
 }
 
 // static
 void
 nsJSNPRuntime::OnPluginDestroyPending(NPP npp)
 {
   if (sJSObjWrappersAccessible) {
--- a/dom/plugins/base/nsNPAPIPlugin.cpp
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -676,17 +676,17 @@ GetChannelFromNPP(NPP npp)
   return channel.forget();
 }
 
 static NPIdentifier
 doGetIdentifier(JSContext *cx, const NPUTF8* name)
 {
   NS_ConvertUTF8toUTF16 utf16name(name);
 
-  JSString *str = ::JS_InternUCStringN(cx, utf16name.get(), utf16name.Length());
+  JSString *str = ::JS_AtomizeAndPinUCStringN(cx, utf16name.get(), utf16name.Length());
 
   if (!str)
     return nullptr;
 
   return StringToNPIdentifier(cx, str);
 }
 
 #if defined(MOZ_MEMORY_WINDOWS)
@@ -1227,23 +1227,19 @@ NPObject*
   if (NS_WARN_IF(!jsapi.Init(doc->GetInnerWindow()))) {
     return nullptr;
   }
   JSContext* cx = jsapi.cx();
 
   nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID()));
   NS_ENSURE_TRUE(xpc, nullptr);
 
-  nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
+  JS::RootedObject obj(cx);
   xpc->WrapNative(cx, ::JS::CurrentGlobalOrNull(cx), element,
-                  NS_GET_IID(nsIDOMElement),
-                  getter_AddRefs(holder));
-  NS_ENSURE_TRUE(holder, nullptr);
-
-  JS::Rooted<JSObject*> obj(cx, holder->GetJSObject());
+                  NS_GET_IID(nsIDOMElement), obj.address());
   NS_ENSURE_TRUE(obj, nullptr);
 
   return nsJSObjWrapper::GetNewOrUsed(npp, cx, obj);
 }
 
 NPIdentifier
 _getstringidentifier(const NPUTF8* name)
 {
--- a/dom/plugins/base/nsNPAPIPlugin.h
+++ b/dom/plugins/base/nsNPAPIPlugin.h
@@ -124,17 +124,17 @@ IntToNPIdentifier(int i)
 }
 
 JSContext* GetJSContext(NPP npp);
 
 inline bool
 NPStringIdentifierIsPermanent(NPIdentifier id)
 {
   AutoSafeJSContext cx;
-  return JS_StringHasBeenInterned(cx, NPIdentifierToString(id));
+  return JS_StringHasBeenPinned(cx, NPIdentifierToString(id));
 }
 
 #define NPIdentifier_VOID (JSIdToNPIdentifier(JSID_VOID))
 
 NPObject*
 _getwindowobject(NPP npp);
 
 NPObject*
--- a/dom/plugins/ipc/PluginScriptableObjectParent.cpp
+++ b/dom/plugins/ipc/PluginScriptableObjectParent.cpp
@@ -16,61 +16,61 @@
 #include "PluginScriptableObjectUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::plugins;
 using namespace mozilla::plugins::parent;
 
 /**
  * NPIdentifiers in the chrome process are stored as jsids. The difficulty is in
- * ensuring that string identifiers are rooted without interning them all. We
+ * ensuring that string identifiers are rooted without pinning them all. We
  * assume that all NPIdentifiers passed into nsJSNPRuntime will not be used
  * outside the scope of the NPAPI call (i.e., they won't be stored in the
  * heap). Rooting is done using the StackIdentifier class, which roots the
  * identifier via RootedId.
  *
  * This system does not allow jsids to be moved, as would be needed for
  * generational or compacting GC. When Firefox implements a moving GC for
  * strings, we will need to ensure that no movement happens while NPAPI code is
  * on the stack: although StackIdentifier roots all identifiers used, the GC has
- * no way to no that a jsid cast to an NPIdentifier needs to be fixed up if it
+ * no way to know that a jsid cast to an NPIdentifier needs to be fixed up if it
  * is moved.
  */
 
 class MOZ_STACK_CLASS StackIdentifier
 {
 public:
   explicit StackIdentifier(const PluginIdentifier& aIdentifier,
-                           bool aIntern = false);
+                           bool aAtomizeAndPin = false);
 
   bool Failed() const { return mFailed; }
   NPIdentifier ToNPIdentifier() const { return mIdentifier; }
 
 private:
   bool mFailed;
   NPIdentifier mIdentifier;
   AutoSafeJSContext mCx;
   JS::RootedId mId;
 };
 
-StackIdentifier::StackIdentifier(const PluginIdentifier& aIdentifier, bool aIntern)
+StackIdentifier::StackIdentifier(const PluginIdentifier& aIdentifier, bool aAtomizeAndPin)
 : mFailed(false),
   mId(mCx)
 {
   if (aIdentifier.type() == PluginIdentifier::TnsCString) {
     // We don't call _getstringidentifier because we may not want to intern the string.
     NS_ConvertUTF8toUTF16 utf16name(aIdentifier.get_nsCString());
     JS::RootedString str(mCx, JS_NewUCStringCopyN(mCx, utf16name.get(), utf16name.Length()));
     if (!str) {
       NS_ERROR("Id can't be allocated");
       mFailed = true;
       return;
     }
-    if (aIntern) {
-      str = JS_InternJSString(mCx, str);
+    if (aAtomizeAndPin) {
+      str = JS_AtomizeAndPinJSString(mCx, str);
       if (!str) {
         NS_ERROR("Id can't be allocated");
         mFailed = true;
         return;
       }
     }
     if (!JS_StringToId(mCx, str, &mId)) {
       NS_ERROR("Id can't be allocated");
@@ -504,19 +504,19 @@ PluginScriptableObjectParent::Scriptable
 
   *aIdentifiers = (NPIdentifier*)npn->memalloc(*aCount * sizeof(NPIdentifier));
   if (!*aIdentifiers) {
     NS_ERROR("Out of memory!");
     return false;
   }
 
   for (uint32_t index = 0; index < *aCount; index++) {
-    // We intern the ID to avoid a GC hazard here. This could probably be fixed
+    // We pin the ID to avoid a GC hazard here. This could probably be fixed
     // if the interface with nsJSNPRuntime were smarter.
-    StackIdentifier stackID(identifiers[index], true /* aIntern */);
+    StackIdentifier stackID(identifiers[index], true /* aAtomizeAndPin */);
     if (stackID.Failed()) {
       return false;
     }
     (*aIdentifiers)[index] = stackID.ToNPIdentifier();
   }
   return true;
 }
 
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -436,16 +436,18 @@ var interfaceNamesInGlobalScope =
     "Event",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "EventSource",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "EventTarget",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "External", b2g: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "ExternalAppEvent", b2g: true, permission: ["external-app"]},
+// IMPORTANT: Do not change this list without review from a DOM peer!
     "File",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "FileList",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "FileReader",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "FMRadio", b2g: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
@@ -514,16 +516,18 @@ var interfaceNamesInGlobalScope =
     "HTMLDListElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLDocument",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLEmbedElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "HTMLExternalAppElement", b2g: true, permission: ["external-app"]},
+// IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLFieldSetElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLFontElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLFormControlsCollection",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLFormElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
new file mode 100644
--- /dev/null
+++ b/dom/webidl/ExternalAppEvent.webidl
@@ -0,0 +1,17 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+[Constructor(DOMString type, optional ExternalAppEventInit eventInitDict),
+ CheckPermissions="external-app"]
+interface ExternalAppEvent : Event
+{
+  readonly attribute DOMString data;
+};
+
+dictionary ExternalAppEventInit : EventInit
+{
+  DOMString data = "";
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/HTMLExtAppElement.webidl
@@ -0,0 +1,16 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+[CheckPermissions="external-app"]
+interface HTMLExtAppElement : HTMLElement {
+  // Gets the value of the property from a property bag
+  // that was provided to the external application.
+  DOMString getCustomProperty(DOMString name);
+
+  // Posts a message to the external application.
+  [Throws]
+  void postMessage(DOMString name);
+};
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -179,16 +179,17 @@ WEBIDL_FILES = [
     'HTMLDataElement.webidl',
     'HTMLDataListElement.webidl',
     'HTMLDirectoryElement.webidl',
     'HTMLDivElement.webidl',
     'HTMLDListElement.webidl',
     'HTMLDocument.webidl',
     'HTMLElement.webidl',
     'HTMLEmbedElement.webidl',
+    'HTMLExtAppElement.webidl',
     'HTMLFieldSetElement.webidl',
     'HTMLFontElement.webidl',
     'HTMLFormControlsCollection.webidl',
     'HTMLFormElement.webidl',
     'HTMLFrameElement.webidl',
     'HTMLFrameSetElement.webidl',
     'HTMLHeadElement.webidl',
     'HTMLHeadingElement.webidl',
@@ -739,16 +740,17 @@ GENERATED_EVENTS_WEBIDL_FILES = [
     'DeviceLightEvent.webidl',
     'DeviceOrientationEvent.webidl',
     'DeviceProximityEvent.webidl',
     'DeviceStorageAreaChangedEvent.webidl',
     'DeviceStorageChangeEvent.webidl',
     'DOMTransactionEvent.webidl',
     'DownloadEvent.webidl',
     'ErrorEvent.webidl',
+    'ExternalAppEvent.webidl',
     'HashChangeEvent.webidl',
     'IccChangeEvent.webidl',
     'ImageCaptureErrorEvent.webidl',
     'MediaStreamEvent.webidl',
     'MediaStreamTrackEvent.webidl',
     'MozApplicationEvent.webidl',
     'MozCellBroadcastEvent.webidl',
     'MozClirModeEvent.webidl',
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -1504,16 +1504,19 @@ RuntimeService::RegisterWorker(JSContext
                                         nsContentUtils::eDOM_PROPERTIES,
                                         "HittingMaxWorkersPerDomain");
         Telemetry::Accumulate(Telemetry::SERVICE_WORKER_SPAWN_GETS_QUEUED, 1);
       }
     }
     else if (parent) {
       domainInfo->mChildWorkerCount++;
     }
+    else if (isServiceWorker) {
+      domainInfo->mActiveServiceWorkers.AppendElement(aWorkerPrivate);
+    }
     else {
       domainInfo->mActiveWorkers.AppendElement(aWorkerPrivate);
     }
 
     if (isSharedOrServiceWorker) {
       const nsCString& sharedWorkerName = aWorkerPrivate->SharedWorkerName();
       const nsCString& cacheName =
         aWorkerPrivate->IsServiceWorker() ?
@@ -1608,22 +1611,27 @@ RuntimeService::UnregisterWorker(JSConte
 
     // Remove old worker from everywhere.
     uint32_t index = domainInfo->mQueuedWorkers.IndexOf(aWorkerPrivate);
     if (index != kNoIndex) {
       // Was queued, remove from the list.
       domainInfo->mQueuedWorkers.RemoveElementAt(index);
     }
     else if (parent) {
-      NS_ASSERTION(domainInfo->mChildWorkerCount, "Must be non-zero!");
+      MOZ_ASSERT(domainInfo->mChildWorkerCount, "Must be non-zero!");
       domainInfo->mChildWorkerCount--;
     }
+    else if (aWorkerPrivate->IsServiceWorker()) {
+      MOZ_ASSERT(domainInfo->mActiveServiceWorkers.Contains(aWorkerPrivate),
+                 "Don't know about this worker!");
+      domainInfo->mActiveServiceWorkers.RemoveElement(aWorkerPrivate);
+    }
     else {
-      NS_ASSERTION(domainInfo->mActiveWorkers.Contains(aWorkerPrivate),
-                   "Don't know about this worker!");
+      MOZ_ASSERT(domainInfo->mActiveWorkers.Contains(aWorkerPrivate),
+                 "Don't know about this worker!");
       domainInfo->mActiveWorkers.RemoveElement(aWorkerPrivate);
     }
 
 
     if (aWorkerPrivate->IsSharedWorker() ||
         aWorkerPrivate->IsServiceWorker()) {
       MatchSharedWorkerInfo match(aWorkerPrivate);
       domainInfo->mSharedWorkerInfos.EnumerateRead(FindSharedWorkerInfo,
@@ -1647,16 +1655,19 @@ RuntimeService::UnregisterWorker(JSConte
     if (domainInfo->ActiveWorkerCount() < gMaxWorkersPerDomain &&
         !domainInfo->mQueuedWorkers.IsEmpty()) {
       queuedWorker = domainInfo->mQueuedWorkers[0];
       domainInfo->mQueuedWorkers.RemoveElementAt(0);
 
       if (queuedWorker->GetParent()) {
         domainInfo->mChildWorkerCount++;
       }
+      else if (queuedWorker->IsServiceWorker()) {
+        domainInfo->mActiveServiceWorkers.AppendElement(queuedWorker);
+      }
       else {
         domainInfo->mActiveWorkers.AppendElement(queuedWorker);
       }
     }
 
     if (!domainInfo->ActiveWorkerCount()) {
       MOZ_ASSERT(domainInfo->mQueuedWorkers.IsEmpty());
       mDomainMap.Remove(domain);
@@ -2209,22 +2220,27 @@ RuntimeService::AddAllTopLevelWorkersToA
                                              WorkerDomainInfo* aData,
                                              void* aUserArg)
 {
   nsTArray<WorkerPrivate*>* array =
     static_cast<nsTArray<WorkerPrivate*>*>(aUserArg);
 
 #ifdef DEBUG
   for (uint32_t index = 0; index < aData->mActiveWorkers.Length(); index++) {
-    NS_ASSERTION(!aData->mActiveWorkers[index]->GetParent(),
-                 "Shouldn't have a parent in this list!");
+    MOZ_ASSERT(!aData->mActiveWorkers[index]->GetParent(),
+               "Shouldn't have a parent in this list!");
+  }
+  for (uint32_t index = 0; index < aData->mActiveServiceWorkers.Length(); index++) {
+    MOZ_ASSERT(!aData->mActiveServiceWorkers[index]->GetParent(),
+               "Shouldn't have a parent in this list!");
   }
 #endif
 
   array->AppendElements(aData->mActiveWorkers);
+  array->AppendElements(aData->mActiveServiceWorkers);
 
   // These might not be top-level workers...
   for (uint32_t index = 0; index < aData->mQueuedWorkers.Length(); index++) {
     WorkerPrivate* worker = aData->mQueuedWorkers[index];
     if (!worker->GetParent()) {
       array->AppendElement(worker);
     }
   }
--- a/dom/workers/RuntimeService.h
+++ b/dom/workers/RuntimeService.h
@@ -38,28 +38,31 @@ class RuntimeService final : public nsIO
     : mWorkerPrivate(aWorkerPrivate), mScriptSpec(aScriptSpec), mName(aName)
     { }
   };
 
   struct WorkerDomainInfo
   {
     nsCString mDomain;
     nsTArray<WorkerPrivate*> mActiveWorkers;
+    nsTArray<WorkerPrivate*> mActiveServiceWorkers;
     nsTArray<WorkerPrivate*> mQueuedWorkers;
     nsClassHashtable<nsCStringHashKey, SharedWorkerInfo> mSharedWorkerInfos;
     uint32_t mChildWorkerCount;
 
     WorkerDomainInfo()
     : mActiveWorkers(1), mChildWorkerCount(0)
     { }
 
     uint32_t
     ActiveWorkerCount() const
     {
-      return mActiveWorkers.Length() + mChildWorkerCount;
+      return mActiveWorkers.Length() +
+             mActiveServiceWorkers.Length() +
+             mChildWorkerCount;
     }
   };
 
   struct IdleThreadInfo;
 
   struct MatchSharedWorkerInfo
   {
     WorkerPrivate* mWorkerPrivate;
--- a/dom/workers/ServiceWorkerClient.cpp
+++ b/dom/workers/ServiceWorkerClient.cpp
@@ -102,17 +102,19 @@ public:
     ErrorResult result;
     dom::Navigator* navigator = window->GetNavigator(result);
     if (NS_WARN_IF(result.Failed())) {
       return result.StealNSResult();
     }
 
     nsRefPtr<ServiceWorkerContainer> container = navigator->ServiceWorker();
     AutoJSAPI jsapi;
-    jsapi.Init(window);
+    if (NS_WARN_IF(!jsapi.Init(window))) {
+      return NS_ERROR_FAILURE;
+    }
     JSContext* cx = jsapi.cx();
 
     return DispatchDOMEvent(cx, container);
   }
 
 private:
   NS_IMETHOD
   DispatchDOMEvent(JSContext* aCx, ServiceWorkerContainer* aTargetContainer)
--- a/dom/workers/test/chrome.ini
+++ b/dom/workers/test/chrome.ini
@@ -45,27 +45,27 @@ support-files =
   fileSubWorker_worker.js
   file_worker.js
   jsm_url_worker.js
   workersDisabled_worker.js
   file_url.jsm
   bug1062920_worker.js
   empty.html
   sharedWorker_privateBrowsing.js
+  test_bug883784.jsm
 
 [test_WorkerDebugger.xul]
 [test_WorkerDebugger.initialize.xul]
 [test_WorkerDebugger.isFrozen.xul]
 [test_WorkerDebugger.postMessage.xul]
 [test_WorkerDebuggerGlobalScope.createSandbox.xul]
 [test_WorkerDebuggerGlobalScope.enterEventLoop.xul]
 [test_WorkerDebuggerGlobalScope.reportError.xul]
 [test_WorkerDebuggerGlobalScope.setImmediate.xul]
 [test_WorkerDebuggerManager.xul]
-[test_bug883784.jsm]
 [test_bug883784.xul]
 [test_chromeWorker.xul]
 [test_chromeWorkerJSM.xul]
 [test_extension.xul]
 [test_extensionBootstrap.xul]
 [test_file.xul]
 [test_fileBlobPosting.xul]
 [test_fileBlobSubWorker.xul]
--- a/dom/xbl/nsXBLMaybeCompiled.h
+++ b/dom/xbl/nsXBLMaybeCompiled.h
@@ -87,29 +87,29 @@ namespace js {
 
 template <class UncompiledT>
 struct GCMethods<nsXBLMaybeCompiled<UncompiledT> >
 {
   typedef struct GCMethods<JSObject *> Base;
 
   static nsXBLMaybeCompiled<UncompiledT> initial() { return nsXBLMaybeCompiled<UncompiledT>(); }
 
-  static bool needsPostBarrier(nsXBLMaybeCompiled<UncompiledT> function)
-  {
-    return function.IsCompiled() && Base::needsPostBarrier(function.GetJSFunction());
-  }
-
-  static void postBarrier(nsXBLMaybeCompiled<UncompiledT>* functionp)
+  static void postBarrier(nsXBLMaybeCompiled<UncompiledT>* functionp,
+                          nsXBLMaybeCompiled<UncompiledT> prev,
+                          nsXBLMaybeCompiled<UncompiledT> next)
   {
-    Base::postBarrier(&functionp->UnsafeGetJSFunction());
-  }
-
-  static void relocate(nsXBLMaybeCompiled<UncompiledT>* functionp)
-  {
-    Base::relocate(&functionp->UnsafeGetJSFunction());
+    if (next.IsCompiled()) {
+      Base::postBarrier(&functionp->UnsafeGetJSFunction(),
+                        prev.IsCompiled() ? prev.UnsafeGetJSFunction() : nullptr,
+                        next.UnsafeGetJSFunction());
+    } else if (prev.IsCompiled()) {
+      Base::postBarrier(&prev.UnsafeGetJSFunction(),
+                        prev.UnsafeGetJSFunction(),
+                        nullptr);
+    }
   }
 };
 
 template <class UncompiledT>
 class HeapBase<nsXBLMaybeCompiled<UncompiledT> >
 {
   const JS::Heap<nsXBLMaybeCompiled<UncompiledT> >& wrapper() const {
     return *static_cast<const JS::Heap<nsXBLMaybeCompiled<UncompiledT> >*>(this);
--- a/editor/libeditor/nsHTMLEditUtils.cpp
+++ b/editor/libeditor/nsHTMLEditUtils.cpp
@@ -659,16 +659,17 @@ static const nsElementInfo kElements[eHT
   ELEM(del, true, true, GROUP_PHRASE | GROUP_BLOCK, GROUP_FLOW_ELEMENT),
   ELEM(dfn, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
   ELEM(dir, true, false, GROUP_BLOCK, GROUP_LI),
   ELEM(div, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
   ELEM(dl, true, false, GROUP_BLOCK, GROUP_DL_CONTENT),
   ELEM(dt, true, true, GROUP_DL_CONTENT, GROUP_INLINE_ELEMENT),
   ELEM(em, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
   ELEM(embed, false, false, GROUP_NONE, GROUP_NONE),
+  ELEM(extapp, true, true, GROUP_NONE, GROUP_NONE),
   ELEM(fieldset, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
   ELEM(figcaption, true, false, GROUP_FIGCAPTION, GROUP_FLOW_ELEMENT),
   ELEM(figure, true, true, GROUP_BLOCK,
        GROUP_FLOW_ELEMENT | GROUP_FIGCAPTION),
   ELEM(font, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT),
   ELEM(footer, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
   ELEM(form, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
   ELEM(frame, false, false, GROUP_FRAME, GROUP_NONE),
new file mode 100644
--- /dev/null
+++ b/embedding/ios/GeckoEmbed/GeckoEmbed.xcodeproj/project.pbxproj
@@ -0,0 +1,595 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 46;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		D2669E901AD6EC830027914D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D2669E8E1AD6EC830027914D /* Main.storyboard */; };
+		D2669E921AD6EC830027914D /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D2669E911AD6EC830027914D /* Images.xcassets */; };
+		D2669E951AD6EC830027914D /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = D2669E931AD6EC830027914D /* LaunchScreen.xib */; };
+		D2C9CF2E1AA4960F0041BE29 /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = D2C9CF2D1AA4960F0041BE29 /* main.mm */; };
+		D2C9CF311AA4960F0041BE29 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = D2C9CF301AA4960F0041BE29 /* AppDelegate.m */; };
+		D2C9CF341AA4960F0041BE29 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D2C9CF331AA4960F0041BE29 /* ViewController.m */; };
+		D2C9CF371AA4960F0041BE29 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D2C9CF351AA4960F0041BE29 /* Main.storyboard */; };
+		D2C9CF391AA4960F0041BE29 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D2C9CF381AA4960F0041BE29 /* Images.xcassets */; };
+		D2C9CF3C1AA4960F0041BE29 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = D2C9CF3A1AA4960F0041BE29 /* LaunchScreen.xib */; };
+		D2C9CF521AA4A23A0041BE29 /* browser in Resources */ = {isa = PBXBuildFile; fileRef = D2C9CF511AA4A23A0041BE29 /* browser */; };
+		D2CA73561ADDE3F10022A192 /* shell.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D2CA73551ADDE3F10022A192 /* shell.cpp */; };
+		D2CA73591ADDEBAB0022A192 /* dirs.m in Sources */ = {isa = PBXBuildFile; fileRef = D2CA73581ADDEBAB0022A192 /* dirs.m */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+		D2669EA81AD6ECFC0027914D /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = D2C9CF201AA4960F0041BE29 /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = D2C9CF271AA4960F0041BE29;
+			remoteInfo = GeckoEmbed;
+		};
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+		D20C8D111ADDD6E600CE4BC8 /* CopyFiles */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 2147483647;
+			dstPath = "";
+			dstSubfolderSpec = 10;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+		D20C8D121ADDD87C00CE4BC8 /* libmozglue.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libmozglue.dylib; path = "../../../../debug-mozilla-central/mozglue/build/libmozglue.dylib"; sourceTree = "<group>"; };
+		D2669E821AD6EC830027914D /* js.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = js.app; sourceTree = BUILT_PRODUCTS_DIR; };
+		D2669E851AD6EC830027914D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		D2669E8F1AD6EC830027914D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
+		D2669E911AD6EC830027914D /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
+		D2669E941AD6EC830027914D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = "<group>"; };
+		D2C9CF281AA4960F0041BE29 /* GeckoEmbed.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GeckoEmbed.app; sourceTree = BUILT_PRODUCTS_DIR; };
+		D2C9CF2C1AA4960F0041BE29 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		D2C9CF2D1AA4960F0041BE29 /* main.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = "<group>"; };
+		D2C9CF2F1AA4960F0041BE29 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
+		D2C9CF301AA4960F0041BE29 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
+		D2C9CF321AA4960F0041BE29 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
+		D2C9CF331AA4960F0041BE29 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
+		D2C9CF361AA4960F0041BE29 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
+		D2C9CF381AA4960F0041BE29 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
+		D2C9CF3B1AA4960F0041BE29 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = "<group>"; };
+		D2C9CF511AA4A23A0041BE29 /* browser */ = {isa = PBXFileReference; lastKnownFileType = folder; path = browser; sourceTree = "<group>"; };
+		D2C9CF541AA4A26E0041BE29 /* libxpcomglue.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libxpcomglue.a; path = "../../../../../iphone-simulator-debug/xpcom/glue/standalone/libxpcomglue.a"; sourceTree = "<group>"; };
+		D2CA73551ADDE3F10022A192 /* shell.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = shell.cpp; sourceTree = "<group>"; };
+		D2CA73581ADDEBAB0022A192 /* dirs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = dirs.m; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		D2669E7F1AD6EC830027914D /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		D2C9CF251AA4960F0041BE29 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		D2669E831AD6EC830027914D /* js */ = {
+			isa = PBXGroup;
+			children = (
+				D2669E8E1AD6EC830027914D /* Main.storyboard */,
+				D2669E911AD6EC830027914D /* Images.xcassets */,
+				D2669E931AD6EC830027914D /* LaunchScreen.xib */,
+				D2669E841AD6EC830027914D /* Supporting Files */,
+				D2CA73551ADDE3F10022A192 /* shell.cpp */,
+				D2CA73581ADDEBAB0022A192 /* dirs.m */,
+			);
+			path = js;
+			sourceTree = "<group>";
+		};
+		D2669E841AD6EC830027914D /* Supporting Files */ = {
+			isa = PBXGroup;
+			children = (
+				D2669E851AD6EC830027914D /* Info.plist */,
+			);
+			name = "Supporting Files";
+			sourceTree = "<group>";
+		};
+		D2C9CF1F1AA4960F0041BE29 = {
+			isa = PBXGroup;
+			children = (
+				D20C8D121ADDD87C00CE4BC8 /* libmozglue.dylib */,
+				D2C9CF2A1AA4960F0041BE29 /* GeckoEmbed */,
+				D2669E831AD6EC830027914D /* js */,
+				D2C9CF291AA4960F0041BE29 /* Products */,
+			);
+			sourceTree = "<group>";
+		};
+		D2C9CF291AA4960F0041BE29 /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				D2C9CF281AA4960F0041BE29 /* GeckoEmbed.app */,
+				D2669E821AD6EC830027914D /* js.app */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		D2C9CF2A1AA4960F0041BE29 /* GeckoEmbed */ = {
+			isa = PBXGroup;
+			children = (
+				D2C9CF541AA4A26E0041BE29 /* libxpcomglue.a */,
+				D2C9CF511AA4A23A0041BE29 /* browser */,
+				D2C9CF2D1AA4960F0041BE29 /* main.mm */,
+				D2C9CF2F1AA4960F0041BE29 /* AppDelegate.h */,
+				D2C9CF301AA4960F0041BE29 /* AppDelegate.m */,
+				D2C9CF321AA4960F0041BE29 /* ViewController.h */,
+				D2C9CF331AA4960F0041BE29 /* ViewController.m */,
+				D2C9CF351AA4960F0041BE29 /* Main.storyboard */,
+				D2C9CF381AA4960F0041BE29 /* Images.xcassets */,
+				D2C9CF3A1AA4960F0041BE29 /* LaunchScreen.xib */,
+				D2C9CF2B1AA4960F0041BE29 /* Supporting Files */,
+			);
+			path = GeckoEmbed;
+			sourceTree = "<group>";
+		};
+		D2C9CF2B1AA4960F0041BE29 /* Supporting Files */ = {
+			isa = PBXGroup;
+			children = (
+				D2C9CF2C1AA4960F0041BE29 /* Info.plist */,
+			);
+			name = "Supporting Files";
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		D2669E811AD6EC830027914D /* js */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = D2669EA61AD6EC830027914D /* Build configuration list for PBXNativeTarget "js" */;
+			buildPhases = (
+				D20C8D141ADDD97100CE4BC8 /* Sources */,
+				D2669E7F1AD6EC830027914D /* Frameworks */,
+				D2669E801AD6EC830027914D /* Resources */,
+				D20C8D111ADDD6E600CE4BC8 /* CopyFiles */,
+				D2CA73571ADDEA7A0022A192 /* Run Script */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				D2669EA91AD6ECFC0027914D /* PBXTargetDependency */,
+			);
+			name = js;
+			productName = js;
+			productReference = D2669E821AD6EC830027914D /* js.app */;
+			productType = "com.apple.product-type.application";
+		};
+		D2C9CF271AA4960F0041BE29 /* GeckoEmbed */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = D2C9CF4B1AA4960F0041BE29 /* Build configuration list for PBXNativeTarget "GeckoEmbed" */;
+			buildPhases = (
+				D2C9CF5D1AA4A6D80041BE29 /* Run Script */,
+				D2C9CF241AA4960F0041BE29 /* Sources */,
+				D2C9CF251AA4960F0041BE29 /* Frameworks */,
+				D2C9CF261AA4960F0041BE29 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = GeckoEmbed;
+			productName = GeckoEmbed;
+			productReference = D2C9CF281AA4960F0041BE29 /* GeckoEmbed.app */;
+			productType = "com.apple.product-type.application";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		D2C9CF201AA4960F0041BE29 /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastUpgradeCheck = 0610;
+				ORGANIZATIONNAME = Mozilla;
+				TargetAttributes = {
+					D2669E811AD6EC830027914D = {
+						CreatedOnToolsVersion = 6.1.1;
+					};
+					D2C9CF271AA4960F0041BE29 = {
+						CreatedOnToolsVersion = 6.1.1;
+					};
+				};
+			};
+			buildConfigurationList = D2C9CF231AA4960F0041BE29 /* Build configuration list for PBXProject "GeckoEmbed" */;
+			compatibilityVersion = "Xcode 3.2";
+			developmentRegion = English;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+				Base,
+			);
+			mainGroup = D2C9CF1F1AA4960F0041BE29;
+			productRefGroup = D2C9CF291AA4960F0041BE29 /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				D2C9CF271AA4960F0041BE29 /* GeckoEmbed */,
+				D2669E811AD6EC830027914D /* js */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+		D2669E801AD6EC830027914D /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				D2669E901AD6EC830027914D /* Main.storyboard in Resources */,
+				D2669E951AD6EC830027914D /* LaunchScreen.xib in Resources */,
+				D2669E921AD6EC830027914D /* Images.xcassets in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		D2C9CF261AA4960F0041BE29 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				D2C9CF371AA4960F0041BE29 /* Main.storyboard in Resources */,
+				D2C9CF3C1AA4960F0041BE29 /* LaunchScreen.xib in Resources */,
+				D2C9CF391AA4960F0041BE29 /* Images.xcassets in Resources */,
+				D2C9CF521AA4A23A0041BE29 /* browser in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+		D2C9CF5D1AA4A6D80041BE29 /* Run Script */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Run Script";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/bash;
+			shellScript = "${SRCROOT}/build-gecko.sh";
+		};
+		D2CA73571ADDEA7A0022A192 /* Run Script */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Run Script";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "${SRCROOT}/copy-jsshell.sh";
+		};
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		D20C8D141ADDD97100CE4BC8 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				D2CA73561ADDE3F10022A192 /* shell.cpp in Sources */,
+				D2CA73591ADDEBAB0022A192 /* dirs.m in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		D2C9CF241AA4960F0041BE29 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				D2C9CF341AA4960F0041BE29 /* ViewController.m in Sources */,
+				D2C9CF311AA4960F0041BE29 /* AppDelegate.m in Sources */,
+				D2C9CF2E1AA4960F0041BE29 /* main.mm in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+		D2669EA91AD6ECFC0027914D /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = D2C9CF271AA4960F0041BE29 /* GeckoEmbed */;
+			targetProxy = D2669EA81AD6ECFC0027914D /* PBXContainerItemProxy */;
+		};
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+		D2669E8E1AD6EC830027914D /* Main.storyboard */ = {
+			isa = PBXVariantGroup;
+			children = (
+				D2669E8F1AD6EC830027914D /* Base */,
+			);
+			name = Main.storyboard;
+			sourceTree = "<group>";
+		};
+		D2669E931AD6EC830027914D /* LaunchScreen.xib */ = {
+			isa = PBXVariantGroup;
+			children = (
+				D2669E941AD6EC830027914D /* Base */,
+			);
+			name = LaunchScreen.xib;
+			sourceTree = "<group>";
+		};
+		D2C9CF351AA4960F0041BE29 /* Main.storyboard */ = {
+			isa = PBXVariantGroup;
+			children = (
+				D2C9CF361AA4960F0041BE29 /* Base */,
+			);
+			name = Main.storyboard;
+			sourceTree = "<group>";
+		};
+		D2C9CF3A1AA4960F0041BE29 /* LaunchScreen.xib */ = {
+			isa = PBXVariantGroup;
+			children = (
+				D2C9CF3B1AA4960F0041BE29 /* Base */,
+			);
+			name = LaunchScreen.xib;
+			sourceTree = "<group>";
+		};
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+		D2669EA21AD6EC830027914D /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"IMPL_MFBT=1",
+					"EXPORT_JS_API=1",
+					"DEBUG=1",
+					"MOZILLA_CLIENT=1",
+				);
+				INFOPLIST_FILE = js/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+				LIBRARY_SEARCH_PATHS = (
+					"$(GECKO_OBJDIR)/config/external/nss",
+					"$(GECKO_OBJDIR)/mozglue/build",
+					"$(GECKO_OBJDIR)/js/src",
+					"$(GECKO_OBJDIR)/js/src/editline",
+				);
+				OTHER_CPLUSPLUSFLAGS = (
+					"$(OTHER_CFLAGS)",
+					"-include",
+					"$(GECKO_OBJDIR)/js/src/js-confdefs.h",
+				);
+				OTHER_LDFLAGS = (
+					"-ljs_static",
+					"-lmozglue",
+					"-lnss3",
+				);
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				USER_HEADER_SEARCH_PATHS = "$(GECKO_OBJDIR)/dist/include/nspr $(GECKO_OBJDIR)/dist/include $(SRCROOT)/../../../js/src $(SRCROOT)/../../../js/src/shell $(SRCROOT)/../../../js/src/editline";
+			};
+			name = Debug;
+		};
+		D2669EA31AD6EC830027914D /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"IMPL_MFBT=1",
+					"EXPORT_JS_API=1",
+					"MOZILLA_CLIENT=1",
+				);
+				INFOPLIST_FILE = js/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+				LIBRARY_SEARCH_PATHS = (
+					"$(GECKO_OBJDIR)/config/external/nss",
+					"$(GECKO_OBJDIR)/mozglue/build",
+					"$(GECKO_OBJDIR)/js/src",
+					"$(GECKO_OBJDIR)/js/src/editline",
+				);
+				OTHER_CPLUSPLUSFLAGS = (
+					"$(OTHER_CFLAGS)",
+					"-include",
+					"$(GECKO_OBJDIR)/js/src/js-confdefs.h",
+				);
+				OTHER_LDFLAGS = (
+					"-ljs_static",
+					"-lmozglue",
+					"-lnss3",
+				);
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				USER_HEADER_SEARCH_PATHS = "$(GECKO_OBJDIR)/dist/include/nspr $(GECKO_OBJDIR)/dist/include $(SRCROOT)/../../../js/src $(SRCROOT)/../../../js/src/shell $(SRCROOT)/../../../js/src/editline";
+			};
+			name = Release;
+		};
+		D2C9CF491AA4960F0041BE29 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				ARCHS = "$(ARCHS_STANDARD)";
+				"ARCHS[sdk=iphoneos*]" = armv7;
+				"ARCHS[sdk=iphonesimulator*]" = "$(ARCHS_STANDARD)";
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				GECKO_OBJDIR = "";
+				"GECKO_OBJDIR[sdk=iphoneos*]" = "$(SRCROOT)/../../../../iphone-device-debug/";
+				"GECKO_OBJDIR[sdk=iphonesimulator*]" = "$(SRCROOT)/../../../../iphone-simulator-debug/";
+				IPHONEOS_DEPLOYMENT_TARGET = 8.1;
+				MTL_ENABLE_DEBUG_INFO = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				SDKROOT = iphoneos;
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Debug;
+		};
+		D2C9CF4A1AA4960F0041BE29 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				ARCHS = "$(ARCHS_STANDARD)";
+				"ARCHS[sdk=iphoneos*]" = armv7;
+				"ARCHS[sdk=iphonesimulator*]" = "$(ARCHS_STANDARD)";
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = YES;
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				GECKO_OBJDIR = "";
+				"GECKO_OBJDIR[sdk=iphoneos*]" = "$(SRCROOT)/../../../../iphone-device-opt/";
+				"GECKO_OBJDIR[sdk=iphonesimulator*]" = "$(SRCROOT)/../../../../iphone-simulator-opt/";
+				IPHONEOS_DEPLOYMENT_TARGET = 8.1;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				SDKROOT = iphoneos;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				VALIDATE_PRODUCT = YES;
+			};
+			name = Release;
+		};
+		D2C9CF4C1AA4960F0041BE29 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ARCHS = "$(ARCHS_STANDARD)";
+				"ARCHS[sdk=iphoneos*]" = armv7;
+				"ARCHS[sdk=iphonesimulator*]" = "$(ARCHS_STANDARD)";
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				GECKO_OBJDIR = "";
+				"GECKO_OBJDIR[sdk=iphoneos*]" = "$(SRCROOT)/../../../../iphone-device-debug/";
+				"GECKO_OBJDIR[sdk=iphonesimulator*]" = "$(SRCROOT)/../../../../iphone-simulator-debug/";
+				INFOPLIST_FILE = GeckoEmbed/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+				LIBRARY_SEARCH_PATHS = (
+					"$(GECKO_OBJDIR)/dist/lib",
+					"$(GECKO_OBJDIR)/mozglue/build",
+					"$(GECKO_OBJDIR)/xpcom/glue/standalone",
+				);
+				OTHER_CODE_SIGN_FLAGS = "";
+				OTHER_LDFLAGS = (
+					"$(GECKO_OBJDIR)/xpcom/glue/standalone/libxpcomglue.a",
+					"$(GECKO_OBJDIR)/mozglue/build/libmozglue.dylib",
+				);
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				USER_HEADER_SEARCH_PATHS = "$(GECKO_OBJDIR)/dist/include $(GECKO_OBJDIR)/dist/include/nspr";
+			};
+			name = Debug;
+		};
+		D2C9CF4D1AA4960F0041BE29 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ARCHS = "$(ARCHS_STANDARD)";
+				"ARCHS[sdk=iphoneos*]" = armv7;
+				"ARCHS[sdk=iphonesimulator*]" = "$(ARCHS_STANDARD)";
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				GECKO_OBJDIR = "";
+				"GECKO_OBJDIR[sdk=iphoneos*]" = "$(SRCROOT)/../../../../iphone-device-opt/";
+				"GECKO_OBJDIR[sdk=iphonesimulator*]" = "$(SRCROOT)/../../../../iphone-simulator-opt/";
+				INFOPLIST_FILE = GeckoEmbed/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+				LIBRARY_SEARCH_PATHS = (
+					"$(GECKO_OBJDIR)/dist/lib",
+					"$(GECKO_OBJDIR)/mozglue/build",
+					"$(GECKO_OBJDIR)/xpcom/glue/standalone",
+				);
+				OTHER_CODE_SIGN_FLAGS = "";
+				OTHER_LDFLAGS = (
+					"$(GECKO_OBJDIR)/xpcom/glue/standalone/libxpcomglue.a",
+					"$(GECKO_OBJDIR)/mozglue/build/libmozglue.dylib",
+				);
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				USER_HEADER_SEARCH_PATHS = "$(GECKO_OBJDIR)/dist/include $(GECKO_OBJDIR)/dist/include/nspr";
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		D2669EA61AD6EC830027914D /* Build configuration list for PBXNativeTarget "js" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				D2669EA21AD6EC830027914D /* Debug */,
+				D2669EA31AD6EC830027914D /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		D2C9CF231AA4960F0041BE29 /* Build configuration list for PBXProject "GeckoEmbed" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				D2C9CF491AA4960F0041BE29 /* Debug */,
+				D2C9CF4A1AA4960F0041BE29 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		D2C9CF4B1AA4960F0041BE29 /* Build configuration list for PBXNativeTarget "GeckoEmbed" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				D2C9CF4C1AA4960F0041BE29 /* Debug */,
+				D2C9CF4D1AA4960F0041BE29 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = D2C9CF201AA4960F0041BE29 /* Project object */;
+}
new file mode 100644
--- /dev/null
+++ b/embedding/ios/GeckoEmbed/GeckoEmbed.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "self:GeckoEmbed.xcodeproj">
+   </FileRef>
+</Workspace>
new file mode 100644
--- /dev/null
+++ b/embedding/ios/GeckoEmbed/GeckoEmbed/AppDelegate.h
@@ -0,0 +1,17 @@
+//
+//  AppDelegate.h
+//  GeckoEmbed
+//
+//  Created by Ted Mielczarek on 3/2/15.
+//  Copyright (c) 2015 Mozilla. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@interface AppDelegate : UIResponder <UIApplicationDelegate>
+
+@property (strong, nonatomic) UIWindow *window;
+
+
+@end
+
new file mode 100644
--- /dev/null
+++ b/embedding/ios/GeckoEmbed/GeckoEmbed/AppDelegate.m
@@ -0,0 +1,45 @@
+//
+//  AppDelegate.m
+//  GeckoEmbed
+//
+//  Created by Ted Mielczarek on 3/2/15.
+//  Copyright (c) 2015 Mozilla. All rights reserved.
+//
+
+#import "AppDelegate.h"
+
+@interface AppDelegate ()
+
+@end
+
+@implementation AppDelegate
+
+
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+    // Override point for customization after application launch.
+    return YES;
+}
+
+- (void)applicationWillResignActive:(UIApplication *)application {
+    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
+    // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
+}
+
+- (void)applicationDidEnterBackground:(UIApplication *)application {
+    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
+    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
+}
+
+- (void)applicationWillEnterForeground:(UIApplication *)application {
+    // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
+}
+
+- (void)applicationDidBecomeActive:(UIApplication *)application {
+    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
+}
+
+- (void)applicationWillTerminate:(UIApplication *)application {
+    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
+}
+
+@end
new file mode 100644
--- /dev/null
+++ b/embedding/ios/GeckoEmbed/GeckoEmbed/Base.lproj/LaunchScreen.xib
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6214" systemVersion="14A314h" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
+    <dependencies>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6207"/>
+        <capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="iN0-l3-epB">
+            <rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
+            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+            <subviews>
+                <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="  Copyright (c) 2015 Mozilla. All rights reserved." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="8ie-xW-0ye">
+                    <rect key="frame" x="20" y="439" width="441" height="21"/>
+                    <fontDescription key="fontDescription" type="system" pointSize="17"/>
+                    <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
+                    <nil key="highlightedColor"/>
+                </label>
+                <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="GeckoEmbed" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="kId-c2-rCX">
+                    <rect key="frame" x="20" y="140" width="441" height="43"/>
+                    <fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
+                    <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
+                    <nil key="highlightedColor"/>
+                </label>
+            </subviews>
+            <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
+            <constraints>
+                <constraint firstItem="kId-c2-rCX" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="bottom" multiplier="1/3" constant="1" id="5cJ-9S-tgC"/>
+                <constraint firstAttribute="centerX" secondItem="kId-c2-rCX" secondAttribute="centerX" id="Koa-jz-hwk"/>
+                <constraint firstAttribute="bottom" secondItem="8ie-xW-0ye" secondAttribute="bottom" constant="20" id="Kzo-t9-V3l"/>
+                <constraint firstItem="8ie-xW-0ye" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="MfP-vx-nX0"/>
+                <constraint firstAttribute="centerX" secondItem="8ie-xW-0ye" secondAttribute="centerX" id="ZEH-qu-HZ9"/>
+                <constraint firstItem="kId-c2-rCX" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="fvb-Df-36g"/>
+            </constraints>
+            <nil key="simulatedStatusBarMetrics"/>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <point key="canvasLocation" x="548" y="455"/>
+        </view>
+    </objects>
+</document>
new file mode 100644
--- /dev/null
+++ b/embedding/ios/GeckoEmbed/GeckoEmbed/Base.lproj/Main.storyboard
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6211" systemVersion="14A298i" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
+    <dependencies>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6204"/>
+    </dependencies>
+    <scenes>
+        <!--View Controller-->
+        <scene sceneID="tne-QT-ifu">
+            <objects>
+                <viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="" sceneMemberID="viewController">
+                    <layoutGuides>
+                        <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
+                        <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
+                    </layoutGuides>
+                    <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
+                        <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
+                    </view>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
+            </objects>
+        </scene>
+    </scenes>
+</document>
new file mode 100644
--- /dev/null
+++ b/embedding/ios/GeckoEmbed/GeckoEmbed/Images.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,68 @@
+{
+  "images" : [
+    {
+      "idiom" : "iphone",
+      "size" : "29x29",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "29x29",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "40x40",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "40x40",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "60x60",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "60x60",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "29x29",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "29x29",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "40x40",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "40x40",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "76x76",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "76x76",
+      "scale" : "2x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/embedding/ios/GeckoEmbed/GeckoEmbed/Info.plist
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>en</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>org.mozilla.$(PRODUCT_NAME:rfc1034identifier)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>2</string>
+	<key>LSRequiresIPhoneOS</key>
+	<true/>
+	<key>UILaunchStoryboardName</key>
+	<string>LaunchScreen</string>
+	<key>UIMainStoryboardFile</key>
+	<string>Main</string>
+	<key>UIRequiredDeviceCapabilities</key>
+	<array>
+		<string>armv7</string>
+	</array>
+	<key>UISupportedInterfaceOrientations</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+	<key>UISupportedInterfaceOrientations~ipad</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationPortraitUpsideDown</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+</dict>
+</plist>
new file mode 100644
--- /dev/null
+++ b/embedding/ios/GeckoEmbed/GeckoEmbed/ViewController.h
@@ -0,0 +1,15 @@
+//
+//  ViewController.h
+//  GeckoEmbed
+//
+//  Created by Ted Mielczarek on 3/2/15.
+//  Copyright (c) 2015 Mozilla. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@interface ViewController : UIViewController
+
+
+@end
+
new file mode 100644
--- /dev/null
+++ b/embedding/ios/GeckoEmbed/GeckoEmbed/ViewController.m
@@ -0,0 +1,27 @@
+//
+//  ViewController.m
+//  GeckoEmbed
+//
+//  Created by Ted Mielczarek on 3/2/15.
+//  Copyright (c) 2015 Mozilla. All rights reserved.
+//
+
+#import "ViewController.h"
+
+@interface ViewController ()
+
+@end
+
+@implementation ViewController
+
+- (void)viewDidLoad {
+    [super viewDidLoad];
+    // Do any additional setup after loading the view, typically from a nib.
+}
+
+- (void)didReceiveMemoryWarning {
+    [super didReceiveMemoryWarning];
+    // Dispose of any resources that can be recreated.
+}
+
+@end
new file mode 100644
--- /dev/null
+++ b/embedding/ios/GeckoEmbed/GeckoEmbed/browser/application.ini
@@ -0,0 +1,10 @@
+[App]
+Vendor=Mozilla
+Name=browser
+Version=1.0
+BuildID=20150209
+ID=browser@mozilla.org
+
+[Gecko]
+MinVersion=34
+MaxVersion=1000
new file mode 100644
--- /dev/null
+++ b/embedding/ios/GeckoEmbed/GeckoEmbed/browser/chrome.manifest
@@ -0,0 +1,1 @@
+content mybrowser file:chrome/content/
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/embedding/ios/GeckoEmbed/GeckoEmbed/browser/chrome/content/hello.js
@@ -0,0 +1,9 @@
+Components.utils.import("resource://gre/modules/Services.jsm");
+addEventListener("DOMContentLoaded", function loaded() {
+  removeEventListener("DOMContentLoaded", loaded);
+  var b = document.getElementById("browser");
+  Services.obs.notifyObservers(b.docShell,
+                               "geckoembed-browser-loaded",
+                               null);
+  b.loadURI("http://people.mozilla.org/~tmielczarek/iosstart.html");
+});
new file mode 100644
--- /dev/null
+++ b/embedding/ios/GeckoEmbed/GeckoEmbed/browser/chrome/content/hello.xul
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<window title="browser"
+        xmlns:html="http://www.w3.org/1999/xhtml"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        sizemode="maximized"
+        flex="1">
+  <browser type="content" src="http://mozilla.org/" flex="1"/>
+</window>
new file mode 100644
--- /dev/null
+++ b/embedding/ios/GeckoEmbed/GeckoEmbed/browser/defaults/preferences/prefs.js
@@ -0,0 +1,3 @@
+pref("toolkit.defaultChromeURI", "chrome://mybrowser/content/hello.xul");
+pref("browser.dom.window.dump.enabled", true);
+pref("dom.max_script_run_time", 0);
new file mode 100644
--- /dev/null
+++ b/embedding/ios/GeckoEmbed/GeckoEmbed/main.mm
@@ -0,0 +1,96 @@
+//
+//  main.m
+//  XulRunner
+//
+//  Created by Ted Mielczarek on 2/9/15.
+//  Copyright (c) 2015 Mozilla. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+#include "mozilla-config.h"
+#define XPCOM_GLUE 1
+#include "nsXULAppAPI.h"
+#include "nsXPCOMGlue.h"
+#include "nsXREAppData.h"
+#include "mozilla/AppData.h"
+#include "nsCOMPtr.h"
+#include "nsIFile.h"
+#include "nsIPrefService.h"
+#include "nsServiceManagerUtils.h"
+
+static const nsXREAppData sAppData = {
+  sizeof(nsXREAppData),
+  nullptr, // directory
+  "Mozilla",
+  "Browser",
+  nullptr,
+  "38.0a1",
+  "201502090123",
+  "browser@mozilla.org",
+  nullptr, // copyright
+  0,
+  nullptr, // xreDirectory
+  "38.0a1",
+  "*",
+  "https://crash-reports.mozilla.com/submit",
+  nullptr,
+  "Firefox"
+};
+
+XRE_GetFileFromPathType XRE_GetFileFromPath;
+XRE_CreateAppDataType XRE_CreateAppData;
+XRE_FreeAppDataType XRE_FreeAppData;
+XRE_mainType XRE_main;
+
+static const nsDynamicFunctionLoad kXULFuncs[] = {
+    { "XRE_GetFileFromPath", (NSFuncPtr*) &XRE_GetFileFromPath },
+    { "XRE_CreateAppData", (NSFuncPtr*) &XRE_CreateAppData },
+    { "XRE_FreeAppData", (NSFuncPtr*) &XRE_FreeAppData },
+    { "XRE_main", (NSFuncPtr*) &XRE_main },
+    { nullptr, nullptr }
+};
+
+const int MAXPATHLEN = 1024;
+const char* XPCOM_DLL = "XUL";
+
+int main(int argc, char * argv[]) {
+  char exeDir[MAXPATHLEN];
+  NSString* bundlePath = [[NSBundle mainBundle] bundlePath];
+  strncpy(exeDir, [bundlePath UTF8String], MAXPATHLEN);
+  strcat(exeDir, "/Frameworks/");
+  strncat(exeDir, XPCOM_DLL, MAXPATHLEN - strlen(exeDir));
+
+  nsresult rv = XPCOMGlueStartup(exeDir);
+  if (NS_FAILED(rv)) {
+    printf("Couldn't load XPCOM (0x%08x) from %s\n", rv, exeDir);
+    return 255;
+  }
+
+  rv = XPCOMGlueLoadXULFunctions(kXULFuncs);
+  if (NS_FAILED(rv)) {
+    printf("Couldn't load XRE functions.\n");
+    return 255;
+  }
+
+  mozilla::ScopedAppData appData(&sAppData);
+
+  nsCOMPtr<nsIFile> greDir;
+  rv = NS_NewNativeLocalFile(nsDependentCString([bundlePath UTF8String]), true,
+                             getter_AddRefs(greDir));
+  if (NS_FAILED(rv)) {
+    printf("Couldn't find the application directory.\n");
+    return 255;
+  }
+    
+  nsCOMPtr<nsIFile> appSubdir;
+  greDir->Clone(getter_AddRefs(appSubdir));
+  greDir->Append(NS_LITERAL_STRING("Frameworks"));
+  appSubdir->Append(NS_LITERAL_STRING("browser"));
+
+  mozilla::SetStrongPtr(appData.directory, static_cast<nsIFile*>(appSubdir.get()));
+  greDir.forget(&appData.xreDirectory);
+
+  int result = XRE_main(argc, argv, &appData, 0);
+  return result;
+}
new file mode 100755
--- /dev/null
+++ b/embedding/ios/GeckoEmbed/build-gecko.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+set -e
+
+if test "${ACTION}" != "clean"; then
+    echo "Building in ${GECKO_OBJDIR}"
+    make -j8 -s -C $GECKO_OBJDIR binaries
+
+    echo "Copying files from ${GECKO_OBJDIR}/dist/bin"
+    rsync -pvtrlL --exclude "Test*" \
+          --exclude "test_*" --exclude "*_unittest" \
+          --exclude xulrunner  \
+          ${GECKO_OBJDIR}/dist/bin/ $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Frameworks
+
+    if test ${ARCHS} == "armv7"; then
+        for x in $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Frameworks/*.dylib $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Frameworks/XUL; do
+            echo "Signing $x"
+            /usr/bin/codesign --force --sign "${EXPANDED_CODE_SIGN_IDENTITY}" --preserve-metadata=identifier,entitlements,resource-rules $x
+        done
+    fi
+fi
new file mode 100755
--- /dev/null
+++ b/embedding/ios/GeckoEmbed/copy-jsshell.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+set -e
+
+if test -z ${GECKO_OBJDIR}; then
+    echo "Error: GECKO_OBJDIR not set!"
+    exit 1
+fi
+
+if test "${ACTION}" != "clean"; then
+    echo "Copying files from ${GECKO_OBJDIR}/dist/bin"
+    mkdir -p $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Frameworks
+    cp ${GECKO_OBJDIR}/mozglue/build/libmozglue.dylib $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Frameworks
+    cp ${GECKO_OBJDIR}/config/external/nss/libnss3.dylib $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Frameworks
+
+    if test ${ARCHS} == "armv7"; then
+        for x in $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Frameworks/*.dylib; do
+            echo "Signing $x"
+            /usr/bin/codesign --force --sign "${EXPANDED_CODE_SIGN_IDENTITY}" --preserve-metadata=identifier,entitlements,resource-rules $x
+        done
+    fi
+fi
new file mode 100644
--- /dev/null
+++ b/embedding/ios/GeckoEmbed/js/Base.lproj/LaunchScreen.xib
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6214" systemVersion="14A314h" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
+    <dependencies>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6207"/>
+        <capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="iN0-l3-epB">
+            <rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
+            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+            <subviews>
+                <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="  Copyright (c) 2015 Mozilla. All rights reserved." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="8ie-xW-0ye">
+                    <rect key="frame" x="20" y="439" width="441" height="21"/>
+                    <fontDescription key="fontDescription" type="system" pointSize="17"/>
+                    <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
+                    <nil key="highlightedColor"/>
+                </label>
+                <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="js" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="kId-c2-rCX">
+                    <rect key="frame" x="20" y="140" width="441" height="43"/>
+                    <fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
+                    <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
+                    <nil key="highlightedColor"/>
+                </label>
+            </subviews>
+            <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
+            <constraints>
+                <constraint firstItem="kId-c2-rCX" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="bottom" multiplier="1/3" constant="1" id="5cJ-9S-tgC"/>
+                <constraint firstAttribute="centerX" secondItem="kId-c2-rCX" secondAttribute="centerX" id="Koa-jz-hwk"/>
+                <constraint firstAttribute="bottom" secondItem="8ie-xW-0ye" secondAttribute="bottom" constant="20" id="Kzo-t9-V3l"/>
+                <constraint firstItem="8ie-xW-0ye" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="MfP-vx-nX0"/>
+                <constraint firstAttribute="centerX" secondItem="8ie-xW-0ye" secondAttribute="centerX" id="ZEH-qu-HZ9"/>
+                <constraint firstItem="kId-c2-rCX" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="fvb-Df-36g"/>
+            </constraints>
+            <nil key="simulatedStatusBarMetrics"/>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <point key="canvasLocation" x="548" y="455"/>
+        </view>
+    </objects>
+</document>
new file mode 100644
--- /dev/null
+++ b/embedding/ios/GeckoEmbed/js/Base.lproj/Main.storyboard
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6211" systemVersion="14A298i" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
+    <dependencies>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6204"/>
+    </dependencies>
+    <scenes>
+        <!--View Controller-->
+        <scene sceneID="tne-QT-ifu">
+            <objects>
+                <viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="" sceneMemberID="viewController">
+                    <layoutGuides>
+                        <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
+                        <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
+                    </layoutGuides>
+                    <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
+                        <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
+                    </view>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
+            </objects>
+        </scene>
+    </scenes>
+</document>
new file mode 100644
--- /dev/null
+++ b/embedding/ios/GeckoEmbed/js/Images.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,68 @@
+{
+  "images" : [
+    {
+      "idiom" : "iphone",
+      "size" : "29x29",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "29x29",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "40x40",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "40x40",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "60x60",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "60x60",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "29x29",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "29x29",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "40x40",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "40x40",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "76x76",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "76x76",
+      "scale" : "2x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/embedding/ios/GeckoEmbed/js/Info.plist
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>en</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>org.mozilla.$(PRODUCT_NAME:rfc1034identifier)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+	<key>LSRequiresIPhoneOS</key>
+	<true/>
+	<key>UILaunchStoryboardName</key>
+	<string>LaunchScreen</string>
+	<key>UIMainStoryboardFile</key>
+	<string>Main</string>
+	<key>UIRequiredDeviceCapabilities</key>
+	<array>
+		<string>armv7</string>
+	</array>
+	<key>UISupportedInterfaceOrientations</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+	<key>UISupportedInterfaceOrientations~ipad</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationPortraitUpsideDown</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+</dict>
+</plist>
new file mode 100644
--- /dev/null
+++ b/embedding/ios/GeckoEmbed/js/dirs.m
@@ -0,0 +1,12 @@
+#import <Foundation/Foundation.h>
+
+bool GetDocumentsDirectory(char* dir)
+{
+    NSSearchPathDirectory directory = NSDocumentDirectory;
+    NSArray* paths = NSSearchPathForDirectoriesInDomains(directory, NSUserDomainMask, YES);
+    if ([paths count] == 0) {
+        return false;
+    }
+    strcpy(dir, [[paths objectAtIndex:0] UTF8String]);
+    return true;
+}
new file mode 100644
--- /dev/null
+++ b/embedding/ios/GeckoEmbed/js/shell.cpp
@@ -0,0 +1,25 @@
+#include "OSObject.cpp"
+#include "jsoptparse.cpp"
+#define main shell_main
+#include "js.cpp"
+#undef main
+
+#include <unistd.h>
+
+extern "C" bool GetDocumentsDirectory(char *dir);
+
+// Fake editline
+char* readline(const char* prompt)
+{
+  return nullptr;
+}
+
+void add_history(char* line) {}
+
+int main(int argc, char** argv, char** envp)
+{
+    char dir[1024];
+    GetDocumentsDirectory(dir);
+    chdir(dir);
+    return shell_main(argc, argv, envp);
+}
new file mode 100644
--- /dev/null
+++ b/embedding/ios/app.mozbuild
@@ -0,0 +1,10 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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('/toolkit/toolkit.mozbuild')
+
+if CONFIG['MOZ_EXTENSIONS']:
+    DIRS += ['/extensions']
new file mode 100644
--- /dev/null
+++ b/embedding/ios/build.mk
@@ -0,0 +1,29 @@
+# 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/.
+
+installer:
+
+package:
+
+package-compare:
+
+stage-package:
+
+sdk:
+
+install::
+
+clean::
+
+distclean::
+
+source-package::
+
+upload::
+
+source-upload::
+
+hg-bundle::
+
+l10n-check::
new file mode 100644
--- /dev/null
+++ b/embedding/ios/confvars.sh
@@ -0,0 +1,19 @@
+#! /bin/sh
+# 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/.
+
+MOZ_APP_NAME=geckoembed
+MOZ_APP_DISPLAYNAME=GeckoEmbed
+MOZ_UPDATER=
+MOZ_CHROME_FILE_FORMAT=omni
+MOZ_APP_VERSION=$MOZILLA_VERSION
+MOZ_PLACES=1
+MOZ_EXTENSIONS_DEFAULT=" gio"
+MOZ_SERVICES_COMMON=1
+MOZ_SERVICES_CRYPTO=1
+MOZ_SERVICES_METRICS=1
+MOZ_SERVICES_SYNC=1
+MOZ_MEDIA_NAVIGATOR=1
+MOZ_SERVICES_HEALTHREPORT=1
+MOZ_DISABLE_EXPORT_JS=1
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -307,16 +307,23 @@ public:
     virtual bool IsDoubleBuffered() const {
         return false;
     }
 
     virtual GLContextType GetContextType() const = 0;
 
     virtual bool IsCurrent() = 0;
 
+    /**
+     * Get the default framebuffer for this context.
+     */
+    virtual GLuint GetDefaultFramebuffer() {
+        return 0;
+    }
+
 protected:
     bool mInitialized;
     bool mIsOffscreen;
     bool mIsGlobalSharedContext;
     bool mContextLost;
 
     /**
      * mVersion store the OpenGL's version, multiplied by 100. For example, if
--- a/gfx/ipc/GfxMessageUtils.h
+++ b/gfx/ipc/GfxMessageUtils.h
@@ -728,16 +728,17 @@ struct ParamTraits<mozilla::layers::Fram
     WriteParam(aMsg, aParam.mExtraResolution);
     WriteParam(aMsg, aParam.mBackgroundColor);
     WriteParam(aMsg, aParam.mDoSmoothScroll);
     WriteParam(aMsg, aParam.mSmoothScrollOffset);
     WriteParam(aMsg, aParam.GetLineScrollAmount());
     WriteParam(aMsg, aParam.GetPageScrollAmount());
     WriteParam(aMsg, aParam.AllowVerticalScrollWithWheel());
     WriteParam(aMsg, aParam.mClipRect);
+    WriteParam(aMsg, aParam.mMaskLayerIndex);
     WriteParam(aMsg, aParam.mIsLayersIdRoot);
     WriteParam(aMsg, aParam.mUsesContainerScrolling);
     WriteParam(aMsg, aParam.GetContentDescription());
   }
 
   static bool ReadContentDescription(const Message* aMsg, void** aIter, paramType* aResult)
   {
     nsCString str;
@@ -773,16 +774,17 @@ struct ParamTraits<mozilla::layers::Fram
             ReadParam(aMsg, aIter, &aResult->mExtraResolution) &&
             ReadParam(aMsg, aIter, &aResult->mBackgroundColor) &&
             ReadParam(aMsg, aIter, &aResult->mDoSmoothScroll) &&
             ReadParam(aMsg, aIter, &aResult->mSmoothScrollOffset) &&
             ReadParam(aMsg, aIter, &aResult->mLineScrollAmount) &&
             ReadParam(aMsg, aIter, &aResult->mPageScrollAmount) &&
             ReadParam(aMsg, aIter, &aResult->mAllowVerticalScrollWithWheel) &&
             ReadParam(aMsg, aIter, &aResult->mClipRect) &&
+            ReadParam(aMsg, aIter, &aResult->mMaskLayerIndex) &&
             ReadParam(aMsg, aIter, &aResult->mIsLayersIdRoot) &&
             ReadParam(aMsg, aIter, &aResult->mUsesContainerScrolling) &&
             ReadContentDescription(aMsg, aIter, aResult));
   }
 };
 
 template<>
 struct ParamTraits<mozilla::layers::TextureFactoryIdentifier>
--- a/gfx/layers/FrameMetrics.h
+++ b/gfx/layers/FrameMetrics.h
@@ -99,16 +99,17 @@ public:
            mScrollGeneration == aOther.mScrollGeneration &&
            mExtraResolution == aOther.mExtraResolution &&
            mBackgroundColor == aOther.mBackgroundColor &&
            mDoSmoothScroll == aOther.mDoSmoothScroll &&
            mLineScrollAmount == aOther.mLineScrollAmount &&
            mPageScrollAmount == aOther.mPageScrollAmount &&
            mAllowVerticalScrollWithWheel == aOther.mAllowVerticalScrollWithWheel &&
            mClipRect == aOther.mClipRect &&
+           mMaskLayerIndex == aOther.mMaskLayerIndex &&
            mIsLayersIdRoot == aOther.mIsLayersIdRoot &&
 		   mUsesContainerScrolling == aOther.mUsesContainerScrolling;
   }
   bool operator!=(const FrameMetrics& aOther) const
   {
     return !operator==(aOther);
   }
 
@@ -520,16 +521,23 @@ public:
   }
   bool HasClipRect() const {
     return mClipRect.isSome();
   }
   const ParentLayerIntRect& ClipRect() const {
     return mClipRect.ref();
   }
 
+  void SetMaskLayerIndex(const Maybe<size_t>& aIndex) {
+    mMaskLayerIndex = aIndex;
+  }
+  const Maybe<size_t>& GetMaskLayerIndex() const {
+    return mMaskLayerIndex;
+  }
+
   void SetIsLayersIdRoot(bool aValue) {
     mIsLayersIdRoot = aValue;
   }
   bool IsLayersIdRoot() const {
     return mIsLayersIdRoot;
   }
 
   void SetUsesContainerScrolling(bool aValue) {
@@ -709,16 +717,21 @@ private:
   LayoutDeviceIntSize mPageScrollAmount;
 
   // Whether or not the frame can be vertically scrolled with a mouse wheel.
   bool mAllowVerticalScrollWithWheel;
 
   // The clip rect to use when compositing a layer with this FrameMetrics.
   Maybe<ParentLayerIntRect> mClipRect;
 
+  // An extra clip mask layer to use when compositing a layer with this
+  // FrameMetrics. This is an index into the MetricsMaskLayers array on
+  // the Layer.
+  Maybe<size_t> mMaskLayerIndex;
+
   // Whether these framemetrics are for the root scroll frame (root element if
   // we don't have a root scroll frame) for its layers id.
   bool mIsLayersIdRoot;
 
   // True if scrolling using containers, false otherwise. This can be removed
   // when containerful scrolling is eliminated.
   bool mUsesContainerScrolling;
 
--- a/gfx/layers/ImageLayers.cpp
+++ b/gfx/layers/ImageLayers.cpp
@@ -52,13 +52,13 @@ void ImageLayer::ComputeEffectiveTransfo
 
     mEffectiveTransformForBuffer =
         SnapTransform(local, sourceRect, nullptr) *
         SnapTransformTranslation(aTransformToSurface, nullptr);
   } else {
     mEffectiveTransformForBuffer = mEffectiveTransform;
   }
 
-  ComputeEffectiveTransformForMaskLayer(aTransformToSurface);
+  ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
 }
 
 }
 }
--- a/gfx/layers/LayerTreeInvalidation.cpp
+++ b/gfx/layers/LayerTreeInvalidation.cpp
@@ -78,16 +78,20 @@ static void
 NotifySubdocumentInvalidationRecursive(Layer* aLayer, NotifySubDocInvalidationFunc aCallback)
 {
   aLayer->ClearInvalidRect();
   ContainerLayer* container = aLayer->AsContainerLayer();
 
   if (aLayer->GetMaskLayer()) {
     NotifySubdocumentInvalidationRecursive(aLayer->GetMaskLayer(), aCallback);
   }
+  for (size_t i = 0; i < aLayer->GetAncestorMaskLayerCount(); i++) {
+    Layer* maskLayer = aLayer->GetAncestorMaskLayerAt(i);
+    NotifySubdocumentInvalidationRecursive(maskLayer, aCallback);
+  }
 
   if (!container) {
     return;
   }
 
   for (Layer* child = container->GetFirstChild(); child; child = child->GetNextSibling()) {
     NotifySubdocumentInvalidationRecursive(child, aCallback);
   }
@@ -106,16 +110,20 @@ struct LayerPropertiesBase : public Laye
     , mPostYScale(aLayer->GetPostYScale())
     , mOpacity(aLayer->GetLocalOpacity())
     , mUseClipRect(!!aLayer->GetClipRect())
   {
     MOZ_COUNT_CTOR(LayerPropertiesBase);
     if (aLayer->GetMaskLayer()) {
       mMaskLayer = CloneLayerTreePropertiesInternal(aLayer->GetMaskLayer(), true);
     }
+    for (size_t i = 0; i < aLayer->GetAncestorMaskLayerCount(); i++) {
+      Layer* maskLayer = aLayer->GetAncestorMaskLayerAt(i);
+      mAncestorMaskLayers.AppendElement(CloneLayerTreePropertiesInternal(maskLayer, true));
+    }
     if (mUseClipRect) {
       mClipRect = *aLayer->GetClipRect();
     }
     mTransform = aLayer->GetLocalTransform();
   }
   LayerPropertiesBase()
     : mLayer(nullptr)
     , mMaskLayer(nullptr)
@@ -134,20 +142,32 @@ struct LayerPropertiesBase : public Laye
   virtual void MoveBy(const IntPoint& aOffset);
 
   nsIntRegion ComputeChange(NotifySubDocInvalidationFunc aCallback,
                             bool& aGeometryChanged)
   {
     bool transformChanged = !mTransform.FuzzyEqualsMultiplicative(mLayer->GetLocalTransform()) ||
                             mLayer->GetPostXScale() != mPostXScale ||
                             mLayer->GetPostYScale() != mPostYScale;
-    Layer* otherMask = mLayer->GetMaskLayer();
     const Maybe<ParentLayerIntRect>& otherClip = mLayer->GetClipRect();
     nsIntRegion result;
+
+    bool ancestorMaskChanged = mAncestorMaskLayers.Length() != mLayer->GetAncestorMaskLayerCount();
+    if (!ancestorMaskChanged) {
+      for (size_t i = 0; i < mAncestorMaskLayers.Length(); i++) {
+        if (mLayer->GetAncestorMaskLayerAt(i) != mAncestorMaskLayers[i]->mLayer) {
+          ancestorMaskChanged = true;
+          break;
+        }
+      }
+    }
+
+    Layer* otherMask = mLayer->GetMaskLayer();
     if ((mMaskLayer ? mMaskLayer->mLayer : nullptr) != otherMask ||
+        ancestorMaskChanged ||
         (mUseClipRect != !!otherClip) ||
         mLayer->GetLocalOpacity() != mOpacity ||
         transformChanged) 
     {
       aGeometryChanged = true;
       result = OldTransformedBounds();
       AddRegion(result, NewTransformedBounds());
 
@@ -157,16 +177,25 @@ struct LayerPropertiesBase : public Laye
     AddRegion(result, ComputeChangeInternal(aCallback, aGeometryChanged));
     AddTransformedRegion(result, mLayer->GetInvalidRegion(), mTransform);
 
     if (mMaskLayer && otherMask) {
       AddTransformedRegion(result, mMaskLayer->ComputeChange(aCallback, aGeometryChanged),
                            mTransform);
     }
 
+    for (size_t i = 0;
+         i < std::min(mAncestorMaskLayers.Length(), mLayer->GetAncestorMaskLayerCount());
+         i++)
+    {
+      AddTransformedRegion(result,
+                           mAncestorMaskLayers[i]->ComputeChange(aCallback, aGeometryChanged),
+                           mTransform);
+    }
+
     if (mUseClipRect && otherClip) {
       if (!mClipRect.IsEqualInterior(*otherClip)) {
         aGeometryChanged = true;
         nsIntRegion tmp; 
         tmp.Xor(ParentLayerIntRect::ToUntyped(mClipRect), ParentLayerIntRect::ToUntyped(*otherClip)); 
         AddRegion(result, tmp);
       }
     }
@@ -188,16 +217,17 @@ struct LayerPropertiesBase : public Laye
   virtual nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback,
                                             bool& aGeometryChanged)
   {
     return IntRect();
   }
 
   nsRefPtr<Layer> mLayer;
   UniquePtr<LayerPropertiesBase> mMaskLayer;
+  nsTArray<UniquePtr<LayerPropertiesBase>> mAncestorMaskLayers;
   nsIntRegion mVisibleRegion;
   nsIntRegion mInvalidRegion;
   Matrix4x4 mTransform;
   float mPostXScale;
   float mPostYScale;
   float mOpacity;
   ParentLayerIntRect mClipRect;
   bool mUseClipRect;
@@ -438,16 +468,19 @@ LayerProperties::CloneFrom(Layer* aRoot)
 
 /* static */ void 
 LayerProperties::ClearInvalidations(Layer *aLayer)
 {
   aLayer->ClearInvalidRect();
   if (aLayer->GetMaskLayer()) {
     ClearInvalidations(aLayer->GetMaskLayer());
   }
+  for (size_t i = 0; i < aLayer->GetAncestorMaskLayerCount(); i++) {
+    ClearInvalidations(aLayer->GetAncestorMaskLayerAt(i));
+  }
 
   ContainerLayer* container = aLayer->AsContainerLayer();
   if (!container) {
     return;
   }
 
   for (Layer* child = container->GetFirstChild(); child; child = child->GetNextSibling()) {
     ClearInvalidations(child);
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -852,31 +852,41 @@ Layer::GetEffectiveMixBlendMode()
 
 gfxContext::GraphicsOperator
 Layer::DeprecatedGetEffectiveMixBlendMode()
 {
   return ThebesOp(GetEffectiveMixBlendMode());
 }
 
 void
-Layer::ComputeEffectiveTransformForMaskLayer(const Matrix4x4& aTransformToSurface)
+Layer::ComputeEffectiveTransformForMaskLayers(const gfx::Matrix4x4& aTransformToSurface)
 {
-  if (mMaskLayer) {
-    mMaskLayer->mEffectiveTransform = aTransformToSurface;
+  if (GetMaskLayer()) {
+    ComputeEffectiveTransformForMaskLayer(GetMaskLayer(), aTransformToSurface);
+  }
+  for (size_t i = 0; i < GetAncestorMaskLayerCount(); i++) {
+    Layer* maskLayer = GetAncestorMaskLayerAt(i);
+    ComputeEffectiveTransformForMaskLayer(maskLayer, aTransformToSurface);
+  }
+}
+
+/* static */ void
+Layer::ComputeEffectiveTransformForMaskLayer(Layer* aMaskLayer, const gfx::Matrix4x4& aTransformToSurface)
+{
+  aMaskLayer->mEffectiveTransform = aTransformToSurface;
 
 #ifdef DEBUG
-    bool maskIs2D = mMaskLayer->GetTransform().CanDraw2D();
-    NS_ASSERTION(maskIs2D, "How did we end up with a 3D transform here?!");
+  bool maskIs2D = aMaskLayer->GetTransform().CanDraw2D();
+  NS_ASSERTION(maskIs2D, "How did we end up with a 3D transform here?!");
 #endif
-    // The mask layer can have an async transform applied to it in some
-    // situations, so be sure to use its GetLocalTransform() rather than
-    // its GetTransform().
-    mMaskLayer->mEffectiveTransform = mMaskLayer->GetLocalTransform() *
-      mMaskLayer->mEffectiveTransform;
-  }
+  // The mask layer can have an async transform applied to it in some
+  // situations, so be sure to use its GetLocalTransform() rather than
+  // its GetTransform().
+  aMaskLayer->mEffectiveTransform = aMaskLayer->GetLocalTransform() *
+    aMaskLayer->mEffectiveTransform;
 }
 
 RenderTargetRect
 Layer::TransformRectToRenderTarget(const LayerIntRect& aRect)
 {
   LayerRect rect(aRect);
   RenderTargetRect quad = RenderTargetRect::FromUnknown(
     GetEffectiveTransform().TransformBounds(
@@ -1185,17 +1195,17 @@ void
 ContainerLayer::DefaultComputeEffectiveTransforms(const Matrix4x4& aTransformToSurface)
 {
   Matrix residual;
   Matrix4x4 idealTransform = GetLocalTransform() * aTransformToSurface;
   idealTransform.ProjectTo2D();
   mEffectiveTransform = SnapTransformTranslation(idealTransform, &residual);
 
   bool useIntermediateSurface;
-  if (GetMaskLayer() ||
+  if (HasMaskLayers() ||
       GetForceIsolatedGroup()) {
     useIntermediateSurface = true;
 #ifdef MOZ_DUMP_PAINTING
   } else if (gfxUtils::sDumpPaintingIntermediate) {
     useIntermediateSurface = true;
 #endif
   } else {
     float opacity = GetEffectiveOpacity();
@@ -1214,36 +1224,36 @@ ContainerLayer::DefaultComputeEffectiveT
         for (Layer* child = GetFirstChild(); child; child = child->GetNextSibling()) {
           const Maybe<ParentLayerIntRect>& clipRect = child->GetEffectiveClipRect();
           /* We can't (easily) forward our transform to children with a non-empty clip
            * rect since it would need to be adjusted for the transform. See
            * the calculations performed by CalculateScissorRect above.
            * Nor for a child with a mask layer.
            */
           if ((clipRect && !clipRect->IsEmpty() && !child->GetVisibleRegion().IsEmpty()) ||
-              child->GetMaskLayer()) {
+              child->HasMaskLayers()) {
             useIntermediateSurface = true;
             break;
           }
         }
       }
     }
   }
 
   mUseIntermediateSurface = useIntermediateSurface && !GetEffectiveVisibleRegion().IsEmpty();
   if (useIntermediateSurface) {
     ComputeEffectiveTransformsForChildren(Matrix4x4::From2D(residual));
   } else {
     ComputeEffectiveTransformsForChildren(idealTransform);
   }
 
   if (idealTransform.CanDraw2D()) {
-    ComputeEffectiveTransformForMaskLayer(aTransformToSurface);
+    ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
   } else {
-    ComputeEffectiveTransformForMaskLayer(Matrix4x4());
+    ComputeEffectiveTransformForMaskLayers(Matrix4x4());
   }
 }
 
 void
 ContainerLayer::DefaultComputeSupportsComponentAlphaChildren(bool* aNeedsSurfaceCopy)
 {
   if (!(GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA_DESCENDANT) ||
       !Manager()->AreComponentAlphaLayersEnabled()) {
@@ -1516,16 +1526,23 @@ Layer::Dump(std::stringstream& aStream, 
 
   if (Layer* mask = GetMaskLayer()) {
     aStream << nsPrintfCString("%s  Mask layer:\n", aPrefix).get();
     nsAutoCString pfx(aPrefix);
     pfx += "    ";
     mask->Dump(aStream, pfx.get(), aDumpHtml);
   }
 
+  for (size_t i = 0; i < GetAncestorMaskLayerCount(); i++) {
+    aStream << nsPrintfCString("%s  Ancestor mask layer %d:\n", aPrefix, uint32_t(i)).get();
+    nsAutoCString pfx(aPrefix);
+    pfx += "    ";
+    GetAncestorMaskLayerAt(i)->Dump(aStream, pfx.get(), aDumpHtml);
+  }
+
 #ifdef MOZ_DUMP_PAINTING
   for (size_t i = 0; i < mExtraDumpInfo.Length(); i++) {
     const nsCString& str = mExtraDumpInfo[i];
     aStream << aPrefix << "  Info:\n" << str.get();
   }
 #endif
 
   if (Layer* kid = GetFirstChild()) {
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -1055,16 +1055,28 @@ public:
       MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) MaskLayer", this));
       mMaskLayer = aMaskLayer;
       Mutated();
     }
   }
 
   /**
    * CONSTRUCTION PHASE ONLY
+   * Add a FrameMetrics-associated mask layer.
+   */
+  void SetAncestorMaskLayers(const nsTArray<nsRefPtr<Layer>>& aLayers) {
+    if (aLayers != mAncestorMaskLayers) {
+      MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) AncestorMaskLayers", this));
+      mAncestorMaskLayers = aLayers;
+      Mutated();
+    }
+  }
+
+  /**
+   * CONSTRUCTION PHASE ONLY
    * Tell this layer what its transform should be. The transformation
    * is applied when compositing the layer into its parent container.
    */
   void SetBaseTransform(const gfx::Matrix4x4& aMatrix)
   {
     NS_ASSERTION(!aMatrix.IsSingular(),
                  "Shouldn't be trying to draw with a singular matrix!");
     mPendingTransform = nullptr;
@@ -1266,16 +1278,29 @@ public:
   const LayerRect& GetStickyScrollRangeOuter() { return mStickyPositionData->mOuter; }
   const LayerRect& GetStickyScrollRangeInner() { return mStickyPositionData->mInner; }
   FrameMetrics::ViewID GetScrollbarTargetContainerId() { return mScrollbarTargetId; }
   ScrollDirection GetScrollbarDirection() { return mScrollbarDirection; }
   float GetScrollbarThumbRatio() { return mScrollbarThumbRatio; }
   bool IsScrollbarContainer() { return mIsScrollbarContainer; }
   Layer* GetMaskLayer() const { return mMaskLayer; }
 
+  // Ancestor mask layers are associated with FrameMetrics, but for simplicity
+  // in maintaining the layer tree structure we attach them to the layer.
+  size_t GetAncestorMaskLayerCount() const {
+    return mAncestorMaskLayers.Length();
+  }
+  Layer* GetAncestorMaskLayerAt(size_t aIndex) const {
+    return mAncestorMaskLayers.ElementAt(aIndex);
+  }
+
+  bool HasMaskLayers() const {
+    return GetMaskLayer() || mAncestorMaskLayers.Length() > 0;
+  }
+
   /*
    * Get the combined clip rect of the Layer clip and all clips on FrameMetrics.
    * This is intended for use in Layout. The compositor needs to apply async
    * transforms to find the combined clip.
    */
   Maybe<ParentLayerIntRect> GetCombinedClipRect() const;
 
   /**
@@ -1484,19 +1509,21 @@ public:
    * pixel grid (whatever aTransformToSurface is relative to).
    *
    * We promise that when this is called on a layer, all ancestor layers
    * have already had ComputeEffectiveTransforms called.
    */
   virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) = 0;
 
   /**
-   * computes the effective transform for a mask layer, if this layer has one
+   * Computes the effective transform for mask layers, if this layer has any.
    */
-  void ComputeEffectiveTransformForMaskLayer(const gfx::Matrix4x4& aTransformToSurface);
+  void ComputeEffectiveTransformForMaskLayers(const gfx::Matrix4x4& aTransformToSurface);
+  static void ComputeEffectiveTransformForMaskLayer(Layer* aMaskLayer,
+                                                    const gfx::Matrix4x4& aTransformToSurface);
 
   /**
    * Calculate the scissor rect required when rendering this layer.
    * Returns a rectangle relative to the intermediate surface belonging to the
    * nearest ancestor that has an intermediate surface, or relative to the root
    * viewport if no ancestor has an intermediate surface, corresponding to the
    * clip rect for this layer intersected with aCurrentScissorRect.
    */
@@ -1690,16 +1717,17 @@ protected:
                                gfx::Matrix* aResidualTransform);
 
   LayerManager* mManager;
   ContainerLayer* mParent;
   Layer* mNextSibling;
   Layer* mPrevSibling;
   void* mImplData;
   nsRefPtr<Layer> mMaskLayer;
+  nsTArray<nsRefPtr<Layer>> mAncestorMaskLayers;
   gfx::UserData mUserData;
   gfx::IntRect mLayerBounds;
   nsIntRegion mVisibleRegion;
   nsTArray<FrameMetrics> mFrameMetrics;
   EventRegions mEventRegions;
   gfx::Matrix4x4 mTransform;
   // A mutation of |mTransform| that we've queued to be applied at the
   // end of the next transaction (if nothing else overrides it in the
@@ -1801,17 +1829,17 @@ public:
                  "Residual transform can only be a translation");
     if (!gfx::ThebesPoint(residual.GetTranslation()).WithinEpsilonOf(mResidualTranslation, 1e-3f)) {
       mResidualTranslation = gfx::ThebesPoint(residual.GetTranslation());
       NS_ASSERTION(-0.5 <= mResidualTranslation.x && mResidualTranslation.x < 0.5 &&
                    -0.5 <= mResidualTranslation.y && mResidualTranslation.y < 0.5,
                    "Residual translation out of range");
       mValidRegion.SetEmpty();
     }
-    ComputeEffectiveTransformForMaskLayer(aTransformToSurface);
+    ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
   }
 
   LayerManager::PaintedLayerCreationHint GetCreationHint() const { return mCreationHint; }
 
   bool UsedForReadback() { return mUsedForReadback; }
   void SetUsedForReadback(bool aUsed) { mUsedForReadback = aUsed; }
   /**
    * Returns the residual translation. Apply this translation when drawing
@@ -2112,17 +2140,17 @@ public:
   virtual const gfxRGBA& GetColor() { return mColor; }
 
   MOZ_LAYER_DECL_NAME("ColorLayer", TYPE_COLOR)
 
   virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
   {
     gfx::Matrix4x4 idealTransform = GetLocalTransform() * aTransformToSurface;
     mEffectiveTransform = SnapTransformTranslation(idealTransform, nullptr);
-    ComputeEffectiveTransformForMaskLayer(aTransformToSurface);
+    ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
   }
 
 protected:
   ColorLayer(LayerManager* aManager, void* aImplData)
     : Layer(aManager, aImplData),
       mColor(0.0, 0.0, 0.0, 0.0)
   {}
 
@@ -2264,17 +2292,17 @@ public:
     // Snap our local transform first, and snap the inherited transform as well.
     // This makes our snapping equivalent to what would happen if our content
     // was drawn into a PaintedLayer (gfxContext would snap using the local
     // transform, then we'd snap again when compositing the PaintedLayer).
     mEffectiveTransform =
         SnapTransform(GetLocalTransform(), gfxRect(0, 0, mBounds.width, mBounds.height),
                       nullptr)*
         SnapTransformTranslation(aTransformToSurface, nullptr);
-    ComputeEffectiveTransformForMaskLayer(aTransformToSurface);
+    ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
   }
 
 protected:
   CanvasLayer(LayerManager* aManager, void* aImplData)
     : Layer(aManager, aImplData)
     , mPreTransCallback(nullptr)
     , mPreTransCallbackData(nullptr)
     , mPostTransCallback(nullptr)
--- a/gfx/layers/ReadbackProcessor.cpp
+++ b/gfx/layers/ReadbackProcessor.cpp
@@ -68,19 +68,21 @@ FindBackgroundLayer(ReadbackLayer* aLaye
     if (!visibleRegion.Intersects(rectInBackground))
       continue;
     // Since l is present in the background, from here on we either choose l
     // or nothing.
     if (!visibleRegion.Contains(rectInBackground))
       return nullptr;
 
     if (l->GetEffectiveOpacity() != 1.0 ||
-        l->GetMaskLayer() ||
+        l->HasMaskLayers() ||
         !(l->GetContentFlags() & Layer::CONTENT_OPAQUE))
+    {
       return nullptr;
+    }
 
     // cliprects are post-transform
     const Maybe<ParentLayerIntRect>& clipRect = l->GetEffectiveClipRect();
     if (clipRect && !clipRect->Contains(ViewAs<ParentLayerPixel>(IntRect(transformOffset, aLayer->GetSize()))))
       return nullptr;
 
     Layer::LayerType type = l->GetType();
     if (type != Layer::TYPE_COLOR && type != Layer::TYPE_PAINTED)
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -2912,16 +2912,17 @@ void AsyncPanZoomController::NotifyLayer
     mFrameMetrics.SetCompositionBounds(aLayerMetrics.GetCompositionBounds());
     mFrameMetrics.SetRootCompositionSize(aLayerMetrics.GetRootCompositionSize());
     mFrameMetrics.SetPresShellResolution(aLayerMetrics.GetPresShellResolution());
     mFrameMetrics.SetCumulativeResolution(aLayerMetrics.GetCumulativeResolution());
     mFrameMetrics.SetHasScrollgrab(aLayerMetrics.GetHasScrollgrab());
     mFrameMetrics.SetLineScrollAmount(aLayerMetrics.GetLineScrollAmount());
     mFrameMetrics.SetPageScrollAmount(aLayerMetrics.GetPageScrollAmount());
     mFrameMetrics.SetClipRect(aLayerMetrics.GetClipRect());
+    mFrameMetrics.SetMaskLayerIndex(aLayerMetrics.GetMaskLayerIndex());
     mFrameMetrics.SetIsLayersIdRoot(aLayerMetrics.IsLayersIdRoot());
     mFrameMetrics.SetUsesContainerScrolling(aLayerMetrics.UsesContainerScrolling());
 
     if (scrollOffsetUpdated) {
       APZC_LOG("%p updating scroll offset from %s to %s\n", this,
         ToString(mFrameMetrics.GetScrollOffset()).c_str(),
         ToString(aLayerMetrics.GetScrollOffset()).c_str());
 
--- a/gfx/layers/basic/BasicContainerLayer.cpp
+++ b/gfx/layers/basic/BasicContainerLayer.cpp
@@ -39,27 +39,27 @@ BasicContainerLayer::ComputeEffectiveTra
   // containers.
   Matrix residual;
   Matrix4x4 idealTransform = GetLocalTransform() * aTransformToSurface;
   idealTransform.ProjectTo2D();
 
   if (!idealTransform.CanDraw2D()) {
     mEffectiveTransform = idealTransform;
     ComputeEffectiveTransformsForChildren(Matrix4x4());
-    ComputeEffectiveTransformForMaskLayer(Matrix4x4());
+    ComputeEffectiveTransformForMaskLayers(Matrix4x4());
     mUseIntermediateSurface = true;
     return;
   }
 
   mEffectiveTransform = SnapTransformTranslation(idealTransform, &residual);
   // We always pass the ideal matrix down to our children, so there is no
   // need to apply any compensation using the residual from SnapTransformTranslation.
   ComputeEffectiveTransformsForChildren(idealTransform);
 
-  ComputeEffectiveTransformForMaskLayer(aTransformToSurface);
+  ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
 
   Layer* child = GetFirstChild();
   bool hasSingleBlendingChild = false;
   if (!HasMultipleChildren() && child) {
     hasSingleBlendingChild = child->GetMixBlendMode() != CompositionOp::OP_OVER;
   }
 
   /* If we have a single childand it is not blending,, it can just inherit our opacity,
--- a/gfx/layers/basic/BasicPaintedLayer.h
+++ b/gfx/layers/basic/BasicPaintedLayer.h
@@ -81,17 +81,17 @@ public:
     if (!BasicManager()->IsRetained()) {
       // Don't do any snapping of our transform, since we're just going to
       // draw straight through without intermediate buffers.
       mEffectiveTransform = GetLocalTransform() * aTransformToSurface;
       if (gfxPoint(0,0) != mResidualTranslation) {
         mResidualTranslation = gfxPoint(0,0);
         mValidRegion.SetEmpty();
       }
-      ComputeEffectiveTransformForMaskLayer(aTransformToSurface);
+      ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
       return;
     }
     PaintedLayer::ComputeEffectiveTransforms(aTransformToSurface);
   }
 
   BasicLayerManager* BasicManager()
   {
     return static_cast<BasicLayerManager*>(mManager);
--- a/gfx/layers/client/ClientCanvasLayer.cpp
+++ b/gfx/layers/client/ClientCanvasLayer.cpp
@@ -128,19 +128,17 @@ ClientCanvasLayer::Initialize(const Data
 }
 
 void
 ClientCanvasLayer::RenderLayer()
 {
   PROFILER_LABEL("ClientCanvasLayer", "RenderLayer",
     js::ProfileEntry::Category::GRAPHICS);
 
-  if (GetMaskLayer()) {
-    ToClientLayer(GetMaskLayer())->RenderLayer();
-  }
+  RenderMaskLayers(this);
 
   if (!IsDirty()) {
     return;
   }
   Painted();
 
   if (!mCanvasClient) {
     TextureFlags flags = TextureFlags::IMMEDIATE_UPLOAD;
--- a/gfx/layers/client/ClientColorLayer.cpp
+++ b/gfx/layers/client/ClientColorLayer.cpp
@@ -38,19 +38,17 @@ public:
   {
     NS_ASSERTION(ClientManager()->InConstruction(),
                  "Can only set properties in construction phase");
     ColorLayer::SetVisibleRegion(aRegion);
   }
 
   virtual void RenderLayer()
   {
-    if (GetMaskLayer()) {
-      ToClientLayer(GetMaskLayer())->RenderLayer();
-    }
+    RenderMaskLayers(this);
   }
 
   virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs)
   {
     aAttrs = ColorLayerAttributes(GetColor(), GetBounds());
   }
 
   virtual Layer* AsLayer() { return this; }
--- a/gfx/layers/client/ClientContainerLayer.h
+++ b/gfx/layers/client/ClientContainerLayer.h
@@ -42,19 +42,17 @@ protected:
     }
 
     MOZ_COUNT_DTOR(ClientContainerLayer);
   }
 
 public:
   virtual void RenderLayer() override
   {
-    if (GetMaskLayer()) {
-      ToClientLayer(GetMaskLayer())->RenderLayer();
-    }
+    RenderMaskLayers(this);
     
     DefaultComputeSupportsComponentAlphaChildren();
 
     nsAutoTArray<Layer*, 12> children;
     SortChildrenBy3DZOrder(children);
 
     ReadbackProcessor readback;
     readback.BuildUpdates(this);
--- a/gfx/layers/client/ClientImageLayer.cpp
+++ b/gfx/layers/client/ClientImageLayer.cpp
@@ -121,19 +121,17 @@ protected:
 
   RefPtr<ImageClient> mImageClient;
   CompositableType mImageClientTypeContainer;
 };
 
 void
 ClientImageLayer::RenderLayer()
 {
-  if (GetMaskLayer()) {
-    ToClientLayer(GetMaskLayer())->RenderLayer();
-  }
+  RenderMaskLayers(this);
 
   if (!mContainer) {
      return;
   }
 
   if (mImageClient) {
     mImageClient->OnTransaction();
   }
--- a/gfx/layers/client/ClientLayerManager.h
+++ b/gfx/layers/client/ClientLayerManager.h
@@ -385,16 +385,26 @@ public:
 
   virtual ClientPaintedLayer* AsThebes() { return nullptr; }
 
   static inline ClientLayer *
   ToClientLayer(Layer* aLayer)
   {
     return static_cast<ClientLayer*>(aLayer->ImplData());
   }
+
+  template <typename LayerType>
+  static inline void RenderMaskLayers(LayerType* aLayer) {
+    if (aLayer->GetMaskLayer()) {
+      ToClientLayer(aLayer->GetMaskLayer())->RenderLayer();
+    }
+    for (size_t i = 0; i < aLayer->GetAncestorMaskLayerCount(); i++) {
+      ToClientLayer(aLayer->GetAncestorMaskLayerAt(i))->RenderLayer();
+    }
+  }
 };
 
 // Create a shadow layer (PLayerChild) for aLayer, if we're forwarding
 // our layer tree to a parent process.  Record the new layer creation
 // in the current open transaction as a side effect.
 template<typename CreatedMethod> void
 CreateShadowFor(ClientLayer* aLayer,
                 ClientLayerManager* aMgr,
--- a/gfx/layers/client/ClientPaintedLayer.cpp
+++ b/gfx/layers/client/ClientPaintedLayer.cpp
@@ -112,19 +112,17 @@ ClientPaintedLayer::PaintThebes()
                                  mVisibleRegion,
                                  state.mDidSelfCopy);
   }
 }
 
 void
 ClientPaintedLayer::RenderLayerWithReadback(ReadbackProcessor *aReadback)
 {
-  if (GetMaskLayer()) {
-    ToClientLayer(GetMaskLayer())->RenderLayer();
-  }
+  RenderMaskLayers(this);
   
   if (!mContentClient) {
     mContentClient = ContentClient::CreateContentClient(ClientManager()->AsShadowForwarder());
     if (!mContentClient) {
       return;
     }
     mContentClient->Connect();
     ClientManager()->AsShadowForwarder()->Attach(mContentClient, this);
--- a/gfx/layers/client/ClientTiledPaintedLayer.cpp
+++ b/gfx/layers/client/ClientTiledPaintedLayer.cpp
@@ -438,20 +438,18 @@ ClientTiledPaintedLayer::RenderLayer()
   nsIntRegion invalidRegion;
   invalidRegion.Sub(neededRegion, mValidRegion);
   if (invalidRegion.IsEmpty()) {
     EndPaint();
     return;
   }
 
   if (!ClientManager()->IsRepeatTransaction()) {
-    // Only paint the mask layer on the first transaction.
-    if (GetMaskLayer()) {
-      ToClientLayer(GetMaskLayer())->RenderLayer();
-    }
+    // Only paint the mask layers on the first transaction.
+    RenderMaskLayers(this);
 
     // For more complex cases we need to calculate a bunch of metrics before we
     // can do the paint.
     BeginPaint();
     if (mPaintData.mPaintFinished) {
       return;
     }
 
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -609,16 +609,28 @@ AsyncCompositionManager::ApplyAsyncConte
   // Each layer has multiple clips. Its local clip, which must move with async
   // transforms, and its scrollframe clips, which are the clips between each
   // scrollframe and its ancestor scrollframe. Scrollframe clips include the
   // composition bounds and any other clips induced by layout.
   //
   // The final clip for the layer is the intersection of these clips.
   Maybe<ParentLayerIntRect> asyncClip = aLayer->GetClipRect();
 
+  // The transform of a mask layer is relative to the masked layer's parent
+  // layer. So whenever we apply an async transform to a layer, we need to
+  // apply that same transform to the layer's own mask layer.
+  // A layer can also have "ancestor" mask layers for any rounded clips from
+  // its ancestor scroll frames. A scroll frame mask layer only needs to be
+  // async transformed for async scrolls of this scroll frame's ancestor
+  // scroll frames, not for async scrolls of this scroll frame itself.
+  // In the loop below, we iterate over scroll frames from inside to outside.
+  // At each iteration, this array contains the layer's ancestor mask layers 
+  // of all scroll frames inside the current one.
+  nsTArray<Layer*> ancestorMaskLayers;
+
   for (uint32_t i = 0; i < aLayer->GetFrameMetricsCount(); i++) {
     AsyncPanZoomController* controller = aLayer->GetAsyncPanZoomController(i);
     if (!controller) {
       continue;
     }
 
     hasAsyncTransform = true;
 
@@ -671,32 +683,53 @@ AsyncCompositionManager::ApplyAsyncConte
       ParentLayerIntRect clip = metrics.ClipRect();
       if (asyncClip) {
         asyncClip = Some(clip.Intersect(*asyncClip));
       } else {
         asyncClip = Some(clip);
       }
     }
 
+    // Do the same for the ancestor mask layers: ancestorMaskLayers contains
+    // the ancestor mask layers for scroll frames *inside* the current scroll
+    // frame, so these are the ones we need to shift by our async transform.
+    for (Layer* ancestorMaskLayer : ancestorMaskLayers) {
+      SetShadowTransform(ancestorMaskLayer,
+          ancestorMaskLayer->GetLocalTransform() * asyncTransform);
+    }
+
+    // Append the ancestor mask layer for this scroll frame to ancestorMaskLayers.
+    if (metrics.GetMaskLayerIndex()) {
+      size_t maskLayerIndex = metrics.GetMaskLayerIndex().value();
+      Layer* ancestorMaskLayer = aLayer->GetAncestorMaskLayerAt(maskLayerIndex);
+      ancestorMaskLayers.AppendElement(ancestorMaskLayer);
+    }
+
     combinedAsyncTransformWithoutOverscroll *= asyncTransformWithoutOverscroll;
     combinedAsyncTransform *= asyncTransform;
   }
 
   if (hasAsyncTransform) {
     if (asyncClip) {
       aLayer->AsLayerComposite()->SetShadowClipRect(asyncClip);
     }
 
     // Apply the APZ transform on top of GetLocalTransform() here (rather than
     // GetTransform()) in case the OMTA code in SampleAnimations already set a
     // shadow transform; in that case we want to apply ours on top of that one
     // rather than clobber it.
     SetShadowTransform(aLayer,
         aLayer->GetLocalTransform() * AdjustForClip(combinedAsyncTransform, aLayer));
 
+    // Do the same for the layer's own mask layer, if it has one.
+    if (Layer* maskLayer = aLayer->GetMaskLayer()) {
+      SetShadowTransform(maskLayer,
+          maskLayer->GetLocalTransform() * combinedAsyncTransform);
+    }
+
     const FrameMetrics& bottom = LayerMetricsWrapper::BottommostScrollableMetrics(aLayer);
     MOZ_ASSERT(bottom.IsScrollable());  // must be true because hasAsyncTransform is true
 
     // For the purpose of aligning fixed and sticky layers, we disregard
     // the overscroll transform as well as any OMTA transform when computing the
     // 'aCurrentTransformForRoot' parameter. This ensures that the overscroll
     // and OMTA transforms are not unapplied, and therefore that the visual
     // effects apply to fixed and sticky layers. We do this by using
--- a/gfx/layers/composite/CanvasLayerComposite.cpp
+++ b/gfx/layers/composite/CanvasLayerComposite.cpp
@@ -90,27 +90,25 @@ CanvasLayerComposite::RenderLayer(const 
 
 #ifdef MOZ_DUMP_PAINTING
   if (gfxUtils::sDumpPainting) {
     RefPtr<gfx::DataSourceSurface> surf = mCompositableHost->GetAsSurface();
     WriteSnapshotToDumpFile(this, surf);
   }
 #endif
 
-  EffectChain effectChain(this);
-  AddBlendModeEffect(effectChain);
-
-  LayerManagerComposite::AutoAddMaskEffect autoMaskEffect(mMaskLayer, effectChain);
-  gfx::Rect clipRect(aClipRect.x, aClipRect.y, aClipRect.width, aClipRect.height);
+  RenderWithAllMasks(this, mCompositor, aClipRect,
+                     [&](EffectChain& effectChain, const Rect& clipRect) {
+    mCompositableHost->Composite(effectChain,
+                          GetEffectiveOpacity(),
+                          GetEffectiveTransform(),
+                          GetEffectFilter(),
+                          clipRect);
+  });
 
-  mCompositableHost->Composite(effectChain,
-                        GetEffectiveOpacity(),
-                        GetEffectiveTransform(),
-                        GetEffectFilter(),
-                        clipRect);
   mCompositableHost->BumpFlashCounter();
 }
 
 CompositableHost*
 CanvasLayerComposite::GetCompositableHost()
 {
   if (mCompositableHost && mCompositableHost->IsAttached()) {
     return mCompositableHost.get();
--- a/gfx/layers/composite/ColorLayerComposite.cpp
+++ b/gfx/layers/composite/ColorLayerComposite.cpp
@@ -16,38 +16,27 @@
 #include "mozilla/mozalloc.h"           // for operator delete, etc
 
 namespace mozilla {
 namespace layers {
 
 void
 ColorLayerComposite::RenderLayer(const gfx::IntRect& aClipRect)
 {
-  EffectChain effects(this);
-
-  GenEffectChain(effects);
-
-  gfx::IntRect boundRect = GetBounds();
-
-  LayerManagerComposite::AutoAddMaskEffect autoMaskEffect(GetMaskLayer(),
-                                                          effects);
+  gfx::Rect rect(GetBounds());
+  const gfx::Matrix4x4& transform = GetEffectiveTransform();
 
-  gfx::Rect rect(boundRect.x, boundRect.y,
-                 boundRect.width, boundRect.height);
-  gfx::Rect clipRect(aClipRect.x, aClipRect.y,
-                     aClipRect.width, aClipRect.height);
-
-  float opacity = GetEffectiveOpacity();
+  RenderWithAllMasks(this, mCompositor, aClipRect,
+                     [&](EffectChain& effectChain, const Rect& clipRect) {
+    GenEffectChain(effectChain);
+    mCompositor->DrawQuad(rect, clipRect, effectChain, GetEffectiveOpacity(), transform);
+  });
 
-  AddBlendModeEffect(effects);
-
-  const gfx::Matrix4x4& transform = GetEffectiveTransform();
-  mCompositor->DrawQuad(rect, clipRect, effects, opacity, transform);
   mCompositor->DrawDiagnostics(DiagnosticFlags::COLOR,
-                               rect, clipRect,
+                               rect, Rect(aClipRect),
                                transform);
 }
 
 void
 ColorLayerComposite::GenEffectChain(EffectChain& aEffect)
 {
   aEffect.mLayerRef = this;
   gfxRGBA color(GetColor());
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -508,44 +508,35 @@ ContainerRender(ContainerT* aContainer,
       surface = aContainer->mPrepared->mTmpTarget;
     }
 
     if (!surface) {
       aContainer->mPrepared = nullptr;
       return;
     }
 
-    float opacity = aContainer->GetEffectiveOpacity();
-
-    gfx::IntRect visibleRect = aContainer->GetEffectiveVisibleRegion().GetBounds();
+    gfx::Rect visibleRect(aContainer->GetEffectiveVisibleRegion().GetBounds());
+    nsRefPtr<Compositor> compositor = aManager->GetCompositor();
 #ifdef MOZ_DUMP_PAINTING
     if (gfxUtils::sDumpPainting) {
-      RefPtr<gfx::DataSourceSurface> surf = surface->Dump(aManager->GetCompositor());
+      RefPtr<gfx::DataSourceSurface> surf = surface->Dump(compositor);
       if (surf) {
         WriteSnapshotToDumpFile(aContainer, surf);
       }
     }
 #endif
 
-    EffectChain effectChain(aContainer);
-    LayerManagerComposite::AutoAddMaskEffect autoMaskEffect(aContainer->GetMaskLayer(),
-                                                            effectChain,
-                                                            !aContainer->GetTransform().CanDraw2D());
-    if (autoMaskEffect.Failed()) {
-      NS_WARNING("Failed to apply a mask effect.");
-      return;
-    }
-
-    aContainer->AddBlendModeEffect(effectChain);
-    effectChain.mPrimaryEffect = new EffectRenderTarget(surface);
-
-    gfx::Rect rect(visibleRect.x, visibleRect.y, visibleRect.width, visibleRect.height);
-    gfx::Rect clipRect(aClipRect.x, aClipRect.y, aClipRect.width, aClipRect.height);
-    aManager->GetCompositor()->DrawQuad(rect, clipRect, effectChain, opacity,
-                                        aContainer->GetEffectiveTransform());
+    nsRefPtr<ContainerT> container = aContainer;
+    RenderWithAllMasks(aContainer, compositor, aClipRect,
+                       [&, surface, compositor, container](EffectChain& effectChain, const Rect& clipRect) {
+      effectChain.mPrimaryEffect = new EffectRenderTarget(surface);
+      compositor->DrawQuad(visibleRect, clipRect, effectChain,
+                           container->GetEffectiveOpacity(),
+                           container->GetEffectiveTransform());
+    });
   } else {
     RenderLayers(aContainer, aManager, RenderTargetPixel::FromUntyped(aClipRect));
   }
   aContainer->mPrepared = nullptr;
 
   // If it is a scrollable container layer with no child layers, and one of the APZCs
   // attached to it has a nonempty async transform, then that transform is not applied
   // to any visible content. Display a warning box (conditioned on the FPS display being
--- a/gfx/layers/composite/ImageLayerComposite.cpp
+++ b/gfx/layers/composite/ImageLayerComposite.cpp
@@ -90,27 +90,25 @@ ImageLayerComposite::RenderLayer(const I
   if (gfxUtils::sDumpPainting) {
     RefPtr<gfx::DataSourceSurface> surf = mImageHost->GetAsSurface();
     WriteSnapshotToDumpFile(this, surf);
   }
 #endif
 
   mCompositor->MakeCurrent();
 
-  EffectChain effectChain(this);
-  LayerManagerComposite::AutoAddMaskEffect autoMaskEffect(mMaskLayer, effectChain);
-  AddBlendModeEffect(effectChain);
-
-  gfx::Rect clipRect(aClipRect.x, aClipRect.y, aClipRect.width, aClipRect.height);
-  mImageHost->SetCompositor(mCompositor);
-  mImageHost->Composite(effectChain,
-                        GetEffectiveOpacity(),
-                        GetEffectiveTransformForBuffer(),
-                        GetEffectFilter(),
-                        clipRect);
+  RenderWithAllMasks(this, mCompositor, aClipRect,
+                     [&](EffectChain& effectChain, const Rect& clipRect) {
+    mImageHost->SetCompositor(mCompositor);
+    mImageHost->Composite(effectChain,
+                          GetEffectiveOpacity(),
+                          GetEffectiveTransformForBuffer(),
+                          GetEffectFilter(),
+                          clipRect);
+  });
   mImageHost->BumpFlashCounter();
 }
 
 void
 ImageLayerComposite::ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface)
 {
   gfx::Matrix4x4 local = GetLocalTransform();
 
@@ -138,17 +136,17 @@ ImageLayerComposite::ComputeEffectiveTra
 
     mEffectiveTransformForBuffer =
         SnapTransform(local, sourceRect, nullptr) *
         SnapTransformTranslation(aTransformToSurface, nullptr);
   } else {
     mEffectiveTransformForBuffer = mEffectiveTransform;
   }
 
-  ComputeEffectiveTransformForMaskLayer(aTransformToSurface);
+  ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
 }
 
 CompositableHost*
 ImageLayerComposite::GetCompositableHost()
 {
   if (mImageHost && mImageHost->IsAttached()) {
     return mImageHost.get();
   }
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -229,17 +229,17 @@ LayerManagerComposite::ApplyOcclusionCul
   // contribute to localOpaque.
   for (Layer* child = aLayer->GetLastChild(); child; child = child->GetPrevSibling()) {
     ApplyOcclusionCulling(child, localOpaque);
   }
 
   // If we have a simple transform, then we can add our opaque area into
   // aOpaqueRegion.
   if (isTranslation &&
-      !aLayer->GetMaskLayer() &&
+      !aLayer->HasMaskLayers() &&
       aLayer->GetLocalOpacity() == 1.0f) {
     if (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) {
       localOpaque.Or(localOpaque, composite->GetFullyRenderedRegion());
     }
     localOpaque.MoveBy(transform2d._31, transform2d._32);
     const Maybe<ParentLayerIntRect>& clip = aLayer->GetEffectiveClipRect();
     if (clip) {
       localOpaque.And(localOpaque, ParentLayerIntRect::ToUntyped(*clip));
--- a/gfx/layers/composite/LayerManagerComposite.h
+++ b/gfx/layers/composite/LayerManagerComposite.h
@@ -13,16 +13,17 @@
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/Attributes.h"         // for override
 #include "mozilla/RefPtr.h"             // for RefPtr, already_AddRefed
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/Point.h"          // for IntSize
 #include "mozilla/gfx/Rect.h"           // for Rect
 #include "mozilla/gfx/Types.h"          // for SurfaceFormat
 #include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/Effects.h"     // for EffectChain
 #include "mozilla/layers/LayersTypes.h"  // for LayersBackend, etc
 #include "mozilla/Maybe.h"              // for Maybe
 #include "mozilla/RefPtr.h"
 #include "mozilla/UniquePtr.h"
 #include "nsAString.h"
 #include "nsRefPtr.h"                   // for nsRefPtr
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsDebug.h"                    // for NS_ASSERTION
@@ -449,13 +450,145 @@ protected:
   RefPtr<Compositor> mCompositor;
   float mShadowOpacity;
   bool mShadowTransformSetByAnimation;
   bool mDestroyed;
   bool mLayerComposited;
   gfx::IntRect mClearRect;
 };
 
+// Render aLayer using aCompositor and apply all mask layers of aLayer: The
+// layer's own mask layer (aLayer->GetMaskLayer()), and any ancestor mask
+// layers.
+// If more than one mask layer needs to be applied, we use intermediate surfaces
+// (CompositingRenderTargets) for rendering, applying one mask layer at a time.
+// Callers need to provide a callback function aRenderCallback that does the
+// actual rendering of the source. It needs to have the following form:
+// void (EffectChain& effectChain, const Rect& clipRect)
+// aRenderCallback is called exactly once, inside this function, unless aLayer's
+// visible region is completely clipped out (in that case, aRenderCallback won't
+// be called at all).
+// This function calls aLayer->AsLayerComposite()->AddBlendModeEffect for the
+// final rendering pass.
+//
+// (This function should really live in LayerManagerComposite.cpp, but we
+// need to use templates for passing lambdas until bug 1164522 is resolved.)
+template<typename RenderCallbackType>
+void
+RenderWithAllMasks(Layer* aLayer, Compositor* aCompositor,
+                   const gfx::IntRect& aClipRect,
+                   RenderCallbackType aRenderCallback)
+{
+  Layer* firstMask = nullptr;
+  size_t maskLayerCount = 0;
+  size_t nextAncestorMaskLayer = 0;
+
+  size_t ancestorMaskLayerCount = aLayer->GetAncestorMaskLayerCount();
+  if (Layer* ownMask = aLayer->GetMaskLayer()) {
+    firstMask = ownMask;
+    maskLayerCount = ancestorMaskLayerCount + 1;
+    nextAncestorMaskLayer = 0;
+  } else if (ancestorMaskLayerCount > 0) {
+    firstMask = aLayer->GetAncestorMaskLayerAt(0);
+    maskLayerCount = ancestorMaskLayerCount;
+    nextAncestorMaskLayer = 1;
+  } else {
+    // no mask layers at all
+  }
+
+  bool firstMaskIs3D = false;
+  if (ContainerLayer* container = aLayer->AsContainerLayer()) {
+    firstMaskIs3D = !container->GetTransform().CanDraw2D();
+  }
+
+  if (maskLayerCount <= 1) {
+    // This is the common case. Render in one pass and return.
+    EffectChain effectChain(aLayer);
+    LayerManagerComposite::AutoAddMaskEffect
+      autoMaskEffect(firstMask, effectChain, firstMaskIs3D);
+    aLayer->AsLayerComposite()->AddBlendModeEffect(effectChain);
+    aRenderCallback(effectChain, gfx::Rect(aClipRect));
+    return;
+  }
+
+  // We have multiple mask layers.
+  // We split our list of mask layers into three parts:
+  //  (1) The first mask
+  //  (2) The list of intermediate masks (every mask except first and last)
+  //  (3) The final mask.
+  // Part (2) can be empty.
+  // For parts (1) and (2) we need to allocate intermediate surfaces to render
+  // into. The final mask gets rendered into the original render target.
+
+  // Calculate the size of the intermediate surfaces.
+  gfx::Rect visibleRect(aLayer->GetEffectiveVisibleRegion().GetBounds());
+  gfx::Matrix4x4 transform = aLayer->GetEffectiveTransform();
+  // TODO: Use RenderTargetIntRect and TransformTo<...> here
+  gfx::IntRect surfaceRect =
+    RoundedOut(transform.TransformBounds(visibleRect)).Intersect(aClipRect);
+  if (surfaceRect.IsEmpty()) {
+    return;
+  }
+
+  RefPtr<CompositingRenderTarget> originalTarget =
+    aCompositor->GetCurrentRenderTarget();
+
+  RefPtr<CompositingRenderTarget> firstTarget =
+    aCompositor->CreateRenderTarget(surfaceRect, INIT_MODE_CLEAR);
+  if (!firstTarget) {
+    return;
+  }
+
+  // Render the source while applying the first mask.
+  aCompositor->SetRenderTarget(firstTarget);
+  {
+    EffectChain firstEffectChain(aLayer);
+    LayerManagerComposite::AutoAddMaskEffect
+      firstMaskEffect(firstMask, firstEffectChain, firstMaskIs3D);
+    aRenderCallback(firstEffectChain, gfx::Rect(aClipRect - surfaceRect.TopLeft()));
+    // firstTarget now contains the transformed source with the first mask and
+    // opacity already applied.
+  }
+
+  // Apply the intermediate masks.
+  gfx::Rect intermediateClip(surfaceRect - surfaceRect.TopLeft());
+  RefPtr<CompositingRenderTarget> previousTarget = firstTarget;
+  for (size_t i = nextAncestorMaskLayer; i < ancestorMaskLayerCount - 1; i++) {
+    Layer* intermediateMask = aLayer->GetAncestorMaskLayerAt(i);
+    RefPtr<CompositingRenderTarget> intermediateTarget =
+      aCompositor->CreateRenderTarget(surfaceRect, INIT_MODE_CLEAR);
+    if (!intermediateTarget) {
+      break;
+    }
+    aCompositor->SetRenderTarget(intermediateTarget);
+    EffectChain intermediateEffectChain(aLayer);
+    LayerManagerComposite::AutoAddMaskEffect
+      intermediateMaskEffect(intermediateMask, intermediateEffectChain);
+    if (intermediateMaskEffect.Failed()) {
+      continue;
+    }
+    intermediateEffectChain.mPrimaryEffect = new EffectRenderTarget(previousTarget);
+    aCompositor->DrawQuad(gfx::Rect(surfaceRect), intermediateClip,
+                          intermediateEffectChain, 1.0, gfx::Matrix4x4());
+    previousTarget = intermediateTarget;
+  }
+
+  aCompositor->SetRenderTarget(originalTarget);
+
+  // Apply the final mask, rendering into originalTarget.
+  EffectChain finalEffectChain(aLayer);
+  finalEffectChain.mPrimaryEffect = new EffectRenderTarget(previousTarget);
+  Layer* finalMask = aLayer->GetAncestorMaskLayerAt(ancestorMaskLayerCount - 1);
+
+  // The blend mode needs to be applied in this final step, because this is
+  // where we're blending with the actual background (which is in originalTarget).
+  aLayer->AsLayerComposite()->AddBlendModeEffect(finalEffectChain);
+  LayerManagerComposite::AutoAddMaskEffect autoMaskEffect(finalMask, finalEffectChain);
+  if (!autoMaskEffect.Failed()) {
+    aCompositor->DrawQuad(gfx::Rect(surfaceRect), gfx::Rect(aClipRect),
+                          finalEffectChain, 1.0, gfx::Matrix4x4());
+  }
+}
 
 } /* layers */
 } /* mozilla */
 
 #endif /* GFX_LayerManagerComposite_H */
--- a/gfx/layers/composite/PaintedLayerComposite.cpp
+++ b/gfx/layers/composite/PaintedLayerComposite.cpp
@@ -102,47 +102,49 @@ void
 PaintedLayerComposite::RenderLayer(const gfx::IntRect& aClipRect)
 {
   if (!mBuffer || !mBuffer->IsAttached()) {
     return;
   }
   PROFILER_LABEL("PaintedLayerComposite", "RenderLayer",
     js::ProfileEntry::Category::GRAPHICS);
 
-  MOZ_ASSERT(mBuffer->GetCompositor() == mCompositeManager->GetCompositor() &&
+  Compositor* compositor = mCompositeManager->GetCompositor();
+
+  MOZ_ASSERT(mBuffer->GetCompositor() == compositor &&
              mBuffer->GetLayer() == this,
              "buffer is corrupted");
 
   const nsIntRegion& visibleRegion = GetEffectiveVisibleRegion();
-  gfx::Rect clipRect(aClipRect.x, aClipRect.y, aClipRect.width, aClipRect.height);
 
 #ifdef MOZ_DUMP_PAINTING
   if (gfxUtils::sDumpPainting) {
     RefPtr<gfx::DataSourceSurface> surf = mBuffer->GetAsSurface();
     if (surf) {
       WriteSnapshotToDumpFile(this, surf);
     }
   }
 #endif
 
-  EffectChain effectChain(this);
-  LayerManagerComposite::AutoAddMaskEffect autoMaskEffect(mMaskLayer, effectChain);
-  AddBlendModeEffect(effectChain);
 
-  mBuffer->SetPaintWillResample(MayResample());
+  RenderWithAllMasks(this, compositor, aClipRect,
+                     [&](EffectChain& effectChain, const Rect& clipRect) {
+    mBuffer->SetPaintWillResample(MayResample());
 
-  mBuffer->Composite(effectChain,
-                     GetEffectiveOpacity(),
-                     GetEffectiveTransform(),
-                     GetEffectFilter(),
-                     clipRect,
-                     &visibleRegion);
+    mBuffer->Composite(effectChain,
+                       GetEffectiveOpacity(),
+                       GetEffectiveTransform(),
+                       GetEffectFilter(),
+                       clipRect,
+                       &visibleRegion);
+  });
+
   mBuffer->BumpFlashCounter();
 
-  mCompositeManager->GetCompositor()->MakeCurrent();
+  compositor->MakeCurrent();
 }
 
 CompositableHost*
 PaintedLayerComposite::GetCompositableHost()
 {
   if (mBuffer && mBuffer->IsAttached()) {
     return mBuffer.get();
   }
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -1066,16 +1066,19 @@ CompositorParent::ScheduleComposition()
 // Go down the composite layer tree, setting properties to match their
 // content-side counterparts.
 /* static */ void
 CompositorParent::SetShadowProperties(Layer* aLayer)
 {
   if (Layer* maskLayer = aLayer->GetMaskLayer()) {
     SetShadowProperties(maskLayer);
   }
+  for (size_t i = 0; i < aLayer->GetAncestorMaskLayerCount(); i++) {
+    SetShadowProperties(aLayer->GetAncestorMaskLayerAt(i));
+  }
 
   // FIXME: Bug 717688 -- Do these updates in LayerTransactionParent::RecvUpdate.
   LayerComposite* layerComposite = aLayer->AsLayerComposite();
   // Set the layerComposite's base transform to the layer's base transform.
   layerComposite->SetShadowTransform(aLayer->GetBaseTransform());
   layerComposite->SetShadowTransformSetByAnimation(false);
   layerComposite->SetShadowVisibleRegion(aLayer->GetVisibleRegion());
   layerComposite->SetShadowClipRect(aLayer->GetClipRect());
--- a/gfx/layers/ipc/LayerTransactionParent.cpp
+++ b/gfx/layers/ipc/LayerTransactionParent.cpp
@@ -347,16 +347,23 @@ LayerTransactionParent::RecvUpdate(Infal
         layer->SetMaskLayer(cast(maskLayer)->AsLayer());
       } else {
         layer->SetMaskLayer(nullptr);
       }
       layer->SetAnimations(common.animations());
       layer->SetInvalidRegion(common.invalidRegion());
       layer->SetFrameMetrics(common.metrics());
 
+      nsTArray<nsRefPtr<Layer>> maskLayers;
+      for (size_t i = 0; i < common.ancestorMaskLayersParent().Length(); i++) {
+        Layer* maskLayer = cast(common.ancestorMaskLayersParent().ElementAt(i))->AsLayer();
+        maskLayers.AppendElement(maskLayer);
+      }
+      layer->SetAncestorMaskLayers(maskLayers);
+
       typedef SpecificLayerAttributes Specific;
       const SpecificLayerAttributes& specific = attrs.specific();
       switch (specific.type()) {
       case Specific::Tnull_t:
         break;
 
       case Specific::TPaintedLayerAttributes: {
         MOZ_LAYERS_LOG(("[ParentSide]   painted layer"));
--- a/gfx/layers/ipc/LayersMessages.ipdlh
+++ b/gfx/layers/ipc/LayersMessages.ipdlh
@@ -220,16 +220,17 @@ struct CommonLayerAttributes {
   LayerRect stickyScrollRangeOuter;
   LayerRect stickyScrollRangeInner;
   uint64_t scrollbarTargetContainerId;
   uint32_t scrollbarDirection;
   float scrollbarThumbRatio;
   int8_t mixBlendMode;
   bool forceIsolatedGroup;
   nullable PLayer maskLayer;
+  PLayer[] ancestorMaskLayers;
   // Animated colors will only honored for ColorLayers.
   Animation[] animations;
   nsIntRegion invalidRegion;
   FrameMetrics[] metrics;
   string contentDescription;
 };
 
 struct PaintedLayerAttributes {
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -602,16 +602,20 @@ ShadowLayerForwarder::EndTransaction(Inf
       common.maskLayerChild() = Shadow(maskLayer->AsShadowableLayer());
     } else {
       common.maskLayerChild() = nullptr;
     }
     common.maskLayerParent() = nullptr;
     common.animations() = mutant->GetAnimations();
     common.invalidRegion() = mutant->GetInvalidRegion();
     common.metrics() = mutant->GetAllFrameMetrics();
+    for (size_t i = 0; i < mutant->GetAncestorMaskLayerCount(); i++) {
+      auto layer = Shadow(mutant->GetAncestorMaskLayerAt(i)->AsShadowableLayer());
+      common.ancestorMaskLayersChild().AppendElement(layer);
+    }
     attrs.specific() = null_t();
     mutant->FillSpecificAttributes(attrs.specific());
 
     MOZ_LAYERS_LOG(("[LayersForwarder] OpSetLayerAttributes(%p)\n", mutant));
 
     mTxn->AddEdit(OpSetLayerAttributes(nullptr, Shadow(shadow), attrs));
   }
 
--- a/gfx/layers/opengl/CompositingRenderTargetOGL.cpp
+++ b/gfx/layers/opengl/CompositingRenderTargetOGL.cpp
@@ -32,17 +32,18 @@ CompositingRenderTargetOGL::BindTexture(
 
 void
 CompositingRenderTargetOGL::BindRenderTarget()
 {
   if (mInitParams.mStatus != InitParams::INITIALIZED) {
     InitializeImpl();
   } else {
     MOZ_ASSERT(mInitParams.mStatus == InitParams::INITIALIZED);
-    mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mFBO);
+    GLuint fbo = mFBO == 0 ? mGL->GetDefaultFramebuffer() : mFBO;
+    mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, fbo);
     GLenum result = mGL->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
     if (result != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
       // The main framebuffer (0) of non-offscreen contexts
       // might be backed by a EGLSurface that needs to be renewed.
       if (mFBO == 0 && !mGL->IsOffscreen()) {
         mGL->RenewSurface();
         result = mGL->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
       }
@@ -78,17 +79,19 @@ CompositingRenderTargetOGL::Dump(Composi
 }
 #endif
 
 void
 CompositingRenderTargetOGL::InitializeImpl()
 {
   MOZ_ASSERT(mInitParams.mStatus == InitParams::READY);
 
-  mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mFBO);
+  //TODO: call mGL->GetBackbufferFB(), use that
+  GLuint fbo = mFBO == 0 ? mGL->GetDefaultFramebuffer() : mFBO;
+  mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, fbo);
   mGL->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
                               LOCAL_GL_COLOR_ATTACHMENT0,
                               mInitParams.mFBOTextureTarget,
                               mTextureHandle,
                               0);
 
   // Making this call to fCheckFramebufferStatus prevents a crash on
   // PowerVR. See bug 695246.
--- a/gfx/skia/generate_mozbuild.py
+++ b/gfx/skia/generate_mozbuild.py
@@ -23,17 +23,18 @@ header = """
 #
 """
 
 footer = """
 
 # can we find a better way of dealing with asm sources?
 
 # left out of UNIFIED_SOURCES for now; that's not C++ anyway, nothing else to unify it with
-if not CONFIG['INTEL_ARCHITECTURE'] and CONFIG['CPU_ARCH'] == 'arm' and CONFIG['GNU_CC']:
+#XXX: doesn't build with Apple's assembler
+if not CONFIG['INTEL_ARCHITECTURE'] and CONFIG['CPU_ARCH'] == 'arm' and CONFIG['GNU_CC'] and CONFIG['OS_TARGET'] != 'Darwin':
     SOURCES += [
         'trunk/src/opts/memset.arm.S',
     ]
     if CONFIG['BUILD_ARM_NEON']:
         SOURCES += [
             'trunk/src/opts/memset16_neon.S',
             'trunk/src/opts/memset32_neon.S',
         ]
--- a/gfx/skia/moz.build
+++ b/gfx/skia/moz.build
@@ -855,17 +855,18 @@ else:
         'trunk/src/opts/SkUtils_opts_none.cpp',
         'trunk/src/opts/SkXfermode_opts_none.cpp',
     ]
 
 
 # can we find a better way of dealing with asm sources?
 
 # left out of UNIFIED_SOURCES for now; that's not C++ anyway, nothing else to unify it with
-if not CONFIG['INTEL_ARCHITECTURE'] and CONFIG['CPU_ARCH'] == 'arm' and CONFIG['GNU_CC']:
+#XXX: doesn't build with Apple's assembler
+if not CONFIG['INTEL_ARCHITECTURE'] and CONFIG['CPU_ARCH'] == 'arm' and CONFIG['GNU_CC'] and CONFIG['OS_TARGET'] != 'Darwin':
     SOURCES += [
         'trunk/src/opts/memset.arm.S',
     ]
     if CONFIG['BUILD_ARM_NEON']:
         SOURCES += [
             'trunk/src/opts/memset16_neon.S',
             'trunk/src/opts/memset32_neon.S',
         ]
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -2470,17 +2470,17 @@ gfxPlatform::GetApzSupportInfo(mozilla::
   if (SupportsApzTouchInput()) {
     aObj.DefineProperty("ApzTouchInput", 1);
   }
 }
 
 /*static*/ bool
 gfxPlatform::AsyncPanZoomEnabled()
 {
-#if !defined(MOZ_B2G) && !defined(MOZ_WIDGET_ANDROID)
+#if !defined(MOZ_B2G) && !defined(MOZ_WIDGET_ANDROID) && !defined(MOZ_WIDGET_UIKIT)
   // For XUL applications (everything but B2G on mobile and desktop, and
   // Firefox on Android) we only want to use APZ when E10S is enabled. If
   // we ever get input events off the main thread we can consider relaxing
   // this requirement.
   if (!BrowserTabsRemoteAutostart()) {
     return false;
   }
 #endif
--- a/gfx/thebes/gfxTextRun.cpp
+++ b/gfx/thebes/gfxTextRun.cpp
@@ -1703,55 +1703,52 @@ gfxFontGroup::BuildFontList()
 
 void
 gfxFontGroup::FindPlatformFont(const nsAString& aName,
                                bool aUseFontSet,
                                void *aClosure)
 {
     bool needsBold;
     gfxFontFamily *family = nullptr;
-    gfxFontEntry *fe = nullptr;
 
     if (aUseFontSet) {
         // First, look up in the user font set...
         // If the fontSet matches the family, we must not look for a platform
         // font of the same name, even if we fail to actually get a fontEntry
         // here; we'll fall back to the next name in the CSS font-family list.
         if (mUserFontSet) {
             // Add userfonts to the fontlist whether already loaded
             // or not. Loading is initiated during font matching.
             family = mUserFontSet->LookupFamily(aName);
-            if (family) {
-                nsAutoTArray<gfxFontEntry*,4> userfonts;
-                family->FindAllFontsForStyle(mStyle, userfonts, needsBold);
-                // add these to the fontlist
-                uint32_t count = userfonts.Length();
-                for (uint32_t i = 0; i < count; i++) {
-                    fe = userfonts[i];
-                    FamilyFace ff(family, fe, needsBold);
-                    ff.CheckState(mSkipDrawing);
-                    mFonts.AppendElement(ff);
-                }
-            }
         }
     }
 
     // Not known in the user font set ==> check system fonts
     if (!family) {
         gfxPlatformFontList *fontList = gfxPlatformFontList::PlatformFontList();
         family = fontList->FindFamily(aName, mStyle.language, mStyle.systemFont);
-        if (family) {
-            fe = family->FindFontForStyle(mStyle, needsBold);
+    }
+
+    // if family found, do style matching and add all font entries to mFonts
+    if (family) {
+        nsAutoTArray<gfxFontEntry*,4> fontEntryList;
+        family->FindAllFontsForStyle(mStyle, fontEntryList, needsBold);
+        // add these to the fontlist
+        uint32_t n = fontEntryList.Length();
+        for (uint32_t i = 0; i < n; i++) {
+            gfxFontEntry* fe = fontEntryList[i];
+            if (!HasFont(fe)) {
+                FamilyFace ff(family, fe, needsBold);
+                if (fe->mIsUserFontContainer) {
+                    ff.CheckState(mSkipDrawing);
+                }
+                mFonts.AppendElement(ff);
+            }
         }
     }
-
-    // add to the font group, unless it's already there
-    if (fe && !HasFont(fe)) {
-        mFonts.AppendElement(FamilyFace(family, fe, needsBold));
-    }
 }
 
 bool
 gfxFontGroup::HasFont(const gfxFontEntry *aFontEntry)
 {
     uint32_t count = mFonts.Length();
     for (uint32_t i = 0; i < count; ++i) {
         if (mFonts[i].FontEntry() == aFontEntry) {
--- a/gfx/ycbcr/ycbcr_to_rgb565.cpp
+++ b/gfx/ycbcr/ycbcr_to_rgb565.cpp
@@ -60,17 +60,18 @@ struct yuv2rgb565_row_scale_nearest_ctx{
 typedef void (*yuv2rgb565_row_scale_bilinear_func)(
  const yuv2rgb565_row_scale_bilinear_ctx *ctx, int dither);
 
 typedef void (*yuv2rgb565_row_scale_nearest_func)(
  const yuv2rgb565_row_scale_nearest_ctx *ctx, int dither);
 
 
 
-# if defined(MOZILLA_MAY_SUPPORT_NEON)
+//TODO: fix NEON asm for iOS
+# if defined(MOZILLA_MAY_SUPPORT_NEON) && !defined(__APPLE__)
 
 extern "C" void ScaleYCbCr42xToRGB565_BilinearY_Row_NEON(
  const yuv2rgb565_row_scale_bilinear_ctx *ctx, int dither);
 
 void __attribute((noinline)) yuv42x_to_rgb565_row_neon(uint16 *dst,
                                                        const uint8 *y,
                                                        const uint8 *u,
                                                        const uint8 *v,
@@ -470,17 +471,18 @@ NS_GFX_(void) ScaleYCbCrToRGB565(const u
      && abs(source_dx_q16) <= uvxscale_max
      && uvyscale_min <= abs(source_dy_q16)
      && abs(source_dy_q16) <= uvyscale_max) {
       /*Add the rounding offsets now.*/
       source_uv_xoffs_q16 += 1<<(15+x_shift);
       source_uv_yoffs_q16 += 1<<(15+y_shift);
       if (yuv_type != YV24) {
         scale_row =
-#  if defined(MOZILLA_MAY_SUPPORT_NEON)
+//TODO: fix NEON asm for iOS
+#  if defined(MOZILLA_MAY_SUPPORT_NEON) && !defined(__APPLE__)
          supports_neon() ? ScaleYCbCr42xToRGB565_BilinearY_Row_NEON :
 #  endif
          ScaleYCbCr42xToRGB565_BilinearY_Row_C;
       }
       else
         scale_row = ScaleYCbCr444ToRGB565_BilinearY_Row_C;
     }
     else {
@@ -609,17 +611,18 @@ NS_GFX_(void) ConvertYCbCrToRGB565(const
                                    int uv_pitch,
                                    int rgb_pitch,
                                    YUVType yuv_type)
 {
   int x_shift;
   int y_shift;
   x_shift = yuv_type != YV24;
   y_shift = yuv_type == YV12;
-#  ifdef MOZILLA_MAY_SUPPORT_NEON
+//TODO: fix NEON asm for iOS
+#  if defined(MOZILLA_MAY_SUPPORT_NEON) && !defined(__APPLE__)
   if (yuv_type != YV24 && supports_neon())
   {
     for (int i = 0; i < pic_height; i++) {
       int yoffs;
       int uvoffs;
       yoffs = y_pitch * (pic_y+i) + pic_x;
       uvoffs = uv_pitch * ((pic_y+i)>>y_shift) + (pic_x>>x_shift);
       yuv42x_to_rgb565_row_neon((uint16*)(rgb_buf + rgb_pitch * i),
--- a/image/SurfaceCache.cpp
+++ b/image/SurfaceCache.cpp
@@ -1018,16 +1018,21 @@ SurfaceCache::Insert(imgFrame*         a
                      const ImageKey    aImageKey,
                      const SurfaceKey& aSurfaceKey,
                      Lifetime          aLifetime)
 {
   if (!sInstance) {
     return InsertOutcome::FAILURE;
   }
 
+  // Refuse null surfaces.
+  if (!aSurface) {
+    MOZ_CRASH("Don't pass null surfaces to SurfaceCache::Insert");
+  }
+
   MutexAutoLock lock(sInstance->GetMutex());
   Cost cost = ComputeCost(aSurface->GetSize(), aSurface->GetBytesPerPixel());
   return sInstance->Insert(aSurface, cost, aImageKey, aSurfaceKey, aLifetime);
 }
 
 /* static */ bool
 SurfaceCache::CanHold(const IntSize& aSize, uint32_t aBytesPerPixel /* = 4 */)
 {
--- a/ipc/glue/MessageChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -156,17 +156,17 @@ public:
         MOZ_ASSERT_IF(!mMessageName, mMoved);
 
         if (mMessageName)
             free(const_cast<char*>(mMessageName));
     }
 
     InterruptFrame& operator=(InterruptFrame&& aOther)
     {
-        MOZ_ASSERT(&aOther != this);
+        MOZ_RELEASE_ASSERT(&aOther != this);
         this->~InterruptFrame();
         new (this) InterruptFrame(mozilla::Move(aOther));
         return *this;
     }
 
     bool IsInterruptIncall() const
     {
         return INTR_SEMS == mMesageSemantics && IN_MESSAGE == mDirection;
--- a/js/public/Conversions.h
+++ b/js/public/Conversions.h
@@ -344,17 +344,19 @@ ToIntWidth(double d)
 }
 
 } // namespace detail
 
 /* ES5 9.5 ToInt32 (specialized for doubles). */
 inline int32_t
 ToInt32(double d)
 {
-#if defined (__arm__) && defined (__GNUC__)
+    // clang crashes compiling this when targeting arm-darwin:
+    // https://llvm.org/bugs/show_bug.cgi?id=22974
+#if defined (__arm__) && defined (__GNUC__) && !defined(__APPLE__)
     int32_t i;
     uint32_t    tmp0;
     uint32_t    tmp1;
     uint32_t    tmp2;
     asm (
     // We use a pure integer solution here. In the 'softfp' ABI, the argument
     // will start in r0 and r1, and VFP can't do all of the necessary ECMA
     // conversions by itself so some integer code will be required anyway. A
--- a/js/public/Id.h
+++ b/js/public/Id.h
@@ -166,19 +166,17 @@ extern JS_PUBLIC_DATA(const jsid) JSID_E
 extern JS_PUBLIC_DATA(const JS::HandleId) JSID_VOIDHANDLE;
 extern JS_PUBLIC_DATA(const JS::HandleId) JSID_EMPTYHANDLE;
 
 namespace js {
 
 template <> struct GCMethods<jsid>
 {
     static jsid initial() { return JSID_VOID; }
-    static bool needsPostBarrier(jsid id) { return false; }
-    static void postBarrier(jsid* idp) {}
-    static void relocate(jsid* idp) {}
+    static void postBarrier(jsid* idp, jsid prev, jsid next) {}
 };
 
 // If the jsid is a GC pointer type, convert to that type and call |f| with
 // the pointer. If the jsid is not a GC type, calls F::defaultValue.
 template <typename F, typename... Args>
 auto
 DispatchIdTyped(F f, jsid& id, Args&&... args)
   -> decltype(f(static_cast<JSString*>(nullptr), mozilla::Forward<Args>(args)...))
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -169,18 +169,17 @@ struct PersistentRootedMarker;
 namespace JS {
 
 template <typename T> class Rooted;
 template <typename T> class PersistentRooted;
 
 /* This is exposing internal state of the GC for inlining purposes. */
 JS_FRIEND_API(bool) isGCEnabled();
 
-JS_FRIEND_API(void) HeapObjectPostBarrier(JSObject** objp);
-JS_FRIEND_API(void) HeapObjectRelocate(JSObject** objp);
+JS_FRIEND_API(void) HeapObjectPostBarrier(JSObject** objp, JSObject* prev, JSObject* next);
 
 #ifdef JS_DEBUG
 /*
  * For generational GC, assert that an object is in the tenured generation as
  * opposed to being in the nursery.
  */
 extern JS_FRIEND_API(void)
 AssertGCThingMustBeTenured(JSObject* obj);
@@ -227,18 +226,17 @@ class Heap : public js::HeapBase<T>
      * For Heap, move semantics are equivalent to copy semantics. In C++, a
      * copy constructor taking const-ref is the way to get a single function
      * that will be used for both lvalue and rvalue copies, so we can simply
      * omit the rvalue variant.
      */
     explicit Heap(const Heap<T>& p) { init(p.ptr); }
 
     ~Heap() {
-        if (js::GCMethods<T>::needsPostBarrier(ptr))
-            relocate();
+        post(ptr, js::GCMethods<T>::initial());
     }
 
     DECLARE_POINTER_CONSTREF_OPS(T);
     DECLARE_POINTER_ASSIGN_OPS(Heap, T);
     DECLARE_NONPOINTER_ACCESSOR_METHODS(ptr);
 
     T* unsafeGet() { return &ptr; }
 
@@ -252,39 +250,27 @@ class Heap : public js::HeapBase<T>
 
     bool isSetToCrashOnTouch() {
         return ptr == crashOnTouchPointer;
     }
 
   private:
     void init(T newPtr) {
         ptr = newPtr;
-        if (js::GCMethods<T>::needsPostBarrier(ptr))
-            post();
+        post(js::GCMethods<T>::initial(), ptr);
     }
 
     void set(T newPtr) {
-        if (js::GCMethods<T>::needsPostBarrier(newPtr)) {
-            ptr = newPtr;
-            post();
-        } else if (js::GCMethods<T>::needsPostBarrier(ptr)) {
-            relocate();  /* Called before overwriting ptr. */
-            ptr = newPtr;
-        } else {
-            ptr = newPtr;
-        }
+        T tmp = ptr;
+        ptr = newPtr;
+        post(tmp, ptr);
     }
 
-    void post() {
-        MOZ_ASSERT(js::GCMethods<T>::needsPostBarrier(ptr));
-        js::GCMethods<T>::postBarrier(&ptr);
-    }
-
-    void relocate() {
-        js::GCMethods<T>::relocate(&ptr);
+    void post(const T& prev, const T& next) {
+        js::GCMethods<T>::postBarrier(&ptr, prev, next);
     }
 
     enum {
         crashOnTouchPointer = 1
     };
 
     T ptr;
 };
@@ -599,57 +585,46 @@ struct RootKind<T*>
 {
     static ThingRootKind rootKind() { return T::rootKind(); }
 };
 
 template <typename T>
 struct GCMethods<T*>
 {
     static T* initial() { return nullptr; }
-    static bool needsPostBarrier(T* v) { return false; }
-    static void postBarrier(T** vp) {
-        if (vp)
-            JS::AssertGCThingIsNotAnObjectSubclass(reinterpret_cast<js::gc::Cell*>(vp));
+    static void postBarrier(T** vp, T* prev, T* next) {
+        if (next)
+            JS::AssertGCThingIsNotAnObjectSubclass(reinterpret_cast<js::gc::Cell*>(next));
     }
     static void relocate(T** vp) {}
 };
 
 template <>
 struct GCMethods<JSObject*>
 {
     static JSObject* initial() { return nullptr; }
     static gc::Cell* asGCThingOrNull(JSObject* v) {
         if (!v)
             return nullptr;
         MOZ_ASSERT(uintptr_t(v) > 32);
         return reinterpret_cast<gc::Cell*>(v);
     }
-    static bool needsPostBarrier(JSObject* v) {
-        return v != nullptr && gc::IsInsideNursery(reinterpret_cast<gc::Cell*>(v));
-    }
-    static void postBarrier(JSObject** vp) {
-        JS::HeapObjectPostBarrier(vp);
-    }
-    static void relocate(JSObject** vp) {
-        JS::HeapObjectRelocate(vp);
+    static void postBarrier(JSObject** vp, JSObject* prev, JSObject* next) {
+        JS::HeapObjectPostBarrier(vp, prev, next);
     }
 };
 
 template <>
 struct GCMethods<JSFunction*>
 {
     static JSFunction* initial() { return nullptr; }
-    static bool needsPostBarrier(JSFunction* v) {
-        return v != nullptr && gc::IsInsideNursery(reinterpret_cast<gc::Cell*>(v));
-    }
-    static void postBarrier(JSFunction** vp) {
-        JS::HeapObjectPostBarrier(reinterpret_cast<JSObject**>(vp));
-    }
-    static void relocate(JSFunction** vp) {
-        JS::HeapObjectRelocate(reinterpret_cast<JSObject**>(vp));
+    static void postBarrier(JSFunction** vp, JSFunction* prev, JSFunction* next) {
+        JS::HeapObjectPostBarrier(reinterpret_cast<JSObject**>(vp),
+                                  reinterpret_cast<JSObject*>(prev),
+                                  reinterpret_cast<JSObject*>(next));
     }
 };
 
 } /* namespace js */
 
 namespace JS {
 
 /*
--- a/js/public/Utility.h
+++ b/js/public/Utility.h
@@ -77,27 +77,35 @@ extern JS_PUBLIC_DATA(bool) OOM_failAlwa
 static MOZ_NEVER_INLINE void js_failedAllocBreakpoint() { asm(""); }
 #define JS_OOM_CALL_BP_FUNC() js_failedAllocBreakpoint()
 #else
 #define JS_OOM_CALL_BP_FUNC() do {} while(0)
 #endif
 
 namespace js {
 namespace oom {
-static inline bool ShouldFailWithOOM()
+
+static inline bool
+IsSimulatedOOMAllocation()
+{
+    return OOM_counter == OOM_maxAllocations ||
+           (OOM_counter > OOM_maxAllocations && OOM_failAlways);
+}
+
+static inline bool
+ShouldFailWithOOM()
 {
     OOM_counter++;
-    if (OOM_counter == OOM_maxAllocations ||
-        (OOM_counter > OOM_maxAllocations && OOM_failAlways))
-    {
+    if (IsSimulatedOOMAllocation()) {
         JS_OOM_CALL_BP_FUNC();
         return true;
     }
     return false;
 }
+
 } /* namespace oom */
 } /* namespace js */
 
 #  define JS_OOM_POSSIBLY_FAIL()                                              \
     do {                                                                      \
         if (js::oom::ShouldFailWithOOM())                                     \
             return nullptr;                                                   \
     } while (0)
@@ -109,16 +117,17 @@ static inline bool ShouldFailWithOOM()
     } while (0)
 
 # else
 
 #  define JS_OOM_POSSIBLY_FAIL() do {} while(0)
 #  define JS_OOM_POSSIBLY_FAIL_BOOL() do {} while(0)
 namespace js {
 namespace oom {
+static inline bool IsSimulatedOOMAllocation() { return false; }
 static inline bool ShouldFailWithOOM() { return false; }
 } /* namespace oom */
 } /* namespace js */
 
 # endif /* DEBUG || JS_OOM_BREAKPOINT */
 
 static inline void* js_malloc(size_t bytes)
 {
--- a/js/public/Value.h
+++ b/js/public/Value.h
@@ -1622,38 +1622,35 @@ SameType(const Value& lhs, const Value& 
     return JSVAL_SAME_TYPE_IMPL(lhs.data, rhs.data);
 }
 
 } // namespace JS
 
 /************************************************************************/
 
 namespace JS {
-JS_PUBLIC_API(void) HeapValuePostBarrier(Value* valuep);
-JS_PUBLIC_API(void) HeapValueRelocate(Value* valuep);
+JS_PUBLIC_API(void) HeapValuePostBarrier(Value* valuep, const Value& prev, const Value& next);
 }
 
 namespace js {
 
 template <> struct GCMethods<const JS::Value>
 {
     static JS::Value initial() { return JS::UndefinedValue(); }
 };
 
 template <> struct GCMethods<JS::Value>
 {
     static JS::Value initial() { return JS::UndefinedValue(); }
     static gc::Cell* asGCThingOrNull(const JS::Value& v) {
         return v.isMarkable() ? v.toGCThing() : nullptr;
     }
-    static bool needsPostBarrier(const JS::Value& v) {
-        return v.isObject() && gc::IsInsideNursery(reinterpret_cast<gc::Cell*>(&v.toObject()));
+    static void postBarrier(JS::Value* v, const JS::Value& prev, const JS::Value& next) {
+        JS::HeapValuePostBarrier(v, prev, next);
     }
-    static void postBarrier(JS::Value* v) { JS::HeapValuePostBarrier(v); }
-    static void relocate(JS::Value* v) { JS::HeapValueRelocate(v); }
 };
 
 template <class Outer> class MutableValueOperations;
 
 /*
  * A class designed for CRTP use in implementing the non-mutating parts of the
  * Value interface in Value-like classes.  Outer must be a class inheriting
  * ValueOperations<Outer> with a visible extract() method returning the
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -470,18 +470,21 @@ intl_availableLocales(JSContext* cx, Cou
 
 /**
  * Returns the object holding the internal properties for obj.
  */
 static bool
 GetInternals(JSContext* cx, HandleObject obj, MutableHandleObject internals)
 {
     RootedValue getInternalsValue(cx);
-    if (!GlobalObject::getIntrinsicValue(cx, cx->global(), cx->names().getInternals, &getInternalsValue))
+    if (!GlobalObject::getIntrinsicValue(cx, cx->global(), cx->names().getInternals,
+                                         &getInternalsValue))
+    {
         return false;
+    }
     MOZ_ASSERT(getInternalsValue.isObject());
     MOZ_ASSERT(getInternalsValue.toObject().is<JSFunction>());
 
     InvokeArgs args(cx);
     if (!args.init(1))
         return false;
 
     args.setCallee(getInternalsValue);
@@ -954,17 +957,18 @@ NewUCollator(JSContext* cx, HandleObject
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
         return nullptr;
     }
 
     return coll;
 }
 
 static bool
-intl_CompareStrings(JSContext* cx, UCollator* coll, HandleString str1, HandleString str2, MutableHandleValue result)
+intl_CompareStrings(JSContext* cx, UCollator* coll, HandleString str1, HandleString str2,
+                    MutableHandleValue result)
 {
     MOZ_ASSERT(str1);
     MOZ_ASSERT(str2);
 
     if (str1 == str2) {
         result.setInt32(0);
         return true;
     }
@@ -1005,17 +1009,18 @@ js::intl_CompareStrings(JSContext* cx, u
 
     RootedObject collator(cx, &args[0].toObject());
 
     // Obtain a UCollator object, cached if possible.
     // XXX Does this handle Collator instances from other globals correctly?
     bool isCollatorInstance = collator->getClass() == &CollatorClass;
     UCollator* coll;
     if (isCollatorInstance) {
-        coll = static_cast<UCollator*>(collator->as<NativeObject>().getReservedSlot(UCOLLATOR_SLOT).toPrivate());
+        void* priv = collator->as<NativeObject>().getReservedSlot(UCOLLATOR_SLOT).toPrivate();
+        coll = static_cast<UCollator*>(priv);
         if (!coll) {
             coll = NewUCollator(cx, collator);
             if (!coll)
                 return false;
             collator->as<NativeObject>().setReservedSlot(UCOLLATOR_SLOT, PrivateValue(coll));
         }
     } else {
         // There's no good place to cache the ICU collator for an object
@@ -1173,17 +1178,18 @@ numberFormat_finalize(FreeOp* fop, JSObj
         if (UNumberFormat* nf = static_cast<UNumberFormat*>(slot.toPrivate()))
             unum_close(nf);
     }
 }
 
 static JSObject*
 InitNumberFormatClass(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global)
 {
-    RootedFunction ctor(cx, global->createConstructor(cx, &NumberFormat, cx->names().NumberFormat, 0));
+    RootedFunction ctor(cx);
+    ctor = global->createConstructor(cx, &NumberFormat, cx->names().NumberFormat, 0);
     if (!ctor)
         return nullptr;
 
     RootedObject proto(cx, global->as<GlobalObject>().getOrCreateNumberFormatPrototype(cx));
     if (!proto)
         return nullptr;
     if (!LinkConstructorAndPrototype(cx, ctor, proto))
         return nullptr;
@@ -1197,32 +1203,38 @@ InitNumberFormatClass(JSContext* cx, Han
         return nullptr;
 
     /*
      * Install the getter for NumberFormat.prototype.format, which returns a
      * bound formatting function for the specified NumberFormat object (suitable
      * for passing to methods like Array.prototype.map).
      */
     RootedValue getter(cx);
-    if (!GlobalObject::getIntrinsicValue(cx, cx->global(), cx->names().NumberFormatFormatGet, &getter))
+    if (!GlobalObject::getIntrinsicValue(cx, cx->global(), cx->names().NumberFormatFormatGet,
+                                         &getter))
+    {
         return nullptr;
+    }
     if (!DefineProperty(cx, proto, cx->names().format, UndefinedHandleValue,
                         JS_DATA_TO_FUNC_PTR(JSGetterOp, &getter.toObject()),
                         nullptr, JSPROP_GETTER | JSPROP_SHARED))
     {
         return nullptr;
     }
 
     RootedValue options(cx);
     if (!CreateDefaultOptions(cx, &options))
         return nullptr;
 
     // 11.2.1 and 11.3
-    if (!IntlInitialize(cx, proto, cx->names().InitializeNumberFormat, UndefinedHandleValue, options))
+    if (!IntlInitialize(cx, proto, cx->names().InitializeNumberFormat, UndefinedHandleValue,
+                        options))
+    {
         return nullptr;
+    }
 
     // 8.1
     RootedValue ctorValue(cx, ObjectValue(*ctor));
     if (!DefineProperty(cx, Intl, cx->names().NumberFormat, ctorValue, nullptr, nullptr, 0))
         return nullptr;
 
     return ctor;
 }
@@ -1321,17 +1333,18 @@ NewUNumberFormat(JSContext* cx, HandleOb
     JSAutoByteString style(cx, value.toString());
     if (!style)
         return nullptr;
 
     if (equal(style, "currency")) {
         if (!GetProperty(cx, internals, internals, cx->names().currency, &value))
             return nullptr;
         currency = value.toString();
-        MOZ_ASSERT(currency->length() == 3, "IsWellFormedCurrencyCode permits only length-3 strings");
+        MOZ_ASSERT(currency->length() == 3,
+                   "IsWellFormedCurrencyCode permits only length-3 strings");
         if (!currency->ensureFlat(cx) || !stableChars.initTwoByte(cx, currency))
             return nullptr;
         // uCurrency remains owned by stableChars.
         uCurrency = Char16ToUChar(stableChars.twoByteRange().start().get());
         if (!uCurrency)
             return nullptr;
 
         if (!GetProperty(cx, internals, internals, cx->names().currencyDisplay, &value))
@@ -1467,17 +1480,19 @@ js::intl_FormatNumber(JSContext* cx, uns
     MOZ_ASSERT(args[1].isNumber());
 
     RootedObject numberFormat(cx, &args[0].toObject());
 
     // Obtain a UNumberFormat object, cached if possible.
     bool isNumberFormatInstance = numberFormat->getClass() == &NumberFormatClass;
     UNumberFormat* nf;
     if (isNumberFormatInstance) {
-        nf = static_cast<UNumberFormat*>(numberFormat->as<NativeObject>().getReservedSlot(UNUMBER_FORMAT_SLOT).toPrivate());
+        void* priv =
+            numberFormat->as<NativeObject>().getReservedSlot(UNUMBER_FORMAT_SLOT).toPrivate();
+        nf = static_cast<UNumberFormat*>(priv);
         if (!nf) {
             nf = NewUNumberFormat(cx, numberFormat);
             if (!nf)
                 return false;
             numberFormat->as<NativeObject>().setReservedSlot(UNUMBER_FORMAT_SLOT, PrivateValue(nf));
         }
     } else {
         // There's no good place to cache the ICU number format for an object
@@ -1633,17 +1648,18 @@ dateTimeFormat_finalize(FreeOp* fop, JSO
         if (UDateFormat* df = static_cast<UDateFormat*>(slot.toPrivate()))
             udat_close(df);
     }
 }
 
 static JSObject*
 InitDateTimeFormatClass(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global)
 {
-    RootedFunction ctor(cx, global->createConstructor(cx, &DateTimeFormat, cx->names().DateTimeFormat, 0));
+    RootedFunction ctor(cx);
+    ctor = global->createConstructor(cx, &DateTimeFormat, cx->names().DateTimeFormat, 0);
     if (!ctor)
         return nullptr;
 
     RootedObject proto(cx, global->as<GlobalObject>().getOrCreateDateTimeFormatPrototype(cx));
     if (!proto)
         return nullptr;
     if (!LinkConstructorAndPrototype(cx, ctor, proto))
         return nullptr;
@@ -1657,32 +1673,38 @@ InitDateTimeFormatClass(JSContext* cx, H
         return nullptr;
 
     /*
      * Install the getter for DateTimeFormat.prototype.format, which returns a
      * bound formatting function for the specified DateTimeFormat object
      * (suitable for passing to methods like Array.prototype.map).
      */
     RootedValue getter(cx);
-    if (!GlobalObject::getIntrinsicValue(cx, cx->global(), cx->names().DateTimeFormatFormatGet, &getter))
+    if (!GlobalObject::getIntrinsicValue(cx, cx->global(), cx->names().DateTimeFormatFormatGet,
+                                         &getter))
+    {
         return nullptr;
+    }
     if (!DefineProperty(cx, proto, cx->names().format, UndefinedHandleValue,
                         JS_DATA_TO_FUNC_PTR(JSGetterOp, &getter.toObject()),
                         nullptr, JSPROP_GETTER | JSPROP_SHARED))
     {
         return nullptr;
     }
 
     RootedValue options(cx);
     if (!CreateDefaultOptions(cx, &options))
         return nullptr;
 
     // 12.2.1 and 12.3
-    if (!IntlInitialize(cx, proto, cx->names().InitializeDateTimeFormat, UndefinedHandleValue, options))
+    if (!IntlInitialize(cx, proto, cx->names().InitializeDateTimeFormat, UndefinedHandleValue,
+                        options))
+    {
         return nullptr;
+    }
 
     // 8.1
     RootedValue ctorValue(cx, ObjectValue(*ctor));
     if (!DefineProperty(cx, Intl, cx->names().DateTimeFormat, ctorValue, nullptr, nullptr, 0))
         return nullptr;
 
     return ctor;
 }
@@ -1971,17 +1993,19 @@ js::intl_FormatDateTime(JSContext* cx, u
     MOZ_ASSERT(args[1].isNumber());
 
     RootedObject dateTimeFormat(cx, &args[0].toObject());
 
     // Obtain a UDateFormat object, cached if possible.
     bool isDateTimeFormatInstance = dateTimeFormat->getClass() == &DateTimeFormatClass;
     UDateFormat* df;
     if (isDateTimeFormatInstance) {
-        df = static_cast<UDateFormat*>(dateTimeFormat->as<NativeObject>().getReservedSlot(UDATE_FORMAT_SLOT).toPrivate());
+        void* priv =
+            dateTimeFormat->as<NativeObject>().getReservedSlot(UDATE_FORMAT_SLOT).toPrivate();
+        df = static_cast<UDateFormat*>(priv);
         if (!df) {
             df = NewUDateFormat(cx, dateTimeFormat);
             if (!df)
                 return false;
             dateTimeFormat->as<NativeObject>().setReservedSlot(UDATE_FORMAT_SLOT, PrivateValue(df));
         }
     } else {
         // There's no good place to cache the ICU date-time format for an object
--- a/js/src/builtin/Intl.js
+++ b/js/src/builtin/Intl.js
@@ -250,21 +250,25 @@ function getDuplicateVariantRE() {
         // before extension and privateuse components);
         "(?:" + alphanum + "{2,8}-)*" +
         // and the same variant again
         "\\1" +
         // ...but not followed by any characters that would turn it into a
         // different subtag.
         "(?!" + alphanum + ")";
 
-    // Language tags are case insensitive (RFC 5646 section 2.1.1), but for
-    // this regular expression that's covered by having its character classes
-    // list both upper- and lower-case characters.
+    // Language tags are case insensitive (RFC 5646 section 2.1.1).  Using
+    // character classes covering both upper- and lower-case characters nearly
+    // addresses this -- but for the possibility of variant repetition with
+    // differing case, e.g. "en-variant-Variant".  Use a case-insensitive
+    // regular expression to address this.  (Note that there's no worry about
+    // case transformation accepting invalid characters here: users have
+    // already verified the string is alphanumeric Latin plus "-".)
     return (internalIntlRegExps.duplicateVariantRE =
-            regexp_construct_no_statics(duplicateVariant));
+            regexp_construct_no_statics(duplicateVariant, "i"));
 }
 
 
 function getDuplicateSingletonRE() {
     if (internalIntlRegExps.duplicateSingletonRE)
         return internalIntlRegExps.duplicateSingletonRE;
 
     // RFC 5234 section B.1
@@ -292,21 +296,25 @@ function getDuplicateSingletonRE() {
         // then zero or more subtags;
         "(?:" + alphanum + "+-)*" +
         // and the same singleton again
         "\\1" +
         // ...but not followed by any characters that would turn it into a
         // different subtag.
         "(?!" + alphanum + ")";
 
-    // Language tags are case insensitive (RFC 5646 section 2.1.1), but for
-    // this regular expression that's covered by having its character classes
-    // list both upper- and lower-case characters.
+    // Language tags are case insensitive (RFC 5646 section 2.1.1).  Using
+    // character classes covering both upper- and lower-case characters nearly
+    // addresses this -- but for the possibility of singleton repetition with
+    // differing case, e.g. "en-u-foo-U-foo".  Use a case-insensitive regular
+    // expression to address this.  (Note that there's no worry about case
+    // transformation accepting invalid characters here: users have already
+    // verified the string is alphanumeric Latin plus "-".)
     return (internalIntlRegExps.duplicateSingletonRE =
-            regexp_construct_no_statics(duplicateSingleton));
+            regexp_construct_no_statics(duplicateSingleton, "i"));
 }
 
 
 /**
  * Verifies that the given string is a well-formed BCP 47 language tag
  * with no duplicate variant or singleton subtags.
  *
  * Spec: ECMAScript Internationalization API Specification, 6.2.2.
@@ -1890,21 +1898,21 @@ function InitializeNumberFormat(numberFo
  *
  * Spec: ISO 4217 Currency and Funds Code List.
  * http://www.currency-iso.org/en/home/tables/table-a1.html
  */
 var currencyDigits = {
     BHD: 3,
     BIF: 0,
     BYR: 0,
-    CLF: 0,
+    CLF: 4,
     CLP: 0,
     DJF: 0,
+    GNF: 0,
     IQD: 3,
-    GNF: 0,
     ISK: 0,
     JOD: 3,
     JPY: 0,
     KMF: 0,
     KRW: 0,
     KWD: 3,
     LYD: 3,
     OMR: 3,
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -779,17 +779,17 @@ class OrderedHashSet
 
 /*** HashableValue *******************************************************************************/
 
 bool
 HashableValue::setValue(JSContext* cx, HandleValue v)
 {
     if (v.isString()) {
         // Atomize so that hash() and operator==() are fast and infallible.
-        JSString* str = AtomizeString(cx, v.toString(), DoNotInternAtom);
+        JSString* str = AtomizeString(cx, v.toString(), DoNotPinAtom);
         if (!str)
             return false;
         value = StringValue(str);
     } else if (v.isDouble()) {
         double d = v.toDouble();
         int32_t i;
         if (NumberEqualsInt32(d, &i)) {
             // Normalize int32_t-valued doubles to int32_t for faster hashing and testing.
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -2489,16 +2489,30 @@ GetConstructorName(JSContext* cx, unsign
     if (name) {
         args.rval().setString(name);
     } else {
         args.rval().setNull();
     }
     return true;
 }
 
+static bool
+AllocationMarker(JSContext* cx, unsigned argc, jsval* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    static const JSClass cls = { "AllocationMarker" };
+
+    RootedObject obj(cx, JS_NewObject(cx, &cls));
+    if (!obj)
+        return false;
+    args.rval().setObject(*obj);
+    return true;
+}
+
 static const JSFunctionSpecWithHelp TestingFunctions[] = {
     JS_FN_HELP("gc", ::GC, 0, 0,
 "gc([obj] | 'compartment' [, 'shrinking'])",
 "  Run the garbage collector. When obj is given, GC only its compartment.\n"
 "  If 'compartment' is given, GC any compartments that were scheduled for\n"
 "  GC via schedulegc.\n"
 "  If 'shrinking' is passed as the optional second argument, perform a\n"
 "  shrinking GC rather than a normal GC."),
@@ -2906,16 +2920,23 @@ gc::ZealModeHelpText),
 "  Explicitly enable source discarding in the current compartment.  The default is that "
 "  source discarding is not explicitly enabled."),
 
     JS_FN_HELP("getConstructorName", GetConstructorName, 1, 0,
 "getConstructorName(object)",
 "  If the given object was created with `new Ctor`, return the constructor's display name. "
 "  Otherwise, return null."),
 
+    JS_FN_HELP("allocationMarker", AllocationMarker, 0, 0,
+"allocationMarker()",
+"  Return a freshly allocated object whose [[Class]] name is\n"
+"  \"AllocationMarker\". Such objects are allocated only by calls\n"
+"  to this function, never implicitly by the system, making them\n"
+"  suitable for use in allocation tooling tests.\n"),
+
     JS_FS_HELP_END
 };
 
 static const JSPropertySpec TestingProperties[] = {
     JS_PSG("timesAccessed", TimesAccessed, 0),
     JS_PS_END
 };
 
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -1782,17 +1782,18 @@ InitTypeClasses(JSContext* cx, HandleObj
   //   * __proto__ === ctypes.{Pointer,Array,Struct,Function}Type.prototype
   //   * A constructor which creates and returns a CData object, containing
   //     binary data of the given type.
   //   * 'prototype' property:
   //     * [[Class]] "CDataProto"
   //     * __proto__ === 'p', the prototype object from above
   //     * 'constructor' property === 't'
   AutoObjectVector protos(cx);
-  protos.resize(CTYPEPROTO_SLOTS);
+  if (!protos.resize(CTYPEPROTO_SLOTS))
+      return false;
   if (!InitTypeConstructor(cx, ctypesObj, CTypeProto, CDataProto,
          sPointerFunction, nullptr, sPointerProps,
          sPointerInstanceFunctions, sPointerInstanceProps,
          protos[SLOT_POINTERPROTO], protos[SLOT_POINTERDATAPROTO]))
     return false;
 
   if (!InitTypeConstructor(cx, ctypesObj, CTypeProto, CDataProto,
          sArrayFunction, nullptr, sArrayProps,
--- a/js/src/doc/Debugger/Debugger.Memory.md
+++ b/js/src/doc/Debugger/Debugger.Memory.md
@@ -242,52 +242,203 @@ Function Properties of the `Debugger.Mem
 
     * *constructorName* is the constructor function's display name for objects
       created by `new Ctor`. If that data is not available, or the object was
       not created with a `new` expression, this property is `null`.
 
     When `trackingAllocationSites` is `false`, `drainAllocationsLog()` throws an
     `Error`.
 
-<code id='take-census'>takeCensus()</code>
+<code id='take-census'>takeCensus(<i>options</i>)</code>
 :   Carry out a census of the debuggee compartments' contents. A *census* is a
     complete traversal of the graph of all reachable memory items belonging to a
     particular `Debugger`'s debuggees. The census produces a count of those
     items, broken down by various criteria.
 
-    The `takeCensus` method returns an object of the form:
+    The <i>options</i> argument is an object whose properties specify how the
+    census should be carried out.
+
+    If <i>options</i> has a `breakdown` property, that determines how the census
+    categorizes the items it finds, and what data it collects about them. For
+    example, if `dbg` is a `Debugger` instance, the following performs a simple
+    count of debuggee items:
+
+        dbg.memory.takeCensus({ breakdown: { by: 'count' } })
+
+    That might produce a result like:
+
+        { "count": 1616, "bytes": 93240 }
+
+    Here is a breakdown that groups JavaScript objects by their class name, and
+    non-string, non-script items by their C++ type name:
+
+        {
+          by: "coarseType",
+          objects: { by: "objectClass" },
+          other:   { by: "internalType" }
+        }
+
+    which produces a result like this:
+
+        {
+          "objects": {
+            "Function":         { "count": 404, "bytes": 37328 },
+            "Object":           { "count": 11,  "bytes": 1264 },
+            "Debugger":         { "count": 1,   "bytes": 416 },
+            "ScriptSource":     { "count": 1,   "bytes": 64 },
+            // ... omitted for brevity...
+          },
+          "scripts":            { "count": 1,   "bytes": 0 },
+          "strings":            { "count": 701, "bytes": 49080 },
+          "other": {
+            "js::Shape":        { "count": 450, "bytes": 0 },
+            "js::BaseShape":    { "count": 21,  "bytes": 0 },
+            "js::ObjectGroup":  { "count": 17,  "bytes": 0 }
+          }
+        }
+
+    In general, a `breakdown` value has one of the following forms:
+
+    <code>{ by: "count", count:<i>count<i>, bytes:<i>bytes</i> }</code>
+    :   The trivial categorization: none whatsoever. Simply tally up the items
+        visited. If <i>count</i> is true, count the number of items visited; if
+        <i>bytes</i> is true, total the number of bytes the items use directly.
+        Both <i>count</i> and <i>bytes</i> are optional; if omitted, they
+        default to `true`. In the result of the census, this breakdown produces
+        a value of the form:
+
+            { "count":<i>n</b>, "bytes":<i>b</i> }
+
+        where the `count` and `bytes` properties are present as directed by the
+        <i>count</i> and <i>bytes</i> properties on the breakdown.
+
+        Note that the census can produce byte sizes only for the most common
+        types. When the census cannot find the byte size for a given type, it
+        returns zero.
+
+    <code>{ by: "allocationStack", then:<i>breakdown</i>, noStack:<i>noStackBreakdown</i> }</code>
+    :   Group items by the full JavaScript stack trace at which they were
+        allocated.
+
+        Further categorize all the items allocated at each distinct stack using
+        <i>breakdown</i>.
+
+        In the result of the census, this breakdown produces a JavaScript `Map`
+        value whose keys are `SavedFrame` values, and whose values are whatever
+        sort of result <i>breakdown</i> produces. Objects allocated on an empty
+        JavaScript stack appear under the key `null`.
+
+        SpiderMonkey only tracks allocation sites for items if requested via the
+        [`trackingAllocationSites`][tracking-allocs] flag; even then, it does
+        not record allocation sites for every kind of item that appears in the
+        heap. Items that lack allocation site information are counted using
+        <i>noStackBreakdown</i>. These appear in the result `Map` under the key
+        string `"noStack"`.
+
+    <code>{ by: "objectClass", then:<i>breakdown</i>, other:<i>otherBreakdown</i> }</code>
+    :   Group JavaScript objects by their ECMAScript `[[Class]]` internal property values.
+
+        Further categorize JavaScript objects in each class using
+        <i>breakdown</i>. Further categorize items that are not JavaScript
+        objects using <i>otherBreakdown</i>.
+
+        In the result of the census, this breakdown produces a JavaScript object
+        with no prototype whose own property names are strings naming classes,
+        and whose values are whatever sort of result <i>breakdown</i> produces.
+        The results for non-object items appear as the value of the property
+        named `"other"`.
+
+    <code>{ by: "coarseType", objects:<i>objects</i>, scripts:<i>scripts</i>, strings:<i>strings</i>, other:<i>other</i> }</code>
+    :   Group items by their coarse type.
+
+        Use the breakdown value <i>objects</i> for items that are JavaScript
+        objects.
+
+        Use the breakdown value <i>scripts</i> for items that are
+        representations of JavaScript code. This includes bytecode, compiled
+        machine code, and saved source code.
+
+        Use the breakdown value <i>strings</i> for JavaScript strings.
+
+        Use the breakdown value <i>other</i> for items that don't fit into any of
+        the above categories.
+
+        In the result of the census, this breakdown produces a JavaScript object
+        of the form:
+
+        <pre class='language-js'><code>
+        {
+          "objects": <i>result</i>,
+          "scripts": <i>result</i>,
+          "strings": <i>result</i>,
+          "other": <i>result</i>
+        }
+        </code></pre>
+
+        where each <i>result</i> is a value of whatever sort the corresponding
+        breakdown value produces. All breakdown values are optional, and default
+        to `{ type: "count" }`.
+
+    <code>{ by: "internalType", then:<i>breakdown</i> }</code>
+    :   Group items by the names given their types internally by SpiderMonkey.
+        These names are not meaningful to web developers, but this type of
+        breakdown does serve as a catch-all that can be useful to Firefox tool
+        developers.
+
+        For example, a census of a pristine debuggee global broken down by
+        internal type name typically looks like this:
+
+            {
+              "JSString":        { "count": 701, "bytes": 49080 },
+              "js::Shape":       { "count": 450, "bytes": 0 },
+              "JSObject":        { "count": 426, "bytes": 44160 },
+              "js::BaseShape":   { "count": 21,  "bytes": 0 },
+              "js::ObjectGroup": { "count": 17,  "bytes": 0 },
+              "JSScript":        { "count": 1,   "bytes": 0 }
+            }
+
+        In the result of the census, this breakdown produces a JavaScript object
+        with no prototype whose own property names are strings naming types,
+        and whose values are whatever sort of result <i>breakdown</i> produces.
+
+    <code>[ <i>breakdown</i>, ... ]</code>
+    :   Group each item using all the given breakdown values. In the result of
+        the census, this breakdown produces an array of values of the sort
+        produced by each listed breakdown.
+
+    To simplify breakdown values, all `then` and `other` properties are optional.
+    If omitted, they are treated as if they were `{ type: "count" }`.
+
+    If the `options` argument has no `breakdown` property, `takeCensus` defaults
+    to the following:
 
     <pre class='language-js'><code>
     {
-      "objects": { <i>class</i>: <i>tally</i>, ... },
-      "scripts": <i>tally</i>,
-      "strings": <i>tally</i>,
-      "other": { <i>type name</i>: <i>tally</i>, ... }
+      by: "coarseType",
+      objects: { by: "objectClass" },
+      other:   { by: "internalType" }
     }
     </code></pre>
 
-    Each <i>tally</i> has the form:
+    which produces results of the form:
 
     <pre class='language-js'><code>
-    { "count": <i>count</i> }
+    {
+      objects: { <i>class</i>: <i>count</i>, ... },
+      scripts: <i>count</i>,
+      strings: <i>count</i>,
+      other:   { <i>type name</i>: <i>count</i>, ... }
+    }
     </code></pre>
 
-    where <i>count</i> is the number of items in the category.
-
-    The `"objects"` property's value contains the tallies of JavaScript objects,
-    broken down by their ECMAScript `[[Class]]` internal property values. Each
-    <i>class</i> is a string.
+    where each <i>count</i> has the form:
 
-    The `"scripts"` property's value tallies the in-memory representation of
-    JavaScript code.
-
-    The `"strings"` property's value tallies the debuggee's strings.
-
-    The `"other"` property's value contains the tallies of other items used
-    internally by SpiderMonkey, broken down by their C++ type name.
+    <pre class='language-js'><code>
+    { "count": <i>count</i>, bytes:<i>bytes</i> }
+    </code></pre>
 
     Because performing a census requires traversing the entire graph of objects
     in debuggee compartments, it is an expensive operation. On developer
     hardware in 2014, traversing a memory graph containing roughly 130,000 nodes
     and 410,000 edges took roughly 100ms. The traversal itself temporarily
     allocates one hash table entry per node (roughly two address-sized words) in
     addition to the per-category counts, whose size depends on the number of
     categories.
@@ -337,17 +488,17 @@ SpiderMonkey has three representations o
   strings, but the memory consumed by the string itself is a small constant
   independent of its size, since it is simply a pair of pointers.
 
 SpiderMonkey converts strings from the more complex representations to the
 simpler ones when it pleases. Such conversions usually increase memory
 consumption.
 
 SpiderMonkey shares some strings amongst all web pages and browser JS. These
-shared strings, called *atoms*, are not included in censuses' string tallies.
+shared strings, called *atoms*, are not included in censuses' string counts.
 
 
 ### Scripts
 
 SpiderMonkey has a complex, hybrid representation of JavaScript code. There
 are four representations kept in memory:
 
 - _Source code_. SpiderMonkey retains a copy of most JavaScript source code.
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -1911,18 +1911,20 @@ Parser<FullParseHandler>::checkFunctionD
             /*
              * Instead of setting bindingsAccessedDynamically, which would be
              * overly conservative, remember the names of all function
              * statements and mark any bindings with the same as aliased at the
              * end of functionBody.
              */
             if (!pc->funcStmts) {
                 pc->funcStmts = alloc.new_<FuncStmtSet>(alloc);
-                if (!pc->funcStmts || !pc->funcStmts->init())
+                if (!pc->funcStmts || !pc->funcStmts->init()) {
+                    ReportOutOfMemory(context);
                     return false;
+                }
             }
             if (!pc->funcStmts->put(funName))
                 return false;
 
             /*
              * Due to the implicit declaration mechanism, 'arguments' will not
              * have decls and, even if it did, they will not be noted as closed
              * in the emitter. Thus, in the corner case of function statements
--- a/js/src/gc/Allocator.cpp
+++ b/js/src/gc/Allocator.cpp
@@ -171,18 +171,21 @@ GCRuntime::tryNewNurseryObject(JSContext
 template <AllowGC allowGC>
 JSObject*
 GCRuntime::tryNewTenuredObject(ExclusiveContext* cx, AllocKind kind, size_t thingSize,
                                size_t nDynamicSlots)
 {
     HeapSlot* slots = nullptr;
     if (nDynamicSlots) {
         slots = cx->zone()->pod_malloc<HeapSlot>(nDynamicSlots);
-        if (MOZ_UNLIKELY(!slots))
+        if (MOZ_UNLIKELY(!slots)) {
+            if (allowGC)
+                ReportOutOfMemory(cx);
             return nullptr;
+        }
         Debug_SetSlotRangeToCrashOnTouch(slots, nDynamicSlots);
     }
 
     JSObject* obj = tryNewTenuredThing<JSObject, allowGC>(cx, kind, thingSize);
 
     if (obj)
         obj->setInitialSlotsMaybeNonNative(slots);
     else
--- a/js/src/gc/Barrier.cpp
+++ b/js/src/gc/Barrier.cpp
@@ -85,36 +85,20 @@ js::PreBarrierFunctor<S>::operator()(T* 
 }
 template void js::PreBarrierFunctor<JS::Value>::operator()<JS::Symbol>(JS::Symbol*);
 template void js::PreBarrierFunctor<JS::Value>::operator()<JSObject>(JSObject*);
 template void js::PreBarrierFunctor<JS::Value>::operator()<JSString>(JSString*);
 template void js::PreBarrierFunctor<jsid>::operator()<JS::Symbol>(JS::Symbol*);
 template void js::PreBarrierFunctor<jsid>::operator()<JSString>(JSString*);
 
 JS_PUBLIC_API(void)
-JS::HeapObjectPostBarrier(JSObject** objp)
+JS::HeapObjectPostBarrier(JSObject** objp, JSObject* prev, JSObject* next)
 {
     MOZ_ASSERT(objp);
-    MOZ_ASSERT(*objp);
-    js::InternalGCMethods<JSObject*>::postBarrierRelocate(objp);
+    js::InternalGCMethods<JSObject*>::postBarrier(objp, prev, next);
 }
 
 JS_PUBLIC_API(void)
-JS::HeapObjectRelocate(JSObject** objp)
-{
-    MOZ_ASSERT(objp);
-    MOZ_ASSERT(*objp);
-    js::InternalGCMethods<JSObject*>::postBarrierRemove(objp);
-}
-
-JS_PUBLIC_API(void)
-JS::HeapValuePostBarrier(JS::Value* valuep)
+JS::HeapValuePostBarrier(JS::Value* valuep, const Value& prev, const Value& next)
 {
     MOZ_ASSERT(valuep);
-    js::InternalGCMethods<JS::Value>::postBarrierRelocate(valuep);
+    js::InternalGCMethods<JS::Value>::postBarrier(valuep, prev, next);
 }
-
-JS_PUBLIC_API(void)
-JS::HeapValueRelocate(JS::Value* valuep)
-{
-    MOZ_ASSERT(valuep);
-    js::InternalGCMethods<JS::Value>::postBarrierRemove(valuep);
-}
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -234,19 +234,17 @@ struct InternalGCMethods {};
 
 template <typename T>
 struct InternalGCMethods<T*>
 {
     static bool isMarkable(T* v) { return v != nullptr; }
 
     static void preBarrier(T* v) { T::writeBarrierPre(v); }
 
-    static void postBarrier(T** vp) { T::writeBarrierPost(*vp, vp); }
-    static void postBarrierRelocate(T** vp) { T::writeBarrierPostRelocate(*vp, vp); }
-    static void postBarrierRemove(T** vp) { T::writeBarrierPostRemove(*vp, vp); }
+    static void postBarrier(T** vp, T* prev, T* next) { T::writeBarrierPost(vp, prev, next); }
 
     static void readBarrier(T* v) { T::readBarrier(v); }
 };
 
 template <typename S> struct PreBarrierFunctor : VoidDefaultAdaptor<S> {
     template <typename T> void operator()(T* t);
 };
 
@@ -258,57 +256,49 @@ template <>
 struct InternalGCMethods<Value>
 {
     static bool isMarkable(Value v) { return v.isMarkable(); }
 
     static void preBarrier(Value v) {
         DispatchValueTyped(PreBarrierFunctor<Value>(), v);
     }
 
-    static void postBarrier(Value* vp) {
-        MOZ_ASSERT(!CurrentThreadIsIonCompiling());
-        if (vp->isObject()) {
-            gc::StoreBuffer* sb = reinterpret_cast<gc::Cell*>(&vp->toObject())->storeBuffer();
-            if (sb)
-                sb->putValueFromAnyThread(vp);
-        }
-    }
-
-    static void postBarrierRelocate(Value* vp) {
+    static void postBarrier(Value* vp, const Value& prev, const Value& next) {
         MOZ_ASSERT(!CurrentThreadIsIonCompiling());
-        if (vp->isObject()) {
-            gc::StoreBuffer* sb = reinterpret_cast<gc::Cell*>(&vp->toObject())->storeBuffer();
-            if (sb)
-                sb->putValueFromAnyThread(vp);
-        }
-    }
+        MOZ_ASSERT(vp);
 
-    static void postBarrierRemove(Value* vp) {
-        MOZ_ASSERT(vp);
-        MOZ_ASSERT(vp->isMarkable());
-        MOZ_ASSERT(!CurrentThreadIsIonCompiling());
-        JSRuntime* rt = static_cast<js::gc::Cell*>(vp->toGCThing())->runtimeFromAnyThread();
-        JS::shadow::Runtime* shadowRuntime = JS::shadow::Runtime::asShadowRuntime(rt);
-        shadowRuntime->gcStoreBufferPtr()->unputValueFromAnyThread(vp);
+        // If the target needs an entry, add it.
+        js::gc::StoreBuffer* sb;
+        if (next.isObject() && (sb = reinterpret_cast<gc::Cell*>(&next.toObject())->storeBuffer())) {
+            // If we know that the prev has already inserted an entry, we can skip
+            // doing the lookup to add the new entry.
+            if (prev.isObject() && reinterpret_cast<gc::Cell*>(&prev.toObject())->storeBuffer()) {
+                sb->assertHasValueEdge(vp);
+                return;
+            }
+            sb->putValueFromAnyThread(vp);
+            return;
+        }
+        // Remove the prev entry if the new value does not need it.
+        if (prev.isObject() && (sb = reinterpret_cast<gc::Cell*>(&prev.toObject())->storeBuffer()))
+            sb->unputValueFromAnyThread(vp);
     }
 
     static void readBarrier(const Value& v) {
         DispatchValueTyped(ReadBarrierFunctor<Value>(), v);
     }
 };
 
 template <>
 struct InternalGCMethods<jsid>
 {
     static bool isMarkable(jsid id) { return JSID_IS_STRING(id) || JSID_IS_SYMBOL(id); }
 
     static void preBarrier(jsid id) { DispatchIdTyped(PreBarrierFunctor<jsid>(), id); }
-    static void postBarrier(jsid* idp) {}
-    static void postBarrierRelocate(jsid* idp) {}
-    static void postBarrierRemove(jsid* idp) {}
+    static void postBarrier(jsid* idp, jsid prev, jsid next) {}
 };
 
 template <typename T>
 class BarrieredBaseMixins {};
 
 /*
  * Base class for barriered pointer types.
  */
@@ -336,17 +326,16 @@ class BarrieredBase : public BarrieredBa
      * Obviously this is dangerous unless you know the barrier is not needed.
      */
     T* unsafeGet() { return &value; }
     const T* unsafeGet() const { return &value; }
     void unsafeSet(T v) { value = v; }
 
     /* For users who need to manually barrier the raw types. */
     static void writeBarrierPre(const T& v) { InternalGCMethods<T>::preBarrier(v); }
-    static void writeBarrierPost(const T& v, T* vp) { InternalGCMethods<T>::postBarrier(vp); }
 
   protected:
     void pre() { InternalGCMethods<T>::preBarrier(value); }
 };
 
 template <>
 class BarrieredBaseMixins<JS::Value> : public ValueOperations<BarrieredBase<JS::Value> >
 {
@@ -404,41 +393,42 @@ class PreBarriered : public BarrieredBas
  * implemented by RelocatablePtr<T> or JS::Heap<T> at the cost of not
  * automatically handling deletion or movement.
  */
 template <class T>
 class HeapPtr : public BarrieredBase<T>
 {
   public:
     HeapPtr() : BarrieredBase<T>(GCMethods<T>::initial()) {}
-    explicit HeapPtr(T v) : BarrieredBase<T>(v) { post(); }
-    explicit HeapPtr(const HeapPtr<T>& v) : BarrieredBase<T>(v) { post(); }
+    explicit HeapPtr(T v) : BarrieredBase<T>(v) { post(GCMethods<T>::initial(), v); }
+    explicit HeapPtr(const HeapPtr<T>& v) : BarrieredBase<T>(v) { post(GCMethods<T>::initial(), v); }
 #ifdef DEBUG
     ~HeapPtr() {
         // No prebarrier necessary as this only happens when we are sweeping or
         // before the containing obect becomes part of the GC graph.
         MOZ_ASSERT(CurrentThreadIsGCSweeping() || CurrentThreadIsHandlingInitFailure());
     }
 #endif
 
     void init(T v) {
         this->value = v;
-        post();
+        post(GCMethods<T>::initial(), v);
     }
 
     DECLARE_POINTER_ASSIGN_OPS(HeapPtr, T);
 
   protected:
-    void post() { InternalGCMethods<T>::postBarrier(&this->value); }
+    void post(T prev, T next) { InternalGCMethods<T>::postBarrier(&this->value, prev, next); }
 
   private:
     void set(const T& v) {
         this->pre();
+        T tmp = this->value;
         this->value = v;
-        post();
+        post(tmp, this->value);
     }
 
     /*
      * Unlike RelocatablePtr<T>, HeapPtr<T> must be managed with GC lifetimes.
      * Specifically, the memory used by the pointer itself must be live until
      * at least the next minor GC. For that reason, move semantics are invalid
      * and are deleted here. Please note that not all containers support move
      * semantics, so this does not completely prevent invalid uses.
@@ -489,35 +479,32 @@ class ImmutableTenuredPtr
  * used in contexts where this ability is necessary.
  */
 template <class T>
 class RelocatablePtr : public BarrieredBase<T>
 {
   public:
     RelocatablePtr() : BarrieredBase<T>(GCMethods<T>::initial()) {}
     explicit RelocatablePtr(T v) : BarrieredBase<T>(v) {
-        if (GCMethods<T>::needsPostBarrier(v))
-            post();
+        post(GCMethods<T>::initial(), this->value);
     }
 
     /*
      * For RelocatablePtr, move semantics are equivalent to copy semantics. In
      * C++, a copy constructor taking const-ref is the way to get a single
      * function that will be used for both lvalue and rvalue copies, so we can
      * simply omit the rvalue variant.
      */
     RelocatablePtr(const RelocatablePtr<T>& v) : BarrieredBase<T>(v) {
-        if (GCMethods<T>::needsPostBarrier(this->value))
-            post();
+        post(GCMethods<T>::initial(), this->value);
     }
 
     ~RelocatablePtr() {
         this->pre();
-        if (GCMethods<T>::needsPostBarrier(this->value))
-            relocate();
+        post(this->value, GCMethods<T>::initial());
     }
 
     DECLARE_POINTER_ASSIGN_OPS(RelocatablePtr, T);
 
     /* Make this friend so it can access pre() and post(). */
     template <class T1, class T2>
     friend inline void
     BarrieredSetPair(Zone* zone,
@@ -526,35 +513,23 @@ class RelocatablePtr : public BarrieredB
 
   protected:
     void set(const T& v) {
         this->pre();
         postBarrieredSet(v);
     }
 
     void postBarrieredSet(const T& v) {
-        if (GCMethods<T>::needsPostBarrier(v)) {
-            this->value = v;
-            post();
-        } else if (GCMethods<T>::needsPostBarrier(this->value)) {
-            relocate();
-            this->value = v;
-        } else {
-            this->value = v;
-        }
+        T tmp = this->value;
+        this->value = v;
+        post(tmp, this->value);
     }
 
-    void post() {
-        MOZ_ASSERT(GCMethods<T>::needsPostBarrier(this->value));
-        InternalGCMethods<T>::postBarrierRelocate(&this->value);
-    }
-
-    void relocate() {
-        MOZ_ASSERT(GCMethods<T>::needsPostBarrier(this->value));
-        InternalGCMethods<T>::postBarrierRemove(&this->value);
+    void post(T prev, T next) {
+        InternalGCMethods<T>::postBarrier(&this->value, prev, next);
     }
 };
 
 /*
  * This is a hack for RegExpStatics::updateFromMatch. It allows us to do two
  * barriers with only one branch to check if we're in an incremental GC.
  */
 template <class T1, class T2>
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -280,19 +280,18 @@ class TenuredCell : public Cell
     }
     MOZ_ALWAYS_INLINE JS::shadow::Zone* shadowZoneFromAnyThread() const {
         return JS::shadow::Zone::asShadowZone(zoneFromAnyThread());
     }
 
     static MOZ_ALWAYS_INLINE void readBarrier(TenuredCell* thing);
     static MOZ_ALWAYS_INLINE void writeBarrierPre(TenuredCell* thing);
 
-    static MOZ_ALWAYS_INLINE void writeBarrierPost(TenuredCell* thing, void* cellp);
-    static MOZ_ALWAYS_INLINE void writeBarrierPostRelocate(TenuredCell* thing, void* cellp);
-    static MOZ_ALWAYS_INLINE void writeBarrierPostRemove(TenuredCell* thing, void* cellp);
+    static MOZ_ALWAYS_INLINE void writeBarrierPost(void* cellp, TenuredCell* prior,
+                                                   TenuredCell* next);
 
 #ifdef DEBUG
     inline bool isAligned() const;
 #endif
 };
 
 /*
  * The mark bitmap has one bit per each GC cell. For multi-cell GC things this
@@ -1465,31 +1464,19 @@ TenuredCell::writeBarrierPre(TenuredCell
 static MOZ_ALWAYS_INLINE void
 AssertValidToSkipBarrier(TenuredCell* thing)
 {
     MOZ_ASSERT(!IsInsideNursery(thing));
     MOZ_ASSERT_IF(thing, MapAllocToTraceKind(thing->getAllocKind()) != JS::TraceKind::Object);
 }
 
 /* static */ MOZ_ALWAYS_INLINE void
-TenuredCell::writeBarrierPost(TenuredCell* thing, void* cellp)
-{
-    AssertValidToSkipBarrier(thing);
-}
-
-/* static */ MOZ_ALWAYS_INLINE void
-TenuredCell::writeBarrierPostRelocate(TenuredCell* thing, void* cellp)
+TenuredCell::writeBarrierPost(void* cellp, TenuredCell* prior, TenuredCell* next)
 {
-    AssertValidToSkipBarrier(thing);
-}
-
-/* static */ MOZ_ALWAYS_INLINE void
-TenuredCell::writeBarrierPostRemove(TenuredCell* thing, void* cellp)
-{
-    AssertValidToSkipBarrier(thing);
+    AssertValidToSkipBarrier(next);
 }
 
 #ifdef DEBUG
 bool
 Cell::isAligned() const
 {
     if (!isTenured())
         return true;
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -1176,16 +1176,51 @@ GCMarker::drainMarkStack(SliceBudget& bu
             saveValueRanges();
             return false;
         }
     }
 
     return true;
 }
 
+inline static bool
+ObjectDenseElementsMayBeMarkable(NativeObject* nobj)
+{
+    /*
+     * For arrays that are large enough it's worth checking the type information
+     * to see if the object's elements contain any GC pointers.  If not, we
+     * don't need to trace them.
+     */
+    const unsigned MinElementsLength = 32;
+    if (nobj->getDenseInitializedLength() < MinElementsLength || nobj->isSingleton())
+        return true;
+
+    ObjectGroup* group = nobj->group();
+    if (group->needsSweep() || group->unknownProperties())
+        return true;
+
+    HeapTypeSet* typeSet = group->maybeGetProperty(JSID_VOID);
+    if (!typeSet)
+        return true;
+
+    static const uint32_t flagMask =
+        TYPE_FLAG_STRING | TYPE_FLAG_SYMBOL | TYPE_FLAG_LAZYARGS | TYPE_FLAG_ANYOBJECT;
+    bool mayBeMarkable = typeSet->hasAnyFlag(flagMask) || typeSet->getObjectCount() != 0;
+
+#ifdef DEBUG
+    if (!mayBeMarkable) {
+        const Value* elements = nobj->getDenseElementsAllowCopyOnWrite();
+        for (unsigned i = 0; i < nobj->getDenseInitializedLength(); i++)
+            MOZ_ASSERT(!elements[i].isMarkable());
+    }
+#endif
+
+    return mayBeMarkable;
+}
+
 inline void
 GCMarker::processMarkStackTop(SliceBudget& budget)
 {
     /*
      * The function uses explicit goto and implements the scanning of the
      * object directly. It allows to eliminate the tail recursion and
      * significantly improve the marking performance, see bug 641025.
      */
@@ -1300,18 +1335,22 @@ GCMarker::processMarkStackTop(SliceBudge
             if (nobj->denseElementsAreCopyOnWrite()) {
                 JSObject* owner = nobj->getElementsHeader()->ownerObject();
                 if (owner != nobj) {
                     traverseEdge(obj, owner);
                     break;
                 }
             }
 
+            if (!ObjectDenseElementsMayBeMarkable(nobj))
+                break;
+
             vp = nobj->getDenseElementsAllowCopyOnWrite();
             end = vp + nobj->getDenseInitializedLength();
+
             if (!nslots)
                 goto scan_value_array;
             pushValueArray(nobj, vp, end);
         } while (false);
 
         vp = nobj->fixedSlots();
         if (nobj->slots_) {
             unsigned nfixed = nobj->numFixedSlots();
@@ -1906,17 +1945,20 @@ js::TenuringTracer::traceObject(JSObject
 {
     NativeObject *nobj = CallTraceHook(TenuringFunctor(), this, obj,
                                        CheckGeneration::NoChecks, *this);
     if (!nobj)
         return;
 
     // Note: the contents of copy on write elements pointers are filled in
     // during parsing and cannot contain nursery pointers.
-    if (!nobj->hasEmptyElements() && !nobj->denseElementsAreCopyOnWrite()) {
+    if (!nobj->hasEmptyElements() &&
+        !nobj->denseElementsAreCopyOnWrite() &&
+        ObjectDenseElementsMayBeMarkable(nobj))
+    {
         Value* elems = static_cast<HeapSlot*>(nobj->getDenseElements())->unsafeGet();
         traceSlots(elems, elems + nobj->getDenseInitializedLength());
     }
 
     traceObjectSlots(nobj, 0, nobj->slotSpan());
 }
 
 void
--- a/js/src/gc/StoreBuffer.h
+++ b/js/src/gc/StoreBuffer.h
@@ -116,16 +116,21 @@ class StoreBuffer
         }
 
         /* Remove an item from the store buffer. */
         void unput(StoreBuffer* owner, const T& v) {
             sinkStores(owner);
             stores_.remove(v);
         }
 
+        bool has(StoreBuffer* owner, const T& v) {
+            sinkStores(owner);
+            return stores_.has(v);
+        }
+
         /* Trace the source of all edges in the store buffer. */
         void trace(StoreBuffer* owner, TenuringTracer& mover);
 
         size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) {
             return stores_.sizeOfExcludingThis(mallocSizeOf);
         }
 
       private:
@@ -428,16 +433,29 @@ class StoreBuffer
     void putGeneric(const T& t) { putFromAnyThread(bufferGeneric, t);}
 
     /* Insert or update a callback entry. */
     template <typename Key>
     void putCallback(void (*callback)(JSTracer* trc, Key* key, void* data), Key* key, void* data) {
         putFromAnyThread(bufferGeneric, CallbackRef<Key>(callback, key, data));
     }
 
+    void assertHasCellEdge(Cell** cellp) {
+        CellPtrEdge cpe(cellp);
+
+        MOZ_ASSERT(bufferCell.has(this, CellPtrEdge(cellp)) ||
+                   !CellPtrEdge(cellp).maybeInRememberedSet(nursery_));
+
+    }
+
+    void assertHasValueEdge(Value* vp) {
+        MOZ_ASSERT(bufferVal.has(this, ValueEdge(vp)) ||
+                   !ValueEdge(vp).maybeInRememberedSet(nursery_));
+    }
+
     void setShouldCancelIonCompilations() {
         cancelIonCompilations_ = true;
     }
 
     /* Methods to trace the source of all edges in the store buffer. */
     void traceValues(TenuringTracer& mover)            { bufferVal.trace(this, mover); }
     void traceCells(TenuringTracer& mover)             { bufferCell.trace(this, mover); }
     void traceSlots(TenuringTracer& mover)             { bufferSlot.trace(this, mover); }
--- a/js/src/gdb/tests/test-jsid.cpp
+++ b/js/src/gdb/tests/test-jsid.cpp
@@ -1,14 +1,14 @@
 #include "gdb-tests.h"
 #include "jsapi.h"
 
 FRAGMENT(jsid, simple) {
   JS::Rooted<JSString*> string(cx, JS_NewStringCopyZ(cx, "moon"));
-  JS::Rooted<JSString*> interned(cx, JS_InternJSString(cx, string));
+  JS::Rooted<JSString*> interned(cx, JS_AtomizeAndPinJSString(cx, string));
   JS::Rooted<jsid> string_id(cx, INTERNED_STRING_TO_JSID(cx, interned));
   jsid int_id = INT_TO_JSID(1729);
   JS::Rooted<jsid> unique_symbol_id(
       cx, SYMBOL_TO_JSID(JS::NewSymbol(cx, interned)));
   JS::Rooted<jsid> registry_symbol_id(
       cx, SYMBOL_TO_JSID(JS::GetSymbolFor(cx, interned)));
   JS::Rooted<jsid> well_known_symbol_id(
       cx, SYMBOL_TO_JSID(JS::GetWellKnownSymbol(cx, JS::SymbolCode::iterator)));
@@ -33,12 +33,12 @@ jsid_handles(JS::Handle<jsid> jsid_handl
   // Prevent the linker from unifying this function with others that are
   // equivalent in machine code but not type.
   fprintf(stderr, "Called " __FILE__ ":jsid_handles\n");
   breakpoint();
 }
 
 FRAGMENT(jsid, handles) {
   JS::Rooted<JSString*> string(cx, JS_NewStringCopyZ(cx, "shovel"));
-  JS::Rooted<JSString*> interned(cx, JS_InternJSString(cx, string));
+  JS::Rooted<JSString*> interned(cx, JS_AtomizeAndPinJSString(cx, string));
   JS::Rooted<jsid> string_id(cx, INTERNED_STRING_TO_JSID(cx, interned));
   jsid_handles(string_id, &string_id);
 }
--- a/js/src/jit-test/tests/debug/Memory-takeCensus-04.js
+++ b/js/src/jit-test/tests/debug/Memory-takeCensus-04.js
@@ -1,26 +1,26 @@
 // Test that Debugger.Memory.prototype.takeCensus finds GC roots that are on the
 // stack.
 
 var g = newGlobal();
 var dbg = new Debugger(g);
 
 g.eval(`
-  function withTypedArrayOnStack(f) {
+  function withAllocationMarkerOnStack(f) {
     (function () {
-      var onStack = new Int8Array();
+      var onStack = allocationMarker();
       f();
     }())
   }
 `);
 
-assertEq("Int8Array" in dbg.memory.takeCensus().objects, false,
-         "There shouldn't exist any typed arrays in the census.");
+assertEq("AllocationMarker" in dbg.memory.takeCensus().objects, false,
+         "There shouldn't exist any allocation markers in the census.");
 
-var typedArrayCount;
-g.withTypedArrayOnStack(() => {
-  typedArrayCount = dbg.memory.takeCensus().objects.Int8Array.count;
+var allocationMarkerCount;
+g.withAllocationMarkerOnStack(() => {
+  allocationMarkerCount = dbg.memory.takeCensus().objects.AllocationMarker.count;
 });
 
-assertEq(typedArrayCount, 1,
-         "Should have one typed array in the census, because there " +
+assertEq(allocationMarkerCount, 1,
+         "Should have one allocation marker in the census, because there " +
          "was one on the stack.");
--- a/js/src/jit-test/tests/debug/Memory-takeCensus-05.js
+++ b/js/src/jit-test/tests/debug/Memory-takeCensus-05.js
@@ -1,14 +1,14 @@
 // Test that Debugger.Memory.prototype.takeCensus finds cross compartment
 // wrapper GC roots.
 
 var g = newGlobal();
 var dbg = new Debugger(g);
 
-assertEq("Int8Array" in dbg.memory.takeCensus().objects, false,
-         "There shouldn't exist any typed arrays in the census.");
+assertEq("AllocationMarker" in dbg.memory.takeCensus().objects, false,
+         "There shouldn't exist any allocation markers in the census.");
 
-this.ccw = g.eval("new Int8Array()");
+this.ccw = g.allocationMarker();
 
-assertEq(dbg.memory.takeCensus().objects.Int8Array.count, 1,
-         "Should have one typed array in the census, because there " +
-         "is one cross-compartment wrapper.");
+assertEq(dbg.memory.takeCensus().objects.AllocationMarker.count, 1,
+         "Should have one allocation marker in the census, because there " +
+         "is one cross-compartment wrapper referring to it.");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Memory-takeCensus-06.js
@@ -0,0 +1,117 @@
+// Check Debugger.Memory.prototype.takeCensus handling of 'breakdown' argument.
+
+load(libdir + 'match.js');
+var Pattern = Match.Pattern;
+
+var g = newGlobal();
+var dbg = new Debugger(g);
+
+Pattern({ count: Pattern.NATURAL,
+          bytes: Pattern.NATURAL })
+  .assert(dbg.memory.takeCensus({ breakdown: { by: 'count' } }));
+
+let census = dbg.memory.takeCensus({ breakdown: { by: 'count', count: false, bytes: false } });
+assertEq('count' in census, false);
+assertEq('bytes' in census, false);
+
+let census = dbg.memory.takeCensus({ breakdown: { by: 'count', count: true,  bytes: false } });
+assertEq('count' in census, true);
+assertEq('bytes' in census, false);
+
+let census = dbg.memory.takeCensus({ breakdown: { by: 'count', count: false, bytes: true } });
+assertEq('count' in census, false);
+assertEq('bytes' in census, true);
+
+let census = dbg.memory.takeCensus({ breakdown: { by: 'count', count: true,  bytes: true } });
+assertEq('count' in census, true);
+assertEq('bytes' in census, true);
+
+
+// Pattern doesn't mind objects with extra properties, so we'll restrict this
+// list to the object classes we're pretty sure are going to stick around for
+// the forseeable future.
+Pattern({
+          Function:       { count: Pattern.NATURAL },
+          Object:         { count: Pattern.NATURAL },
+          Debugger:       { count: Pattern.NATURAL },
+          global:         { count: Pattern.NATURAL },
+
+          // The below are all Debugger prototype objects.
+          Source:         { count: Pattern.NATURAL },
+          Environment:    { count: Pattern.NATURAL },
+          Script:         { count: Pattern.NATURAL },
+          Memory:         { count: Pattern.NATURAL },
+          Frame:          { count: Pattern.NATURAL }
+        })
+  .assert(dbg.memory.takeCensus({ breakdown: { by: 'objectClass' } }));
+
+Pattern({
+          objects:        { count: Pattern.NATURAL },
+          scripts:        { count: Pattern.NATURAL },
+          strings:        { count: Pattern.NATURAL },
+          other:          { count: Pattern.NATURAL }
+        })
+  .assert(dbg.memory.takeCensus({ breakdown: { by: 'coarseType' } }));
+
+// As for { by: 'objectClass' }, restrict our pattern to the types
+// we predict will stick around for a long time.
+Pattern({
+          JSString:             { count: Pattern.NATURAL },
+          'js::Shape':          { count: Pattern.NATURAL },
+          JSObject:             { count: Pattern.NATURAL },
+          JSScript:             { count: Pattern.NATURAL }
+        })
+  .assert(dbg.memory.takeCensus({ breakdown: { by: 'internalType' } }));
+
+
+// Nested breakdowns.
+
+let coarse_type_pattern = {
+  objects:        { count: Pattern.NATURAL },
+  scripts:        { count: Pattern.NATURAL },
+  strings:        { count: Pattern.NATURAL },
+  other:          { count: Pattern.NATURAL }
+};
+
+Pattern({
+          JSString:    coarse_type_pattern,
+          'js::Shape': coarse_type_pattern,
+          JSObject:    coarse_type_pattern,
+          JSScript:    coarse_type_pattern,
+        })
+  .assert(dbg.memory.takeCensus({
+    breakdown: { by: 'internalType',
+                 then: { by: 'coarseType' }
+    }
+  }));
+
+Pattern({
+          Function:       { count: Pattern.NATURAL },
+          Object:         { count: Pattern.NATURAL },
+          Debugger:       { count: Pattern.NATURAL },
+          global:         { count: Pattern.NATURAL },
+          other:          coarse_type_pattern
+        })
+  .assert(dbg.memory.takeCensus({
+    breakdown: {
+      by: 'objectClass',
+      then:  { by: 'count' },
+      other: { by: 'coarseType' }
+    }
+  }));
+
+Pattern({
+          objects: { count: Pattern.NATURAL, label: "object" },
+          scripts: { count: Pattern.NATURAL, label: "scripts" },
+          strings: { count: Pattern.NATURAL, label: "strings" },
+          other:   { count: Pattern.NATURAL, label: "other" }
+        })
+  .assert(dbg.memory.takeCensus({
+    breakdown: {
+      by: 'coarseType',
+      objects: { by: 'count', label: 'object' },
+      scripts: { by: 'count', label: 'scripts' },
+      strings: { by: 'count', label: 'strings' },
+      other:   { by: 'count', label: 'other' }
+    }
+  }));
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Memory-takeCensus-07.js
@@ -0,0 +1,75 @@
+// Debugger.Memory.prototype.takeCensus breakdown: check error handling on
+// property gets.
+
+load(libdir + 'asserts.js');
+
+var g = newGlobal();
+var dbg = new Debugger(g);
+
+assertThrowsValue(() => {
+  dbg.memory.takeCensus({
+    breakdown: { get by() { throw "ಠ_ಠ" } }
+  });
+}, "ಠ_ಠ");
+
+
+
+assertThrowsValue(() => {
+  dbg.memory.takeCensus({
+    breakdown: { by: 'count', get count() { throw "ಠ_ಠ" } }
+  });
+}, "ಠ_ಠ");
+
+assertThrowsValue(() => {
+  dbg.memory.takeCensus({
+    breakdown: { by: 'count', get bytes() { throw "ಠ_ಠ" } }
+  });
+}, "ಠ_ಠ");
+
+
+
+assertThrowsValue(() => {
+  dbg.memory.takeCensus({
+    breakdown: { by: 'objectClass', get then() { throw "ಠ_ಠ" } }
+  });
+}, "ಠ_ಠ");
+
+assertThrowsValue(() => {
+  dbg.memory.takeCensus({
+    breakdown: { by: 'objectClass', get other() { throw "ಠ_ಠ" } }
+  });
+}, "ಠ_ಠ");
+
+
+
+assertThrowsValue(() => {
+  dbg.memory.takeCensus({
+    breakdown: { by: 'coarseType', get objects() { throw "ಠ_ಠ" } }
+  });
+}, "ಠ_ಠ");
+
+assertThrowsValue(() => {
+  dbg.memory.takeCensus({
+    breakdown: { by: 'coarseType', get scripts() { throw "ಠ_ಠ" } }
+  });
+}, "ಠ_ಠ");
+
+assertThrowsValue(() => {
+  dbg.memory.takeCensus({
+    breakdown: { by: 'coarseType', get strings() { throw "ಠ_ಠ" } }
+  });
+}, "ಠ_ಠ");
+
+assertThrowsValue(() => {
+  dbg.memory.takeCensus({
+    breakdown: { by: 'coarseType', get other() { throw "ಠ_ಠ" } }
+  });
+}, "ಠ_ಠ");
+
+
+
+assertThrowsValue(() => {
+  dbg.memory.takeCensus({
+    breakdown: { by: 'internalType', get then() { throw "ಠ_ಠ" } }
+  });
+}, "ಠ_ಠ");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Memory-takeCensus-08.js
@@ -0,0 +1,73 @@
+// Debugger.Memory.prototype.takeCensus: test by: 'count' breakdown
+
+let g = newGlobal();
+let dbg = new Debugger(g);
+
+g.eval(`
+       var stuff = [];
+       function add(n, c) {
+         for (let i = 0; i < n; i++)
+           stuff.push(c());
+       }
+
+       let count = 0;
+
+       function obj() { return { count: count++ }; }
+       obj.factor = 1;
+
+       // This creates a closure (a function JSObject) that has captured
+       // a Call object. So each call creates two items.
+       function fun() { let v = count; return () => { return v; } }
+       fun.factor = 2;
+
+       function str() { return 'perambulator' + count++; }
+       str.factor = 1;
+
+       // Eval a fresh text each time, allocating:
+       // - a fresh ScriptSourceObject
+       // - a new JSScripts, not an eval cache hits
+       // - a fresh prototype object
+       // - a fresh Call object, since the eval makes 'ev' heavyweight
+       // - the new function itself
+       function ev()  {
+         return eval(\`(function () { return \${ count++ } })\`);
+       }
+       ev.factor = 5;
+
+       // A new object (1) with a new shape (2) with a new atom (3)
+       function shape() { return { [ 'theobroma' + count++ ]: count }; }
+       shape.factor = 3;
+       `);
+
+let baseline = 0;
+function countIncreasedByAtLeast(n) {
+  let oldBaseline = baseline;
+
+  // Since a census counts only reachable objects, one might assume that calling
+  // GC here would have no effect on the census results. But GC also throws away
+  // JIT code and any objects it might be holding (template objects, say);
+  // takeCensus reaches those. Shake everything loose that we can, to make the
+  // census approximate reachability a bit more closely, and make our results a
+  // bit more predictable.
+  gc(g, 'shrinking');
+
+  baseline = dbg.memory.takeCensus({ breakdown: { by: 'count' } }).count;
+  return baseline >= oldBaseline + n;
+}
+
+countIncreasedByAtLeast(0);
+
+g.add(100, g.obj);
+assertEq(countIncreasedByAtLeast(g.obj.factor * 100), true);
+
+g.add(100, g.fun);
+assertEq(countIncreasedByAtLeast(g.fun.factor * 100), true);
+
+g.add(100, g.str);
+assertEq(countIncreasedByAtLeast(g.str.factor * 100), true);
+
+g.add(100, g.ev);
+assertEq(countIncreasedByAtLeast(g.ev.factor * 100), true);
+
+g.add(100, g.shape);
+assertEq(countIncreasedByAtLeast(g.shape.factor * 100), true);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Memory-takeCensus-09.js
@@ -0,0 +1,74 @@
+// Debugger.Memory.prototype.takeCensus: by: allocationStack breakdown
+
+var g = newGlobal();
+var dbg = new Debugger(g);
+
+g.evaluate(`
+           var log = [];
+           function f() { log.push(allocationMarker()); }
+           function g() { f(); }
+           function h() { f(); }
+           `,
+           { fileName: "Rockford", lineNumber: 1000 });
+
+// Create one allocationMarker with tracking turned off,
+// so it will have no associated stack.
+g.f();
+
+dbg.memory.allocationSamplingProbability = 1;
+dbg.memory.trackingAllocationSites = true;
+
+for ([f, n] of [[g.f, 20], [g.g, 10], [g.h, 5]])
+  for (let i = 0; i < n; i++)
+    f();  // all allocations of allocationMarker occur with this line as the
+          // oldest stack frame.
+
+let census = dbg.memory.takeCensus({ breakdown: { by: 'objectClass',
+                                                  then: { by: 'allocationStack',
+                                                          then: { by: 'count',
+                                                                  label: 'haz stack'
+                                                                },
+                                                          noStack: { by: 'count',
+                                                                     label: 'no haz stack'
+                                                                   }
+                                                        }
+                                                }
+                                   });
+
+let map = census.AllocationMarker;
+assertEq(map instanceof Map, true);
+
+// Gather the stacks we are expecting to appear as keys, and
+// check that there are no unexpected keys.
+let stacks = { };
+
+map.forEach((v, k) => {
+  if (k === 'noStack') {
+    // No need to save this key.
+  } else if (k.functionDisplayName === 'f' &&
+             k.parent.functionDisplayName === null) {
+    stacks.f = k;
+  } else if (k.functionDisplayName === 'f' &&
+             k.parent.functionDisplayName === 'g' &&
+             k.parent.parent.functionDisplayName === null) {
+    stacks.fg = k;
+  } else if (k.functionDisplayName === 'f' &&
+             k.parent.functionDisplayName === 'h' &&
+             k.parent.parent.functionDisplayName === null) {
+    stacks.fh = k;
+  } else {
+    assertEq(true, false);
+  }
+});
+
+assertEq(map.get('noStack').label, 'no haz stack');
+assertEq(map.get('noStack').count, 1);
+
+assertEq(map.get(stacks.f).label, 'haz stack');
+assertEq(map.get(stacks.f).count, 20);
+
+assertEq(map.get(stacks.fg).label, 'haz stack');
+assertEq(map.get(stacks.fg).count, 10);
+
+assertEq(map.get(stacks.fh).label, 'haz stack');
+assertEq(map.get(stacks.fh).count, 5);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Memory-takeCensus-10.js
@@ -0,0 +1,57 @@
+// Check byte counts produced by takeCensus.
+
+let g = newGlobal();
+let dbg = new Debugger(g);
+
+let sizeOfAM = byteSize(allocationMarker());
+
+// Allocate a single allocation marker, and check that we can find it.
+g.eval('let hold = allocationMarker();');
+let census = dbg.memory.takeCensus({ breakdown: { by: 'objectClass' } });
+assertEq(census.AllocationMarker.count, 1);
+assertEq(census.AllocationMarker.bytes, sizeOfAM);
+
+g.evaluate(`
+           var objs = [];
+           function fnerd() {
+             objs.push(allocationMarker());
+             for (let i = 0; i < 10; i++)
+               objs.push(allocationMarker());
+           }
+           `,
+           { fileName: 'J. Edgar Hoover', lineNumber: 2000 });
+
+dbg.memory.allocationSamplingProbability = 1;
+dbg.memory.trackingAllocationSites = true;
+
+g.hold = null;
+g.fnerd();
+
+let census = dbg.memory.takeCensus({
+  breakdown: { by: 'objectClass',
+               then: { by: 'allocationStack' }
+             }
+});
+
+let seen = 0;
+census.AllocationMarker.forEach((v, k) => {
+  assertEq(k.functionDisplayName, 'fnerd');
+  assertEq(k.source, 'J. Edgar Hoover');
+  switch (k.line) {
+  case 2003:
+    assertEq(v.count, 1);
+    assertEq(v.bytes, sizeOfAM);
+    seen++;
+    break;
+
+  case 2005:
+    assertEq(v.count, 10);
+    assertEq(v.bytes, 10 * sizeOfAM);
+    seen++;
+    break;
+
+  default: assertEq(true, false);
+  }
+});
+
+assertEq(seen, 2);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/oomInDebugger.js
@@ -0,0 +1,5 @@
+// |jit-test| --no-ggc; allow-unhandlable-oom
+
+load(libdir + 'oomTest.js');
+var g = newGlobal();
+oomTest(() => Debugger(g));
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/oomInParseFunction.js
@@ -0,0 +1,4 @@
+// |jit-test| --no-ggc; allow-unhandlable-oom
+
+load(libdir + 'oomTest.js');
+oomTest(() => eval("function f() {}"));
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1172498-2.js
@@ -0,0 +1,12 @@
+gczeal(2);
+for (var x = 0; x < 99; x++) {
+    (function() {
+        return function() {
+            new function(y) {
+                return {
+                    e: q => q, function() {}
+                }
+            }
+        }
+    })()()
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1172498.js
@@ -0,0 +1,3 @@
+for(var e=1; e<10000; e++) {
+  new (function  (c) { eval("var y"); });
+}
--- a/js/src/jit/BacktrackingAllocator.cpp
+++ b/js/src/jit/BacktrackingAllocator.cpp
@@ -895,19 +895,27 @@ BacktrackingAllocator::tryMergeBundles(L
     if (IsArgumentSlotDefinition(reg0.def()) || IsArgumentSlotDefinition(reg1.def())) {
         JSScript* script = graph.mir().entryBlock()->info().script();
         if (script && script->argumentsHasVarBinding()) {
             if (*reg0.def()->output() != *reg1.def()->output())
                 return true;
         }
     }
 
+    // Limit the number of times we compare ranges if there are many ranges in
+    // one of the bundles, to avoid quadratic behavior.
+    static const size_t MAX_RANGES = 200;
+
     // Make sure that ranges in the bundles do not overlap.
     LiveRange::BundleLinkIterator iter0 = bundle0->rangesBegin(), iter1 = bundle1->rangesBegin();
+    size_t count = 0;
     while (iter0 && iter1) {
+        if (++count >= MAX_RANGES)
+            return true;
+
         LiveRange* range0 = LiveRange::get(*iter0);
         LiveRange* range1 = LiveRange::get(*iter1);
 
         if (range0->from() >= range1->to())
             iter1++;
         else if (range1->from() >= range0->to())
             iter0++;
         else
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -201,18 +201,20 @@ BaselineCompiler::compile()
                             traceLoggerEnterToggleOffset_.offset(),
                             traceLoggerExitToggleOffset_.offset(),
                             postDebugPrologueOffset_.offset(),
                             icEntries_.length(),
                             pcMappingIndexEntries.length(),
                             pcEntries.length(),
                             bytecodeTypeMapEntries,
                             yieldOffsets_.length()));
-    if (!baselineScript)
+    if (!baselineScript) {
+        ReportOutOfMemory(cx);
         return Method_Error;
+    }
 
     baselineScript->setMethod(code);
     baselineScript->setTemplateScope(templateScope);
 
     JitSpew(JitSpew_BaselineScripts, "Created BaselineScript %p (raw %p) for %s:%d",
             (void*) baselineScript.get(), (void*) code->raw(),
             script->filename(), script->lineno());
 
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -374,19 +374,24 @@ JitCompartment::~JitCompartment()
 {
     js_delete(stubCodes_);
 }
 
 bool
 JitCompartment::initialize(JSContext* cx)
 {
     stubCodes_ = cx->new_<ICStubCodeMap>(cx);
-    if (!stubCodes_ || !stubCodes_->init())
+    if (!stubCodes_)
         return false;
 
+    if (!stubCodes_->init()) {
+        ReportOutOfMemory(cx);
+        return false;
+    }
+
     return true;
 }
 
 bool
 JitCompartment::ensureIonStubsExist(JSContext* cx)
 {
     if (!stringConcatStub_) {
         stringConcatStub_ = generateStringConcatStub(cx);
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -6,16 +6,17 @@
 
 #include "jit/JitFrames-inl.h"
 
 #include "mozilla/SizePrintfMacros.h"
 
 #include "jsfun.h"
 #include "jsobj.h"
 #include "jsscript.h"
+#include "jsutil.h"
 
 #include "gc/Marking.h"
 #include "jit/BaselineDebugModeOSR.h"
 #include "jit/BaselineFrame.h"
 #include "jit/BaselineIC.h"
 #include "jit/BaselineJIT.h"
 #include "jit/Ion.h"
 #include "jit/JitcodeMap.h"
@@ -1042,30 +1043,36 @@ MarkThisAndArguments(JSTracer* trc, JitF
 {
     // Mark |this| and any extra actual arguments for an Ion frame. Marking of
     // formal arguments is taken care of by the frame's safepoint/snapshot,
     // except when the script might have lazy arguments, in which case we mark
     // them as well.
 
     size_t nargs = layout->numActualArgs();
     size_t nformals = 0;
+    size_t newTargetOffset = 0;
     if (CalleeTokenIsFunction(layout->calleeToken())) {
         JSFunction* fun = CalleeTokenToFunction(layout->calleeToken());
         nformals = fun->nonLazyScript()->argumentsHasVarBinding() ? 0 : fun->nargs();
+        newTargetOffset = Max(nargs, fun->nargs());
     }
 
     Value* argv = layout->argv();
 
     // Trace |this|.
     TraceRoot(trc, argv, "ion-thisv");
 
-    // Trace actual arguments and newTarget beyond the formals. Note + 1 for thisv.
-    bool constructing = CalleeTokenIsConstructing(layout->calleeToken());
-    for (size_t i = nformals + 1; i < nargs + 1 + constructing; i++)
+    // Trace actual arguments beyond the formals. Note + 1 for thisv.
+    for (size_t i = nformals + 1; i < nargs + 1; i++)
         TraceRoot(trc, &argv[i], "ion-argv");
+
+    // Always mark the new.target from the frame. It's not in the snapshots.
+    // +1 to pass |this|
+    if (CalleeTokenIsConstructing(layout->calleeToken()))
+        TraceRoot(trc, &argv[1 + newTargetOffset], "ion-newTarget");
 }
 
 static void
 MarkThisAndArguments(JSTracer* trc, const JitFrameIterator& frame)
 {
     JitFrameLayout* layout = frame.jsFrame();
     MarkThisAndArguments(trc, layout);
 }
--- a/js/src/jit/JitcodeMap.cpp
+++ b/js/src/jit/JitcodeMap.cpp
@@ -7,16 +7,17 @@
 #include "jit/JitcodeMap.h"
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/SizePrintfMacros.h"
 #include "mozilla/UniquePtr.h"
 #include "jsprf.h"
 #include "gc/Marking.h"
+#include "gc/Statistics.h"
 
 #include "jit/BaselineJIT.h"
 #include "jit/JitSpewer.h"
 
 #include "js/Vector.h"
 #include "vm/SPSProfiler.h"
 #include "jsscriptinlines.h"
 
@@ -470,18 +471,22 @@ JitcodeGlobalTable::lookupForSampler(voi
     }
 
 #ifdef DEBUG
     // JitcodeGlobalEntries are marked during the beginning of the sweep
     // phase. A read barrier is not needed, as any JS frames sampled during
     // the sweep phase of the GC must be on stack, and on-stack frames must
     // already be marked at the beginning of the sweep phase. This assumption
     // is verified below.
-    if (rt->isHeapBusy() && rt->gc.state() == gc::SWEEP)
+    if (rt->isHeapBusy() &&
+        rt->gc.stats.currentPhase() >= gcstats::PHASE_FINALIZE_START &&
+        rt->gc.stats.currentPhase() <= gcstats::PHASE_FINALIZE_END)
+    {
         MOZ_ASSERT(entry->isMarkedFromAnyThread(rt));
+    }
 #endif
 
     *result = *entry;
     return true;
 }
 
 JitcodeGlobalEntry*
 JitcodeGlobalTable::lookupInternal(void* ptr)
--- a/js/src/jit/arm64/Trampoline-arm64.cpp
+++ b/js/src/jit/arm64/Trampoline-arm64.cpp
@@ -80,24 +80,22 @@ JitRuntime::generateEnterJIT(JSContext* 
     masm.Mov(PseudoStackPointer64, sp);
     masm.SetStackPointer64(PseudoStackPointer64);
 
     // Save the stack pointer at this point for Baseline OSR.
     masm.moveStackPtrTo(BaselineFrameReg);
     // Remember stack depth without padding and arguments.
     masm.moveStackPtrTo(r19);
 
-    // If constructing, include newTarget.
-    // TODO: ARM64 support must be written.
-    //  Refer to git commit 7180509ff, Bug 1141865 Part 2.
+    // If constructing, include newTarget in argument vector.
     {
         Label noNewTarget;
         Imm32 constructingToken(CalleeToken_FunctionConstructing);
         masm.branchTest32(Assembler::Zero, reg_callee, constructingToken, &noNewTarget);
-        masm.breakpoint(); // TODO: include newTarget in the vector accounting.
+        masm.add32(Imm32(1), reg_argc);
         masm.bind(&noNewTarget);
     }
 
     // JitFrameLayout is as follows (higher is higher in memory):
     //  N*8  - [ JS argument vector ] (base 16-byte aligned)
     //  8    - numActualArgs
     //  8    - calleeToken (16-byte aligned)
     //  8    - frameDescriptor
@@ -323,74 +321,92 @@ JitRuntime::generateArgumentsRectifier(J
 
     // Save the return address for later.
     masm.push(lr);
 
     // Load the information that the rectifier needs from the stack.
     masm.Ldr(w0, MemOperand(masm.GetStackPointer64(), RectifierFrameLayout::offsetOfNumActualArgs()));
     masm.Ldr(x1, MemOperand(masm.GetStackPointer64(), RectifierFrameLayout::offsetOfCalleeToken()));
 
-    // Extract a JSFunction pointer from the callee token.
-    masm.And(x6, x1, Operand(CalleeTokenMask));
+    // Extract a JSFunction pointer from the callee token and keep the
+    // intermediary to avoid later recalculation.
+    masm.And(x5, x1, Operand(CalleeTokenMask));
 
     // Get the arguments from the function object.
-    masm.Ldrh(x6, MemOperand(x6, JSFunction::offsetOfNargs()));
-    masm.Mov(x7, x6);
+    masm.Ldrh(x6, MemOperand(x5, JSFunction::offsetOfNargs()));
+
+    static_assert(CalleeToken_FunctionConstructing == 0x1, "Constructing must be low-order bit");
+    masm.And(x4, x1, Operand(CalleeToken_FunctionConstructing));
+    masm.Add(x7, x6, x4);
 
     // Calculate the position that our arguments are at before sp gets modified.
+    MOZ_ASSERT(ArgumentsRectifierReg == r8, "x8 used for argc in Arguments Rectifier");
     masm.Add(x3, masm.GetStackPointer64(), Operand(x8, vixl::LSL, 3));
     masm.Add(x3, x3, Operand(sizeof(RectifierFrameLayout)));
 
-    // Pad to a multiple of 16 bytes.
+    // Pad to a multiple of 16 bytes. This neglects the |this| value,
+    // which will also be pushed, because the rest of the frame will
+    // round off that value. See pushes of |argc|, |callee| and |desc| below.
     Label noPadding;
-    masm.Tbnz(x6, 0, &noPadding);
+    masm.Tbnz(x7, 0, &noPadding);
     masm.asVIXL().Push(xzr);
     masm.Add(x7, x7, Operand(1));
     masm.bind(&noPadding);
 
+    {
+        Label notConstructing;
+        masm.Cbz(x4, &notConstructing);
+
+        // new.target lives at the end of the pushed args
+        // NB: The arg vector holder starts at the beginning of the last arg,
+        //     add a value to get to argv[argc]
+        masm.loadPtr(Address(r3, sizeof(Value)), r4);
+        masm.Push(r4);
+
+        masm.bind(&notConstructing);
+    }
+
     // Calculate the number of undefineds that need to be pushed.
     masm.Sub(w2, w6, w8);
 
     // Put an undefined in a register so it can be pushed.
     masm.moveValue(UndefinedValue(), r4);
 
     // Push undefined N times.
     {
         Label undefLoopTop;
         masm.bind(&undefLoopTop);
         masm.Push(r4);
         masm.Subs(w2, w2, Operand(1));
         masm.B(&undefLoopTop, Assembler::NonZero);
     }
 
-    // Arguments copy loop.
+    // Arguments copy loop. Copy for x8 >= 0 to include |this|.
     {
         Label copyLoopTop;
         masm.bind(&copyLoopTop);
         masm.Ldr(x4, MemOperand(x3, -sizeof(Value), vixl::PostIndex));
         masm.Push(r4);
         masm.Subs(x8, x8, Operand(1));
         masm.B(&copyLoopTop, Assembler::NotSigned);
     }
 
-    // Fix up the size of the stack frame.
+    // Fix up the size of the stack frame. +1 accounts for |this|.
     masm.Add(x6, x7, Operand(1));
     masm.Lsl(x6, x6, 3);
 
     // Make that into a frame descriptor.
     masm.makeFrameDescriptor(r6, JitFrame_Rectifier);
 
     masm.push(r0,  // Number of actual arguments.
               r1,  // Callee token.
               r6); // Frame descriptor.
 
-    // Didn't we just compute this? Can't we just stick that value in one of our 30 GPR's?
     // Load the address of the code that is getting called.
-    masm.And(x1, x1, Operand(CalleeTokenMask));
-    masm.Ldr(x3, MemOperand(x1, JSFunction::offsetOfNativeOrScript()));
+    masm.Ldr(x3, MemOperand(x5, JSFunction::offsetOfNativeOrScript()));
     masm.loadBaselineOrIonRaw(r3, r3, nullptr);
     masm.call(r3);
     uint32_t returnOffset = masm.currentOffset();
 
     // Clean up!
     // Get the size of the stack frame, and clean up the later fixed frame.
     masm.Ldr(x4, MemOperand(masm.GetStackPointer64(), 24, vixl::PostIndex));
 
--- a/js/src/jit/shared/CodeGenerator-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-shared.cpp
@@ -1026,17 +1026,18 @@ CodeGeneratorShared::ensureOsiSpace()
     // At points where we want to ensure that invalidation won't corrupt an
     // important instruction, we make sure to pad with nops.
     if (masm.currentOffset() - lastOsiPointOffset_ < Assembler::PatchWrite_NearCallSize()) {
         int32_t paddingSize = Assembler::PatchWrite_NearCallSize();
         paddingSize -= masm.currentOffset() - lastOsiPointOffset_;
         for (int32_t i = 0; i < paddingSize; ++i)
             masm.nop();
     }
-    MOZ_ASSERT(masm.currentOffset() - lastOsiPointOffset_ >= Assembler::PatchWrite_NearCallSize());
+    MOZ_ASSERT_IF(!masm.oom(),
+                  masm.currentOffset() - lastOsiPointOffset_ >= Assembler::PatchWrite_NearCallSize());
     lastOsiPointOffset_ = masm.currentOffset();
 }
 
 uint32_t
 CodeGeneratorShared::markOsiPoint(LOsiPoint* ins)
 {
     encode(ins->snapshot());
     ensureOsiSpace();
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -411,16 +411,17 @@ MSG_DEF(JSMSG_DEBUG_VARIABLE_NOT_FOUND,0
 MSG_DEF(JSMSG_DEBUG_WRAPPER_IN_WAY,    3, JSEXN_TYPEERR, "{0} is {1}{2}a global object, but a direct reference is required")
 MSG_DEF(JSMSG_NOT_CALLABLE_OR_UNDEFINED, 0, JSEXN_TYPEERR, "value is not a function or undefined")
 MSG_DEF(JSMSG_NOT_TRACKING_ALLOCATIONS, 1, JSEXN_ERR, "Cannot call {0} without setting trackingAllocationSites to true")
 MSG_DEF(JSMSG_OBJECT_METADATA_CALLBACK_ALREADY_SET, 0, JSEXN_ERR, "Cannot track object allocation, because other tools are already doing so")
 MSG_DEF(JSMSG_QUERY_INNERMOST_WITHOUT_LINE_URL, 0, JSEXN_TYPEERR, "findScripts query object with 'innermost' property must have 'line' and either 'displayURL', 'url', or 'source'")
 MSG_DEF(JSMSG_QUERY_LINE_WITHOUT_URL, 0, JSEXN_TYPEERR, "findScripts query object has 'line' property, but no 'displayURL', 'url', or 'source' property")
 MSG_DEF(JSMSG_DEBUG_CANT_SET_OPT_ENV, 1, JSEXN_REFERENCEERR, "can't set `{0}' in an optimized-out environment")
 MSG_DEF(JSMSG_DEBUG_INVISIBLE_COMPARTMENT, 0, JSEXN_TYPEERR, "object in compartment marked as invisible to Debugger")
+MSG_DEF(JSMSG_DEBUG_CENSUS_BREAKDOWN,  1, JSEXN_TYPEERR, "unrecognized 'by' value in takeCensus breakdown: {0}")
 
 // Tracelogger
 MSG_DEF(JSMSG_TRACELOGGER_ENABLE_FAIL, 1, JSEXN_ERR, "enabling tracelogger failed: {0}")
 
 // Intl
 MSG_DEF(JSMSG_DATE_NOT_FINITE,         0, JSEXN_RANGEERR, "date value is not finite in DateTimeFormat.format()")
 MSG_DEF(JSMSG_INTERNAL_INTL_ERROR,     0, JSEXN_ERR, "internal error while computing Intl data")
 MSG_DEF(JSMSG_INTL_OBJECT_NOT_INITED,  3, JSEXN_TYPEERR, "Intl.{0}.prototype.{1} called on value that's not an object initialized as a {2}")
--- a/js/src/jsapi-tests/testIntString.cpp
+++ b/js/src/jsapi-tests/testIntString.cpp
@@ -8,35 +8,35 @@
 #include "jsapi-tests/tests.h"
 
 BEGIN_TEST(testIntString_bug515273)
 {
     JS::RootedValue v(cx);
 
     EVAL("'1';", &v);
     JSString* str = v.toString();
-    CHECK(JS_StringHasBeenInterned(cx, str));
+    CHECK(JS_StringHasBeenPinned(cx, str));
     CHECK(JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(str), "1"));
 
     EVAL("'42';", &v);
     str = v.toString();
-    CHECK(JS_StringHasBeenInterned(cx, str));
+    CHECK(JS_StringHasBeenPinned(cx, str));
     CHECK(JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(str), "42"));
 
     EVAL("'111';", &v);
     str = v.toString();
-    CHECK(JS_StringHasBeenInterned(cx, str));
+    CHECK(JS_StringHasBeenPinned(cx, str));
     CHECK(JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(str), "111"));
 
     /* Test other types of static strings. */
     EVAL("'a';", &v);
     str = v.toString();
-    CHECK(JS_StringHasBeenInterned(cx, str));
+    CHECK(JS_StringHasBeenPinned(cx, str));
     CHECK(JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(str), "a"));
 
     EVAL("'bc';", &v);
     str = v.toString();
-    CHECK(JS_StringHasBeenInterned(cx, str));
+    CHECK(JS_StringHasBeenPinned(cx, str));
     CHECK(JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(str), "bc"));
 
     return true;
 }
 END_TEST(testIntString_bug515273)
--- a/js/src/jsapi-tests/testIntern.cpp
+++ b/js/src/jsapi-tests/testIntern.cpp
@@ -5,44 +5,44 @@
 #include "jsatom.h"
 
 #include "gc/Marking.h"
 #include "jsapi-tests/tests.h"
 #include "vm/String.h"
 
 using mozilla::ArrayLength;
 
-BEGIN_TEST(testAtomizedIsNotInterned)
+BEGIN_TEST(testAtomizedIsNotPinned)
 {
     /* Try to pick a string that won't be interned by other tests in this runtime. */
     static const char someChars[] = "blah blah blah? blah blah blah";
     JS::Rooted<JSAtom*> atom(cx, js::Atomize(cx, someChars, ArrayLength(someChars)));
-    CHECK(!JS_StringHasBeenInterned(cx, atom));
-    CHECK(JS_InternJSString(cx, atom));
-    CHECK(JS_StringHasBeenInterned(cx, atom));
+    CHECK(!JS_StringHasBeenPinned(cx, atom));
+    CHECK(JS_AtomizeAndPinJSString(cx, atom));
+    CHECK(JS_StringHasBeenPinned(cx, atom));
     return true;
 }
-END_TEST(testAtomizedIsNotInterned)
+END_TEST(testAtomizedIsNotPinned)
 
 struct StringWrapperStruct
 {
     JSString* str;
     bool     strOk;
 } sw;
 
-BEGIN_TEST(testInternAcrossGC)
+BEGIN_TEST(testPinAcrossGC)
 {
-    sw.str = JS_InternString(cx, "wrapped chars that another test shouldn't be using");
+    sw.str = JS_AtomizeAndPinString(cx, "wrapped chars that another test shouldn't be using");
     sw.strOk = false;
     CHECK(sw.str);
     JS_AddFinalizeCallback(rt, FinalizeCallback, nullptr);
     JS_GC(rt);
     CHECK(sw.strOk);
     return true;
 }
 
 static void
 FinalizeCallback(JSFreeOp* fop, JSFinalizeStatus status, bool isCompartmentGC, void* data)
 {
     if (status == JSFINALIZE_GROUP_START)
         sw.strOk = js::gc::IsMarkedUnbarriered(&sw.str);
 }
-END_TEST(testInternAcrossGC)
+END_TEST(testPinAcrossGC)
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -991,17 +991,17 @@ JS_PUBLIC_API(void*)
 JS_GetCompartmentPrivate(JSCompartment* compartment)
 {
     return compartment->data;
 }
 
 JS_PUBLIC_API(JSAddonId*)
 JS::NewAddonId(JSContext* cx, HandleString str)
 {
-    return static_cast<JSAddonId*>(JS_InternJSString(cx, str));
+    return static_cast<JSAddonId*>(JS_AtomizeAndPinJSString(cx, str));
 }
 
 JS_PUBLIC_API(JSString*)
 JS::StringOfAddonId(JSAddonId* id)
 {
     return id;
 }
 
@@ -2740,39 +2740,39 @@ PropertySpecNameToSymbolCode(const char*
 {
     MOZ_ASSERT(JS::PropertySpecNameIsSymbol(name));
     uintptr_t u = reinterpret_cast<uintptr_t>(name);
     return JS::SymbolCode(u - 1);
 }
 
 static bool
 PropertySpecNameToId(JSContext* cx, const char* name, MutableHandleId id,
-                     js::InternBehavior ib = js::DoNotInternAtom)
+                     js::PinningBehavior pin = js::DoNotPinAtom)
 {
     if (JS::PropertySpecNameIsSymbol(name)) {
         JS::SymbolCode which = PropertySpecNameToSymbolCode(name);
         id.set(SYMBOL_TO_JSID(cx->wellKnownSymbols().get(which)));
     } else {
-        JSAtom* atom = Atomize(cx, name, strlen(name), ib);
+        JSAtom* atom = Atomize(cx, name, strlen(name), pin);
         if (!atom)
             return false;
         id.set(AtomToId(atom));
     }
     return true;
 }
 
 JS_PUBLIC_API(bool)
 JS::PropertySpecNameToPermanentId(JSContext* cx, const char* name, jsid* idp)
 {
     // We are calling fromMarkedLocation(idp) even though idp points to a
     // location that will never be marked. This is OK because the whole point
     // of this API is to populate *idp with a jsid that does not need to be
     // marked.
     return PropertySpecNameToId(cx, name, MutableHandleId::fromMarkedLocation(idp),
-                                js::InternAtom);
+                                js::PinAtom);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineProperties(JSContext* cx, HandleObject obj, const JSPropertySpec* ps)
 {
     RootedId id(cx);
 
     for (; ps->name; ps++) {
@@ -4765,59 +4765,59 @@ JS_NewStringCopyZ(JSContext* cx, const c
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     if (!s || !*s)
         return cx->runtime()->emptyString;
     return NewStringCopyZ<CanGC>(cx, s);
 }
 
 JS_PUBLIC_API(bool)
-JS_StringHasBeenInterned(JSContext* cx, JSString* str)
+JS_StringHasBeenPinned(JSContext* cx, JSString* str)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
 
     if (!str->isAtom())
         return false;
 
-    return AtomIsInterned(cx, &str->asAtom());
+    return AtomIsPinned(cx, &str->asAtom());
 }
 
 JS_PUBLIC_API(jsid)
 INTERNED_STRING_TO_JSID(JSContext* cx, JSString* str)
 {
     MOZ_ASSERT(str);
     MOZ_ASSERT(((size_t)str & JSID_TYPE_MASK) == 0);
-    MOZ_ASSERT_IF(cx, JS_StringHasBeenInterned(cx, str));
+    MOZ_ASSERT_IF(cx, JS_StringHasBeenPinned(cx, str));
     return AtomToId(&str->asAtom());
 }
 
 JS_PUBLIC_API(JSString*)
-JS_InternJSString(JSContext* cx, HandleString str)
+JS_AtomizeAndPinJSString(JSContext* cx, HandleString str)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
-    JSAtom* atom = AtomizeString(cx, str, InternAtom);
-    MOZ_ASSERT_IF(atom, JS_StringHasBeenInterned(cx, atom));
+    JSAtom* atom = AtomizeString(cx, str, PinAtom);
+    MOZ_ASSERT_IF(atom, JS_StringHasBeenPinned(cx, atom));
     return atom;
 }
 
 JS_PUBLIC_API(JSString*)
-JS_InternString(JSContext* cx, const char* s)
-{
-    return JS_InternStringN(cx, s, strlen(s));
+JS_AtomizeAndPinString(JSContext* cx, const char* s)
+{
+    return JS_AtomizeAndPinStringN(cx, s, strlen(s));
 }
 
 JS_PUBLIC_API(JSString*)
-JS_InternStringN(JSContext* cx, const char* s, size_t length)
+JS_AtomizeAndPinStringN(JSContext* cx, const char* s, size_t length)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
-    JSAtom* atom = Atomize(cx, s, length, InternAtom);
-    MOZ_ASSERT_IF(atom, JS_StringHasBeenInterned(cx, atom));
+    JSAtom* atom = Atomize(cx, s, length, PinAtom);
+    MOZ_ASSERT_IF(atom, JS_StringHasBeenPinned(cx, atom));
     return atom;
 }
 
 JS_PUBLIC_API(JSString*)
 JS_NewUCString(JSContext* cx, char16_t* chars, size_t length)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
@@ -4840,29 +4840,29 @@ JS_NewUCStringCopyZ(JSContext* cx, const
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     if (!s)
         return cx->runtime()->emptyString;
     return NewStringCopyZ<CanGC>(cx, s);
 }
 
 JS_PUBLIC_API(JSString*)
-JS_InternUCStringN(JSContext* cx, const char16_t* s, size_t length)
+JS_AtomizeAndPinUCStringN(JSContext* cx, const char16_t* s, size_t length)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
-    JSAtom* atom = AtomizeChars(cx, s, length, InternAtom);
-    MOZ_ASSERT_IF(atom, JS_StringHasBeenInterned(cx, atom));
+    JSAtom* atom = AtomizeChars(cx, s, length, PinAtom);
+    MOZ_ASSERT_IF(atom, JS_StringHasBeenPinned(cx, atom));
     return atom;
 }
 
 JS_PUBLIC_API(JSString*)
-JS_InternUCString(JSContext* cx, const char16_t* s)
-{
-    return JS_InternUCStringN(cx, s, js_strlen(s));
+JS_AtomizeAndPinUCString(JSContext* cx, const char16_t* s)
+{
+    return JS_AtomizeAndPinUCStringN(cx, s, js_strlen(s));
 }
 
 JS_PUBLIC_API(size_t)
 JS_GetStringLength(JSString* str)
 {
     return str->length();
 }
 
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -742,17 +742,17 @@ JS_NumberValue(double d)
     if (mozilla::NumberIsInt32(d, &i))
         return JS::Int32Value(i);
     return DOUBLE_TO_JSVAL(d);
 }
 
 /************************************************************************/
 
 JS_PUBLIC_API(bool)
-JS_StringHasBeenInterned(JSContext* cx, JSString* str);
+JS_StringHasBeenPinned(JSContext* cx, JSString* str);
 
 namespace JS {
 
 // Container class for passing in script source buffers to the JS engine.  This
 // not only groups the buffer and length values, it also provides a way to
 // optionally pass ownership of the buffer to the JS engine without copying.
 // Rules for use:
 //
@@ -4066,38 +4066,38 @@ class MOZ_STACK_CLASS JS_PUBLIC_API(Auto
  */
 extern JS_PUBLIC_API(JSString*)
 JS_NewStringCopyN(JSContext* cx, const char* s, size_t n);
 
 extern JS_PUBLIC_API(JSString*)
 JS_NewStringCopyZ(JSContext* cx, const char* s);
 
 extern JS_PUBLIC_API(JSString*)
-JS_InternJSString(JSContext* cx, JS::HandleString str);
+JS_AtomizeAndPinJSString(JSContext* cx, JS::HandleString str);
 
 extern JS_PUBLIC_API(JSString*)
-JS_InternStringN(JSContext* cx, const char* s, size_t length);
+JS_AtomizeAndPinStringN(JSContext* cx, const char* s, size_t length);
 
 extern JS_PUBLIC_API(JSString*)
-JS_InternString(JSContext* cx, const char* s);
+JS_AtomizeAndPinString(JSContext* cx, const char* s);
 
 extern JS_PUBLIC_API(JSString*)
 JS_NewUCString(JSContext* cx, char16_t* chars, size_t length);
 
 extern JS_PUBLIC_API(JSString*)
 JS_NewUCStringCopyN(JSContext* cx, const char16_t* s, size_t n);
 
 extern JS_PUBLIC_API(JSString*)
 JS_NewUCStringCopyZ(JSContext* cx, const char16_t* s);
 
 extern JS_PUBLIC_API(JSString*)
-JS_InternUCStringN(JSContext* cx, const char16_t* s, size_t length);
+JS_AtomizeAndPinUCStringN(JSContext* cx, const char16_t* s, size_t length);
 
 extern JS_PUBLIC_API(JSString*)
-JS_InternUCString(JSContext* cx, const char16_t* s);
+JS_AtomizeAndPinUCString(JSContext* cx, const char16_t* s);
 
 extern JS_PUBLIC_API(bool)
 JS_CompareStrings(JSContext* cx, JSString* str1, JSString* str2, int32_t* result);
 
 extern JS_PUBLIC_API(bool)
 JS_StringEqualsAscii(JSContext* cx, JSString* str, const char* asciiBytes, bool* match);
 
 extern JS_PUBLIC_API(size_t)
@@ -4108,19 +4108,18 @@ JS_FileEscapedString(FILE* fp, JSString*
 
 /*
  * Extracting string characters and length.
  *
  * While getting the length of a string is infallible, getting the chars can
  * fail. As indicated by the lack of a JSContext parameter, there are two
  * special cases where getting the chars is infallible:
  *
- * The first case is interned strings, i.e., strings from JS_InternString or
- * JSID_TO_STRING(id), using JS_GetLatin1InternedStringChars or
- * JS_GetTwoByteInternedStringChars.
+ * The first case is for strings that have been atomized, e.g. directly by
+ * JS_AtomizeAndPinString or implicitly because it is stored in a jsid.
  *
  * The second case is "flat" strings that have been explicitly prepared in a
  * fallible context by JS_FlattenString. To catch errors, a separate opaque
  * JSFlatString type is returned by JS_FlattenString and expected by
  * JS_GetFlatStringChars. Note, though, that this is purely a syntactic
  * distinction: the input and output of JS_FlattenString are the same actual
  * GC-thing. If a JSString is known to be flat, JS_ASSERT_STRING_IS_FLAT can be
  * used to make a debug-checked cast. Example:
@@ -4171,22 +4170,16 @@ extern JS_PUBLIC_API(char16_t)
 JS_GetFlatStringCharAt(JSFlatString* str, size_t index);
 
 extern JS_PUBLIC_API(const char16_t*)
 JS_GetTwoByteExternalStringChars(JSString* str);
 
 extern JS_PUBLIC_API(bool)
 JS_CopyStringChars(JSContext* cx, mozilla::Range<char16_t> dest, JSString* str);
 
-extern JS_PUBLIC_API(const JS::Latin1Char*)
-JS_GetLatin1InternedStringChars(const JS::AutoCheckCannotGC& nogc, JSString* str);
-
-extern JS_PUBLIC_API(const char16_t*)
-JS_GetTwoByteInternedStringChars(const JS::AutoCheckCannotGC& nogc, JSString* str);
-
 extern JS_PUBLIC_API(JSFlatString*)
 JS_FlattenString(JSContext* cx, JSString* str);
 
 extern JS_PUBLIC_API(const JS::Latin1Char*)
 JS_GetLatin1FlatStringChars(const JS::AutoCheckCannotGC& nogc, JSFlatString* str);
 
 extern JS_PUBLIC_API(const char16_t*)
 JS_GetTwoByteFlatStringChars(const JS::AutoCheckCannotGC& nogc, JSFlatString* str);
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -138,17 +138,17 @@ JSRuntime::initializeAtoms(JSContext* cx
     };
 
     commonNames = cx->new_<JSAtomState>();
     if (!commonNames)
         return false;
 
     ImmutablePropertyNamePtr* names = reinterpret_cast<ImmutablePropertyNamePtr*>(commonNames);
     for (size_t i = 0; i < ArrayLength(cachedNames); i++, names++) {
-        JSAtom* atom = Atomize(cx, cachedNames[i].str, cachedNames[i].length, InternAtom);
+        JSAtom* atom = Atomize(cx, cachedNames[i].str, cachedNames[i].length, PinAtom);
         if (!atom)
             return false;
         names->init(atom->asPropertyName());
     }
     MOZ_ASSERT(uintptr_t(names) == uintptr_t(commonNames + 1));
 
     emptyString = commonNames->empty;
 
@@ -192,21 +192,21 @@ JSRuntime::finishAtoms()
 }
 
 void
 js::MarkAtoms(JSTracer* trc)
 {
     JSRuntime* rt = trc->runtime();
     for (AtomSet::Enum e(rt->atoms()); !e.empty(); e.popFront()) {
         const AtomStateEntry& entry = e.front();
-        if (!entry.isTagged())
+        if (!entry.isPinned())
             continue;
 
         JSAtom* atom = entry.asPtr();
-        bool tagged = entry.isTagged();
+        bool tagged = entry.isPinned();
         TraceRoot(trc, &atom, "interned_atom");
         if (entry.asPtr() != atom)
             e.rekeyFront(AtomHasher::Lookup(atom), AtomStateEntry(atom, tagged));
     }
 }
 
 void
 js::MarkPermanentAtoms(JSTracer* trc)
@@ -252,17 +252,17 @@ JSRuntime::sweepAtoms()
         return;
 
     for (AtomSet::Enum e(*atoms_); !e.empty(); e.popFront()) {
         AtomStateEntry entry = e.front();
         JSAtom* atom = entry.asPtr();
         bool isDying = IsAboutToBeFinalizedUnbarriered(&atom);
 
         /* Pinned or interned key cannot be finalized. */
-        MOZ_ASSERT_IF(hasContexts() && entry.isTagged(), !isDying);
+        MOZ_ASSERT_IF(hasContexts() && entry.isPinned(), !isDying);
 
         if (isDying)
             e.removeFront();
     }
 }
 
 bool
 JSRuntime::transformToPermanentAtoms(JSContext* cx)
@@ -284,17 +284,17 @@ JSRuntime::transformToPermanentAtoms(JSC
         JSAtom* atom = entry.asPtr();
         atom->morphIntoPermanentAtom();
     }
 
     return true;