Merge mozilla-central to b2g-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 22 Jul 2015 14:12:03 +0200
changeset 254092 11a3ce33bf67534bc659876618e63c93babaa8da
parent 254091 db418aba269a5aaafc057e67b4badbef6ff30af9 (current diff)
parent 254082 e7434cafdf2f35ebd89f0120e33b7d10c73b84a7 (diff)
child 254093 e20f0a1b77802e7bb8713cbca1887ec22ffb6714
push id29088
push userryanvm@gmail.com
push dateWed, 22 Jul 2015 20:26:24 +0000
treeherdermozilla-central@1b34c4729114 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
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-central to b2g-inbound
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 1164310 requires a clobber due to bug 1177844
+Bug 1186003 needed a CLOBBER 
--- a/b2g/components/FxAccountsMgmtService.jsm
+++ b/b2g/components/FxAccountsMgmtService.jsm
@@ -88,22 +88,29 @@ this.FxAccountsMgmtService = {
       return;
     }
     // Backwards compatibility: handle accountId coming from Gaia
     if (data.accountId && typeof(data.email === "undefined")) {
       data.email = data.accountId;
       delete data.accountId;
     }
 
+    // XXX dirty hack because Gaia is sending getAccounts.
+    if (data.method == "getAccounts") {
+      data.method = "getAccount";
+    }
+
     switch(data.method) {
-      case "getAccounts":
-        FxAccountsManager.getAccount().then(
-          account => {
-            // We only expose the email and verification status so far.
-            self._onFulfill(msg.id, account);
+      case "getAccount":
+      case "getKeys":
+        FxAccountsManager[data.method]().then(
+          result => {
+            // For the getAccounts case, we only expose the email and
+            // verification status so far.
+            self._onFulfill(msg.id, result);
           },
           reason => {
             self._onReject(msg.id, reason);
           }
         ).then(null, Components.utils.reportError);
         break;
       case "logout":
         FxAccountsManager.signOut().then(
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -427,16 +427,19 @@ pref("browser.search.showOneOffButtons",
 pref("browser.search.hiddenOneOffs", "");
 
 #ifdef XP_WIN
 pref("browser.search.redirectWindowsSearch", true);
 #else
 pref("browser.search.redirectWindowsSearch", false);
 #endif
 
+pref("browser.usedOnWindows10", false);
+pref("browser.usedOnWindows10.introURL", "https://www.mozilla.org/%LOCALE%/firefox/windows-10/welcome/?utm_source=firefox-browser&utm_medium=firefox-browser");
+
 pref("browser.sessionhistory.max_entries", 50);
 
 // Built-in default permissions.
 pref("permissions.manager.defaultsUrl", "resource://app/defaults/permissions");
 
 // handle links targeting new windows
 // 1=current window/tab, 2=new window, 3=new tab in most recent window
 pref("browser.link.open_newwindow", 3);
@@ -1922,16 +1925,22 @@ pref("privacy.trackingprotection.introUR
 #ifdef NIGHTLY_BUILD
 // At the moment, autostart.2 is used, while autostart.1 is unused.
 // We leave it here set to false to reset users' defaults and allow
 // us to change everybody to true in the future, when desired.
 pref("browser.tabs.remote.autostart.1", false);
 pref("browser.tabs.remote.autostart.2", true);
 #endif
 
+#ifdef NIGHTLY_BUILD
+#if defined(XP_MACOSX)
+pref("layers.async-pan-zoom.enabled", true);
+#endif
+#endif
+
 #ifdef E10S_TESTING_ONLY
 // Enable e10s add-on interposition by default.
 pref("extensions.interposition.enabled", true);
 pref("extensions.interposition.prefetching", true);
 #endif
 
 pref("browser.defaultbrowser.notificationbar", false);
 
--- a/browser/base/content/newtab/newTab.css
+++ b/browser/base/content/newtab/newTab.css
@@ -460,18 +460,16 @@ input[type=button] {
 
 #newtab-customize-panel {
   z-index: 999;
   margin-top: 55px;
   min-width: 270px;
   position: absolute;
   top: 100%;
   right: -25px;
-  background-color: white;
-  border-radius: 6px;
   filter: drop-shadow(0 0 1px rgba(0,0,0,0.4)) drop-shadow(0 3px 4px rgba(0,0,0,0.4));
   transition: all 200ms ease-in-out;
   transform-origin: top right;
   transform: translate(-30px, -20px) scale(0) translate(30px, 20px);
 }
 
 #newtab-customize-panel:-moz-locale-dir(rtl) {
   transform-origin: 40px top 20px;
@@ -507,16 +505,22 @@ input[type=button] {
   cursor: default;
   border-radius: 5px 5px 0px 0px;
   max-width: 300px;
   overflow: hidden;
   display: table-cell;
   border-top: none;
 }
 
+#newtab-customize-panel-inner-wrapper {
+  background-color: #FFFFFF;
+  border-radius: 6px;
+  overflow: hidden;
+}
+
 #newtab-customize-title > label {
   cursor: default;
 }
 
 #newtab-customize-panel > .panel-arrowcontainer > .panel-arrowcontent {
   padding: 0;
 }
 
--- a/browser/base/content/newtab/newTab.xul
+++ b/browser/base/content/newtab/newTab.xul
@@ -18,34 +18,36 @@
 
 <xul:window id="newtab-window" xmlns="http://www.w3.org/1999/xhtml"
             xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
             title="&newtab.pageTitle;">
 
   <div class="newtab-customize-panel-container">
     <div id="newtab-customize-panel" orient="vertical">
         <div id="newtab-customize-panel-anchor"></div>
-        <div id="newtab-customize-title" class="newtab-customize-panel-item">
+        <div id="newtab-customize-panel-inner-wrapper">
+          <div id="newtab-customize-title" class="newtab-customize-panel-item">
             <label>&newtab.customize.cog.title2;</label>
-        </div>
+          </div>
 
-        <div class="newtab-customize-complex-option">
+          <div class="newtab-customize-complex-option">
             <div id="newtab-customize-classic" class="newtab-customize-panel-superitem newtab-customize-panel-item selectable">
                 <label>&newtab.customize.classic;</label>
             </div>
             <div id="newtab-customize-enhanced" class="newtab-customize-panel-subitem">
                 <label class="checkbox"></label>
                 <label>&newtab.customize.cog.enhanced;</label>
             </div>
-        </div>
-        <div id="newtab-customize-blank" class="newtab-customize-panel-item selectable">
+          </div>
+          <div id="newtab-customize-blank" class="newtab-customize-panel-item selectable">
             <label>&newtab.customize.blank2;</label>
-        </div>
-        <div id="newtab-customize-learn" class="newtab-customize-panel-item">
+          </div>
+          <div id="newtab-customize-learn" class="newtab-customize-panel-item">
             <label>&newtab.customize.cog.learn;</label>
+          </div>
         </div>
     </div>
   </div>
 
   <div id="newtab-customize-overlay"></div>
 
   <div id="newtab-intro-mask">
     <div id="newtab-intro-modal">
--- a/browser/base/content/test/general/browser_bug427559.js
+++ b/browser/base/content/test/general/browser_bug427559.js
@@ -13,41 +13,26 @@ const URL = 'data:text/html;charset=utf-
 
 function getFocusedLocalName(browser) {
   return ContentTask.spawn(browser, null, function* () {
     return content.document.activeElement.localName;
   });
 }
 
 add_task(function* () {
-  gBrowser.selectedTab = gBrowser.addTab(URL);
-  let browser = gBrowser.selectedBrowser;
-  yield BrowserTestUtils.browserLoaded(browser);
+  let testTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, URL);
+
+  let browser = testTab.linkedBrowser;
 
   is((yield getFocusedLocalName(browser)), "button", "button is focused");
 
-  let promiseFocused = ContentTask.spawn(browser, null, function* () {
-    return new Promise(resolve => {
-      content.addEventListener("focus", function onFocus({target}) {
-        if (String(target.location).startsWith("data:")) {
-          content.removeEventListener("focus", onFocus);
-          resolve();
-        }
-      });
-    });
-  });
+  let blankTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
 
-  // The test page loaded, so open an empty tab, select it, then restore
-  // the test tab. This causes the test page's focused element to be removed
-  // from its document.
-  gBrowser.selectedTab = gBrowser.addTab("about:blank");
-  yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
-  gBrowser.removeCurrentTab();
-
-  // Wait until the original tab is focused again.
-  yield promiseFocused;
+  yield BrowserTestUtils.switchTab(gBrowser, testTab);
 
   // Make sure focus is given to the window because the element is now gone.
   is((yield getFocusedLocalName(browser)), "body", "body is focused");
 
   // Cleanup.
+  gBrowser.removeTab(blankTab);
   gBrowser.removeCurrentTab();
+
 });
--- a/browser/components/nsBrowserContentHandler.js
+++ b/browser/components/nsBrowserContentHandler.js
@@ -547,20 +547,19 @@ nsBrowserContentHandler.prototype = {
     // formatURLPref might return "about:blank" if getting the pref fails
     if (overridePage == "about:blank")
       overridePage = "";
 
     // Temporary override page for users who are running Firefox on Windows 10 for their first time.
     let platformVersion = Services.sysinfo.getProperty("version");
     if (AppConstants.platform == "win" &&
         Services.vc.compare(platformVersion, "10") == 0 &&
-        Services.prefs.getPrefType("browser.usedOnWindows10") == Services.prefs.PREF_BOOL &&
         !Services.prefs.getBoolPref("browser.usedOnWindows10")) {
       Services.prefs.setBoolPref("browser.usedOnWindows10", true);
-      let firstUseOnWindows10URL = Services.urlFormatter.formatURL("https://www.mozilla.org/%LOCALE%/firefox/windows10/");
+      let firstUseOnWindows10URL = Services.urlFormatter.formatURLPref("browser.usedOnWindows10.introURL");
 
       if (firstUseOnWindows10URL && firstUseOnWindows10URL.length) {
         if (overridePage) {
           overridePage += "|" + firstUseOnWindows10URL;
         } else {
           overridePage = firstUseOnWindows10URL;
         }
       }
--- a/browser/components/preferences/permissions.js
+++ b/browser/components/preferences/permissions.js
@@ -83,17 +83,17 @@ var gPermissionManager = {
   {
     var textbox = document.getElementById("url");
     var input_url = textbox.value.replace(/^\s*/, ""); // trim any leading space
     let principal;
     try {
       // If the uri doesn't successfully parse, try adding a http:// and parsing again
       let uri;
       try {
-        let uri = Services.io.newURI(input_url, null, null);
+        uri = Services.io.newURI(input_url, null, null);
       } catch(ex) {
         uri = Services.io.newURI("http://" + input_url, null, null);
       }
       principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri);
     } catch(ex) {
       var message = this._bundle.getString("invalidURI");
       var title = this._bundle.getString("invalidURITitle");
       Services.prompt.alert(window, title, message);
--- a/browser/components/preferences/tests/browser_cookies_exceptions.js
+++ b/browser/components/preferences/tests/browser_cookies_exceptions.js
@@ -11,38 +11,44 @@ var testRunner = {
 
   tests:
     [
       {
         test: function(params) {
           params.url.value = "test.com";
           params.btnAllow.doCommand();
           is(params.tree.view.rowCount, 1, "added exception shows up in treeview");
+          is(params.tree.view.getCellText(0, params.nameCol), "http://test.com",
+                                          "origin name should be set correctly");
           is(params.tree.view.getCellText(0, params.statusCol), params.allowText,
                                           "permission text should be set correctly");
           params.btnApplyChanges.doCommand();
         },
         observances: [{ type: "cookie", origin: "http://test.com", data: "added",
                         capability: Ci.nsIPermissionManager.ALLOW_ACTION }],
       },
       {
         test: function(params) {
           params.url.value = "test.com";
           params.btnBlock.doCommand();
+          is(params.tree.view.getCellText(0, params.nameCol), "http://test.com",
+                                          "origin name should be set correctly");
           is(params.tree.view.getCellText(0, params.statusCol), params.denyText,
                                           "permission should change to deny in UI");
           params.btnApplyChanges.doCommand();
         },
         observances: [{ type: "cookie", origin: "http://test.com", data: "changed",
                         capability: Ci.nsIPermissionManager.DENY_ACTION  }],
       },
       {
         test: function(params) {
           params.url.value = "test.com";
           params.btnAllow.doCommand();
+          is(params.tree.view.getCellText(0, params.nameCol), "http://test.com",
+                                          "origin name should be set correctly");
           is(params.tree.view.getCellText(0, params.statusCol), params.allowText,
                                           "permission should revert back to allow");
           params.btnApplyChanges.doCommand();
         },
         observances: [{ type: "cookie", origin: "http://test.com", data: "changed",
                         capability: Ci.nsIPermissionManager.ALLOW_ACTION }],
       },
       {
@@ -63,16 +69,65 @@ var testRunner = {
         },
         observances: [{ type: "popup", origin: "http://test.com", data: "added",
                         capability: Ci.nsIPermissionManager.DENY_ACTION }],
         cleanUp: function(params) {
           let uri = params.ioService.newURI("http://test.com", null, null);
           params.pm.remove(uri, "popup");
         },
       },
+      {
+        test: function(params) {
+          params.url.value = "https://test.com:12345";
+          params.btnAllow.doCommand();
+          is(params.tree.view.rowCount, 1, "added exception shows up in treeview");
+          is(params.tree.view.getCellText(0, params.nameCol), "https://test.com:12345",
+                                          "origin name should be set correctly");
+          is(params.tree.view.getCellText(0, params.statusCol), params.allowText,
+                                          "permission text should be set correctly");
+          params.btnApplyChanges.doCommand();
+        },
+        observances: [{ type: "cookie", origin: "https://test.com:12345", data: "added",
+                        capability: Ci.nsIPermissionManager.ALLOW_ACTION }],
+      },
+      {
+        test: function(params) {
+          params.url.value = "https://test.com:12345";
+          params.btnBlock.doCommand();
+          is(params.tree.view.getCellText(0, params.nameCol), "https://test.com:12345",
+                                          "origin name should be set correctly");
+          is(params.tree.view.getCellText(0, params.statusCol), params.denyText,
+                                          "permission should change to deny in UI");
+          params.btnApplyChanges.doCommand();
+        },
+        observances: [{ type: "cookie", origin: "https://test.com:12345", data: "changed",
+                        capability: Ci.nsIPermissionManager.DENY_ACTION  }],
+      },
+      {
+        test: function(params) {
+          params.url.value = "https://test.com:12345";
+          params.btnAllow.doCommand();
+          is(params.tree.view.getCellText(0, params.nameCol), "https://test.com:12345",
+                                          "origin name should be set correctly");
+          is(params.tree.view.getCellText(0, params.statusCol), params.allowText,
+                                          "permission should revert back to allow");
+          params.btnApplyChanges.doCommand();
+        },
+        observances: [{ type: "cookie", origin: "https://test.com:12345", data: "changed",
+                        capability: Ci.nsIPermissionManager.ALLOW_ACTION }],
+      },
+      {
+        test: function(params) {
+          params.url.value = "https://test.com:12345";
+          params.btnRemove.doCommand();
+          is(params.tree.view.rowCount, 0, "exception should be removed");
+          params.btnApplyChanges.doCommand();
+        },
+        observances: [{ type: "cookie", origin: "https://test.com:12345", data: "deleted" }],
+      },
     ],
 
   _currentTest: -1,
 
   runTests: function() {
     this._currentTest++;
 
     info("Running test #" + (this._currentTest + 1) + "\n");
@@ -122,16 +177,17 @@ var testRunner = {
             SimpleTest.executeSoon(function() helperFunctions.windowLoad(event, win));
           }, false);
         },
 
         windowLoad: function(event, win) {
           let params = {
             doc: event.target,
             tree: event.target.getElementById("permissionsTree"),
+            nameCol: event.target.getElementById("permissionsTree").treeBoxObject.columns.getColumnAt(0),
             statusCol: event.target.getElementById("permissionsTree").treeBoxObject.columns.getColumnAt(1),
             url: event.target.getElementById("url"),
             btnAllow: event.target.getElementById("btnAllow"),
             btnBlock: event.target.getElementById("btnBlock"),
             btnApplyChanges: event.target.getElementById("btnApplyChanges"),
             btnRemove: event.target.getElementById("removePermission"),
             pm: Cc["@mozilla.org/permissionmanager;1"]
                        .getService(Ci.nsIPermissionManager),
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_DownloadLastDirWithCPS.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_DownloadLastDirWithCPS.js
@@ -2,17 +2,16 @@
 /* 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/. */
 
 let gTests;
 function test() {
   waitForExplicitFinish();
   requestLongerTimeout(2);
-  requestCompleteLog();
   gTests = runTest();
   gTests.next();
 }
 
 /*
  * ================
  * Helper functions
  * ================
--- a/browser/config/tooltool-manifests/linux32/releng.manifest
+++ b/browser/config/tooltool-manifests/linux32/releng.manifest
@@ -2,15 +2,22 @@
 {
 "size": 80458572,
 "digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
 "algorithm": "sha512", 
 "filename": "gcc.tar.xz",
 "unpack": true
 },
 {
+"size": 4079256,
+"digest": "bb5238558bcf6db2ca395513c8dccaa15dd61b3c375598eb6a685356b0c1a2d9840e3bf81bc00242b872fd798541f53d723777c754412abf0e772b7cc284937c",
+"algorithm": "sha512",
+"filename": "gtk3.tar.xz",
+"unpack": true
+},
+{
 "size": 167175,
 "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
 "algorithm": "sha512",
 "filename": "sccache.tar.bz2",
 "unpack": true
 }
 ]
--- a/browser/config/tooltool-manifests/linux64/releng.manifest
+++ b/browser/config/tooltool-manifests/linux64/releng.manifest
@@ -2,15 +2,22 @@
 {
 "size": 80458572,
 "digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
 "algorithm": "sha512", 
 "filename": "gcc.tar.xz",
 "unpack": true
 },
 {
+"size": 4431740,
+"digest": "68fc56b0fb0cdba629b95683d6649ff76b00dccf97af90960c3d7716f6108b2162ffd5ffcd5c3a60a21b28674df688fe4dabc67345e2da35ec5abeae3d48c8e3",
+"algorithm": "sha512",
+"filename": "gtk3.tar.xz",
+"unpack": true
+},
+{
 "size": 167175,
 "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
 "algorithm": "sha512",
 "filename": "sccache.tar.bz2",
 "unpack": true
 }
 ]
--- a/browser/devtools/debugger/debugger-controller.js
+++ b/browser/devtools/debugger/debugger-controller.js
@@ -2181,17 +2181,18 @@ let Prefs = new ViewHelpers.Prefs("devto
   variablesSearchboxVisible: ["Bool", "debugger.ui.variables-searchbox-visible"],
   pauseOnExceptions: ["Bool", "debugger.pause-on-exceptions"],
   ignoreCaughtExceptions: ["Bool", "debugger.ignore-caught-exceptions"],
   sourceMapsEnabled: ["Bool", "debugger.source-maps-enabled"],
   prettyPrintEnabled: ["Bool", "debugger.pretty-print-enabled"],
   autoPrettyPrint: ["Bool", "debugger.auto-pretty-print"],
   workersEnabled: ["Bool", "debugger.workers"],
   editorTabSize: ["Int", "editor.tabsize"],
-  autoBlackBox: ["Bool", "debugger.auto-black-box"]
+  autoBlackBox: ["Bool", "debugger.auto-black-box"],
+  promiseDebuggerEnabled: ["Bool", "debugger.promise"]
 });
 
 /**
  * Convenient way of emitting events from the panel window.
  */
 EventEmitter.decorate(this);
 
 /**
--- a/browser/devtools/debugger/debugger-view.js
+++ b/browser/devtools/debugger/debugger-view.js
@@ -105,16 +105,17 @@ let DebuggerView = {
   _initializePanes: function() {
     dumpn("Initializing the DebuggerView panes");
 
     this._body = document.getElementById("body");
     this._editorDeck = document.getElementById("editor-deck");
     this._workersAndSourcesPane = document.getElementById("workers-and-sources-pane");
     this._instrumentsPane = document.getElementById("instruments-pane");
     this._instrumentsPaneToggleButton = document.getElementById("instruments-pane-toggle");
+    this._promisePane = document.getElementById("promise-debugger-pane");
 
     this.showEditor = this.showEditor.bind(this);
     this.showBlackBoxMessage = this.showBlackBoxMessage.bind(this);
     this.showProgressBar = this.showProgressBar.bind(this);
     this.maybeShowBlackBoxMessage = this.maybeShowBlackBoxMessage.bind(this);
 
     this._onTabSelect = this._onInstrumentsPaneTabSelect.bind(this);
     this._instrumentsPane.tabpanels.addEventListener("select", this._onTabSelect);
@@ -141,16 +142,17 @@ let DebuggerView = {
     if (gHostType != "side") {
       Prefs.workersAndSourcesWidth = this._workersAndSourcesPane.getAttribute("width");
       Prefs.instrumentsWidth = this._instrumentsPane.getAttribute("width");
     }
 
     this._workersAndSourcesPane = null;
     this._instrumentsPane = null;
     this._instrumentsPaneToggleButton = null;
+    this._promisePane = null;
   },
 
   /**
    * Initializes the VariablesView instance and attaches a controller.
    */
   _initializeVariablesView: function() {
     this.Variables = new VariablesView(document.getElementById("variables"), {
       searchPlaceholder: L10N.getStr("emptyVariablesFilterText"),
@@ -596,17 +598,16 @@ let DebuggerView = {
     this._body.setAttribute("layout", newLayout);
     window.emit(EVENTS.LAYOUT_CHANGED, newLayout);
   },
 
   /**
    * Switches the debugger widgets to a horizontal layout.
    */
   _enterVerticalLayout: function() {
-    let normContainer = document.getElementById("debugger-widgets");
     let vertContainer = document.getElementById("vertical-layout-panes-container");
 
     // Move the soruces and instruments panes in a different container.
     let splitter = document.getElementById("sources-and-instruments-splitter");
     vertContainer.insertBefore(this._workersAndSourcesPane, splitter);
     vertContainer.appendChild(this._instrumentsPane);
 
     // Make sure the vertical layout container's height doesn't repeatedly
@@ -615,23 +616,23 @@ let DebuggerView = {
       vertContainer.getBoundingClientRect().height);
   },
 
   /**
    * Switches the debugger widgets to a vertical layout.
    */
   _enterHorizontalLayout: function() {
     let normContainer = document.getElementById("debugger-widgets");
-    let vertContainer = document.getElementById("vertical-layout-panes-container");
+    let editorPane = document.getElementById("editor-and-instruments-pane");
 
     // The sources and instruments pane need to be inserted at their
     // previous locations in their normal container.
     let splitter = document.getElementById("sources-and-editor-splitter");
     normContainer.insertBefore(this._workersAndSourcesPane, splitter);
-    normContainer.appendChild(this._instrumentsPane);
+    editorPane.appendChild(this._instrumentsPane);
 
     // Revert to the preferred sources and instruments widths, because
     // they flexed in the vertical layout.
     this._workersAndSourcesPane.setAttribute("width", Prefs.workersAndSourcesWidth);
     this._instrumentsPane.setAttribute("width", Prefs.instrumentsWidth);
   },
 
   /**
--- a/browser/devtools/debugger/debugger.xul
+++ b/browser/devtools/debugger/debugger.xul
@@ -336,66 +336,81 @@
                 <vbox class="devtools-separator"/>
                 <toolbarbutton id="toggle-breakpoints"
                                class="devtools-toolbarbutton"
                                tooltiptext="&debuggerUI.sources.toggleBreakpoints;"
                                command="toggleBreakpointsCommand"/>
                 <toolbarbutton id="toggle-pause-exceptions"
                                class="devtools-toolbarbutton"
                                command="togglePauseOnExceptionsCommand"/>
+                <toolbarbutton id="toggle-promise-debugger"
+                               class="devtools-toolbarbutton"
+                               tooltiptext="&debuggerUI.sources.togglePromiseDebugger;"
+                               command="togglePromiseDebuggerCommand"
+                               hidden="true"/>
               </toolbar>
             </tabpanel>
             <tabpanel id="callstack-tabpanel">
               <vbox id="callstack-list" flex="1"/>
             </tabpanel>
           </tabpanels>
         </tabbox>
       </vbox>
       <splitter id="sources-and-editor-splitter"
                 class="devtools-side-splitter"/>
-      <deck id="editor-deck" flex="1" class="devtools-main-content">
-        <vbox id="editor"/>
-        <vbox id="black-boxed-message"
-              align="center"
-              pack="center">
-          <description id="black-boxed-message-label">
-            &debuggerUI.blackBoxMessage.label;
-          </description>
-          <button id="black-boxed-message-button"
-                  class="devtools-toolbarbutton"
-                  label="&debuggerUI.blackBoxMessage.unBlackBoxButton;"
-                  command="unBlackBoxCommand"/>
+      <vbox id="debugger-content" flex="1">
+        <hbox id="editor-and-instruments-pane" flex="1">
+          <deck id="editor-deck" flex="1" class="devtools-main-content">
+            <vbox id="editor"/>
+            <vbox id="black-boxed-message"
+                  align="center"
+                  pack="center">
+              <description id="black-boxed-message-label">
+                &debuggerUI.blackBoxMessage.label;
+              </description>
+              <button id="black-boxed-message-button"
+                      class="devtools-toolbarbutton"
+                      label="&debuggerUI.blackBoxMessage.unBlackBoxButton;"
+                      command="unBlackBoxCommand"/>
+            </vbox>
+            <html:div id="source-progress-container"
+                      align="center">
+              <html:div id="hbox">
+                <html:progress id="source-progress"></html:progress>
+              </html:div>
+            </html:div>
+          </deck>
+          <splitter id="editor-and-instruments-splitter"
+                    class="devtools-side-splitter"/>
+          <tabbox id="instruments-pane"
+                  class="devtools-sidebar-tabs"
+                  hidden="true">
+            <tabs>
+              <tab id="variables-tab" label="&debuggerUI.tabs.variables;"/>
+              <tab id="events-tab" label="&debuggerUI.tabs.events;"/>
+            </tabs>
+            <tabpanels flex="1">
+              <tabpanel id="variables-tabpanel">
+                <vbox id="expressions"/>
+                <splitter class="devtools-horizontal-splitter"/>
+                <vbox id="variables" flex="1"/>
+              </tabpanel>
+              <tabpanel id="events-tabpanel">
+                <vbox id="event-listeners" flex="1"/>
+              </tabpanel>
+            </tabpanels>
+          </tabbox>
+        </hbox>
+        <splitter id="editor-and-promise-splitter"
+                class="devtools-horizontal-splitter"/>
+        <vbox id="promise-debugger-pane"
+              flex="1"
+              hidden="true">
         </vbox>
-        <html:div id="source-progress-container"
-                  align="center">
-          <html:div id="hbox">
-            <html:progress id="source-progress"></html:progress>
-          </html:div>
-        </html:div>
-      </deck>
-      <splitter id="editor-and-instruments-splitter"
-                class="devtools-side-splitter"/>
-      <tabbox id="instruments-pane"
-              class="devtools-sidebar-tabs"
-              hidden="true">
-        <tabs>
-          <tab id="variables-tab" label="&debuggerUI.tabs.variables;"/>
-          <tab id="events-tab" label="&debuggerUI.tabs.events;"/>
-        </tabs>
-        <tabpanels flex="1">
-          <tabpanel id="variables-tabpanel">
-            <vbox id="expressions"/>
-            <splitter class="devtools-horizontal-splitter"/>
-            <vbox id="variables" flex="1"/>
-          </tabpanel>
-          <tabpanel id="events-tabpanel">
-            <vbox id="event-listeners" flex="1"/>
-          </tabpanel>
-        </tabpanels>
-      </tabbox>
+      </vbox>
       <splitter id="vertical-layout-splitter"
                 class="devtools-horizontal-splitter"/>
       <hbox id="vertical-layout-panes-container">
         <splitter id="sources-and-instruments-splitter"
                   class="devtools-side-splitter"/>
         <!-- The sources-pane and instruments-pane will be moved in this
              container if the toolbox's host requires it. -->
       </hbox>
--- a/browser/devtools/debugger/test/browser_dbg_host-layout.js
+++ b/browser/devtools/debugger/test/browser_dbg_host-layout.js
@@ -68,37 +68,55 @@ function testHost(aTab, aPanel, aHostTyp
     "The default host type should've been set on the panel window (2).");
 
   is(gView._body.getAttribute("layout"), aLayoutType,
     "The default host type is present as an attribute on the panel's body.");
 
   if (aLayoutType == "horizontal") {
     is(gView._workersAndSourcesPane.parentNode.id, "debugger-widgets",
       "The workers and sources pane's parent is correct for the horizontal layout.");
-    is(gView._instrumentsPane.parentNode.id, "debugger-widgets",
+    is(gView._instrumentsPane.parentNode.id, "editor-and-instruments-pane",
       "The instruments pane's parent is correct for the horizontal layout.");
+    is(gDebugger.document.getElementById("promise-debugger-pane").parentNode.id,
+      "debugger-content",
+      "The promise pane's parent is correct for the horizontal layout.");
   } else {
     is(gView._workersAndSourcesPane.parentNode.id, "vertical-layout-panes-container",
       "The workers and sources pane's parent is correct for the vertical layout.");
     is(gView._instrumentsPane.parentNode.id, "vertical-layout-panes-container",
       "The instruments pane's parent is correct for the vertical layout.");
+    is(gDebugger.document.getElementById("promise-debugger-pane").parentNode.id,
+      "debugger-content",
+      "The promise pane's parent is correct for the horizontal layout.");
   }
 
   let widgets = gDebugger.document.getElementById("debugger-widgets").childNodes;
-  let panes = gDebugger.document.getElementById("vertical-layout-panes-container").childNodes;
+  let content = gDebugger.document.getElementById("debugger-content").childNodes;
+  let editorPane =
+    gDebugger.document.getElementById("editor-and-instruments-pane").childNodes;
+  let verticalPane =
+    gDebugger.document.getElementById("vertical-layout-panes-container").childNodes;
 
   if (aLayoutType == "horizontal") {
-    is(widgets.length, 7, // 2 panes, 1 editor, 3 splitters and a phantom box.
+    is(widgets.length, 5, // 1 pane, 1 content box, 2 splitters and a phantom box.
       "Found the correct number of debugger widgets.");
-    is(panes.length, 1, // 1 lonely splitter in the phantom box.
+    is(content.length, 3, // 2 panes, 1 splitter.
+      "Found the correct number of debugger content.");
+    is(editorPane.length, 3, // 2 panes, 1 splitter
+      "Found the correct number of debugger panes.");
+    is(verticalPane.length, 1, // 1 lonely splitter in the phantom box.
       "Found the correct number of debugger panes.");
   } else {
-    is(widgets.length, 5, // 1 editor, 3 splitters and a phantom box.
+    is(widgets.length, 4, // 1 content box, 2 splitters and a phantom box.
       "Found the correct number of debugger widgets.");
-    is(panes.length, 3, // 2 panes and 1 splitter in the phantom box.
+    is(content.length, 3, // 2 panes, 1 splitter.
+      "Found the correct number of debugger content.");
+    is(editorPane.length, 2, // 1 pane, 1 splitter
+      "Found the correct number of debugger panes.");
+    is(verticalPane.length, 3, // 2 panes and 1 splitter in the phantom box.
       "Found the correct number of debugger panes.");
   }
 }
 
 registerCleanupFunction(function() {
   Services.prefs.setCharPref("devtools.toolbox.host", gDefaultHostType);
   gDefaultHostType = null;
 });
--- a/browser/devtools/debugger/test/browser_dbg_variables-view-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_variables-view-02.js
@@ -1,13 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
- * Tests that creating, collpasing and expanding variables in the
+ * Tests that creating, collapsing and expanding variables in the
  * variables view works as expected.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_recursion-stack.html";
 
 function test() {
   initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
     let variables = aPanel.panelWin.DebuggerView.Variables;
@@ -17,17 +17,17 @@ function test() {
 
     info("Scope id: " + testScope.id);
     info("Scope name: " + testScope.name);
     info("Variable id: " + testVar.id);
     info("Variable name: " + testVar.name);
 
     ok(testScope,
       "Should have created a scope.");
-    is(duplVar, null,
+    is(duplVar, testVar,
       "Shouldn't be able to duplicate variables in the same scope.");
 
     ok(testVar,
       "Should have created a variable.");
     ok(testVar.id.includes("something"),
       "The newly created variable should have the default id set.");
     is(testVar.name, "something",
       "The newly created variable should have the desired name set.");
--- a/browser/devtools/debugger/test/browser_dbg_variables-view-filter-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_variables-view-filter-01.js
@@ -91,18 +91,20 @@ function testVariablesAndPropertiesFilte
       "constructor", "The first inner property displayed should be 'constructor'");
     is(localScope.target.querySelectorAll(".variables-view-property:not([unmatched]) > .title > .name")[1].getAttribute("value"),
       "__proto__", "The second inner property displayed should be '__proto__'");
     is(localScope.target.querySelectorAll(".variables-view-property:not([unmatched]) > .title > .name")[2].getAttribute("value"),
       "constructor", "The third inner property displayed should be 'constructor'");
   }
 
   function firstFilter() {
+    let expanded = once(gVariables, "fetched");
     typeText(gSearchBox, "constructor");
-    testFiltered();
+    gSearchBox.doCommand();
+    return expanded.then(testFiltered);
   }
 
   function secondFilter() {
     localScope.collapse();
     withScope.collapse();
     functionScope.collapse();
     globalScope.collapse();
     protoVar.collapse();
@@ -123,23 +125,23 @@ function testVariablesAndPropertiesFilte
       "The protoVar should not be expanded.");
     is(constrVar.expanded, false,
       "The constrVar should not be expanded.");
     is(proto2Var.expanded, false,
       "The proto2Var should not be expanded.");
     is(constr2Var.expanded, false,
       "The constr2Var should not be expanded.");
 
+    let expanded = once(gVariables, "fetched");
     clearText(gSearchBox);
     typeText(gSearchBox, "constructor");
-    testFiltered();
+    expanded.then(testFiltered);
   }
 
-  firstFilter();
-  secondFilter();
+  firstFilter().then(secondFilter);
 }
 
 function prepareVariablesAndProperties() {
   let deferred = promise.defer();
 
   let localScope = gVariables.getScopeAtIndex(0);
   let withScope = gVariables.getScopeAtIndex(1);
   let functionScope = gVariables.getScopeAtIndex(2);
--- a/browser/devtools/debugger/test/browser_dbg_variables-view-filter-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_variables-view-filter-02.js
@@ -98,18 +98,20 @@ function testVariablesAndPropertiesFilte
 
     is(localScope.target.querySelectorAll(".variables-view-property:not([unmatched]) > .title > .name")[3].getAttribute("value"),
       "name", "The fourth inner property displayed should be 'name'");
     is(localScope.target.querySelectorAll(".variables-view-property:not([unmatched]) > .title > .value")[3].getAttribute("value"),
       "\"Function\"", "The fourth inner property displayed should be '\"Function\"'");
   }
 
   function firstFilter() {
+    let expanded = once(gVariables, "fetched");
     typeText(gSearchBox, "\"Function\"");
-    testFiltered();
+    gSearchBox.doCommand();
+    return expanded.then(testFiltered);
   }
 
   function secondFilter() {
     localScope.collapse();
     withScope.collapse();
     functionScope.collapse();
     globalScope.collapse();
     protoVar.collapse();
@@ -131,22 +133,23 @@ function testVariablesAndPropertiesFilte
     is(constrVar.expanded, false,
       "The constrVar should not be expanded.");
     is(proto2Var.expanded, false,
       "The proto2Var should not be expanded.");
     is(constr2Var.expanded, false,
       "The constr2Var should not be expanded.");
 
     backspaceText(gSearchBox, 10);
+    let expanded = once(gVariables, "fetched");
     typeText(gSearchBox, "\"Function\"");
-    testFiltered();
+    gSearchBox.doCommand();
+    expanded.then(testFiltered);
   }
 
-  firstFilter();
-  secondFilter();
+  firstFilter().then(secondFilter);
 }
 
 function prepareVariablesAndProperties() {
   let deferred = promise.defer();
 
   let localScope = gVariables.getScopeAtIndex(0);
   let withScope = gVariables.getScopeAtIndex(1);
   let functionScope = gVariables.getScopeAtIndex(2);
--- a/browser/devtools/debugger/views/sources-view.js
+++ b/browser/devtools/debugger/views/sources-view.js
@@ -146,16 +146,17 @@ SourcesView.prototype = Heritage.extend(
     XULUtils.addCommands(this._commandset, {
       addBreakpointCommand: e => this._onCmdAddBreakpoint(e),
       addConditionalBreakpointCommand: e => this._onCmdAddConditionalBreakpoint(e),
       blackBoxCommand: () => this.toggleBlackBoxing(),
       unBlackBoxButton: () => this._onStopBlackBoxing(),
       prettyPrintCommand: () => this.togglePrettyPrint(),
       toggleBreakpointsCommand: () =>this.toggleBreakpoints(),
       togglePauseOnExceptionsCommand: () => this.togglePauseOnExceptions(),
+      togglePromiseDebuggerCommand: () => this.togglePromiseDebugger(),
       nextSourceCommand: () => this.selectNextItem(),
       prevSourceCommand: () => this.selectPrevItem()
     });
   },
 
   /**
    * Sets the preferred location to be selected in this sources container.
    * @param string aUrl
@@ -654,16 +655,23 @@ SourcesView.prototype = Heritage.extend(
     this.DebuggerController.activeThread.pauseOnExceptions(
       Prefs.pauseOnExceptions,
       Prefs.ignoreCaughtExceptions);
 
     this._togglePauseOnExceptionsButton.setAttribute("tooltiptext", tooltip);
     this._togglePauseOnExceptionsButton.setAttribute("state", state);
   },
 
+  togglePromiseDebugger: function() {
+    if (Prefs.promiseDebuggerEnabled) {
+      let promisePane = this.DebuggerView._promisePane;
+      promisePane.hidden = !promisePane.hidden;
+    }
+  },
+
   hidePrettyPrinting: function() {
     this._prettyPrintButton.style.display = 'none';
 
     if (this._blackBoxButton.style.display === 'none') {
       let sep = document.querySelector('#sources-toolbar .devtools-separator');
       sep.style.display = 'none';
     }
   },
--- a/browser/devtools/netmonitor/test/browser_net_copy_as_curl.js
+++ b/browser/devtools/netmonitor/test/browser_net_copy_as_curl.js
@@ -16,32 +16,36 @@ function test() {
       "-H 'User-Agent: " + navigator.userAgent + "'",
       "-H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'",
       "-H 'Accept-Language: " + navigator.language + "'",
       "--compressed",
       "-H 'X-Custom-Header-1: Custom value'",
       "-H 'X-Custom-Header-2: 8.8.8.8'",
       "-H 'X-Custom-Header-3: Mon, 3 Mar 2014 11:11:11 GMT'",
       "-H 'Referer: " + CURL_URL + "'",
-      "-H 'Connection: keep-alive'"
+      "-H 'Connection: keep-alive'",
+      "-H 'Pragma: no-cache'",
+      "-H 'Cache-Control: no-cache'"
     ].join(" ");
 
     const EXPECTED_WIN_RESULT = [
       'curl',
       '"' + SIMPLE_SJS + '"',
       '-H "Host: example.com"',
       '-H "User-Agent: ' + navigator.userAgent + '"',
       '-H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"',
       '-H "Accept-Language: ' + navigator.language + '"',
       "--compressed",
       '-H "X-Custom-Header-1: Custom value"',
       '-H "X-Custom-Header-2: 8.8.8.8"',
       '-H "X-Custom-Header-3: Mon, 3 Mar 2014 11:11:11 GMT"',
       '-H "Referer: ' + CURL_URL + '"',
-      '-H "Connection: keep-alive"'
+      '-H "Connection: keep-alive"',
+      '-H "Pragma: no-cache"', 
+      '-H "Cache-Control: no-cache"'
     ].join(" ");
 
     const EXPECTED_RESULT = Services.appinfo.OS == "WINNT"
       ? EXPECTED_WIN_RESULT
       : EXPECTED_POSIX_RESULT;
 
     let { NetMonitorView } = aMonitor.panelWin;
     let { RequestsMenu } = NetMonitorView;
--- a/browser/devtools/shared/widgets/VariablesView.jsm
+++ b/browser/devtools/shared/widgets/VariablesView.jsm
@@ -448,34 +448,34 @@ VariablesView.prototype = {
     // properties to display.
     container.hidden = !this._store.length;
 
     let searchbox = this._searchboxNode = document.createElement("textbox");
     searchbox.className = "variables-view-searchinput devtools-searchinput";
     searchbox.setAttribute("placeholder", this._searchboxPlaceholder);
     searchbox.setAttribute("type", "search");
     searchbox.setAttribute("flex", "1");
-    searchbox.addEventListener("input", this._onSearchboxInput, false);
+    searchbox.addEventListener("command", this._onSearchboxInput, false);
     searchbox.addEventListener("keypress", this._onSearchboxKeyPress, false);
 
     container.appendChild(searchbox);
     ownerNode.insertBefore(container, this._parent);
   },
 
   /**
    * Disables variable and property searching in this view.
    * Use the "searchEnabled" setter to disable searching.
    */
   _disableSearch: function() {
     // If searching was already disabled, no need to re-disable it again.
     if (!this._searchboxContainer) {
       return;
     }
     this._searchboxContainer.remove();
-    this._searchboxNode.removeEventListener("input", this._onSearchboxInput, false);
+    this._searchboxNode.removeEventListener("command", this._onSearchboxInput, false);
     this._searchboxNode.removeEventListener("keypress", this._onSearchboxKeyPress, false);
 
     this._searchboxContainer = null;
     this._searchboxNode = null;
   },
 
   /**
    * Sets the variables searchbox container hidden or visible.
@@ -545,21 +545,32 @@ VariablesView.prototype = {
    * while the available variables and properties inside those scopes are
    * just unhidden.
    *
    * @param string aToken
    *        The variable or property to search for.
    */
   _doSearch: function(aToken) {
     if (this.controller.supportsSearch()) {
-      this.empty();
-      let scope = this.addScope(aToken);
-      scope.expanded = true; // Expand the scope by default.
-      scope.locked = true; // Prevent collapsing the scope.
+      // Retrieve the main Scope in which we add attributes
+      let scope = this._store[0]._store.get("");
+      if (!aToken) {
+        // Prune the view from old previous content
+        // so that we delete the intermediate search results
+        // we created in previous searches
+        for (let property of scope._store.values()) {
+          property.remove();
+        }
+      }
+      // Retrieve new attributes eventually hidden in splits
       this.controller.performSearch(scope, aToken);
+      // Filter already displayed attributes
+      if (aToken) {
+        scope._performSearch(aToken.toLowerCase());
+      }
       return;
     }
     for (let scope of this._store) {
       switch (aToken) {
         case "":
         case null:
         case undefined:
           scope.expand();
@@ -1283,17 +1294,17 @@ Scope.prototype = {
    * @param boolean aRelaxed [optional]
    *        Pass true if name duplicates should be allowed.
    *        You probably shouldn't do it. Use this with caution.
    * @return Variable
    *         The newly created Variable instance, null if it already exists.
    */
   addItem: function(aName = "", aDescriptor = {}, aRelaxed = false) {
     if (this._store.has(aName) && !aRelaxed) {
-      return null;
+      return this._store.get(aName);
     }
 
     let child = this._createChild(aName, aDescriptor);
     this._store.set(aName, child);
     this._variablesView._itemsByElement.set(child._target, child);
     this._variablesView._currHierarchy.set(child.absoluteName, child);
     child.header = !!aName;
 
--- a/browser/devtools/shared/widgets/VariablesViewController.jsm
+++ b/browser/devtools/shared/widgets/VariablesViewController.jsm
@@ -632,17 +632,20 @@ VariablesViewController.prototype = {
    * Try to use the actor to perform an attribute search.
    *
    * @param Scope aScope
    *        The Scope instance to populate with properties
    * @param string aToken
    *        The query string
    */
   performSearch: function(aScope, aToken) {
-    this._populateFromObjectWithIterator(aScope, this.objectActor, aToken);
+    this._populateFromObjectWithIterator(aScope, this.objectActor, aToken)
+        .then(() => {
+          this.view.emit("fetched", "search", aScope);
+        });
   },
 
   /**
    * Release an actor from the controller.
    *
    * @param object aActor
    *        The actor to release.
    */
--- a/browser/devtools/webconsole/test/browser.ini
+++ b/browser/devtools/webconsole/test/browser.ini
@@ -168,16 +168,17 @@ skip-if = buildapp == 'mulet' || e10s # 
 [browser_console_nsiconsolemessage.js]
 skip-if = buildapp == 'mulet'
 [browser_console_optimized_out_vars.js]
 skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
 [browser_console_private_browsing.js]
 skip-if = buildapp == 'mulet' || e10s # Bug 1042253 - webconsole e10s tests
 [browser_console_variables_view.js]
 skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
+[browser_console_variables_view_filter.js]
 [browser_console_variables_view_dom_nodes.js]
 [browser_console_variables_view_dont_sort_non_sortable_classes_properties.js]
 skip-if = buildapp == 'mulet'
 [browser_console_variables_view_while_debugging.js]
 skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
 [browser_console_variables_view_while_debugging_and_inspecting.js]
 skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
 [browser_eval_in_debugger_stackframe.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser_console_variables_view_filter.js
@@ -0,0 +1,76 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Check that variables view filter feature works fine in the console.
+
+function props(view, prefix = "") {
+  // First match only the visible one, not hidden by a search
+  let visible = [...view].filter(([id, prop]) => prop._isMatch);
+  // Then flatten the list into a list of strings
+  // being the jsonpath of each attribute being visible in the view
+  return visible.reduce((list, [id, prop]) => {
+                   list.push(prefix + id);
+                   return list.concat(props(prop, prefix + id + "."));
+                 }, []);
+}
+
+function assertAttrs(view, expected, message) {
+  is(props(view).join(","), expected, message);
+}
+
+let test = asyncTest(function* () {
+  yield loadTab("data:text/html;charset=utf-8,webconsole-filter");
+
+  let hud = yield openConsole();
+
+  let jsterm = hud.jsterm;
+
+  let fetched = jsterm.once("variablesview-fetched");
+
+  yield jsterm.execute("inspect({ foo: { bar : \"baz\" } })");
+
+  let view = yield fetched;
+  let variablesView = view._variablesView;
+  let searchbox = variablesView._searchboxNode;
+
+  assertAttrs(view, "foo,__proto__", "To start with, we just see the top level foo attr");
+
+  fetched = jsterm.once("variablesview-fetched");
+  searchbox.value = "bar";
+  searchbox.doCommand();
+  view = yield fetched;
+
+  assertAttrs(view, "", "If we don't manually expand nested attr, we don't see them in search");
+
+  fetched = jsterm.once("variablesview-fetched");
+  searchbox.value = "";
+  searchbox.doCommand();
+  view = yield fetched;
+
+  assertAttrs(view, "foo", "If we reset the search, we get back to original state");
+
+  yield [...view][0][1].expand();
+
+  fetched = jsterm.once("variablesview-fetched");
+  searchbox.value = "bar";
+  searchbox.doCommand();
+  view = yield fetched;
+
+  assertAttrs(view, "foo,foo.bar", "Now if we expand, we see the nested attr");
+
+  fetched = jsterm.once("variablesview-fetched");
+  searchbox.value = "baz";
+  searchbox.doCommand();
+  view = yield fetched;
+
+  assertAttrs(view, "foo,foo.bar", "We can also search for attr values");
+
+  fetched = jsterm.once("variablesview-fetched");
+  searchbox.value = "";
+  searchbox.doCommand();
+  view = yield fetched;
+
+  assertAttrs(view, "foo", "If we reset again, we get back to original state again");
+});
--- a/browser/locales/en-US/chrome/browser/devtools/debugger.dtd
+++ b/browser/locales/en-US/chrome/browser/devtools/debugger.dtd
@@ -45,16 +45,20 @@
   -  checkbox that toggles auto pretty print. -->
 <!ENTITY debuggerUI.autoPrettyPrint     "Auto Prettify Minified Sources">
 <!ENTITY debuggerUI.autoPrettyPrint.accesskey "P">
 
 <!-- LOCALIZATION NOTE (debuggerUI.sources.toggleBreakpoints): This is the tooltip for the
   -  button that toggles all breakpoints for all sources. -->
 <!ENTITY debuggerUI.sources.toggleBreakpoints "Enable/disable all breakpoints">
 
+<!-- LOCALIZATION NOTE (debuggerUI.sources.togglePromiseDebugger): This is the
+  -  tooltip for the button that toggles the promise debugger. -->
+<!ENTITY debuggerUI.sources.togglePromiseDebugger "Toggle Promise Debugger">
+
 <!-- LOCALIZATION NOTE (debuggerUI.startTracing): This is the text displayed in
   - the button to start execution tracing. -->
 <!ENTITY debuggerUI.startTracing "Start Tracing">
 
 <!-- LOCALIZATION NOTE (debuggerUI.clearButton): This is the label for
   -  the button that clears the collected tracing data in the tracing tab. -->
 <!ENTITY debuggerUI.clearButton "Clear">
 
--- a/browser/modules/PluginContent.jsm
+++ b/browser/modules/PluginContent.jsm
@@ -575,22 +575,22 @@ PluginContent.prototype = {
     }
     if (!(plugin instanceof Ci.nsIObjectLoadingContent)) {
       Cu.reportError("Attempted to submit crash report on plugin that does not" +
                      "implement nsIObjectLoadingContent.");
       return;
     }
 
     let runID = plugin.runID;
-    let submitURLOptIn = this.getPluginUI(plugin, "submitURLOptIn");
+    let submitURLOptIn = this.getPluginUI(plugin, "submitURLOptIn").checked;
     let keyVals = {};
     let userComment = this.getPluginUI(plugin, "submitComment").value.trim();
     if (userComment)
       keyVals.PluginUserComment = userComment;
-    if (this.getPluginUI(plugin, "submitURLOptIn").checked)
+    if (submitURLOptIn)
       keyVals.PluginContentURL = plugin.ownerDocument.URL;
 
     this.global.sendAsyncMessage("PluginContent:SubmitReport",
                                  { runID, keyVals, submitURLOptIn });
   },
 
   reloadPage: function () {
     this.global.content.location.reload();
--- a/browser/themes/shared/devtools/debugger.inc.css
+++ b/browser/themes/shared/devtools/debugger.inc.css
@@ -117,16 +117,20 @@
 }
 
 @media (min-resolution: 1.25dppx) {
   #toggle-pause-exceptions[state="2"] {
     list-style-image: url(debugger-pause-uncaught-exceptions@2x.png);
   }
 }
 
+#toggle-promise-debugger {
+  /* TODO Bug 1186119: Add a toggle promise debugger image */
+}
+
 #sources-toolbar .devtools-toolbarbutton:not([label]) {
   -moz-image-region: rect(0px,16px,16px,0px);
 }
 
 #sources-toolbar .devtools-toolbarbutton:not([label])[checked] {
   -moz-image-region: rect(0px,32px,16px,16px);
 }
 
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -306,25 +306,32 @@
 #TabsToolbar:not([collapsed="true"]) + #nav-bar {
   margin-top: -1px; /* Move up into the TabsToolbar for the inner highlight at the top of the nav-bar */
   /* Position the toolbar above the bottom of background tabs */
   position: relative;
   z-index: 1;
 }
 
 #nav-bar {
-  border-top: 1px solid @toolbarShadowColor@ !important;
   background-clip: padding-box;
   background-image: linear-gradient(@toolbarHighlight@, transparent);
   box-shadow: 0 1px 0 @toolbarHighlight@ inset;
 }
 
-@media not all and (-moz-windows-compositor) {
-  #TabsToolbar[collapsed="true"] + #nav-bar {
-    border-top-style: none !important;
+@media (-moz-os-version: windows-xp),
+       (-moz-os-version: windows-vista),
+       (-moz-os-version: windows-win7),
+       (-moz-os-version: windows-win8) {
+  #nav-bar {
+    border-top: 1px solid @toolbarShadowColor@ !important;
+  }
+  @media not all and (-moz-windows-compositor) {
+    #TabsToolbar[collapsed="true"] + #nav-bar {
+      border-top-style: none !important;
+    }
   }
 }
 
 #personal-bookmarks {
   min-height: 24px;
 }
 
 #print-preview-toolbar:not(:-moz-lwtheme) {
@@ -2062,16 +2069,40 @@ richlistitem[type~="action"][actiontype=
   }
 
   .tab-background-end[visuallyselected=true]:-moz-locale-dir(ltr)::after,
   .tab-background-start[visuallyselected=true]:-moz-locale-dir(rtl)::after {
     background-image: url(chrome://browser/skin/tabbrowser/tab-stroke-end@2x.png);
   }
 }
 
+/* Remove border between tab strip and navigation toolbar on Windows 10+ */
+@media not all and (-moz-os-version: windows-xp) {
+  @media not all and (-moz-os-version: windows-vista) {
+    @media not all and (-moz-os-version: windows-win7) {
+      @media not all and (-moz-os-version: windows-win8) {
+        .tab-background-end[visuallyselected=true]::after,
+        .tab-background-start[visuallyselected=true]::after {
+          content: none;
+        }
+
+        .tab-background-middle[visuallyselected=true] {
+          /* Setting background-size to "0 0" for the first
+             background-image to remove the stroke. */
+          background-size: 0 0, auto 100%, auto 100%;
+        }
+
+        :root {
+          --tab-toolbar-navbar-overlap: 0;
+        }
+      }
+    }
+  }
+}
+
 /* Use solid tab separators for Windows 8+ */
 @media not all and (-moz-os-version: windows-xp) {
   @media not all and (-moz-os-version: windows-vista) {
     @media not all and (-moz-os-version: windows-win7) {
       #TabsToolbar:not([brighttext]) {
         --tab-separator-image: linear-gradient(transparent 0%, transparent 15%, currentColor 15%, currentColor 90%, transparent 90%);
         --tab-separator-size: 1px 100%;
         --tab-separator-opacity: 0.2;
new file mode 100644
--- /dev/null
+++ b/build/unix/build-gtk3/build-gtk3.sh
@@ -0,0 +1,107 @@
+#!/bin/bash
+
+# Use "build-gtk.sh" or "build-gtk.sh 64" to build a 64-bits tarball for tooltool.
+# Use "build-gtk.sh 32" to build a 32-bits tarball for tooltool.
+
+# Mock environments used:
+# - 64-bits:
+#   https://s3.amazonaws.com/mozilla-releng-mock-archive/67b65e51eb091fba7941a04d249343924a3ee653
+#   + libxml2-devel.x86_64 gettext.x86_64 libjpeg-devel.x86_64
+# - 32-bits:
+#   https://s3.amazonaws.com/mozilla-releng-mock-archive/58d76c6acca148a1aedcbec7fc1b8212e12807b4
+#   + libxml2-devel.i686 gettext.i686 libjpeg-devel.i686
+
+set -e
+
+pkg_config_version=0.28
+fontconfig_version=2.8.0
+libffi_version=3.0.13
+glib_version=2.34.3
+gdk_pixbuf_version=2.26.5
+pixman_version=0.20.2
+cairo_version=1.10.2
+pango_version=1.30.1
+atk_version=2.2.0
+gtk__version=3.4.4
+
+pkg_config_url=http://pkgconfig.freedesktop.org/releases/pkg-config-${pkg_config_version}.tar.gz
+fontconfig_url=http://www.freedesktop.org/software/fontconfig/release/fontconfig-${fontconfig_version}.tar.gz
+libffi_url=ftp://sourceware.org/pub/libffi/libffi-${libffi_version}.tar.gz
+glib_url=http://ftp.gnome.org/pub/gnome/sources/glib/${glib_version%.*}/glib-${glib_version}.tar.xz
+gdk_pixbuf_url=http://ftp.gnome.org/pub/gnome/sources/gdk-pixbuf/${gdk_pixbuf_version%.*}/gdk-pixbuf-${gdk_pixbuf_version}.tar.xz
+pixman_url=http://cairographics.org/releases/pixman-${pixman_version}.tar.gz
+cairo_url=http://cairographics.org/releases/cairo-${cairo_version}.tar.gz
+pango_url=http://ftp.gnome.org/pub/GNOME/sources/pango/${pango_version%.*}/pango-${pango_version}.tar.xz
+atk_url=http://ftp.gnome.org/pub/GNOME/sources/atk/${atk_version%.*}/atk-${atk_version}.tar.xz
+gtk__url=http://ftp.gnome.org/pub/gnome/sources/gtk+/${gtk__version%.*}/gtk+-${gtk__version}.tar.xz
+
+cwd=$(pwd)
+root_dir=$(mktemp -d)
+cd $root_dir
+
+if test -z $TMPDIR; then
+  TMPDIR=/tmp/
+fi
+
+make_flags=-j12
+
+build() {
+	name=$1
+	shift
+	pkg=$(echo $name | tr '+-' '__')
+	version=$(eval echo \$${pkg}_version)
+	url=$(eval echo \$${pkg}_url)
+	wget -c -P $TMPDIR $url
+	tar -axf $TMPDIR/$name-$version.tar.*
+	mkdir -p build/$name
+	cd build/$name
+	eval ../../$name-$version/configure --disable-static $* $configure_args
+	make $make_flags
+	make install-strip DESTDIR=$root_dir/gtk3
+	find $root_dir/gtk3 -name \*.la -delete
+	cd ../..
+}
+
+case "$1" in
+32)
+	configure_args='--host=i686-pc-linux --build=i686-pc-linux CC="gcc -m32" CXX="g++ -m32"'
+        lib=lib
+	;;
+*)
+	configure_args=
+	lib=lib64
+	;;
+esac
+
+export PKG_CONFIG_LIBDIR=/usr/$lib/pkgconfig:/usr/share/pkgconfig
+
+# The pkg-config version in the mock images is buggy is how it handles
+# PKG_CONFIG_SYSROOT_DIR. So we need our own.
+build pkg-config
+
+ln -sf /usr/include $root_dir/gtk3/usr/
+ln -sf /usr/$lib $root_dir/gtk3/usr/
+if [ "$lib" = lib64 ]; then
+	ln -s lib $root_dir/gtk3/usr/local/lib64
+fi
+export PKG_CONFIG_PATH=$root_dir/gtk3/usr/local/lib/pkgconfig
+export PKG_CONFIG_SYSROOT_DIR=$root_dir/gtk3
+export LD_LIBRARY_PATH=$root_dir/gtk3/usr/local/lib
+export PATH=$root_dir/gtk3/usr/local/bin:${PATH}
+
+build fontconfig
+build libffi
+build glib
+build gdk-pixbuf --without-libtiff
+build pixman --disable-gtk
+build cairo --enable-tee
+build pango
+build atk
+make_flags="$make_flags GLIB_COMPILE_SCHEMAS=glib-compile-schemas"
+build gtk+
+
+rm -rf $root_dir/gtk3/usr/local/share/gtk-doc
+rm -rf $root_dir/gtk3/usr/local/share/locale
+
+cd $cwd
+tar -C $root_dir -Jcf gtk3.tar.xz gtk3
--- a/caps/moz.build
+++ b/caps/moz.build
@@ -3,16 +3,21 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 MOCHITEST_MANIFESTS += ['tests/mochitest/mochitest.ini']
 MOCHITEST_CHROME_MANIFESTS += ['tests/mochitest/chrome.ini']
 XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
 
+# Hack to make this file available as a resource:// URI.
+TESTING_JS_MODULES += [
+    'tests/mochitest/resource_test_file.html',
+]
+
 XPIDL_SOURCES += [
     'nsIAddonPolicyService.idl',
     'nsIDomainPolicy.idl',
     'nsIPrincipal.idl',
     'nsIScriptSecurityManager.idl',
 ]
 
 XPIDL_MODULE = 'caps'
--- a/caps/nsIAddonPolicyService.idl
+++ b/caps/nsIAddonPolicyService.idl
@@ -6,17 +6,27 @@
 
 #include "nsISupports.idl"
 #include "nsIURI.idl"
 
 /**
  * This interface allows the security manager to query custom per-addon security
  * policy.
  */
-[scriptable,uuid(fedf126c-988e-42df-82c9-f2ac99cd65f3)]
+[scriptable,uuid(8a034ef9-9d14-4c5d-8319-06c1ab574baa)]
 interface nsIAddonPolicyService : nsISupports
 {
   /**
    * Returns true if unprivileged code associated with the given addon may load
    * data from |aURI|.
    */
   boolean addonMayLoadURI(in AString aAddonId, in nsIURI aURI);
+
+  /**
+   * Returns true if a given extension:// URI is web-accessible.
+   */
+  boolean extensionURILoadableByAnyone(in nsIURI aURI);
+
+  /**
+   * Maps an extension URI to the ID of the addon it belongs to.
+   */
+  AString extensionURIToAddonId(in nsIURI aURI);
 };
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -355,16 +355,30 @@ nsScriptSecurityManager::GetChannelResul
                 triggeringPrincipal.forget(aPrincipal);
                 return NS_OK;
             }
         }
     }
     return GetChannelURIPrincipal(aChannel, aPrincipal);
 }
 
+nsresult
+nsScriptSecurityManager::MaybeSetAddonIdFromURI(OriginAttributes& aAttrs, nsIURI* aURI)
+{
+  nsAutoCString scheme;
+  nsresult rv = aURI->GetScheme(scheme);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (scheme.EqualsLiteral("moz-extension") && GetAddonPolicyService()) {
+    rv = GetAddonPolicyService()->ExtensionURIToAddonId(aURI, aAttrs.mAddonId);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  return NS_OK;
+}
+
 /* The principal of the URI that this channel is loading. This is never
  * affected by things like sandboxed loads, or loads where we forcefully
  * inherit the principal.  Think of this as the principal of the server
  * which this channel is loading from.  Most callers should use
  * GetChannelResultPrincipal instead of GetChannelURIPrincipal.  Only
  * call GetChannelURIPrincipal if you are sure that you want the
  * principal that matches the uri, even in cases when the load is
  * sandboxed or when the load could be a blob or data uri (i.e even when
@@ -386,16 +400,18 @@ nsScriptSecurityManager::GetChannelURIPr
     nsCOMPtr<nsILoadContext> loadContext;
     NS_QueryNotificationCallbacks(aChannel, loadContext);
 
     if (loadContext) {
         return GetLoadContextCodebasePrincipal(uri, loadContext, aPrincipal);
     }
 
     OriginAttributes attrs(UNKNOWN_APP_ID, false);
+    rv = MaybeSetAddonIdFromURI(attrs, uri);
+    NS_ENSURE_SUCCESS(rv, rv);
     nsCOMPtr<nsIPrincipal> prin = BasePrincipal::CreateCodebasePrincipal(uri, attrs);
     prin.forget(aPrincipal);
     return *aPrincipal ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 nsScriptSecurityManager::IsSystemPrincipal(nsIPrincipal* aPrincipal,
                                            bool* aIsSystem)
@@ -740,16 +756,26 @@ nsScriptSecurityManager::CheckLoadURIWit
 
     // If the schemes don't match, the policy is specified by the protocol
     // flags on the target URI.  Note that the order of policy checks here is
     // very important!  We start from most restrictive and work our way down.
     // Note that since we're working with the innermost URI, we can just use
     // the methods that work on chains of nested URIs and they will only look
     // at the flags for our one URI.
 
+    // Special case: moz-extension has a whitelist of URIs that are loadable by
+    // anyone.
+    if (targetScheme.EqualsLiteral("moz-extension") && GetAddonPolicyService()) {
+      bool loadable = false;
+      rv = GetAddonPolicyService()->ExtensionURILoadableByAnyone(targetBaseURI, &loadable);
+      if (NS_SUCCEEDED(rv) && loadable) {
+        return NS_OK;
+      }
+    }
+
     // Check for system target URI
     rv = DenyAccessIfURIHasFlags(targetBaseURI,
                                  nsIProtocolHandler::URI_DANGEROUS_TO_LOAD);
     if (NS_FAILED(rv)) {
         // Deny access, since the origin principal is not system
         if (reportErrors) {
             ReportError(nullptr, errorTag, sourceURI, aTargetURI);
         }
@@ -1082,28 +1108,32 @@ nsScriptSecurityManager::
   GetLoadContextCodebasePrincipal(nsIURI* aURI,
                                   nsILoadContext* aLoadContext,
                                   nsIPrincipal** aPrincipal)
 {
   // XXXbholley - Make this more general in bug 1165466.
   OriginAttributes attrs;
   aLoadContext->GetAppId(&attrs.mAppId);
   aLoadContext->GetIsInBrowserElement(&attrs.mInBrowser);
+  nsresult rv = MaybeSetAddonIdFromURI(attrs, aURI);
+  NS_ENSURE_SUCCESS(rv, rv);
   nsCOMPtr<nsIPrincipal> prin = BasePrincipal::CreateCodebasePrincipal(aURI, attrs);
   prin.forget(aPrincipal);
   return *aPrincipal ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 nsScriptSecurityManager::GetDocShellCodebasePrincipal(nsIURI* aURI,
                                                       nsIDocShell* aDocShell,
                                                       nsIPrincipal** aPrincipal)
 {
   // XXXbholley - Make this more general in bug 1165466.
   OriginAttributes attrs(aDocShell->GetAppId(), aDocShell->GetIsInBrowserElement());
+  nsresult rv = MaybeSetAddonIdFromURI(attrs, aURI);
+  NS_ENSURE_SUCCESS(rv, rv);
   nsCOMPtr<nsIPrincipal> prin = BasePrincipal::CreateCodebasePrincipal(aURI, attrs);
   prin.forget(aPrincipal);
   return *aPrincipal ? NS_OK : NS_ERROR_FAILURE;
 }
 
 // static
 nsIPrincipal*
 nsScriptSecurityManager::doGetObjectPrincipal(JSObject *aObj)
--- a/caps/nsScriptSecurityManager.h
+++ b/caps/nsScriptSecurityManager.h
@@ -3,20 +3,25 @@
 /* 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 nsScriptSecurityManager_h__
 #define nsScriptSecurityManager_h__
 
 #include "nsIScriptSecurityManager.h"
+
+#include "nsIAddonPolicyService.h"
+#include "mozilla/Maybe.h"
+#include "nsIAddonPolicyService.h"
 #include "nsIPrincipal.h"
 #include "nsCOMPtr.h"
 #include "nsIChannelEventSink.h"
 #include "nsIObserver.h"
+#include "nsServiceManagerUtils.h"
 #include "plstr.h"
 #include "js/TypeDecls.h"
 
 #include <stdint.h>
 
 class nsCString;
 class nsIIOService;
 class nsIStringBundle;
@@ -110,25 +115,39 @@ private:
     InitPrefs();
 
     inline void
     ScriptSecurityPrefChanged();
 
     inline void
     AddSitesToFileURIWhitelist(const nsCString& aSiteList);
 
+    // If aURI is a moz-extension:// URI, set mAddonId to the associated addon.
+    nsresult MaybeSetAddonIdFromURI(mozilla::OriginAttributes& aAttrs, nsIURI* aURI);
+
     nsCOMPtr<nsIPrincipal> mSystemPrincipal;
     bool mPrefInitialized;
     bool mIsJavaScriptEnabled;
     nsTArray<nsCOMPtr<nsIURI>> mFileURIWhitelist;
 
     // This machinery controls new-style domain policies. The old-style
     // policy machinery will be removed soon.
     nsCOMPtr<nsIDomainPolicy> mDomainPolicy;
 
+    // Cached addon policy service. We can't generate this in Init() because
+    // that's too early to get a service.
+    mozilla::Maybe<nsCOMPtr<nsIAddonPolicyService>> mAddonPolicyService;
+    nsIAddonPolicyService* GetAddonPolicyService()
+    {
+        if (mAddonPolicyService.isNothing()) {
+            mAddonPolicyService.emplace(do_GetService("@mozilla.org/addons/policy-service;1"));
+        }
+        return mAddonPolicyService.ref();
+    }
+
     static bool sStrictFileOriginPolicy;
 
     static nsIIOService    *sIOService;
     static nsIStringBundle *sStrBundle;
     static JSRuntime       *sRuntime;
 };
 
 namespace mozilla {
--- a/caps/tests/mochitest/mochitest.ini
+++ b/caps/tests/mochitest/mochitest.ini
@@ -6,8 +6,10 @@ support-files =
 [test_app_principal_equality.html]
 skip-if = e10s
 [test_bug246699.html]
 [test_bug292789.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_bug423375.html]
 [test_bug470804.html]
 [test_disallowInheritPrincipal.html]
+[test_extensionURL.html]
+skip-if = (os == 'android' || buildapp == 'b2g') # Bug 1185773 for android, nonsensical on b2g
new file mode 100644
--- /dev/null
+++ b/caps/tests/mochitest/resource_test_file.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<html><head><title>resource test file</title></head><body></body></html>
new file mode 100644
--- /dev/null
+++ b/caps/tests/mochitest/test_extensionURL.html
@@ -0,0 +1,144 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1161831
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1161831</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript">
+
+  /** Test for Bug 1161831 **/
+  SimpleTest.waitForExplicitFinish();
+
+  var aps = SpecialPowers.Cc["@mozilla.org/addons/policy-service;1"]
+                         .getService(SpecialPowers.Ci.nsIAddonPolicyService).wrappedJSObject;
+  var oldLoadCallback = aps.setExtensionURILoadCallback(null);
+  var oldMapCallback = aps.setExtensionURIToAddonIdCallback(null);
+  var resourceHandler = SpecialPowers.Services.io.getProtocolHandler("resource")
+                                     .QueryInterface(SpecialPowers.Ci.nsISubstitutingProtocolHandler);
+  var extensionHandler = SpecialPowers.Services.io.getProtocolHandler("moz-extension")
+                                     .QueryInterface(SpecialPowers.Ci.nsISubstitutingProtocolHandler);
+
+  SimpleTest.registerCleanupFunction(function() {
+      extensionHandler.setSubstitution('cherise', null);
+      extensionHandler.setSubstitution('liebchen', null);
+      aps.setExtensionURILoadCallback(oldLoadCallback);
+      aps.setExtensionURIToAddonIdCallback(oldMapCallback);
+  });
+
+  addLoadEvent(function() {
+
+    // First, get a file:// URI to something - open to suggestions on how to do
+    //  this more easily.
+    var resURI = SpecialPowers.Services.io.newURI('resource://testing-common/resource_test_file.html', null, null);
+    var filePath = resourceHandler.resolveURI(resURI);
+    ok(filePath.startsWith('file://'), 'resource:// URI resolves where we expect: ' + filePath);
+    var fileURI = SpecialPowers.Services.io.newURI(filePath, null, null);
+
+    // Register a moz-extension:// URI.
+    extensionHandler.setSubstitution('cherise', fileURI);
+
+    // Alias the above.
+    extensionHandler.setSubstitution('liebchen', SpecialPowers.Services.io.newURI('moz-extension://cherise', null, null));
+
+    //
+    // Make sure that non-file:// URIs don't work.
+    //
+
+    // resource://
+    try {
+      extensionHandler.setSubstitution('interdit', resURI);
+      ok(false, "Should have thrown for mapping moz-extension to resource");
+    } catch (e) {
+      ok(true, "Threw correctly: " + e);
+    }
+
+    // chrome://
+    try {
+      var chromeURI = SpecialPowers.Services.io.newURI('chrome://global/content/mozilla.xhtml', null, null);
+      extensionHandler.setSubstitution('verboten', chromeURI);
+      ok(false, "Should have thrown for mapping moz-extension to chrome");
+    } catch (e) {
+      ok(true, "Threw correctly: " + e);
+    }
+
+    function navigateWithLocation(ifr, url) { ifr.contentWindow.location = url; }
+    function navigateWithSrc(ifr, url) { ifr.setAttribute('src', url); }
+    function navigateFromChromeWithLocation(ifr, url) { SpecialPowers.wrap(ifr).contentWindow.location = url; }
+    function navigateFromChromeWithWebNav(ifr, url) {
+      SpecialPowers.wrap(ifr).contentWindow
+                   .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
+                   .getInterface(SpecialPowers.Ci.nsIWebNavigation)
+                   .loadURI(url, 0, null, null, null);
+    }
+
+
+    function setWhitelistCallback(rgxp) {
+      var cb = SpecialPowers.wrapCallback(function(uri) { return rgxp.test(uri.spec); });
+      aps.setExtensionURILoadCallback(cb);
+    }
+
+    aps.setExtensionURIToAddonIdCallback(SpecialPowers.wrapCallback(function (uri) { return 'imaginaryaddon-' + uri.host[0]; }));
+
+    function testLoad(url, navigate, shouldThrow) {
+      var ifr = document.createElement('iframe');
+      var p = new Promise(function(resolve, reject) {
+        ifr.onload = function() {
+          ok(true, 'Loaded ' + url);
+          var prin = SpecialPowers.wrap(ifr.contentWindow).document.nodePrincipal;
+          is(prin.originNoSuffix, url, 'Principal origin is correct: ' + url);
+          is(prin.originAttributes.addonId, 'imaginaryaddon-' + url[url.indexOf('/') + 2], 'addonId is correct');
+          is(SpecialPowers.wrap(ifr.contentWindow).document.title, 'resource test file',
+             'document looks right');
+          ifr.remove();
+          resolve();
+        };
+        document.body.appendChild(ifr);
+
+        var threw = false;
+        try {
+          navigate(ifr, url);
+        } catch (e) {
+          ifr.remove();
+          threw = true;
+          ok(/denied|insecure/.test(e), "exceiton correct: " + e);
+        }
+        is(threw, !!shouldThrow, "Correct throwing behavior for: " + url);
+        !threw || resolve();
+      });
+
+      return p;
+    }
+
+    //
+    // Perform some loads and make sure they work correctly.
+    //
+    testLoad.bind(null, 'moz-extension://cherise', navigateFromChromeWithLocation)()
+    .then(testLoad.bind(null, 'moz-extension://cherise', navigateFromChromeWithWebNav))
+    .then(testLoad.bind(null, 'moz-extension://cherise', navigateWithLocation, /* shouldThrow = */ true))
+    .then(setWhitelistCallback.bind(null, /cherise/))
+    .then(testLoad.bind(null, 'moz-extension://cherise', navigateWithLocation))
+    .then(testLoad.bind(null, 'moz-extension://liebchen', navigateWithLocation, /* shouldThrow = */ true))
+    .then(setWhitelistCallback.bind(null, /cherise|liebchen/))
+    .then(testLoad.bind(null, 'moz-extension://liebchen', navigateWithLocation))
+    .then(testLoad.bind(null, 'moz-extension://liebchen', navigateWithSrc))
+    .then(testLoad.bind(null, 'moz-extension://cherise', navigateWithSrc))
+    .then(SimpleTest.finish.bind(SimpleTest),
+          function(e) { ok(false, "rejected promise: " + e); SimpleTest.finish() }
+    );
+  });
+
+  </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1161831">Mozilla Bug 1161831</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
--- a/chrome/RegistryMessageUtils.h
+++ b/chrome/RegistryMessageUtils.h
@@ -34,24 +34,26 @@ struct ChromePackage
     return package.Equals(rhs.package) &&
            contentBaseURI == rhs.contentBaseURI &&
            localeBaseURI == rhs.localeBaseURI &&
            skinBaseURI == rhs.skinBaseURI &&
            flags == rhs.flags;
   }
 };
 
-struct ResourceMapping
+struct SubstitutionMapping
 {
-  nsCString resource;
+  nsCString scheme;
+  nsCString path;
   SerializedURI resolvedURI;
 
-  bool operator ==(const ResourceMapping& rhs) const
+  bool operator ==(const SubstitutionMapping& rhs) const
   {
-    return resource.Equals(rhs.resource) &&
+    return scheme.Equals(rhs.scheme) &&
+           path.Equals(rhs.path) &&
            resolvedURI == rhs.resolvedURI;
   }
 };
 
 struct OverrideMapping
 {
   SerializedURI originalURI;
   SerializedURI overrideURI;
@@ -129,43 +131,48 @@ struct ParamTraits<ChromePackage>
     aLog->append(StringPrintf(L"[%s, %s, %s, %s, %u]", aParam.package.get(),
                              aParam.contentBaseURI.spec.get(),
                              aParam.localeBaseURI.spec.get(),
                              aParam.skinBaseURI.spec.get(), aParam.flags));
   }
 };
 
 template <>
-struct ParamTraits<ResourceMapping>
+struct ParamTraits<SubstitutionMapping>
 {
-  typedef ResourceMapping paramType;
+  typedef SubstitutionMapping paramType;
   
   static void Write(Message* aMsg, const paramType& aParam)
   {
-    WriteParam(aMsg, aParam.resource);
+    WriteParam(aMsg, aParam.scheme);
+    WriteParam(aMsg, aParam.path);
     WriteParam(aMsg, aParam.resolvedURI);
   }
   
   static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
   {
-    nsCString resource;
+    nsCString scheme, path;
     SerializedURI resolvedURI;
     
-    if (ReadParam(aMsg, aIter, &resource) &&
+    if (ReadParam(aMsg, aIter, &scheme) &&
+        ReadParam(aMsg, aIter, &path) &&
         ReadParam(aMsg, aIter, &resolvedURI)) {
-      aResult->resource = resource;
+      aResult->scheme = scheme;
+      aResult->path = path;
       aResult->resolvedURI = resolvedURI;
       return true;
     }
     return false;
   }
 
   static void Log(const paramType& aParam, std::wstring* aLog)
   {
-    aLog->append(StringPrintf(L"[%s, %s, %u]", aParam.resource.get(),
+    aLog->append(StringPrintf(L"[%s://%s, %s, %u]",
+                             aParam.scheme.get(),
+                             aParam.path.get(),
                              aParam.resolvedURI.spec.get()));
   }
 };
 
 template <>
 struct ParamTraits<OverrideMapping>
 {
   typedef OverrideMapping paramType;
--- a/chrome/nsChromeRegistryChrome.cpp
+++ b/chrome/nsChromeRegistryChrome.cpp
@@ -440,17 +440,17 @@ struct EnumerationArgs
   const nsCString& selectedSkin;
 };
 
 void
 nsChromeRegistryChrome::SendRegisteredChrome(
     mozilla::dom::PContentParent* aParent)
 {
   InfallibleTArray<ChromePackage> packages;
-  InfallibleTArray<ResourceMapping> resources;
+  InfallibleTArray<SubstitutionMapping> resources;
   InfallibleTArray<OverrideMapping> overrides;
 
   EnumerationArgs args = {
     packages, mSelectedLocale, mSelectedSkin
   };
   mPackagesHash.EnumerateRead(CollectPackages, &args);
 
   // If we were passed a parent then a new child process has been created and
--- a/chrome/nsChromeRegistryContent.cpp
+++ b/chrome/nsChromeRegistryContent.cpp
@@ -12,17 +12,17 @@
 
 nsChromeRegistryContent::nsChromeRegistryContent()
 {
 }
 
 void
 nsChromeRegistryContent::RegisterRemoteChrome(
     const InfallibleTArray<ChromePackage>& aPackages,
-    const InfallibleTArray<ResourceMapping>& aResources,
+    const InfallibleTArray<SubstitutionMapping>& aSubstitutions,
     const InfallibleTArray<OverrideMapping>& aOverrides,
     const nsACString& aLocale,
     bool aReset)
 {
   MOZ_ASSERT(aReset || mLocale.IsEmpty(),
              "RegisterChrome twice?");
 
   if (aReset) {
@@ -31,19 +31,19 @@ nsChromeRegistryContent::RegisterRemoteC
     // XXX Can't clear resources.
   }
 
   for (uint32_t i = aPackages.Length(); i > 0; ) {
     --i;
     RegisterPackage(aPackages[i]);
   }
 
-  for (uint32_t i = aResources.Length(); i > 0; ) {
+  for (uint32_t i = aSubstitutions.Length(); i > 0; ) {
     --i;
-    RegisterResource(aResources[i]);
+    RegisterSubstitution(aSubstitutions[i]);
   }
 
   for (uint32_t i = aOverrides.Length(); i > 0; ) {
     --i;
     RegisterOverride(aOverrides[i]);
   }
 
   mLocale = aLocale;
@@ -89,42 +89,42 @@ nsChromeRegistryContent::RegisterPackage
   entry->contentBaseURI = content;
   entry->localeBaseURI = locale;
   entry->skinBaseURI = skin;
 
   mPackagesHash.Put(aPackage.package, entry);
 }
 
 void
-nsChromeRegistryContent::RegisterResource(const ResourceMapping& aResource)
+nsChromeRegistryContent::RegisterSubstitution(const SubstitutionMapping& aSubstitution)
 {
   nsCOMPtr<nsIIOService> io (do_GetIOService());
   if (!io)
     return;
 
   nsCOMPtr<nsIProtocolHandler> ph;
-  nsresult rv = io->GetProtocolHandler("resource", getter_AddRefs(ph));
+  nsresult rv = io->GetProtocolHandler(aSubstitution.scheme.get(), getter_AddRefs(ph));
   if (NS_FAILED(rv))
     return;
   
-  nsCOMPtr<nsIResProtocolHandler> rph (do_QueryInterface(ph));
-  if (!rph)
+  nsCOMPtr<nsISubstitutingProtocolHandler> sph (do_QueryInterface(ph));
+  if (!sph)
     return;
 
   nsCOMPtr<nsIURI> resolvedURI;
-  if (aResource.resolvedURI.spec.Length()) {
+  if (aSubstitution.resolvedURI.spec.Length()) {
     nsresult rv = NS_NewURI(getter_AddRefs(resolvedURI),
-                            aResource.resolvedURI.spec,
-                            aResource.resolvedURI.charset.get(),
+                            aSubstitution.resolvedURI.spec,
+                            aSubstitution.resolvedURI.charset.get(),
                             nullptr, io);
     if (NS_FAILED(rv))
       return;
   }
 
-  rv = rph->SetSubstitution(aResource.resource, resolvedURI);
+  rv = sph->SetSubstitution(aSubstitution.path, resolvedURI);
   if (NS_FAILED(rv))
     return;
 }
 
 void
 nsChromeRegistryContent::RegisterOverride(const OverrideMapping& aOverride)
 {
   nsCOMPtr<nsIIOService> io (do_GetIOService());
--- a/chrome/nsChromeRegistryContent.h
+++ b/chrome/nsChromeRegistryContent.h
@@ -5,26 +5,26 @@
 
 #ifndef nsChromeRegistryContent_h
 #define nsChromeRegistryContent_h
 
 #include "nsChromeRegistry.h"
 #include "nsClassHashtable.h"
 
 struct ChromePackage;
-struct ResourceMapping;
+struct SubstitutionMapping;
 struct OverrideMapping;
 
 class nsChromeRegistryContent : public nsChromeRegistry
 {
  public:
   nsChromeRegistryContent();
 
   void RegisterRemoteChrome(const InfallibleTArray<ChromePackage>& aPackages,
-                            const InfallibleTArray<ResourceMapping>& aResources,
+                            const InfallibleTArray<SubstitutionMapping>& aResources,
                             const InfallibleTArray<OverrideMapping>& aOverrides,
                             const nsACString& aLocale,
                             bool aReset);
 
   NS_IMETHOD GetLocalesForPackage(const nsACString& aPackage,
                                   nsIUTF8StringEnumerator* *aResult) override;
   NS_IMETHOD CheckForNewChrome() override;
   NS_IMETHOD CheckForOSAccessibility() override;
@@ -36,17 +36,17 @@ class nsChromeRegistryContent : public n
                                nsACString& aLocale) override;
   NS_IMETHOD GetStyleOverlays(nsIURI *aChromeURL,
                               nsISimpleEnumerator **aResult) override;
   NS_IMETHOD GetXULOverlays(nsIURI *aChromeURL,
                             nsISimpleEnumerator **aResult) override;
 
   void RegisterPackage(const ChromePackage& aPackage);
   void RegisterOverride(const OverrideMapping& aOverride);
-  void RegisterResource(const ResourceMapping& aResource);
+  void RegisterSubstitution(const SubstitutionMapping& aResource);
 
  private:
   struct PackageEntry
   {
     PackageEntry() : flags(0) { }
     ~PackageEntry() { }
 
     nsCOMPtr<nsIURI> contentBaseURI;
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -295,16 +295,17 @@ GK_ATOM(deviceHeight, "device-height")
 GK_ATOM(deviceWidth, "device-width")
 GK_ATOM(dfn, "dfn")
 GK_ATOM(dialog, "dialog")
 GK_ATOM(difference, "difference")
 GK_ATOM(digit, "digit")
 GK_ATOM(dir, "dir")
 GK_ATOM(dirAutoSetBy, "dirAutoSetBy")
 GK_ATOM(directionality, "directionality")
+GK_ATOM(directory, "directory")
 GK_ATOM(disableOutputEscaping, "disable-output-escaping")
 GK_ATOM(disabled, "disabled")
 GK_ATOM(disablehistory, "disablehistory")
 GK_ATOM(display, "display")
 GK_ATOM(distinct, "distinct")
 GK_ATOM(div, "div")
 GK_ATOM(dl, "dl")
 GK_ATOM(doctypePublic, "doctype-public")
@@ -2286,17 +2287,16 @@ GK_ATOM(complementary, "complementary")
 GK_ATOM(containerAtomic, "container-atomic")
 GK_ATOM(containerBusy, "container-busy")
 GK_ATOM(containerLive, "container-live")
 GK_ATOM(containerLiveRole, "container-live-role")
 GK_ATOM(containerRelevant, "container-relevant")
 GK_ATOM(contentinfo, "contentinfo")
 GK_ATOM(cycles, "cycles")
 GK_ATOM(datatable, "datatable")
-GK_ATOM(directory, "directory")
 GK_ATOM(eventFromInput, "event-from-input")
 GK_ATOM(grammar, "grammar")
 GK_ATOM(gridcell, "gridcell")
 GK_ATOM(heading, "heading")
 GK_ATOM(hitregion, "hitregion")
 GK_ATOM(InlineBlockFrame, "InlineBlockFrame")
 GK_ATOM(inlinevalue, "inline")
 GK_ATOM(invalid, "invalid")
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -3773,20 +3773,20 @@ nsGlobalWindow::GetParent(ErrorResult& a
  *
  * In contrast to GetRealParent, GetScriptableParent respects <iframe
  * mozbrowser> boundaries, so if |this| is contained by an <iframe
  * mozbrowser>, we will return |this| as its own parent.
  */
 NS_IMETHODIMP
 nsGlobalWindow::GetScriptableParent(nsIDOMWindow** aParent)
 {
-  FORWARD_TO_INNER(GetScriptableParent, (aParent), NS_ERROR_UNEXPECTED);
-
-  ErrorResult rv;
-  nsCOMPtr<nsIDOMWindow> parent = GetParent(rv);
+  FORWARD_TO_OUTER(GetScriptableParent, (aParent), NS_ERROR_UNEXPECTED);
+
+  ErrorResult rv;
+  nsCOMPtr<nsIDOMWindow> parent = GetParentOuter(rv);
   parent.forget(aParent);
 
   return rv.StealNSResult();
 }
 
 /**
  * nsIDOMWindow::GetParent (when called from C++) is just a wrapper around
  * GetRealParent.
--- a/dom/canvas/WebGL2ContextDraw.cpp
+++ b/dom/canvas/WebGL2ContextDraw.cpp
@@ -8,12 +8,12 @@
 namespace mozilla {
 
 // -------------------------------------------------------------------------
 // Writing to the drawing buffer
 
 void
 WebGL2Context::DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, GLintptr offset)
 {
-    MOZ_CRASH("Not Implemented.");
+    GenerateWarning("drawRangeElements: Not Implemented.");
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGL2ContextFramebuffers.cpp
+++ b/dom/canvas/WebGL2ContextFramebuffers.cpp
@@ -328,23 +328,23 @@ WebGL2Context::BlitFramebuffer(GLint src
     gl->fBlitFramebuffer(srcX0, srcY0, srcX1, srcY1,
                          dstX0, dstY0, dstX1, dstY1,
                          mask, filter);
 }
 
 void
 WebGL2Context::FramebufferTextureLayer(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer)
 {
-    MOZ_CRASH("Not Implemented.");
+    GenerateWarning("framebufferTextureLayer: Not Implemented.");
 }
 
 void
 WebGL2Context::GetInternalformatParameter(JSContext*, GLenum target, GLenum internalformat, GLenum pname, JS::MutableHandleValue retval)
 {
-    MOZ_CRASH("Not Implemented.");
+    GenerateWarning("getInternalformatParameter: Not Implemented.");
 }
 
 // Map attachments intended for the default buffer, to attachments for a non-
 // default buffer.
 static bool
 TranslateDefaultAttachments(const dom::Sequence<GLenum>& in, dom::Sequence<GLenum>* out)
 {
     for (size_t i = 0; i < in.Length(); i++) {
--- a/dom/canvas/WebGL2ContextTextures.cpp
+++ b/dom/canvas/WebGL2ContextTextures.cpp
@@ -426,41 +426,41 @@ WebGL2Context::TexSubImage3D(GLenum rawT
 }
 
 void
 WebGL2Context::TexSubImage3D(GLenum target, GLint level,
                              GLint xoffset, GLint yoffset, GLint zoffset,
                              GLenum format, GLenum type, dom::ImageData* data,
                              ErrorResult& rv)
 {
-    MOZ_CRASH("Not Implemented.");
+    GenerateWarning("texSubImage3D: Not implemented.");
 }
 
 void
 WebGL2Context::CopyTexSubImage3D(GLenum target, GLint level,
                                  GLint xoffset, GLint yoffset, GLint zoffset,
                                  GLint x, GLint y, GLsizei width, GLsizei height)
 {
-    MOZ_CRASH("Not Implemented.");
+    GenerateWarning("copyTexSubImage3D: Not implemented.");
 }
 
 void
 WebGL2Context::CompressedTexImage3D(GLenum target, GLint level, GLenum internalformat,
                                     GLsizei width, GLsizei height, GLsizei depth,
                                     GLint border, GLsizei imageSize, const dom::ArrayBufferView& data)
 {
-    MOZ_CRASH("Not Implemented.");
+    GenerateWarning("compressedTexImage3D: Not implemented.");
 }
 
 void
 WebGL2Context::CompressedTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset,
                                        GLsizei width, GLsizei height, GLsizei depth,
                                        GLenum format, GLsizei imageSize, const dom::ArrayBufferView& data)
 {
-    MOZ_CRASH("Not Implemented.");
+    GenerateWarning("compressedTexSubImage3D: Not implemented.");
 }
 
 JS::Value
 WebGL2Context::GetTexParameterInternal(const TexTarget& target, GLenum pname)
 {
     switch (pname) {
         case LOCAL_GL_TEXTURE_BASE_LEVEL:
         case LOCAL_GL_TEXTURE_COMPARE_FUNC:
--- a/dom/canvas/WebGL2ContextUniforms.cpp
+++ b/dom/canvas/WebGL2ContextUniforms.cpp
@@ -11,119 +11,24 @@
 #include "WebGLBuffer.h"
 #include "WebGLContext.h"
 #include "WebGLProgram.h"
 #include "WebGLVertexArray.h"
 #include "WebGLVertexAttribData.h"
 
 namespace mozilla {
 
-typedef union { GLint i; GLfloat f; GLuint u; } fi_t;
-
-static inline
-GLfloat PuntToFloat(GLint i)
-{
-   fi_t tmp;
-   tmp.i = i;
-   return tmp.f;
-}
-
-static inline
-GLfloat PuntToFloat(GLuint u)
-{
-   fi_t tmp;
-   tmp.u = u;
-   return tmp.f;
-}
-
-bool
-WebGL2Context::ValidateAttribPointerType(bool integerMode, GLenum type,
-                                         GLsizei* out_alignment, const char* info)
-{
-    MOZ_ASSERT(out_alignment);
-
-    switch (type) {
-    case LOCAL_GL_BYTE:
-    case LOCAL_GL_UNSIGNED_BYTE:
-        *out_alignment = 1;
-        return true;
-
-    case LOCAL_GL_SHORT:
-    case LOCAL_GL_UNSIGNED_SHORT:
-        *out_alignment = 2;
-        return true;
-
-    case LOCAL_GL_INT:
-    case LOCAL_GL_UNSIGNED_INT:
-        *out_alignment = 4;
-        return true;
-    }
-
-    if (!integerMode) {
-        switch (type) {
-        case LOCAL_GL_HALF_FLOAT:
-            *out_alignment = 2;
-            return true;
-
-        case LOCAL_GL_FLOAT:
-        case LOCAL_GL_FIXED:
-        case LOCAL_GL_INT_2_10_10_10_REV:
-        case LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV:
-            *out_alignment = 4;
-            return true;
-        }
-    }
-
-    ErrorInvalidEnum("%s: invalid enum value 0x%x", info, type);
-    return false;
-}
-
 bool
 WebGL2Context::ValidateUniformMatrixTranspose(bool /*transpose*/, const char* /*info*/)
 {
     return true;
 }
 
 // -------------------------------------------------------------------------
-// Uniforms and attributes
-
-void
-WebGL2Context::VertexAttribIPointer(GLuint index, GLint size, GLenum type, GLsizei stride,
-                                    GLintptr offset)
-{
-    if (IsContextLost())
-        return;
-
-    if (!ValidateAttribIndex(index, "vertexAttribIPointer"))
-        return;
-
-    if (!ValidateAttribPointer(true, index, size, type, LOCAL_GL_FALSE, stride, offset,
-                               "vertexAttribIPointer"))
-    {
-        return;
-    }
-
-    MOZ_ASSERT(mBoundVertexArray);
-    mBoundVertexArray->EnsureAttrib(index);
-
-    InvalidateBufferFetching();
-
-    WebGLVertexAttribData& vd = mBoundVertexArray->mAttribs[index];
-
-    vd.buf = mBoundArrayBuffer;
-    vd.stride = stride;
-    vd.size = size;
-    vd.byteOffset = offset;
-    vd.type = type;
-    vd.normalized = false;
-    vd.integer = true;
-
-    MakeContextCurrent();
-    gl->fVertexAttribIPointer(index, size, type, stride, reinterpret_cast<void*>(offset));
-}
+// Uniforms
 
 void
 WebGL2Context::Uniform1ui(WebGLUniformLocation* loc, GLuint v0)
 {
     GLuint rawLoc;
     if (!ValidateUniformSetter(loc, 1, LOCAL_GL_UNSIGNED_INT, "uniform1ui", &rawLoc))
         return;
 
@@ -334,95 +239,16 @@ WebGL2Context::UniformMatrix4x3fv_base(W
     {
         return;
     }
 
     MakeContextCurrent();
     gl->fUniformMatrix4x3fv(rawLoc, numElementsToUpload, transpose, data);
 }
 
-void
-WebGL2Context::VertexAttribI4i(GLuint index, GLint x, GLint y, GLint z, GLint w)
-{
-    if (IsContextLost())
-        return;
-
-    if (index || gl->IsGLES()) {
-        MakeContextCurrent();
-        gl->fVertexAttribI4i(index, x, y, z, w);
-    } else {
-        mVertexAttrib0Vector[0] = PuntToFloat(x);
-        mVertexAttrib0Vector[1] = PuntToFloat(y);
-        mVertexAttrib0Vector[2] = PuntToFloat(z);
-        mVertexAttrib0Vector[3] = PuntToFloat(w);
-    }
-}
-
-void
-WebGL2Context::VertexAttribI4iv(GLuint index, size_t length, const GLint* v)
-{
-    if (!ValidateAttribArraySetter("vertexAttribI4iv", 4, length))
-        return;
-
-    if (index || gl->IsGLES()) {
-        MakeContextCurrent();
-        gl->fVertexAttribI4iv(index, v);
-    } else {
-        mVertexAttrib0Vector[0] = PuntToFloat(v[0]);
-        mVertexAttrib0Vector[1] = PuntToFloat(v[1]);
-        mVertexAttrib0Vector[2] = PuntToFloat(v[2]);
-        mVertexAttrib0Vector[3] = PuntToFloat(v[3]);
-    }
-}
-
-void
-WebGL2Context::VertexAttribI4iv(GLuint index, const dom::Sequence<GLint>& v)
-{
-    VertexAttribI4iv(index, v.Length(), v.Elements());
-}
-
-void
-WebGL2Context::VertexAttribI4ui(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w)
-{
-    if (IsContextLost())
-        return;
-
-    if (index || gl->IsGLES()) {
-        MakeContextCurrent();
-        gl->fVertexAttribI4ui(index, x, y, z, w);
-    } else {
-        mVertexAttrib0Vector[0] = PuntToFloat(x);
-        mVertexAttrib0Vector[1] = PuntToFloat(y);
-        mVertexAttrib0Vector[2] = PuntToFloat(z);
-        mVertexAttrib0Vector[3] = PuntToFloat(w);
-    }
-}
-
-void
-WebGL2Context::VertexAttribI4uiv(GLuint index, size_t length, const GLuint* v)
-{
-    if (IsContextLost())
-        return;
-
-    if (index || gl->IsGLES()) {
-        MakeContextCurrent();
-        gl->fVertexAttribI4uiv(index, v);
-    } else {
-        mVertexAttrib0Vector[0] = PuntToFloat(v[0]);
-        mVertexAttrib0Vector[1] = PuntToFloat(v[1]);
-        mVertexAttrib0Vector[2] = PuntToFloat(v[2]);
-        mVertexAttrib0Vector[3] = PuntToFloat(v[3]);
-    }
-}
-
-void
-WebGL2Context::VertexAttribI4uiv(GLuint index, const dom::Sequence<GLuint>& v)
-{
-    VertexAttribI4uiv(index, v.Length(), v.Elements());
-}
 
 // -------------------------------------------------------------------------
 // Uniform Buffer Objects and Transform Feedback Buffers
 // TODO(djg): Implemented in WebGLContext
 /*
     void BindBufferBase(GLenum target, GLuint index, WebGLBuffer* buffer);
     void BindBufferRange(GLenum target, GLuint index, WebGLBuffer* buffer,
                          GLintptr offset, GLsizeiptr size);
new file mode 100644
--- /dev/null
+++ b/dom/canvas/WebGL2ContextVertices.cpp
@@ -0,0 +1,199 @@
+/* -*- 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 "WebGL2Context.h"
+
+#include "GLContext.h"
+#include "WebGLVertexArray.h"
+#include "WebGLVertexAttribData.h"
+
+#include "mozilla/Casting.h"
+
+namespace mozilla {
+
+bool
+WebGL2Context::ValidateAttribPointerType(bool integerMode, GLenum type,
+                                         GLsizei* out_alignment, const char* info)
+{
+  MOZ_ASSERT(out_alignment);
+
+  switch (type) {
+  case LOCAL_GL_BYTE:
+  case LOCAL_GL_UNSIGNED_BYTE:
+    *out_alignment = 1;
+    return true;
+
+  case LOCAL_GL_SHORT:
+  case LOCAL_GL_UNSIGNED_SHORT:
+    *out_alignment = 2;
+    return true;
+
+  case LOCAL_GL_INT:
+  case LOCAL_GL_UNSIGNED_INT:
+    *out_alignment = 4;
+    return true;
+  }
+
+  if (!integerMode) {
+    switch (type) {
+    case LOCAL_GL_HALF_FLOAT:
+      *out_alignment = 2;
+      return true;
+
+    case LOCAL_GL_FLOAT:
+    case LOCAL_GL_FIXED:
+    case LOCAL_GL_INT_2_10_10_10_REV:
+    case LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV:
+      *out_alignment = 4;
+      return true;
+    }
+  }
+
+  ErrorInvalidEnum("%s: invalid enum value 0x%x", info, type);
+  return false;
+}
+
+// -------------------------------------------------------------------------
+// Vertex Attributes
+
+void
+WebGL2Context::VertexAttribIPointer(GLuint index, GLint size, GLenum type, GLsizei stride,
+                                    GLintptr offset)
+{
+  if (IsContextLost())
+    return;
+
+  if (!ValidateAttribIndex(index, "vertexAttribIPointer"))
+    return;
+
+  if (!ValidateAttribPointer(true, index, size, type, LOCAL_GL_FALSE, stride, offset,
+                             "vertexAttribIPointer"))
+  {
+    return;
+  }
+
+  MOZ_ASSERT(mBoundVertexArray);
+  mBoundVertexArray->EnsureAttrib(index);
+
+  InvalidateBufferFetching();
+
+  WebGLVertexAttribData& vd = mBoundVertexArray->mAttribs[index];
+  vd.buf = mBoundArrayBuffer;
+  vd.stride = stride;
+  vd.size = size;
+  vd.byteOffset = offset;
+  vd.type = type;
+  vd.normalized = false;
+  vd.integer = true;
+
+  MakeContextCurrent();
+  gl->fVertexAttribIPointer(index, size, type, stride, reinterpret_cast<void*>(offset));
+}
+
+void
+WebGL2Context::VertexAttribI4i(GLuint index, GLint x, GLint y, GLint z, GLint w)
+{
+  if (IsContextLost())
+    return;
+
+  if (!ValidateAttribIndex(index, "vertexAttribI4i"))
+    return;
+
+  mVertexAttribType[index] = LOCAL_GL_INT;
+
+  if (index || gl->IsGLES()) {
+    MakeContextCurrent();
+    gl->fVertexAttribI4i(index, x, y, z, w);
+  } else {
+    mVertexAttrib0Vector[0] = BitwiseCast<GLfloat>(x);
+    mVertexAttrib0Vector[1] = BitwiseCast<GLfloat>(y);
+    mVertexAttrib0Vector[2] = BitwiseCast<GLfloat>(z);
+    mVertexAttrib0Vector[3] = BitwiseCast<GLfloat>(w);
+  }
+}
+
+void
+WebGL2Context::VertexAttribI4iv(GLuint index, size_t length, const GLint* v)
+{
+  if (!ValidateAttribIndex(index, "vertexAttribI4iv"))
+    return;
+
+  if (!ValidateAttribArraySetter("vertexAttribI4iv", 4, length))
+    return;
+
+  mVertexAttribType[index] = LOCAL_GL_INT;
+
+  if (index || gl->IsGLES()) {
+    MakeContextCurrent();
+    gl->fVertexAttribI4iv(index, v);
+  } else {
+    mVertexAttrib0Vector[0] = BitwiseCast<GLfloat>(v[0]);
+    mVertexAttrib0Vector[1] = BitwiseCast<GLfloat>(v[1]);
+    mVertexAttrib0Vector[2] = BitwiseCast<GLfloat>(v[2]);
+    mVertexAttrib0Vector[3] = BitwiseCast<GLfloat>(v[3]);
+  }
+}
+
+void
+WebGL2Context::VertexAttribI4iv(GLuint index, const dom::Sequence<GLint>& v)
+{
+  VertexAttribI4iv(index, v.Length(), v.Elements());
+}
+
+void
+WebGL2Context::VertexAttribI4ui(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w)
+{
+  if (IsContextLost())
+    return;
+
+  if (!ValidateAttribIndex(index, "vertexAttribI4ui"))
+    return;
+
+  mVertexAttribType[index] = LOCAL_GL_UNSIGNED_INT;
+
+  if (index || gl->IsGLES()) {
+    MakeContextCurrent();
+    gl->fVertexAttribI4ui(index, x, y, z, w);
+  } else {
+    mVertexAttrib0Vector[0] = BitwiseCast<GLfloat>(x);
+    mVertexAttrib0Vector[1] = BitwiseCast<GLfloat>(y);
+    mVertexAttrib0Vector[2] = BitwiseCast<GLfloat>(z);
+    mVertexAttrib0Vector[3] = BitwiseCast<GLfloat>(w);
+  }
+}
+
+void
+WebGL2Context::VertexAttribI4uiv(GLuint index, size_t length, const GLuint* v)
+{
+  if (IsContextLost())
+    return;
+
+  if (!ValidateAttribIndex(index, "vertexAttribI4uiv"))
+    return;
+
+  if (!ValidateAttribIndex(index, "vertexAttribI4uiv"))
+    return;
+
+  mVertexAttribType[index] = LOCAL_GL_UNSIGNED_INT;
+
+  if (index || gl->IsGLES()) {
+    MakeContextCurrent();
+    gl->fVertexAttribI4uiv(index, v);
+  } else {
+    mVertexAttrib0Vector[0] = BitwiseCast<GLfloat>(v[0]);
+    mVertexAttrib0Vector[1] = BitwiseCast<GLfloat>(v[1]);
+    mVertexAttrib0Vector[2] = BitwiseCast<GLfloat>(v[2]);
+    mVertexAttrib0Vector[3] = BitwiseCast<GLfloat>(v[3]);
+  }
+}
+
+void
+WebGL2Context::VertexAttribI4uiv(GLuint index, const dom::Sequence<GLuint>& v)
+{
+  VertexAttribI4uiv(index, v.Length(), v.Elements());
+}
+
+} // namespace mozilla
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -896,16 +896,21 @@ WebGLContext::SetDimensions(int32_t sign
     // If incrementing the generation would cause overflow,
     // don't allow it.  Allowing this would allow us to use
     // resource handles created from older context generations.
     if (!(mGeneration + 1).isValid()) {
         GenerateWarning("Too many WebGL contexts created this run.");
         return NS_ERROR_FAILURE; // exit without changing the value of mGeneration
     }
 
+    // increment the generation number - Do this early because later
+    // in CreateOffscreenGL(), "default" objects are created that will
+    // pick up the old generation.
+    ++mGeneration;
+
     // Get some prefs for some preferred/overriden things
     NS_ENSURE_TRUE(Preferences::GetRootBranch(), NS_ERROR_FAILURE);
 
     bool disabled = Preferences::GetBool("webgl.disabled", false);
 
     // TODO: When we have software webgl support we should use that instead.
     disabled |= gfxPlatform::InSafeMode();
 
@@ -944,19 +949,16 @@ WebGLContext::SetDimensions(int32_t sign
 #ifdef DEBUG
     if (gl->DebugMode())
         printf_stderr("--- WebGL context created: %p\n", gl.get());
 #endif
 
     mResetLayer = true;
     mOptionsFrozen = true;
 
-    // increment the generation number
-    ++mGeneration;
-
     // Update our internal stuff:
     if (gl->WorkAroundDriverBugs() && gl->IsANGLE()) {
         if (!mOptions.alpha && gl->Caps().alpha)
             mNeedsFakeNoAlpha = true;
 
         // ANGLE doesn't quite handle this properly.
         if (gl->Caps().depth && !gl->Caps().stencil)
             mNeedsFakeNoStencil = true;
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -1456,22 +1456,31 @@ protected:
     UniquePtr<FakeBlackTexture> mBlackTransparentTextureCubeMap;
 
     void
     BindFakeBlackTexturesHelper(GLenum target,
                                 const nsTArray<WebGLRefPtr<WebGLTexture> >& boundTexturesArray,
                                 UniquePtr<FakeBlackTexture>& opaqueTextureScopedPtr,
                                 UniquePtr<FakeBlackTexture>& transparentTextureScopedPtr);
 
+    // Generic Vertex Attributes
+    UniquePtr<GLenum[]> mVertexAttribType;
     GLfloat mVertexAttrib0Vector[4];
     GLfloat mFakeVertexAttrib0BufferObjectVector[4];
     size_t mFakeVertexAttrib0BufferObjectSize;
     GLuint mFakeVertexAttrib0BufferObject;
     WebGLVertexAttrib0Status mFakeVertexAttrib0BufferStatus;
 
+    void GetVertexAttribFloat(GLuint index, GLfloat* out_result);
+    void GetVertexAttribInt(GLuint index, GLint* out_result);
+    void GetVertexAttribUint(GLuint index, GLuint* out_result);
+    JSObject* GetVertexAttribFloat32Array(JSContext* cx, GLuint index);
+    JSObject* GetVertexAttribInt32Array(JSContext* cx, GLuint index);
+    JSObject* GetVertexAttribUint32Array(JSContext* cx, GLuint index);
+
     GLint mStencilRefFront;
     GLint mStencilRefBack;
     GLuint mStencilValueMaskFront;
     GLuint mStencilValueMaskBack;
     GLuint mStencilWriteMaskFront;
     GLuint mStencilWriteMaskBack;
     realGLboolean mColorWriteMask[4];
     realGLboolean mDepthWriteMask;
--- a/dom/canvas/WebGLContextExtensions.cpp
+++ b/dom/canvas/WebGLContextExtensions.cpp
@@ -154,16 +154,30 @@ WebGLContext::IsExtensionSupported(WebGL
         return gl->IsExtensionSupported(gl::GLContext::IMG_texture_compression_pvrtc);
     case WebGLExtensionID::WEBGL_compressed_texture_s3tc:
         if (gl->IsExtensionSupported(gl::GLContext::EXT_texture_compression_s3tc))
             return true;
 
         return gl->IsExtensionSupported(gl::GLContext::EXT_texture_compression_dxt1) &&
                gl->IsExtensionSupported(gl::GLContext::ANGLE_texture_compression_dxt3) &&
                gl->IsExtensionSupported(gl::GLContext::ANGLE_texture_compression_dxt5);
+
+    case WebGLExtensionID::WEBGL_debug_renderer_info:
+        {
+            bool isEnabled = true;
+
+#ifdef RELEASE_BUILD
+            // Keep this disabled on Release and Beta for now. (see bug 1171228)
+            isEnabled = false;
+#endif
+            if (Preferences::GetBool("webgl.disable-debug-renderer-info", false))
+                isEnabled = false;
+
+            return isEnabled;
+        }
     case WebGLExtensionID::WEBGL_depth_texture:
         // WEBGL_depth_texture supports DEPTH_STENCIL textures
         if (!gl->IsSupported(gl::GLFeature::packed_depth_stencil))
             return false;
 
         return gl->IsSupported(gl::GLFeature::depth_texture) ||
                gl->IsExtensionSupported(gl::GLContext::ANGLE_depth_texture);
     case WebGLExtensionID::WEBGL_draw_buffers:
--- a/dom/canvas/WebGLContextState.cpp
+++ b/dom/canvas/WebGLContextState.cpp
@@ -1,24 +1,27 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGLContext.h"
+
+#include "GLContext.h"
+#include "mozilla/dom/ToJSValue.h"
+#include "mozilla/Preferences.h"
+#include "nsString.h"
+#include "WebGLBuffer.h"
 #include "WebGLContextUtils.h"
-#include "WebGLBuffer.h"
+#include "WebGLFramebuffer.h"
+#include "WebGLProgram.h"
+#include "WebGLRenderbuffer.h"
 #include "WebGLShader.h"
-#include "WebGLProgram.h"
-#include "WebGLFramebuffer.h"
-#include "WebGLRenderbuffer.h"
 #include "WebGLTexture.h"
 #include "WebGLVertexArray.h"
-#include "GLContext.h"
-#include "mozilla/dom/ToJSValue.h"
 
 namespace mozilla {
 
 void
 WebGLContext::Disable(GLenum cap)
 {
     if (IsContextLost())
         return;
@@ -52,16 +55,28 @@ WebGLContext::Enable(GLenum cap)
     {
         *trackingSlot = 1;
     }
 
     MakeContextCurrent();
     gl->fEnable(cap);
 }
 
+static JS::Value
+StringValue(JSContext* cx, const nsAString& str, ErrorResult& rv)
+{
+    JSString* jsStr = JS_NewUCStringCopyN(cx, str.BeginReading(), str.Length());
+    if (!jsStr) {
+        rv.Throw(NS_ERROR_OUT_OF_MEMORY);
+        return JS::NullValue();
+    }
+
+    return JS::StringValue(jsStr);
+}
+
 bool
 WebGLContext::GetStencilBits(GLint* out_stencilBits)
 {
     *out_stencilBits = 0;
     if (mBoundDrawFramebuffer) {
         if (mBoundDrawFramebuffer->HasDepthStencilConflict()) {
             // Error, we don't know which stencil buffer's bits to use
             ErrorInvalidFramebufferOperation("getParameter: framebuffer has two stencil buffers bound");
@@ -171,29 +186,57 @@ WebGLContext::GetParameter(JSContext* cx
             realGLboolean disjoint = LOCAL_GL_FALSE;
             if (gl->IsExtensionSupported(gl::GLContext::EXT_disjoint_timer_query)) {
                 gl->fGetBooleanv(pname, &disjoint);
             }
             return JS::BooleanValue(bool(disjoint));
         }
     }
 
-    // Privileged string params exposed by WEBGL_debug_renderer_info:
+    // Privileged string params exposed by WEBGL_debug_renderer_info.
+    // The privilege check is done in WebGLContext::IsExtensionSupported.
+    // So here we just have to check that the extension is enabled.
     if (IsExtensionEnabled(WebGLExtensionID::WEBGL_debug_renderer_info)) {
         switch (pname) {
         case UNMASKED_VENDOR_WEBGL:
         case UNMASKED_RENDERER_WEBGL:
-            GLenum glstringname = LOCAL_GL_NONE;
-            if (pname == UNMASKED_VENDOR_WEBGL) {
-                glstringname = LOCAL_GL_VENDOR;
-            } else if (pname == UNMASKED_RENDERER_WEBGL) {
-                glstringname = LOCAL_GL_RENDERER;
+            {
+                const char* overridePref = nullptr;
+                GLenum driverEnum = LOCAL_GL_NONE;
+
+                switch (pname) {
+                case UNMASKED_RENDERER_WEBGL:
+                    overridePref = "webgl.renderer-string-override";
+                    driverEnum = LOCAL_GL_RENDERER;
+                    break;
+                case UNMASKED_VENDOR_WEBGL:
+                    overridePref = "webgl.vendor-string-override";
+                    driverEnum = LOCAL_GL_VENDOR;
+                    break;
+                default:
+                    MOZ_CRASH("bad `pname`");
+                }
+
+                bool hasRetVal = false;
+
+                nsAutoString ret;
+                if (overridePref) {
+                    nsresult res = Preferences::GetString(overridePref, &ret);
+                    if (NS_SUCCEEDED(res) && ret.Length() > 0)
+                        hasRetVal = true;
+                }
+
+                if (!hasRetVal) {
+                    const char* chars = reinterpret_cast<const char*>(gl->fGetString(driverEnum));
+                    ret = NS_ConvertASCIItoUTF16(chars);
+                    hasRetVal = true;
+                }
+
+                return StringValue(cx, ret, rv);
             }
-            const GLchar* string = (const GLchar*) gl->fGetString(glstringname);
-            return StringValue(cx, string, rv);
         }
     }
 
     if (IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives)) {
         if (pname == LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT) {
             GLint i = 0;
             gl->fGetIntegerv(pname, &i);
             return JS::Int32Value(i);
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -1936,17 +1936,19 @@ WebGLContext::InitAndValidateGL()
     if (IsWebGL2() &&
         !InitWebGL2())
     {
         // Todo: Bug 898404: Only allow WebGL2 on GL>=3.0 on desktop GL.
         return false;
     }
 
     // Default value for all disabled vertex attributes is [0, 0, 0, 1]
+    mVertexAttribType = MakeUnique<GLenum[]>(mGLMaxVertexAttribs);
     for (int32_t index = 0; index < mGLMaxVertexAttribs; ++index) {
+        mVertexAttribType[index] = LOCAL_GL_FLOAT;
         VertexAttrib4f(index, 0, 0, 0, 1);
     }
 
     mDefaultVertexArray = WebGLVertexArray::Create(this);
     mDefaultVertexArray->mAttribs.SetLength(mGLMaxVertexAttribs);
     mBoundVertexArray = mDefaultVertexArray;
 
     // OpenGL core profiles remove the default VAO object from version
--- a/dom/canvas/WebGLContextVertices.cpp
+++ b/dom/canvas/WebGLContextVertices.cpp
@@ -11,27 +11,93 @@
 #include "WebGLFramebuffer.h"
 #include "WebGLProgram.h"
 #include "WebGLRenderbuffer.h"
 #include "WebGLShader.h"
 #include "WebGLTexture.h"
 #include "WebGLVertexArray.h"
 #include "WebGLVertexAttribData.h"
 
+#include "mozilla/Casting.h"
+
 namespace mozilla {
 
 void
+WebGLContext::GetVertexAttribFloat(GLuint index, GLfloat* out_result)
+{
+    if (index) {
+        gl->fGetVertexAttribfv(index, LOCAL_GL_CURRENT_VERTEX_ATTRIB, out_result);
+    } else {
+        out_result[0] = mVertexAttrib0Vector[0];
+        out_result[1] = mVertexAttrib0Vector[1];
+        out_result[2] = mVertexAttrib0Vector[2];
+        out_result[3] = mVertexAttrib0Vector[3];
+    }
+}
+
+void
+WebGLContext::GetVertexAttribInt(GLuint index, GLint* out_result)
+{
+    if (index) {
+        gl->fGetVertexAttribIiv(index, LOCAL_GL_CURRENT_VERTEX_ATTRIB, out_result);
+    } else {
+        out_result[0] = BitwiseCast<GLint>(mVertexAttrib0Vector[0]);
+        out_result[1] = BitwiseCast<GLint>(mVertexAttrib0Vector[1]);
+        out_result[2] = BitwiseCast<GLint>(mVertexAttrib0Vector[2]);
+        out_result[3] = BitwiseCast<GLint>(mVertexAttrib0Vector[3]);
+    }
+}
+
+void
+WebGLContext::GetVertexAttribUint(GLuint index, GLuint* out_result)
+{
+    if (index) {
+        gl->fGetVertexAttribIuiv(index, LOCAL_GL_CURRENT_VERTEX_ATTRIB, out_result);
+    } else {
+        out_result[0] = BitwiseCast<GLuint>(mVertexAttrib0Vector[0]);
+        out_result[1] = BitwiseCast<GLuint>(mVertexAttrib0Vector[1]);
+        out_result[2] = BitwiseCast<GLuint>(mVertexAttrib0Vector[2]);
+        out_result[3] = BitwiseCast<GLuint>(mVertexAttrib0Vector[3]);
+    }
+}
+
+JSObject*
+WebGLContext::GetVertexAttribFloat32Array(JSContext* cx, GLuint index)
+{
+    GLfloat attrib[4];
+    GetVertexAttribFloat(index, &attrib[0]);
+    return dom::Float32Array::Create(cx, this, 4, attrib);
+}
+
+JSObject*
+WebGLContext::GetVertexAttribInt32Array(JSContext* cx, GLuint index)
+{
+    GLint attrib[4];
+    GetVertexAttribInt(index, &attrib[0]);
+    return dom::Int32Array::Create(cx, this, 4, attrib);
+}
+
+JSObject*
+WebGLContext::GetVertexAttribUint32Array(JSContext* cx, GLuint index) {
+    GLuint attrib[4];
+    GetVertexAttribUint(index, &attrib[0]);
+    return dom::Uint32Array::Create(cx, this, 4, attrib);
+}
+
+void
 WebGLContext::VertexAttrib1f(GLuint index, GLfloat x0)
 {
     if (IsContextLost())
         return;
 
     if (!ValidateAttribIndex(index, "vertexAttrib1f"))
         return;
 
+    mVertexAttribType[index] = LOCAL_GL_FLOAT;
+
     MakeContextCurrent();
 
     if (index) {
         gl->fVertexAttrib1f(index, x0);
     } else {
         mVertexAttrib0Vector[0] = x0;
         mVertexAttrib0Vector[1] = 0;
         mVertexAttrib0Vector[2] = 0;
@@ -45,16 +111,18 @@ void
 WebGLContext::VertexAttrib2f(GLuint index, GLfloat x0, GLfloat x1)
 {
     if (IsContextLost())
         return;
 
     if (!ValidateAttribIndex(index, "vertexAttrib2f"))
         return;
 
+    mVertexAttribType[index] = LOCAL_GL_FLOAT;
+
     MakeContextCurrent();
 
     if (index) {
         gl->fVertexAttrib2f(index, x0, x1);
     } else {
         mVertexAttrib0Vector[0] = x0;
         mVertexAttrib0Vector[1] = x1;
         mVertexAttrib0Vector[2] = 0;
@@ -68,16 +136,18 @@ void
 WebGLContext::VertexAttrib3f(GLuint index, GLfloat x0, GLfloat x1, GLfloat x2)
 {
     if (IsContextLost())
         return;
 
     if (!ValidateAttribIndex(index, "vertexAttrib3f"))
         return;
 
+    mVertexAttribType[index] = LOCAL_GL_FLOAT;
+
     MakeContextCurrent();
 
     if (index) {
         gl->fVertexAttrib3f(index, x0, x1, x2);
     } else {
         mVertexAttrib0Vector[0] = x0;
         mVertexAttrib0Vector[1] = x1;
         mVertexAttrib0Vector[2] = x2;
@@ -92,16 +162,18 @@ WebGLContext::VertexAttrib4f(GLuint inde
                              GLfloat x2, GLfloat x3)
 {
     if (IsContextLost())
         return;
 
     if (!ValidateAttribIndex(index, "vertexAttrib4f"))
         return;
 
+    mVertexAttribType[index] = LOCAL_GL_FLOAT;
+
     MakeContextCurrent();
 
     if (index) {
         gl->fVertexAttrib4f(index, x0, x1, x2, x3);
     } else {
         mVertexAttrib0Vector[0] = x0;
         mVertexAttrib0Vector[1] = x1;
         mVertexAttrib0Vector[2] = x2;
@@ -117,16 +189,18 @@ WebGLContext::VertexAttrib1fv_base(GLuin
                                    const GLfloat* ptr)
 {
     if (!ValidateAttribArraySetter("VertexAttrib1fv", 1, arrayLength))
         return;
 
     if (!ValidateAttribIndex(index, "vertexAttrib1fv"))
         return;
 
+    mVertexAttribType[index] = LOCAL_GL_FLOAT;
+
     MakeContextCurrent();
     if (index) {
         gl->fVertexAttrib1fv(index, ptr);
     } else {
         mVertexAttrib0Vector[0] = ptr[0];
         mVertexAttrib0Vector[1] = GLfloat(0);
         mVertexAttrib0Vector[2] = GLfloat(0);
         mVertexAttrib0Vector[3] = GLfloat(1);
@@ -140,16 +214,18 @@ WebGLContext::VertexAttrib2fv_base(GLuin
                                    const GLfloat* ptr)
 {
     if (!ValidateAttribArraySetter("VertexAttrib2fv", 2, arrayLength))
         return;
 
     if (!ValidateAttribIndex(index, "vertexAttrib2fv"))
         return;
 
+    mVertexAttribType[index] = LOCAL_GL_FLOAT;
+
     MakeContextCurrent();
     if (index) {
         gl->fVertexAttrib2fv(index, ptr);
     } else {
         mVertexAttrib0Vector[0] = ptr[0];
         mVertexAttrib0Vector[1] = ptr[1];
         mVertexAttrib0Vector[2] = GLfloat(0);
         mVertexAttrib0Vector[3] = GLfloat(1);
@@ -163,16 +239,18 @@ WebGLContext::VertexAttrib3fv_base(GLuin
                                    const GLfloat* ptr)
 {
     if (!ValidateAttribArraySetter("VertexAttrib3fv", 3, arrayLength))
         return;
 
     if (!ValidateAttribIndex(index, "vertexAttrib3fv"))
         return;
 
+    mVertexAttribType[index] = LOCAL_GL_FLOAT;
+
     MakeContextCurrent();
     if (index) {
         gl->fVertexAttrib3fv(index, ptr);
     } else {
         mVertexAttrib0Vector[0] = ptr[0];
         mVertexAttrib0Vector[1] = ptr[1];
         mVertexAttrib0Vector[2] = ptr[2];
         mVertexAttrib0Vector[3] = GLfloat(1);
@@ -186,16 +264,18 @@ WebGLContext::VertexAttrib4fv_base(GLuin
                                    const GLfloat* ptr)
 {
     if (!ValidateAttribArraySetter("VertexAttrib4fv", 4, arrayLength))
         return;
 
     if (!ValidateAttribIndex(index, "vertexAttrib4fv"))
         return;
 
+    mVertexAttribType[index] = LOCAL_GL_FLOAT;
+
     MakeContextCurrent();
     if (index) {
         gl->fVertexAttrib4fv(index, ptr);
     } else {
         mVertexAttrib0Vector[0] = ptr[0];
         mVertexAttrib0Vector[1] = ptr[1];
         mVertexAttrib0Vector[2] = ptr[2];
         mVertexAttrib0Vector[3] = ptr[3];
@@ -254,81 +334,69 @@ WebGLContext::GetVertexAttrib(JSContext*
         return JS::NullValue();
 
     MOZ_ASSERT(mBoundVertexArray);
     mBoundVertexArray->EnsureAttrib(index);
 
     MakeContextCurrent();
 
     switch (pname) {
-        case LOCAL_GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING:
-        {
-            return WebGLObjectAsJSValue(cx, mBoundVertexArray->mAttribs[index].buf.get(), rv);
-        }
+    case LOCAL_GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING:
+        return WebGLObjectAsJSValue(cx, mBoundVertexArray->mAttribs[index].buf.get(), rv);
 
-        case LOCAL_GL_VERTEX_ATTRIB_ARRAY_STRIDE:
-        {
-            return JS::Int32Value(mBoundVertexArray->mAttribs[index].stride);
-        }
+    case LOCAL_GL_VERTEX_ATTRIB_ARRAY_STRIDE:
+        return JS::Int32Value(mBoundVertexArray->mAttribs[index].stride);
 
-        case LOCAL_GL_VERTEX_ATTRIB_ARRAY_SIZE:
-        {
-            if (!mBoundVertexArray->mAttribs[index].enabled)
-                return JS::Int32Value(4);
+    case LOCAL_GL_VERTEX_ATTRIB_ARRAY_SIZE:
+        if (!mBoundVertexArray->mAttribs[index].enabled)
+            return JS::Int32Value(4);
+
+        return JS::Int32Value(mBoundVertexArray->mAttribs[index].size);
 
-            return JS::Int32Value(mBoundVertexArray->mAttribs[index].size);
-        }
+    case LOCAL_GL_VERTEX_ATTRIB_ARRAY_TYPE:
+        if (!mBoundVertexArray->mAttribs[index].enabled)
+            return JS::NumberValue(uint32_t(LOCAL_GL_FLOAT));
 
-        case LOCAL_GL_VERTEX_ATTRIB_ARRAY_TYPE:
-        {
-            if (!mBoundVertexArray->mAttribs[index].enabled)
-                return JS::NumberValue(uint32_t(LOCAL_GL_FLOAT));
+        return JS::NumberValue(uint32_t(mBoundVertexArray->mAttribs[index].type));
 
-            return JS::NumberValue(uint32_t(mBoundVertexArray->mAttribs[index].type));
-        }
+    case LOCAL_GL_VERTEX_ATTRIB_ARRAY_DIVISOR:
+        if (IsExtensionEnabled(WebGLExtensionID::ANGLE_instanced_arrays))
+            return JS::Int32Value(mBoundVertexArray->mAttribs[index].divisor);
+        break;
 
-        case LOCAL_GL_VERTEX_ATTRIB_ARRAY_DIVISOR:
-        {
-            if (IsExtensionEnabled(WebGLExtensionID::ANGLE_instanced_arrays))
-            {
-                return JS::Int32Value(mBoundVertexArray->mAttribs[index].divisor);
-            }
-            break;
-        }
-
-        case LOCAL_GL_CURRENT_VERTEX_ATTRIB:
+    case LOCAL_GL_CURRENT_VERTEX_ATTRIB:
         {
-            GLfloat vec[4] = {0, 0, 0, 1};
-            if (index) {
-                gl->fGetVertexAttribfv(index, LOCAL_GL_CURRENT_VERTEX_ATTRIB, &vec[0]);
-            } else {
-                vec[0] = mVertexAttrib0Vector[0];
-                vec[1] = mVertexAttrib0Vector[1];
-                vec[2] = mVertexAttrib0Vector[2];
-                vec[3] = mVertexAttrib0Vector[3];
+            JS::RootedObject obj(cx);
+            switch (mVertexAttribType[index]) {
+            case LOCAL_GL_FLOAT:
+                obj = GetVertexAttribFloat32Array(cx, index);
+                break;
+
+            case LOCAL_GL_INT:
+                obj =  GetVertexAttribInt32Array(cx, index);
+                break;
+
+            case LOCAL_GL_UNSIGNED_INT:
+                obj = GetVertexAttribUint32Array(cx, index);
+                break;
             }
-            JSObject* obj = dom::Float32Array::Create(cx, this, 4, vec);
-            if (!obj) {
+
+            if (!obj)
                 rv.Throw(NS_ERROR_OUT_OF_MEMORY);
-            }
             return JS::ObjectOrNullValue(obj);
         }
 
-        case LOCAL_GL_VERTEX_ATTRIB_ARRAY_ENABLED:
-        {
-            return JS::BooleanValue(mBoundVertexArray->mAttribs[index].enabled);
-        }
+    case LOCAL_GL_VERTEX_ATTRIB_ARRAY_ENABLED:
+        return JS::BooleanValue(mBoundVertexArray->mAttribs[index].enabled);
 
-        case LOCAL_GL_VERTEX_ATTRIB_ARRAY_NORMALIZED:
-        {
-            return JS::BooleanValue(mBoundVertexArray->mAttribs[index].normalized);
-        }
+    case LOCAL_GL_VERTEX_ATTRIB_ARRAY_NORMALIZED:
+        return JS::BooleanValue(mBoundVertexArray->mAttribs[index].normalized);
 
-        default:
-            break;
+    default:
+        break;
     }
 
     ErrorInvalidEnumInfo("getVertexAttrib: parameter", pname);
     return JS::NullValue();
 }
 
 WebGLsizeiptr
 WebGLContext::GetVertexAttribOffset(GLuint index, GLenum pname)
--- a/dom/canvas/moz.build
+++ b/dom/canvas/moz.build
@@ -62,16 +62,17 @@ UNIFIED_SOURCES += [
     'WebGL2ContextQueries.cpp',
     'WebGL2ContextSamplers.cpp',
     'WebGL2ContextState.cpp',
     'WebGL2ContextSync.cpp',
     'WebGL2ContextTextures.cpp',
     'WebGL2ContextTransformFeedback.cpp',
     'WebGL2ContextUniforms.cpp',
     'WebGL2ContextVAOs.cpp',
+    'WebGL2ContextVertices.cpp',
     'WebGLActiveInfo.cpp',
     'WebGLBuffer.cpp',
     'WebGLContext.cpp',
     'WebGLContextBuffers.cpp',
     'WebGLContextDraw.cpp',
     'WebGLContextExtensions.cpp',
     'WebGLContextFramebufferOperations.cpp',
     'WebGLContextGL.cpp',
--- a/dom/canvas/test/chrome/nonchrome_webgl_debug_renderer_info.html
+++ b/dom/canvas/test/chrome/nonchrome_webgl_debug_renderer_info.html
@@ -1,12 +1,18 @@
 <!DOCTYPE HTML>
 <html>
 <script>
 
+var Cu = parent.Components.utils;
+Cu.import("resource://gre/modules/AppConstants.jsm");
+// This gives us `AppConstants` in the global scope.
+// We need this because we only expose debug_renderer_info #ifndef MOZ_RELEASE_BUILD.
+// This should match AppConstants.RELEASE_BUILD.
+
 // This file has the portion of the test_webgl_renderer_info chrome mochitest
 // that has to run as non-chrome to check that this WebGL extension is not exposed to content
 
 // we can't call the chrome Mochitest ok() function ourselves from non-chrome code.
 // So we remote it to the chrome test.
 
 function ok(res, msg) {
   // Note we post to ourselves as posting to the chrome code doesn't seem to work here.
@@ -23,35 +29,63 @@ function messageListener(e) {
 }
 
 window.addEventListener("message", messageListener, true);
 
 function run() {
   const UNMASKED_VENDOR_WEBGL = 0x9245;
   const UNMASKED_RENDERER_WEBGL = 0x9246;
 
+  var shouldHaveRendererInfo = false;
+  if (!AppConstants.RELEASE_BUILD)
+    shouldHaveRendererInfo = true;
+
   var canvas = document.createElement("canvas");
   var gl = canvas.getContext("experimental-webgl");
 
   ok(!gl.getError(), "getError on newly created WebGL context should return NO_ERROR");
 
   ok(!gl.getParameter(UNMASKED_VENDOR_WEBGL) && gl.getError() == gl.INVALID_ENUM,
-      "Should not be able to query UNMASKED_VENDOR_WEBGL without having enabled the WEBGL_debug_renderer_info extension");
+      "Should not be able to query UNMASKED_VENDOR_WEBGL without having enabled the"
+      + " WEBGL_debug_renderer_info extension");
   ok(!gl.getParameter(UNMASKED_RENDERER_WEBGL) && gl.getError() == gl.INVALID_ENUM,
-      "Should not be able to query UNMASKED_RENDERER_WEBGL without having enabled the WEBGL_debug_renderer_info extension");
+      "Should not be able to query UNMASKED_RENDERER_WEBGL without having enabled the"
+      + " WEBGL_debug_renderer_info extension");
 
   var exts = gl.getSupportedExtensions();
-  ok(exts.indexOf("WEBGL_debug_renderer_info") == -1,
-      "WEBGL_debug_renderer_info should not be listed by getSupportedExtensions in non-chrome contexts");
-  var debugRendererInfoExtension = gl.getExtension("WEBGL_debug_renderer_info");
-  ok(!debugRendererInfoExtension,
-      "WEBGL_debug_renderer_info should not be available through getExtension in non-chrome contexts");
+  if (shouldHaveRendererInfo) {
+    ok(exts.indexOf("WEBGL_debug_renderer_info") != -1,
+       "WEBGL_debug_renderer_info should be listed by getSupportedExtensions in"
+       + " non-chrome contexts on non-RELEASE_BUILDs");
+
+    var ext = gl.getExtension("WEBGL_debug_renderer_info");
+    ok(!!ext,
+       "WEBGL_debug_renderer_info should be available through getExtension in non-chrome"
+       + " contexts on non-RELEASE_BUILDs");
 
-  ok(!gl.getParameter(UNMASKED_VENDOR_WEBGL) && gl.getError() == gl.INVALID_ENUM,
-      "Should not be able to query UNMASKED_VENDOR_WEBGL if enabling WEBGL_debug_renderer_info failed");
-  ok(!gl.getParameter(UNMASKED_RENDERER_WEBGL) && gl.getError() == gl.INVALID_ENUM,
-      "Should not be able to query UNMASKED_RENDERER_WEBGL if enabling WEBGL_debug_renderer_info failed");
+    ok(gl.getParameter(UNMASKED_VENDOR_WEBGL) && gl.getError() == gl.NO_ERROR,
+       "Should be able to query UNMASKED_VENDOR_WEBGL if enabling"
+       + " WEBGL_debug_renderer_info succeeded");
+    ok(gl.getParameter(UNMASKED_RENDERER_WEBGL) && gl.getError() == gl.NO_ERROR,
+       "Should be able to query UNMASKED_RENDERER_WEBGL if enabling"
+       + " WEBGL_debug_renderer_info succeeded");
+  } else {
+    ok(exts.indexOf("WEBGL_debug_renderer_info") == -1,
+       "WEBGL_debug_renderer_info should not be listed by getSupportedExtensions in"
+       + " non-chrome contexts");
+    var ext = gl.getExtension("WEBGL_debug_renderer_info");
+    ok(!ext,
+       "WEBGL_debug_renderer_info should not be available through getExtension in"
+       + " non-chrome contexts");
 
+    ok(!gl.getParameter(UNMASKED_VENDOR_WEBGL) && gl.getError() == gl.INVALID_ENUM,
+        "Should not be able to query UNMASKED_VENDOR_WEBGL if enabling"
+        + " WEBGL_debug_renderer_info failed");
+    ok(!gl.getParameter(UNMASKED_RENDERER_WEBGL) && gl.getError() == gl.INVALID_ENUM,
+        "Should not be able to query UNMASKED_RENDERER_WEBGL if enabling"
+        + " WEBGL_debug_renderer_info failed");
+
+  }
   window.postMessage({allTestsFinished: true}, "*");
 }
 
 </script>
-</html>
\ No newline at end of file
+</html>
--- a/dom/canvas/test/chrome/test_webgl_debug_renderer_info.html
+++ b/dom/canvas/test/chrome/test_webgl_debug_renderer_info.html
@@ -38,23 +38,23 @@ function checkChromeCase(canvas) {
   ok(!gl.getParameter(UNMASKED_VENDOR_WEBGL) && gl.getError() == gl.INVALID_ENUM,
      "Should not be able to query UNMASKED_VENDOR_WEBGL without having enabled the WEBGL_debug_renderer_info extension");
   ok(!gl.getParameter(UNMASKED_RENDERER_WEBGL) && gl.getError() == gl.INVALID_ENUM,
      "Should not be able to query UNMASKED_RENDERER_WEBGL without having enabled the WEBGL_debug_renderer_info extension");
 
   var exts = gl.getSupportedExtensions();
   ok(exts.indexOf("WEBGL_debug_renderer_info") != -1,
      "WEBGL_debug_renderer_info should be listed by getSupportedExtensions in chrome contexts");
-  var debugRendererInfoExtension = gl.getExtension("WEBGL_debug_renderer_info");
-  ok(debugRendererInfoExtension,
+  var ext = gl.getExtension("WEBGL_debug_renderer_info");
+  ok(ext,
      "WEBGL_debug_renderer_info should be available through getExtension in chrome contexts");
 
-  ok(debugRendererInfoExtension.UNMASKED_VENDOR_WEBGL == UNMASKED_VENDOR_WEBGL,
+  ok(ext.UNMASKED_VENDOR_WEBGL == UNMASKED_VENDOR_WEBGL,
      "UNMASKED_VENDOR_WEBGL has the correct value");
-  ok(debugRendererInfoExtension.UNMASKED_RENDERER_WEBGL == UNMASKED_RENDERER_WEBGL,
+  ok(ext.UNMASKED_RENDERER_WEBGL == UNMASKED_RENDERER_WEBGL,
      "UNMASKED_RENDERER_WEBGL has the correct value");
 
   ok(isNonEmptyString(gl.getParameter(UNMASKED_VENDOR_WEBGL)) && gl.getError() == gl.NO_ERROR,
      "Should be able to query UNMASKED_VENDOR_WEBGL in chrome context with WEBGL_debug_renderer_info enabled");
   ok(isNonEmptyString(gl.getParameter(UNMASKED_RENDERER_WEBGL)) && gl.getError() == gl.NO_ERROR,
      "Should be able to query UNMASKED_RENDERER_WEBGL in chrome context with WEBGL_debug_renderer_info enabled");
 }
 
@@ -62,17 +62,17 @@ function main()
 {
   SimpleTest.waitForExplicitFinish();
 
   checkChromeCase(document.createElement("canvas"));
 
   // Now run the non-chrome code to verify the security of this WebGL chrome-only extension.
 
   var iframe = document.createElement("iframe");
-  iframe.src = "http://mochi.test:8888/chrome/dom/canvas/test/chrome/nonchrome_webgl_debug_renderer_info.html";
+  iframe.src = "chrome://mochitests/content/chrome/dom/canvas/test/chrome/nonchrome_webgl_debug_renderer_info.html";
 
   iframe.onload = function () {
 
     // test that chrome can get WEBGL_debug_renderer_info on a canvas on the iframe...
     // this is useful to check in itself, and is also useful so the subsequent non-chrome test
     // will also test that doing so doesn't confuse our chrome-only check.
     checkChromeCase(iframe.contentDocument.createElement("canvas"));
 
--- a/dom/canvas/test/webgl-mochitest.ini
+++ b/dom/canvas/test/webgl-mochitest.ini
@@ -17,16 +17,17 @@ support-files = captureStream_common.js
 [webgl-mochitest/test_hidden_alpha.html]
 skip-if = (os == 'b2g') || buildapp == 'mulet' # Mulet - bug 1093639 (crashes in libLLVM-3.0.so)
 [webgl-mochitest/test_implicit_color_buffer_float.html]
 [webgl-mochitest/test_highp_fs.html]
 [webgl-mochitest/test_no_arr_points.html]
 skip-if = android_version == '10' || android_version == '18' #Android 2.3 and 4.3 aws only; bug 1030942
 [webgl-mochitest/test_noprog_draw.html]
 [webgl-mochitest/test_privileged_exts.html]
+[webgl-mochitest/test_renderer_strings.html]
 [webgl-mochitest/test_texsubimage_float.html]
 [webgl-mochitest/test_uninit_data.html]
 [webgl-mochitest/test_webgl_available.html]
 skip-if = toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
 #[webgl-mochitest/test_webgl_color_buffer_float.html]
 # We haven't cleaned up the Try results yet, but let's get this on the books first.
 [webgl-mochitest/test_webgl_conformance.html]
 skip-if = buildapp == 'mulet' || toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
--- a/dom/canvas/test/webgl-mochitest/test_privileged_exts.html
+++ b/dom/canvas/test/webgl-mochitest/test_privileged_exts.html
@@ -19,14 +19,14 @@ function TestExt(gl, name) {
 (function() {
   var gl = WebGLUtil.getWebGL('c');
   if (!gl) {
     todo(gl, 'Get GL working here first.');
     return;
   }
 
   // Privileged extensions:
-  TestExt(gl, 'WEBGL_debug_renderer_info');
+  TestExt(gl, 'WEBGL_debug_shaders');
 })();
 
 </script>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-mochitest/test_renderer_strings.html
@@ -0,0 +1,98 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+<script>
+
+function AssertError(gl, expected, info) {
+  var actual = gl.getError();
+  while (gl.getError()) {}
+
+  ok(actual == expected,
+     'For ' + info + ', expected 0x' + expected.toString(16) + ', got 0x'
+      + actual.toString(16));
+}
+
+var gl;
+
+var RENDERER_OVERRIDE = 'overridden renderer';
+var VENDOR_OVERRIDE = 'overridden vendor';
+
+function TestExt() {
+  var ext = gl.getExtension('WEBGL_debug_renderer_info');
+  ok(ext, 'Should have access to \'WEBGL_debug_renderer_info\'.');
+  AssertError(gl, 0, 'start of test');
+
+  var renderer = gl.getParameter(ext.UNMASKED_RENDERER_WEBGL);
+  AssertError(gl, 0, 'UNMASKED_RENDERER_WEBGL');
+  ok(renderer,
+     'UNMASKED_RENDERER_WEBGL value should not be empty, was \'' + renderer + '\'');
+
+  var vendor = gl.getParameter(ext.UNMASKED_VENDOR_WEBGL);
+  AssertError(gl, 0, 'UNMASKED_VENDOR_WEBGL');
+  ok(vendor, 'UNMASKED_VENDOR_WEBGL value should not be empty, was \'' + vendor + '\'');
+
+  var prefArrArr = [
+    ['webgl.renderer-string-override', RENDERER_OVERRIDE],
+    ['webgl.vendor-string-override', VENDOR_OVERRIDE],
+  ];
+  var prefEnv = {'set': prefArrArr};
+  SpecialPowers.pushPrefEnv(prefEnv, TestOverrides);
+}
+
+function TestOverrides() {
+  var ext = gl.getExtension('WEBGL_debug_renderer_info');
+  ok(ext, 'Should have access to \'WEBGL_debug_renderer_info\'.');
+  AssertError(gl, 0, 'start of test');
+
+  var renderer = gl.getParameter(ext.UNMASKED_RENDERER_WEBGL);
+  AssertError(gl, 0, 'UNMASKED_RENDERER_WEBGL');
+  ok(renderer == RENDERER_OVERRIDE,
+     'UNMASKED_RENDERER_WEBGL value should be \'' + RENDERER_OVERRIDE + '\', was \''
+     + renderer + '\'');
+
+  var vendor = gl.getParameter(ext.UNMASKED_VENDOR_WEBGL);
+  AssertError(gl, 0, 'UNMASKED_VENDOR_WEBGL');
+  ok(vendor == VENDOR_OVERRIDE,
+     'UNMASKED_VENDOR_WEBGL value should be \'' + VENDOR_OVERRIDE + '\', was \'' + vendor
+     + '\'');
+
+  var prefArrArr = [
+    ['webgl.disable-debug-renderer-info', true],
+  ];
+  var prefEnv = {'set': prefArrArr};
+  SpecialPowers.pushPrefEnv(prefEnv, TestDisable);
+}
+
+function TestDisable() {
+  var ext = gl.getExtension('WEBGL_debug_renderer_info');
+  ok(!ext, 'Should not have access to \'WEBGL_debug_renderer_info\'.');
+
+  ok(true, 'Test complete.');
+  SimpleTest.finish();
+}
+
+(function() {
+  var canvas = document.createElement('canvas');
+  gl = canvas.getContext('experimental-webgl');
+  if (!gl) {
+    todo(gl, 'Get WebGL working here first.');
+    ok(true, 'Test complete.');
+    return;
+  }
+
+  SimpleTest.waitForExplicitFinish();
+
+  var prefArrArr = [
+    ['webgl.disable-debug-renderer-info', false],
+  ];
+  var prefEnv = {'set': prefArrArr};
+  SpecialPowers.pushPrefEnv(prefEnv, TestExt);
+})();
+
+</script>
+</body>
+</html>
--- a/dom/events/IMEContentObserver.cpp
+++ b/dom/events/IMEContentObserver.cpp
@@ -1069,20 +1069,18 @@ IMEContentObserver::SelectionChangeEvent
   }
 
   // The state may be changed since querying content causes flushing layout.
   if (!CanNotifyIME()) {
     return NS_OK;
   }
 
   IMENotification notification(NOTIFY_IME_OF_SELECTION_CHANGE);
-  notification.mSelectionChangeData.mOffset =
-    selection.mReply.mOffset;
-  notification.mSelectionChangeData.mLength =
-    selection.mReply.mString.Length();
+  notification.mSelectionChangeData.mOffset = selection.mReply.mOffset;
+  *notification.mSelectionChangeData.mString = selection.mReply.mString;
   notification.mSelectionChangeData.SetWritingMode(
                                       selection.GetWritingMode());
   notification.mSelectionChangeData.mReversed = selection.mReply.mReversed;
   notification.mSelectionChangeData.mCausedByComposition =
     mCausedByComposition;
   notification.mSelectionChangeData.mCausedBySelectionEvent =
     mCausedBySelectionEvent;
   IMEStateManager::NotifyIME(notification, mIMEContentObserver->mWidget);
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -4008,17 +4008,20 @@ bool
 HTMLMediaElement::NotifyOwnerDocumentActivityChangedInternal()
 {
   nsIDocument* ownerDoc = OwnerDoc();
   if (mDecoder && !IsBeingDestroyed()) {
     mDecoder->SetElementVisibility(!ownerDoc->Hidden());
     mDecoder->NotifyOwnerActivityChanged();
   }
 
-  bool pauseElement = !IsActive() || ComputedMuted();
+  bool pauseElement = !IsActive();
+#ifdef MOZ_B2G
+  pauseElement |= ComputedMuted();
+#endif
 
   SuspendOrResumeElement(pauseElement, !IsActive());
 
   if (!mPausedForInactiveDocumentOrChannel &&
       mPlayBlockedBecauseHidden &&
       !OwnerDoc()->Hidden()) {
     LOG(LogLevel::Debug, ("%p Resuming playback now that owner doc is visble.", this));
     mPlayBlockedBecauseHidden = false;
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -12310,16 +12310,18 @@ Database::EnsureConnection()
 
 bool
 Database::RegisterTransaction(TransactionBase* aTransaction)
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aTransaction);
   MOZ_ASSERT(!mTransactions.GetEntry(aTransaction));
   MOZ_ASSERT(mDirectoryLock);
+  MOZ_ASSERT(!mInvalidated);
+  MOZ_ASSERT(!mClosed);
 
   if (NS_WARN_IF(!mTransactions.PutEntry(aTransaction, fallible))) {
     return false;
   }
 
   return true;
 }
 
@@ -18688,16 +18690,20 @@ FactoryOp::Run()
     case State_WaitingForTransactionsToComplete:
       rv = DispatchToWorkThread();
       break;
 
     case State_SendingResults:
       SendResults();
       return NS_OK;
 
+    // We raced, no need to crash.
+    case State_Completed:
+      return NS_OK;
+
     default:
       MOZ_CRASH("Bad state!");
   }
 
   if (NS_WARN_IF(NS_FAILED(rv)) && mState != State_SendingResults) {
     if (NS_SUCCEEDED(mResultCode)) {
       mResultCode = rv;
     }
@@ -18745,16 +18751,24 @@ FactoryOp::DirectoryLockFailed()
 }
 
 void
 FactoryOp::ActorDestroy(ActorDestroyReason aWhy)
 {
   AssertIsOnBackgroundThread();
 
   NoteActorDestroyed();
+
+  if (mState == State_WaitingForTransactionsToComplete) {
+    // We didn't get an opportunity to clean up.  Do that now.
+    mState = State_SendingResults;
+    IDB_REPORT_INTERNAL_ERR();
+    mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+    SendResults();
+  }
 }
 
 bool
 FactoryOp::RecvPermissionRetry()
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(!IsActorDestroyed());
   MOZ_ASSERT(mState == State_PermissionChallenge);
@@ -19341,19 +19355,20 @@ OpenDatabaseOp::BeginVersionChange()
 }
 
 void
 OpenDatabaseOp::NoteDatabaseClosed(Database* aDatabase)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aDatabase);
   MOZ_ASSERT(mState == State_WaitingForOtherDatabasesToClose ||
+             mState == State_WaitingForTransactionsToComplete ||
              mState == State_DatabaseWorkVersionChange);
 
-  if (mState == State_DatabaseWorkVersionChange) {
+  if (mState != State_WaitingForOtherDatabasesToClose) {
     MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty());
     MOZ_ASSERT(mRequestedVersion >
                  aDatabase->Metadata()->mCommonMetadata.version(),
                "Must only be closing databases for a previous version!");
     return;
   }
 
   MOZ_ASSERT(!mMaybeBlockedDatabases.IsEmpty());
@@ -19407,17 +19422,18 @@ OpenDatabaseOp::DispatchToWorkThread()
   AssertIsOnOwningThread();
   MOZ_ASSERT(mState == State_WaitingForTransactionsToComplete);
   MOZ_ASSERT(mVersionChangeTransaction);
   MOZ_ASSERT(mVersionChangeTransaction->GetMode() ==
                IDBTransaction::VERSION_CHANGE);
   MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty());
 
   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) ||
-      IsActorDestroyed()) {
+      IsActorDestroyed() ||
+      mDatabase->IsInvalidated()) {
     IDB_REPORT_INTERNAL_ERR();
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
   mState = State_DatabaseWorkVersionChange;
 
   // Intentionally empty.
   nsTArray<nsString> objectStoreNames;
--- a/dom/indexedDB/test/unit/test_blocked_order.js
+++ b/dom/indexedDB/test/unit/test_blocked_order.js
@@ -51,17 +51,17 @@ function testSteps()
   let request = indexedDB.open(databaseName, 2);
   request.onerror = errorHandler;
   request.onsuccess = grabEventAndContinueHandler;
 
   request.onblocked = function(event) {
     ok(false, "Should not receive a blocked event");
   };
 
-  event = yield undefined;
+  let event = yield undefined;
 
   is(event.type, "success", "Got success event");
   is(databases.length, 0, "All databases with version 1 were closed");
 
   let db = request.result;
   is(db.version, 2, "Got version 2");
 
   info("Deleting database with version 2");
@@ -140,11 +140,40 @@ function testSteps()
   request = indexedDB.deleteDatabase(databaseName);
   request.onerror = errorHandler;
   request.onsuccess = grabEventAndContinueHandler;
 
   event = yield undefined;
 
   is(event.type, "success", "Got success event");
 
+  // Test 3: A blocked database left in that state should not hang shutdown.
+  info("Opening 1 database with version 1");
+
+  request = indexedDB.open(databaseName, 1);
+  request.onerror = errorHandler;
+  request.onblocked = errorHandler;
+  request.onsuccess = grabEventAndContinueHandler;
+
+  event = yield undefined;
+
+  is(event.type, "success", "Got success event");
+
+  db = request.result;
+  is(db.version, 1, "Got version 1");
+
+  info("Opening database with version 2");
+
+  request = indexedDB.open(databaseName, 2);
+  request.onerror = function(e) {
+    e.preventDefault();
+  };
+  request.onsuccess = errorHandler;
+
+  request.onblocked = grabEventAndContinueHandler;
+
+  event = yield undefined;
+  ok(true, "Got blocked");
+  // Just allow this to remain blocked ...
+
   finishTest();
   yield undefined;
 }
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -1833,17 +1833,17 @@ ContentChild::DeallocPWebrtcGlobalChild(
 #else
     return false;
 #endif
 }
 
 
 bool
 ContentChild::RecvRegisterChrome(InfallibleTArray<ChromePackage>&& packages,
-                                 InfallibleTArray<ResourceMapping>&& resources,
+                                 InfallibleTArray<SubstitutionMapping>&& resources,
                                  InfallibleTArray<OverrideMapping>&& overrides,
                                  const nsCString& locale,
                                  const bool& reset)
 {
     nsCOMPtr<nsIChromeRegistry> registrySvc = nsChromeRegistry::GetService();
     nsChromeRegistryContent* chromeRegistry =
         static_cast<nsChromeRegistryContent*>(registrySvc.get());
     chromeRegistry->RegisterRemoteChrome(packages, resources, overrides,
@@ -1861,18 +1861,18 @@ ContentChild::RecvRegisterChromeItem(con
         case ChromeRegistryItem::TChromePackage:
             chromeRegistry->RegisterPackage(item.get_ChromePackage());
             break;
 
         case ChromeRegistryItem::TOverrideMapping:
             chromeRegistry->RegisterOverride(item.get_OverrideMapping());
             break;
 
-        case ChromeRegistryItem::TResourceMapping:
-            chromeRegistry->RegisterResource(item.get_ResourceMapping());
+        case ChromeRegistryItem::TSubstitutionMapping:
+            chromeRegistry->RegisterSubstitution(item.get_SubstitutionMapping());
             break;
 
         default:
             MOZ_ASSERT(false, "bad chrome item");
             return false;
     }
 
     return true;
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -16,17 +16,17 @@
 #include "nsIObserver.h"
 #include "nsTHashtable.h"
 
 #include "nsWeakPtr.h"
 
 
 struct ChromePackage;
 class nsIObserver;
-struct ResourceMapping;
+struct SubstitutionMapping;
 struct OverrideMapping;
 class nsIDomainPolicy;
 
 namespace mozilla {
 class RemoteSpellcheckEngineChild;
 
 namespace ipc {
 class OptionalURIParams;
@@ -277,17 +277,17 @@ public:
                                  const IPC::Principal& aPrincipal) override;
     virtual bool DeallocPAsmJSCacheEntryChild(
                                     PAsmJSCacheEntryChild* aActor) override;
 
     virtual PSpeechSynthesisChild* AllocPSpeechSynthesisChild() override;
     virtual bool DeallocPSpeechSynthesisChild(PSpeechSynthesisChild* aActor) override;
 
     virtual bool RecvRegisterChrome(InfallibleTArray<ChromePackage>&& packages,
-                                    InfallibleTArray<ResourceMapping>&& resources,
+                                    InfallibleTArray<SubstitutionMapping>&& resources,
                                     InfallibleTArray<OverrideMapping>&& overrides,
                                     const nsCString& locale,
                                     const bool& reset) override;
     virtual bool RecvRegisterChromeItem(const ChromeRegistryItem& item) override;
 
     virtual mozilla::jsipc::PJavaScriptChild* AllocPJavaScriptChild() override;
     virtual bool DeallocPJavaScriptChild(mozilla::jsipc::PJavaScriptChild*) override;
     virtual PRemoteSpellcheckEngineChild* AllocPRemoteSpellcheckEngineChild() override;
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -255,23 +255,23 @@ parent:
      *    widget should return empty string for composition
      *  if cancel is false,
      *    widget should return the current composition text
      */
     prio(urgent) sync EndIMEComposition(bool cancel)
                         returns (bool noCompositionEvent, nsString composition);
 
     /**
-     * OnEventNeedingAckReceived() is called when a child process dispatches a
+     * OnEventNeedingAckHandled() is called after a child process dispatches a
      * composition event or a selection event which is sent from the parent
      * process.
      *
-     * message      The message value of the received event.
+     * message      The message value of the handled event.
      */
-    prio(urgent) async OnEventNeedingAckReceived(uint32_t message);
+    prio(urgent) async OnEventNeedingAckHandled(uint32_t message);
 
     /**
      * Tells chrome to start plugin IME.  If this results in a string getting
      * committed, the result is in aCommitted (otherwise aCommitted is empty).
      *
      * aKeyboardEvent     The event with which plugin IME is to be started
      * panelX and panelY  Location in screen coordinates of the IME input panel
      *                    (should be just under the plugin)
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -66,17 +66,17 @@ include BrowserConfiguration;
 // XXX Remove this once bug 1069073 is fixed
 include "mozilla/dom/PContentBridgeParent.h";
 
 include "mozilla/dom/indexedDB/SerializationHelpers.h";
 
 using GeoPosition from "nsGeoPositionIPCSerialiser.h";
 
 using struct ChromePackage from "mozilla/chrome/RegistryMessageUtils.h";
-using struct ResourceMapping from "mozilla/chrome/RegistryMessageUtils.h";
+using struct SubstitutionMapping from "mozilla/chrome/RegistryMessageUtils.h";
 using struct OverrideMapping from "mozilla/chrome/RegistryMessageUtils.h";
 using base::ChildPrivileges from "base/process_util.h";
 using base::ProcessId from "base/process.h";
 using struct IPC::Permission from "mozilla/net/NeckoMessageUtils.h";
 using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h";
 using struct mozilla::null_t from "ipc/IPCMessageUtils.h";
 using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
 using mozilla::dom::asmjscache::OpenMode from "mozilla/dom/asmjscache/AsmJSCache.h";
@@ -89,17 +89,17 @@ using mozilla::dom::TabId from "mozilla/
 using mozilla::dom::ContentParentId from "mozilla/dom/ipc/IdType.h";
 using struct LookAndFeelInt from "mozilla/widget/WidgetMessageUtils.h";
 using struct mozilla::OwningSerializedStructuredCloneBuffer from "ipc/IPCMessageUtils.h";
 
 union ChromeRegistryItem
 {
     ChromePackage;
     OverrideMapping;
-    ResourceMapping;
+    SubstitutionMapping;
 };
 
 namespace mozilla {
 namespace dom {
 
 struct FontListEntry {
     nsString  familyName;
     nsString  faceName;
@@ -516,17 +516,17 @@ child:
      * nsIMemoryInfoDumper.idl
      */
     PCycleCollectWithLogs(bool dumpAllTraces,
                           FileDescriptor gcLog,
                           FileDescriptor ccLog);
 
     PTestShell();
 
-    RegisterChrome(ChromePackage[] packages, ResourceMapping[] resources,
+    RegisterChrome(ChromePackage[] packages, SubstitutionMapping[] substitutions,
                    OverrideMapping[] overrides, nsCString locale, bool reset);
     RegisterChromeItem(ChromeRegistryItem item);
 
     async SetOffline(bool offline);
     async SetConnectivity(bool connectivity);
 
     async NotifyVisited(URIParams uri);
 
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -2225,30 +2225,30 @@ TabChild::RecvKeyEvent(const nsString& a
   nsContentUtils::SendKeyEvent(mPuppetWidget, aType, aKeyCode, aCharCode,
                                aModifiers, aPreventDefault, &ignored);
   return true;
 }
 
 bool
 TabChild::RecvCompositionEvent(const WidgetCompositionEvent& event)
 {
-  unused << SendOnEventNeedingAckReceived(event.message);
   WidgetCompositionEvent localEvent(event);
   localEvent.widget = mPuppetWidget;
   APZCCallbackHelper::DispatchWidgetEvent(localEvent);
+  unused << SendOnEventNeedingAckHandled(event.message);
   return true;
 }
 
 bool
 TabChild::RecvSelectionEvent(const WidgetSelectionEvent& event)
 {
-  unused << SendOnEventNeedingAckReceived(event.message);
   WidgetSelectionEvent localEvent(event);
   localEvent.widget = mPuppetWidget;
   APZCCallbackHelper::DispatchWidgetEvent(localEvent);
+  unused << SendOnEventNeedingAckHandled(event.message);
   return true;
 }
 
 a11y::PDocAccessibleChild*
 TabChild::AllocPDocAccessibleChild(PDocAccessibleChild*, const uint64_t&)
 {
   MOZ_ASSERT(false, "should never call this!");
   return nullptr;
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -2088,35 +2088,35 @@ TabParent::RecvNotifyIMEPositionChange(c
     return true;
   }
 
   mContentCache.AssignContent(aContentCache, &aIMENotification);
 
   const nsIMEUpdatePreference updatePreference =
     widget->GetIMEUpdatePreference();
   if (updatePreference.WantPositionChanged()) {
-    IMEStateManager::NotifyIME(aIMENotification, widget, true);
+    mContentCache.MaybeNotifyIME(widget, aIMENotification);
   }
   return true;
 }
 
 bool
-TabParent::RecvOnEventNeedingAckReceived(const uint32_t& aMessage)
+TabParent::RecvOnEventNeedingAckHandled(const uint32_t& aMessage)
 {
   // This is called when the child process receives WidgetCompositionEvent or
   // WidgetSelectionEvent.
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget) {
     return true;
   }
 
-  // While calling OnEventNeedingAckReceived(), TabParent *might* be destroyed
+  // While calling OnEventNeedingAckHandled(), TabParent *might* be destroyed
   // since it may send notifications to IME.
   nsRefPtr<TabParent> kungFuDeathGrip(this);
-  mContentCache.OnEventNeedingAckReceived(widget, aMessage);
+  mContentCache.OnEventNeedingAckHandled(widget, aMessage);
   return true;
 }
 
 bool
 TabParent::RecvRequestFocus(const bool& aCanRaise)
 {
   nsCOMPtr<nsIFocusManager> fm = nsFocusManager::GetFocusManager();
   if (!fm) {
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -172,17 +172,17 @@ public:
                                                 const widget::IMENotification& aEventMessage) override;
     virtual bool RecvNotifyIMESelection(const ContentCache& aContentCache,
                                         const widget::IMENotification& aEventMessage) override;
     virtual bool RecvUpdateContentCache(const ContentCache& aContentCache) override;
     virtual bool RecvNotifyIMEMouseButtonEvent(const widget::IMENotification& aEventMessage,
                                                bool* aConsumedByIME) override;
     virtual bool RecvNotifyIMEPositionChange(const ContentCache& aContentCache,
                                              const widget::IMENotification& aEventMessage) override;
-    virtual bool RecvOnEventNeedingAckReceived(const uint32_t& aMessage) override;
+    virtual bool RecvOnEventNeedingAckHandled(const uint32_t& aMessage) override;
     virtual bool RecvEndIMEComposition(const bool& aCancel,
                                        bool* aNoCompositionEvent,
                                        nsString* aComposition) override;
     virtual bool RecvStartPluginIME(const WidgetKeyboardEvent& aKeyboardEvent,
                                     const int32_t& aPanelX,
                                     const int32_t& aPanelY,
                                     nsString* aCommitted) override;
     virtual bool RecvSetPluginFocused(const bool& aFocused) override;
--- a/dom/media/AudioSink.cpp
+++ b/dom/media/AudioSink.cpp
@@ -33,36 +33,37 @@ AudioSink::AudioSink(MediaDecoderStateMa
   , mStopAudioThread(false)
   , mSetVolume(false)
   , mSetPlaybackRate(false)
   , mSetPreservesPitch(false)
   , mPlaying(true)
 {
 }
 
-nsresult
+nsRefPtr<GenericPromise>
 AudioSink::Init()
 {
+  nsRefPtr<GenericPromise> p = mEndPromise.Ensure(__func__);
   nsresult rv = NS_NewNamedThread("Media Audio",
                                   getter_AddRefs(mThread),
                                   nullptr,
                                   MEDIA_THREAD_STACK_SIZE);
   if (NS_FAILED(rv)) {
-    mStateMachine->OnAudioSinkError();
-    return rv;
+    mEndPromise.Reject(rv, __func__);
+    return p;
   }
 
   nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &AudioSink::AudioLoop);
   rv =  mThread->Dispatch(event, NS_DISPATCH_NORMAL);
   if (NS_FAILED(rv)) {
-    mStateMachine->OnAudioSinkError();
-    return rv;
+    mEndPromise.Reject(rv, __func__);
+    return p;
   }
 
-  return NS_OK;
+  return p;
 }
 
 int64_t
 AudioSink::GetPosition()
 {
   AssertCurrentThreadInMonitor();
 
   int64_t pos;
@@ -95,17 +96,20 @@ AudioSink::PrepareToShutdown()
   GetReentrantMonitor().NotifyAll();
 }
 
 void
 AudioSink::Shutdown()
 {
   mThread->Shutdown();
   mThread = nullptr;
-  MOZ_ASSERT(!mAudioStream);
+  if (mAudioStream) {
+    mAudioStream->Shutdown();
+    mAudioStream = nullptr;
+  }
 }
 
 void
 AudioSink::SetVolume(double aVolume)
 {
   AssertCurrentThreadInMonitor();
   mVolume = aVolume;
   mSetVolume = true;
@@ -137,19 +141,20 @@ AudioSink::SetPlaying(bool aPlaying)
 }
 
 void
 AudioSink::AudioLoop()
 {
   AssertOnAudioThread();
   SINK_LOG("AudioLoop started");
 
-  if (NS_FAILED(InitializeAudioStream())) {
+  nsresult rv = InitializeAudioStream();
+  if (NS_FAILED(rv)) {
     NS_WARNING("Initializing AudioStream failed.");
-    mStateMachine->DispatchOnAudioSinkError();
+    mEndPromise.Reject(rv, __func__);
     return;
   }
 
   while (1) {
     {
       ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
       WaitForAudioToPlay();
       if (!IsPlaybackContinuing()) {
@@ -228,26 +233,20 @@ AudioSink::Drain()
     mAudioStream->Drain();
   }
 }
 
 void
 AudioSink::Cleanup()
 {
   AssertCurrentThreadInMonitor();
-  nsRefPtr<AudioStream> audioStream;
-  audioStream.swap(mAudioStream);
-  // Suppress the callback when the stop is requested by MediaDecoderStateMachine.
-  // See Bug 115334.
-  if (!mStopAudioThread) {
-    mStateMachine->DispatchOnAudioSinkComplete();
-  }
-
-  ReentrantMonitorAutoExit exit(GetReentrantMonitor());
-  audioStream->Shutdown();
+  mEndPromise.Resolve(true, __func__);
+  // Since the promise if resolved asynchronously, we don't shutdown
+  // AudioStream here so MDSM::ResyncAudioClock can get the correct
+  // audio position.
 }
 
 bool
 AudioSink::ExpectMoreAudioData()
 {
   return AudioQueue().GetSize() == 0 && !AudioQueue().IsFinished();
 }
 
--- a/dom/media/AudioSink.h
+++ b/dom/media/AudioSink.h
@@ -5,30 +5,33 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #if !defined(AudioSink_h__)
 #define AudioSink_h__
 
 #include "nsISupportsImpl.h"
 #include "MediaDecoderReader.h"
 #include "mozilla/dom/AudioChannelBinding.h"
 #include "mozilla/Atomics.h"
+#include "mozilla/MozPromise.h"
 
 namespace mozilla {
 
 class AudioStream;
 class MediaDecoderStateMachine;
 
 class AudioSink {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AudioSink)
 
   AudioSink(MediaDecoderStateMachine* aStateMachine,
             int64_t aStartTime, AudioInfo aInfo, dom::AudioChannel aChannel);
 
-  nsresult Init();
+  // Return a promise which will be resolved when AudioSink finishes playing,
+  // or rejected if any error.
+  nsRefPtr<GenericPromise> Init();
 
   int64_t GetPosition();
 
   // Thread-safe. Can be called on any thread.
   int64_t GetEndTime() const;
 
   // Check whether we've pushed more frames to the audio hardware than it has
   // played.
@@ -135,13 +138,15 @@ private:
 
   bool mStopAudioThread;
 
   bool mSetVolume;
   bool mSetPlaybackRate;
   bool mSetPreservesPitch;
 
   bool mPlaying;
+
+  MozPromiseHolder<GenericPromise> mEndPromise;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/DecoderTraits.cpp
+++ b/dom/media/DecoderTraits.cpp
@@ -181,39 +181,41 @@ static char const *const gWebMCodecs[7] 
   "vp8",
   "vp8.0",
   "vp9",
   "vp9.0",
   "vorbis",
   "opus",
   nullptr
 };
+#endif
 
 /* static */ bool
 DecoderTraits::IsWebMType(const nsACString& aType)
 {
+#ifdef MOZ_WEBM
   if (!MediaDecoder::IsWebMEnabled()) {
     return false;
   }
 
   return CodecListContains(gWebMTypes, aType);
+#endif
+  return false;
 }
-#endif
 
 #ifdef MOZ_GSTREAMER
 static bool
 IsGStreamerSupportedType(const nsACString& aMimeType)
 {
   if (!MediaDecoder::IsGStreamerEnabled())
     return false;
 
-#ifdef MOZ_WEBM
   if (DecoderTraits::IsWebMType(aMimeType) && !Preferences::GetBool("media.prefer-gstreamer", false))
     return false;
-#endif
+
   if (IsOggType(aMimeType) && !Preferences::GetBool("media.prefer-gstreamer", false))
     return false;
 
   return GStreamerDecoder::CanHandleMediaType(aMimeType, nullptr);
 }
 #endif
 
 #ifdef MOZ_OMX_DECODER
@@ -350,16 +352,26 @@ IsMP4SupportedType(const nsACString& aTy
                    const nsAString& aCodecs = EmptyString())
 {
   // MP4Decoder/Reader is currently used for MSE and mp4 files local playback.
   bool haveAAC, haveMP3, haveH264;
   return Preferences::GetBool("media.fragmented-mp4.exposed", false) &&
          MP4Decoder::CanHandleMediaType(aType, aCodecs, haveAAC, haveH264, haveMP3);
 }
 #endif
+
+/* static */ bool
+DecoderTraits::IsMP4Type(const nsACString& aType)
+{
+#ifdef MOZ_FMP4
+  return IsMP4SupportedType(aType);
+#endif
+  return false;
+}
+
 static bool
 IsMP3SupportedType(const nsACString& aType,
                    const nsAString& aCodecs = EmptyString())
 {
   return aType.EqualsASCII("audio/mpeg") && MP3Decoder::IsEnabled();
 }
 
 #ifdef MOZ_APPLEMEDIA
@@ -407,100 +419,79 @@ bool DecoderTraits::ShouldHandleMediaTyp
     return false;
   }
 #endif
   return CanHandleMediaType(aMIMEType, false, EmptyString()) != CANPLAY_NO;
 }
 
 /* static */
 CanPlayStatus
-DecoderTraits::CanHandleMediaType(const char* aMIMEType,
-                                  bool aHaveRequestedCodecs,
-                                  const nsAString& aRequestedCodecs)
+DecoderTraits::CanHandleCodecsType(const char* aMIMEType,
+                                   const nsAString& aRequestedCodecs)
 {
-  MOZ_ASSERT(NS_IsMainThread());
   char const* const* codecList = nullptr;
-  CanPlayStatus result = CANPLAY_NO;
 #ifdef MOZ_RAW
   if (IsRawType(nsDependentCString(aMIMEType))) {
     codecList = gRawCodecs;
-    result = CANPLAY_MAYBE;
   }
 #endif
   if (IsOggType(nsDependentCString(aMIMEType))) {
     codecList = MediaDecoder::IsOpusEnabled() ? gOggCodecsWithOpus : gOggCodecs;
-    result = CANPLAY_MAYBE;
   }
 #ifdef MOZ_WAVE
   if (IsWaveType(nsDependentCString(aMIMEType))) {
     codecList = gWaveCodecs;
-    result = CANPLAY_MAYBE;
   }
 #endif
-#if defined(MOZ_WEBM) && !defined(MOZ_OMX_WEBM_DECODER)
+#if !defined(MOZ_OMX_WEBM_DECODER)
   if (IsWebMType(nsDependentCString(aMIMEType))) {
     codecList = gWebMCodecs;
-    result = CANPLAY_MAYBE;
   }
 #endif
 #ifdef MOZ_FMP4
-  if (IsMP4SupportedType(nsDependentCString(aMIMEType),
-                                     aRequestedCodecs)) {
-    return aHaveRequestedCodecs ? CANPLAY_YES : CANPLAY_MAYBE;
+  if (IsMP4Type(nsDependentCString(aMIMEType))) {
+    if (IsMP4SupportedType(nsDependentCString(aMIMEType), aRequestedCodecs)) {
+      return CANPLAY_YES;
+    } else {
+      // We can only reach this position if a particular codec was requested,
+      // fmp4 is supported and working: the codec must be invalid.
+      return CANPLAY_NO;
+    }
   }
 #endif
-  if (IsMP3SupportedType(nsDependentCString(aMIMEType),
-                                     aRequestedCodecs)) {
-    return aHaveRequestedCodecs ? CANPLAY_YES : CANPLAY_MAYBE;
+  if (IsMP3SupportedType(nsDependentCString(aMIMEType), aRequestedCodecs)) {
+    return CANPLAY_YES;
   }
-#ifdef MOZ_GSTREAMER
-  if (GStreamerDecoder::CanHandleMediaType(nsDependentCString(aMIMEType),
-                                           aHaveRequestedCodecs ? &aRequestedCodecs : nullptr)) {
-    if (aHaveRequestedCodecs)
-      return CANPLAY_YES;
-    return CANPLAY_MAYBE;
-  }
-#endif
 #ifdef MOZ_OMX_DECODER
   if (IsOmxSupportedType(nsDependentCString(aMIMEType))) {
-    result = CANPLAY_MAYBE;
     if (nsDependentCString(aMIMEType).EqualsASCII("audio/mpeg")) {
       codecList = gMpegAudioCodecs;
 #ifdef MOZ_OMX_WEBM_DECODER
     } else if (nsDependentCString(aMIMEType).EqualsASCII("audio/webm") ||
                nsDependentCString(aMIMEType).EqualsASCII("video/webm")) {
       codecList = gOMXWebMCodecs;
 #endif
     } else {
       codecList = gH264Codecs;
     }
   }
 #endif
 #ifdef MOZ_DIRECTSHOW
-  if (DirectShowDecoder::GetSupportedCodecs(nsDependentCString(aMIMEType), &codecList)) {
-    result = CANPLAY_MAYBE;
-  }
+  DirectShowDecoder::GetSupportedCodecs(nsDependentCString(aMIMEType), &codecList);
 #endif
 #ifdef MOZ_APPLEMEDIA
-  if (IsAppleMediaSupportedType(nsDependentCString(aMIMEType), &codecList)) {
-    result = CANPLAY_MAYBE;
-  }
+  IsAppleMediaSupportedType(nsDependentCString(aMIMEType), &codecList);
 #endif
 #ifdef MOZ_ANDROID_OMX
-  if (MediaDecoder::IsAndroidMediaEnabled() &&
-      EnsureAndroidMediaPluginHost()->FindDecoder(nsDependentCString(aMIMEType), &codecList))
-    result = CANPLAY_MAYBE;
-#endif
-#ifdef NECKO_PROTOCOL_rtsp
-  if (IsRtspSupportedType(nsDependentCString(aMIMEType))) {
-    result = CANPLAY_MAYBE;
+  if (MediaDecoder::IsAndroidMediaEnabled()) {
+    EnsureAndroidMediaPluginHost()->FindDecoder(nsDependentCString(aMIMEType), &codecList);
   }
 #endif
-  if (result == CANPLAY_NO || !aHaveRequestedCodecs || !codecList) {
-    return result;
+  if (!codecList) {
+    return CANPLAY_MAYBE;
   }
 
   // See http://www.rfc-editor.org/rfc/rfc4281.txt for the description
   // of the 'codecs' parameter
   nsCharSeparatedTokenizer tokenizer(aRequestedCodecs, ',');
   bool expectMoreTokens = false;
   while (tokenizer.hasMoreTokens()) {
     const nsSubstring& token = tokenizer.nextToken();
@@ -510,19 +501,93 @@ DecoderTraits::CanHandleMediaType(const 
       return CANPLAY_NO;
     }
     expectMoreTokens = tokenizer.separatorAfterCurrentToken();
   }
   if (expectMoreTokens) {
     // Last codec name was empty
     return CANPLAY_NO;
   }
+
   return CANPLAY_YES;
 }
 
+/* static */
+CanPlayStatus
+DecoderTraits::CanHandleMediaType(const char* aMIMEType,
+                                  bool aHaveRequestedCodecs,
+                                  const nsAString& aRequestedCodecs)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (aHaveRequestedCodecs) {
+    CanPlayStatus result = CanHandleCodecsType(aMIMEType, aRequestedCodecs);
+    if (result == CANPLAY_NO || result == CANPLAY_YES) {
+      return result;
+    }
+  }
+#ifdef MOZ_RAW
+  if (IsRawType(nsDependentCString(aMIMEType))) {
+    return CANPLAY_MAYBE;
+  }
+#endif
+  if (IsOggType(nsDependentCString(aMIMEType))) {
+    return CANPLAY_MAYBE;
+  }
+#ifdef MOZ_WAVE
+  if (IsWaveType(nsDependentCString(aMIMEType))) {
+    return CANPLAY_MAYBE;
+  }
+#endif
+  if (IsMP4Type(nsDependentCString(aMIMEType))) {
+    return CANPLAY_MAYBE;
+  }
+#if !defined(MOZ_OMX_WEBM_DECODER)
+  if (IsWebMType(nsDependentCString(aMIMEType))) {
+    return CANPLAY_MAYBE;
+  }
+#endif
+  if (IsMP3SupportedType(nsDependentCString(aMIMEType))) {
+    return CANPLAY_MAYBE;
+  }
+#ifdef MOZ_GSTREAMER
+  if (GStreamerDecoder::CanHandleMediaType(nsDependentCString(aMIMEType),
+                                           aHaveRequestedCodecs ? &aRequestedCodecs : nullptr)) {
+    return aHaveRequestedCodecs ? CANPLAY_YES : CANPLAY_MAYBE;
+  }
+#endif
+#ifdef MOZ_OMX_DECODER
+  if (IsOmxSupportedType(nsDependentCString(aMIMEType))) {
+    return CANPLAY_MAYBE;
+  }
+#endif
+#ifdef MOZ_DIRECTSHOW
+  if (DirectShowDecoder::GetSupportedCodecs(nsDependentCString(aMIMEType), nullptr)) {
+    return CANPLAY_MAYBE;
+  }
+#endif
+#ifdef MOZ_APPLEMEDIA
+  if (IsAppleMediaSupportedType(nsDependentCString(aMIMEType), nullptr)) {
+    return CANPLAY_MAYBE;
+  }
+#endif
+#ifdef MOZ_ANDROID_OMX
+  if (MediaDecoder::IsAndroidMediaEnabled() &&
+      EnsureAndroidMediaPluginHost()->FindDecoder(nsDependentCString(aMIMEType), nullptr)) {
+    return CANPLAY_MAYBE;
+  }
+#endif
+#ifdef NECKO_PROTOCOL_rtsp
+  if (IsRtspSupportedType(nsDependentCString(aMIMEType))) {
+    return CANPLAY_MAYBE;
+  }
+#endif
+  return CANPLAY_NO;
+}
+
 // Instantiates but does not initialize decoder.
 static
 already_AddRefed<MediaDecoder>
 InstantiateDecoder(const nsACString& aType, MediaDecoderOwner* aOwner)
 {
   MOZ_ASSERT(NS_IsMainThread());
   nsRefPtr<MediaDecoder> decoder;
 
@@ -599,22 +664,20 @@ InstantiateDecoder(const nsACString& aTy
 #endif
 #ifdef MOZ_ANDROID_OMX
   if (MediaDecoder::IsAndroidMediaEnabled() &&
       EnsureAndroidMediaPluginHost()->FindDecoder(aType, nullptr)) {
     decoder = new AndroidMediaDecoder(aType);
     return decoder.forget();
   }
 #endif
-#ifdef MOZ_WEBM
   if (DecoderTraits::IsWebMType(aType)) {
     decoder = new WebMDecoder();
     return decoder.forget();
   }
-#endif
 #ifdef MOZ_DIRECTSHOW
   // Note: DirectShow should come before WMF, so that we prefer DirectShow's
   // MP3 support over WMF's.
   if (IsDirectShowSupportedType(aType)) {
     decoder = new DirectShowDecoder();
     return decoder.forget();
   }
 #endif
@@ -689,23 +752,21 @@ MediaDecoderReader* DecoderTraits::Creat
   } else
 #endif
 #ifdef MOZ_ANDROID_OMX
   if (MediaDecoder::IsAndroidMediaEnabled() &&
       EnsureAndroidMediaPluginHost()->FindDecoder(aType, nullptr)) {
     decoderReader = new AndroidMediaReader(aDecoder, aType);
   } else
 #endif
-#ifdef MOZ_WEBM
   if (IsWebMType(aType)) {
     decoderReader = Preferences::GetBool("media.format-reader.webm", true) ?
       static_cast<MediaDecoderReader*>(new MediaFormatReader(aDecoder, new WebMDemuxer(aDecoder->GetResource()))) :
       new WebMReader(aDecoder);
   } else
-#endif
 #ifdef MOZ_DIRECTSHOW
   if (IsDirectShowSupportedType(aType)) {
     decoderReader = new DirectShowReader(aDecoder);
   } else
 #endif
 #ifdef MOZ_APPLEMEDIA
   if (IsAppleMediaSupportedType(aType)) {
     decoderReader = new AppleMP3Reader(aDecoder);
@@ -731,19 +792,17 @@ bool DecoderTraits::IsSupportedInVideoDo
     IsOggType(aType) ||
 #ifdef MOZ_OMX_DECODER
     // We support the formats in gB2GOnlyTypes only inside WebApps on firefoxOS
     // but not in general web content. Ensure we dont create a VideoDocument
     // when accessing those format URLs directly.
     (IsOmxSupportedType(aType) &&
      !IsB2GSupportOnlyType(aType)) ||
 #endif
-#ifdef MOZ_WEBM
     IsWebMType(aType) ||
-#endif
 #ifdef MOZ_GSTREAMER
     IsGStreamerSupportedType(aType) ||
 #endif
 #ifdef MOZ_ANDROID_OMX
     (MediaDecoder::IsAndroidMediaEnabled() && IsAndroidMediaType(aType)) ||
 #endif
 #ifdef MOZ_FMP4
     IsMP4SupportedType(aType) ||
--- a/dom/media/DecoderTraits.h
+++ b/dom/media/DecoderTraits.h
@@ -24,24 +24,32 @@ enum CanPlayStatus {
   CANPLAY_MAYBE,
   CANPLAY_YES
 };
 
 class DecoderTraits {
 public:
   // Returns the CanPlayStatus indicating if we can handle this
   // MIME type. The MIME type should not include the codecs parameter.
-  // That parameter should be passed in aCodecs, and will only be
+  // That parameter should be passed in aRequestedCodecs, and will only be
   // used if whether a given MIME type being handled depends on the
   // codec that will be used.  If the codecs parameter has not been
   // specified, pass false in aHaveRequestedCodecs.
   static CanPlayStatus CanHandleMediaType(const char* aMIMEType,
                                           bool aHaveRequestedCodecs,
                                           const nsAString& aRequestedCodecs);
 
+  // Returns the CanPlayStatus indicating if we can handle this MIME type and
+  // codecs type natively, excluding any plugins-based reader (such as
+  // GStreamer)
+  // The MIME type should not include the codecs parameter.
+  // That parameter is passed in aRequestedCodecs.
+  static CanPlayStatus CanHandleCodecsType(const char* aMIMEType,
+                                           const nsAString& aRequestedCodecs);
+
   // Returns true if we should handle this MIME type when it appears
   // as an <object> or as a toplevel page. If, in practice, our support
   // for the type is more limited than appears in the wild, we should return
   // false here even if CanHandleMediaType would return true.
   static bool ShouldHandleMediaType(const char* aMIMEType);
 
   // Create a decoder for the given aType. Returns null if we
   // were unable to create the decoder.
@@ -58,14 +66,15 @@ public:
   // vice versa.
   static bool IsSupportedInVideoDocument(const nsACString& aType);
 
   // Returns true if we should not start decoder until we receive
   // OnConnected signal. (currently RTSP only)
   static bool DecoderWaitsForOnConnected(const nsACString& aType);
 
   static bool IsWebMType(const nsACString& aType);
+  static bool IsMP4Type(const nsACString& aType);
 };
 
 } // namespace mozilla
 
 #endif
 
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -1081,18 +1081,17 @@ void MediaDecoderStateMachine::MaybeStar
   }
 
   DECODER_LOG("MaybeStartPlayback() starting playback");
 
   mDecoder->DispatchPlaybackStarted();
   SetPlayStartTime(TimeStamp::Now());
   MOZ_ASSERT(IsPlaying());
 
-  nsresult rv = StartAudioThread();
-  NS_ENSURE_SUCCESS_VOID(rv);
+  StartAudioThread();
 
   // Tell DecodedStream to start playback with specified start time and media
   // info. This is consistent with how we create AudioSink in StartAudioThread().
   if (mAudioCaptured) {
     mDecodedStream->StartPlayback(GetMediaTime(), mInfo);
   }
 
   mDecoder->GetReentrantMonitor().NotifyAll();
@@ -1476,16 +1475,17 @@ void MediaDecoderStateMachine::StopAudio
     DECODER_LOG("Shutdown audio thread");
     mAudioSink->PrepareToShutdown();
     {
       ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
       mAudioSink->Shutdown();
     }
     mAudioSink = nullptr;
   }
+  mAudioSinkPromise.DisconnectIfExists();
 }
 
 nsresult
 MediaDecoderStateMachine::EnqueueDecodeFirstFrameTask()
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   MOZ_ASSERT(mState == DECODER_STATE_DECODING_FIRSTFRAME);
@@ -1778,41 +1778,41 @@ MediaDecoderStateMachine::RequestVideoDa
              &StartTimeRendezvous::FirstSampleRejected<VideoData>)
       ->CompletionPromise()
       ->Then(OwnerThread(), __func__, this,
              &MediaDecoderStateMachine::OnVideoDecoded,
              &MediaDecoderStateMachine::OnVideoNotDecoded));
   }
 }
 
-nsresult
+void
 MediaDecoderStateMachine::StartAudioThread()
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   if (mAudioCaptured) {
     MOZ_ASSERT(!mAudioSink);
-    return NS_OK;
+    return;
   }
 
   if (HasAudio() && !mAudioSink) {
-    auto audioStartTime = GetMediaTime();
     mAudioCompleted = false;
-    mAudioSink = new AudioSink(this, audioStartTime,
+    mAudioSink = new AudioSink(this, GetMediaTime(),
                                mInfo.mAudio, mDecoder->GetAudioChannel());
-    // OnAudioSinkError() will be called before Init() returns if an error
-    // occurs during initialization.
-    nsresult rv = mAudioSink->Init();
-    NS_ENSURE_SUCCESS(rv, rv);
+
+    mAudioSinkPromise.Begin(
+      mAudioSink->Init()->Then(
+        OwnerThread(), __func__, this,
+        &MediaDecoderStateMachine::OnAudioSinkComplete,
+        &MediaDecoderStateMachine::OnAudioSinkError));
 
     mAudioSink->SetVolume(mVolume);
     mAudioSink->SetPlaybackRate(mPlaybackRate);
     mAudioSink->SetPreservesPitch(mPreservesPitch);
   }
-  return NS_OK;
 }
 
 int64_t MediaDecoderStateMachine::AudioDecodedUsecs()
 {
   MOZ_ASSERT(OnTaskQueue());
   NS_ASSERTION(HasAudio(),
                "Should only call AudioDecodedUsecs() when we have audio");
   // The amount of audio we have decoded is the amount of audio data we've
@@ -2451,23 +2451,23 @@ nsresult MediaDecoderStateMachine::RunSt
         clockTime = std::max(int64_t(0), std::max(clockTime, Duration().ToMicroseconds()));
         UpdatePlaybackPosition(clockTime);
 
         nsCOMPtr<nsIRunnable> event =
           NS_NewRunnableMethod(mDecoder, &MediaDecoder::PlaybackEnded);
         AbstractThread::MainThread()->Dispatch(event.forget());
 
         mSentPlaybackEndedEvent = true;
+
+        // Stop audio sink after call to AudioEndTime() above, otherwise it will
+        // return an incorrect value due to a null mAudioSink.
+        StopAudioThread();
+        mDecodedStream->StopPlayback();
       }
 
-      // Stop audio sink after call to AudioEndTime() above, otherwise it will
-      // return an incorrect value due to a null mAudioSink.
-      StopAudioThread();
-      mDecodedStream->StopPlayback();
-
       return NS_OK;
     }
   }
 
   return NS_OK;
 }
 
 void
@@ -3117,34 +3117,33 @@ void MediaDecoderStateMachine::OnPlaybac
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   mDecoder->UpdatePlaybackOffset(aPlaybackOffset);
 }
 
 void MediaDecoderStateMachine::OnAudioSinkComplete()
 {
   MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
-  if (mAudioCaptured) {
-    return;
-  }
+  MOZ_ASSERT(!mAudioCaptured, "Should be disconnected when capturing audio.");
+
+  mAudioSinkPromise.Complete();
   ResyncAudioClock();
   mAudioCompleted = true;
+
   // Kick the decode thread; it may be sleeping waiting for this to finish.
   mDecoder->GetReentrantMonitor().NotifyAll();
 }
 
 void MediaDecoderStateMachine::OnAudioSinkError()
 {
   MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
-  // AudioSink not used with captured streams, so ignore errors in this case.
-  if (mAudioCaptured) {
-    return;
-  }
-
+  MOZ_ASSERT(!mAudioCaptured, "Should be disconnected when capturing audio.");
+
+  mAudioSinkPromise.Complete();
   ResyncAudioClock();
   mAudioCompleted = true;
 
   // Make the best effort to continue playback when there is video.
   if (HasVideo()) {
     return;
   }
 
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -512,17 +512,17 @@ protected:
   void UpdateRenderedVideoFrames();
 
   // Stops the audio thread. The decoder monitor must be held with exactly
   // one lock count. Called on the state machine thread.
   void StopAudioThread();
 
   // Starts the audio thread. The decoder monitor must be held with exactly
   // one lock count. Called on the state machine thread.
-  nsresult StartAudioThread();
+  void StartAudioThread();
 
   // Notification method invoked when mPlayState changes.
   void PlayStateChanged();
 
   // Notification method invoked when mLogicallySeeking changes.
   void LogicallySeekingChanged();
 
   // Sets internal state which causes playback of media to pause.
@@ -674,38 +674,23 @@ public:
   void DispatchOnPlaybackOffsetUpdate(int64_t aPlaybackOffset)
   {
     RefPtr<nsRunnable> r =
       NS_NewRunnableMethodWithArg<int64_t>(this, &MediaDecoderStateMachine::OnPlaybackOffsetUpdate, aPlaybackOffset);
     OwnerThread()->Dispatch(r.forget());
   }
 
 private:
-  // Called by the AudioSink to signal that all outstanding work is complete
+  // Resolved by the AudioSink to signal that all outstanding work is complete
   // and the sink is shutting down.
   void OnAudioSinkComplete();
-public:
-  void DispatchOnAudioSinkComplete()
-  {
-    nsCOMPtr<nsIRunnable> runnable =
-      NS_NewRunnableMethod(this, &MediaDecoderStateMachine::OnAudioSinkComplete);
-    OwnerThread()->Dispatch(runnable.forget());
-  }
-private:
 
-  // Called by the AudioSink to signal errors.
+  // Rejected by the AudioSink to signal errors.
   void OnAudioSinkError();
 
-  void DispatchOnAudioSinkError()
-  {
-    nsCOMPtr<nsIRunnable> runnable =
-      NS_NewRunnableMethod(this, &MediaDecoderStateMachine::OnAudioSinkError);
-    OwnerThread()->Dispatch(runnable.forget());
-  }
-
   // Return true if the video decoder's decode speed can not catch up the
   // play time.
   bool NeedToSkipToNextKeyframe();
 
   // The decoder object that created this state machine. The state machine
   // holds a strong reference to the decoder to ensure that the decoder stays
   // alive once media element has started the decoder shutdown process, and has
   // dropped its reference to the decoder. This enables the state machine to
@@ -1312,16 +1297,18 @@ private:
   // Only written on the main thread while holding the monitor. Therefore it
   // can be read on any thread while holding the monitor, or on the main thread
   // without holding the monitor.
   nsRefPtr<DecodedStream> mDecodedStream;
 
   // Media data resource from the decoder.
   nsRefPtr<MediaResource> mResource;
 
+  MozPromiseRequestHolder<GenericPromise> mAudioSinkPromise;
+
 private:
   // The buffered range. Mirrored from the decoder thread.
   Mirror<media::TimeIntervals> mBuffered;
 
   // The duration according to the demuxer's current estimate, mirrored from the main thread.
   Mirror<media::NullableTimeUnit> mEstimatedDuration;
 
   // The duration explicitly set by JS, mirrored from the main thread.
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -1133,16 +1133,20 @@ MediaFormatReader::SizeOfQueue(TrackType
 
 nsRefPtr<MediaDecoderReader::WaitForDataPromise>
 MediaFormatReader::WaitForData(MediaData::Type aType)
 {
   MOZ_ASSERT(OnTaskQueue());
   TrackType trackType = aType == MediaData::VIDEO_DATA ?
     TrackType::kVideoTrack : TrackType::kAudioTrack;
   auto& decoder = GetDecoderData(trackType);
+  if (!decoder.mWaitingForData) {
+    // We aren't waiting for data any longer.
+    return WaitForDataPromise::CreateAndResolve(decoder.mType, __func__);
+  }
   nsRefPtr<WaitForDataPromise> p = decoder.mWaitingPromise.Ensure(__func__);
   ScheduleUpdate(trackType);
   return p;
 }
 
 nsresult
 MediaFormatReader::ResetDecode()
 {
--- a/dom/media/fmp4/MP4Decoder.cpp
+++ b/dom/media/fmp4/MP4Decoder.cpp
@@ -66,18 +66,19 @@ MP4Decoder::SetCDMProxy(CDMProxy* aProxy
 #endif
 
 static bool
 IsSupportedAudioCodec(const nsAString& aCodec,
                       bool& aOutContainsAAC,
                       bool& aOutContainsMP3)
 {
   // AAC-LC or HE-AAC in M4A.
-  aOutContainsAAC = aCodec.EqualsASCII("mp4a.40.2") ||
-                    aCodec.EqualsASCII("mp4a.40.5");
+  aOutContainsAAC = aCodec.EqualsASCII("mp4a.40.2")     // MPEG4 AAC-LC
+                    || aCodec.EqualsASCII("mp4a.40.5")  // MPEG4 HE-AAC
+                    || aCodec.EqualsASCII("mp4a.67");   // MPEG2 AAC-LC
   if (aOutContainsAAC) {
     return true;
   }
 #ifndef MOZ_GONK_MEDIACODEC // B2G doesn't support MP3 in MP4 yet.
   aOutContainsMP3 = aCodec.EqualsASCII("mp3");
   if (aOutContainsMP3) {
     return true;
   }
--- a/dom/media/mediasource/MediaSource.cpp
+++ b/dom/media/mediasource/MediaSource.cpp
@@ -78,56 +78,51 @@ IsTypeSupported(const nsAString& aType)
 {
   if (aType.IsEmpty()) {
     return NS_ERROR_DOM_INVALID_ACCESS_ERR;
   }
   nsContentTypeParser parser(aType);
   nsAutoString mimeType;
   nsresult rv = parser.GetType(mimeType);
   if (NS_FAILED(rv)) {
-    return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+    return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
-  bool found = false;
+  NS_ConvertUTF16toUTF8 mimeTypeUTF8(mimeType);
+
+  nsAutoString codecs;
+  bool hasCodecs = NS_SUCCEEDED(parser.GetParameter("codecs", codecs));
+
   for (uint32_t i = 0; gMediaSourceTypes[i]; ++i) {
     if (mimeType.EqualsASCII(gMediaSourceTypes[i])) {
-      if ((mimeType.EqualsASCII("video/mp4") ||
-           mimeType.EqualsASCII("audio/mp4")) &&
-          (!Preferences::GetBool("media.mediasource.mp4.enabled", false)
-#ifdef MOZ_WIDGET_ANDROID
-          // MP4 won't work unless we have JellyBean+
-          || AndroidBridge::Bridge()->GetAPIVersion() < 16
-#endif
-          )) {
-        return NS_ERROR_DOM_INVALID_STATE_ERR;
+      if (DecoderTraits::IsMP4Type(mimeTypeUTF8)) {
+        if (!Preferences::GetBool("media.mediasource.mp4.enabled", false)) {
+          return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+        }
+        if (hasCodecs &&
+            DecoderTraits::CanHandleCodecsType(mimeTypeUTF8.get(),
+                                               codecs) == CANPLAY_NO) {
+          return NS_ERROR_DOM_INVALID_STATE_ERR;
+        }
+        return NS_OK;
+      } else if (DecoderTraits::IsWebMType(mimeTypeUTF8)) {
+        if (!Preferences::GetBool("media.mediasource.webm.enabled", false) ||
+            Preferences::GetBool("media.mediasource.format-reader", false)) {
+          return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+        }
+        if (hasCodecs &&
+            DecoderTraits::CanHandleCodecsType(mimeTypeUTF8.get(),
+                                               codecs) == CANPLAY_NO) {
+          return NS_ERROR_DOM_INVALID_STATE_ERR;
+        }
+        return NS_OK;
       }
-      if ((mimeType.EqualsASCII("video/webm") ||
-           mimeType.EqualsASCII("audio/webm")) &&
-          !Preferences::GetBool("media.mediasource.webm.enabled", false)) {
-        return NS_ERROR_DOM_INVALID_STATE_ERR;
-      }
-      found = true;
-      break;
     }
   }
-  if (!found) {
-    return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
-  }
-  // Check aType against HTMLMediaElement list of MIME types.  Since we've
-  // already restricted the container format, this acts as a specific check
-  // of any specified "codecs" parameter of aType.
-  if (dom::HTMLMediaElement::GetCanPlay(aType) == CANPLAY_NO) {
-    return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
-  }
 
-  if (Preferences::GetBool("media.mediasource.format-reader", false) &&
-      !mimeType.EqualsASCII("video/mp4") && !mimeType.EqualsASCII("audio/mp4")) {
-    return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
-  }
-
-  return NS_OK;
+  return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
 }
 
 namespace dom {
 
 /* static */ already_AddRefed<MediaSource>
 MediaSource::Constructor(const GlobalObject& aGlobal,
                          ErrorResult& aRv)
 {
--- a/dom/media/mediasource/test/mochitest.ini
+++ b/dom/media/mediasource/test/mochitest.ini
@@ -30,62 +30,76 @@ support-files =
   bipbop/bipbop8.m4s^headers^ bipbop/bipbop_audio8.m4s^headers^ bipbop/bipbop_video8.m4s^headers^
   bipbop/bipbop9.m4s^headers^ bipbop/bipbop_audio9.m4s^headers^ bipbop/bipbop_video9.m4s^headers^
   bipbop/bipbop10.m4s^headers^ bipbop/bipbop_audio10.m4s^headers^ bipbop/bipbop_video10.m4s^headers^
   bipbop/bipbop11.m4s^headers^ bipbop/bipbop_audio11.m4s^headers^ bipbop/bipbop_video11.m4s^headers^
   bipbop/bipbop12.m4s^headers^ bipbop/bipbop_video12.m4s^headers^
   bipbop/bipbop13.m4s^headers^ bipbop/bipbop_video13.m4s^headers^
 
 [test_BufferedSeek.html]
+skip-if = true # bug 1182946
 [test_BufferedSeek_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_BufferingWait.html]
-skip-if = true # bug 1093133
+skip-if = true # bug 1182946
 [test_BufferingWait_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_EndOfStream.html]
-skip-if = (toolkit == 'android' || buildapp == 'mulet') #timeout android/mulet only bug 1101187
+skip-if = (true || toolkit == 'android' || buildapp == 'mulet') #timeout android/mulet only bug 1101187 and bug 1182946
 [test_EndOfStream_mp4.html]
 skip-if = (toolkit == 'android' || buildapp == 'mulet') || ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_DurationUpdated.html]
+skip-if = true # bug 1182946
 [test_DurationUpdated_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_FrameSelection.html]
+skip-if = true # bug 1182946
 [test_HaveMetadataUnbufferedSeek.html]
+skip-if = true # bug 1182946
 [test_HaveMetadataUnbufferedSeek_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_LoadedMetadataFired.html]
+skip-if = true # bug 1182946
 [test_LoadedMetadataFired_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_MediaSource.html]
+skip-if = true # bug 1182946
 [test_MediaSource_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_MediaSource_disabled.html]
 [test_SeekableAfterEndOfStream.html]
+skip-if = true # bug 1182946
 [test_SeekableAfterEndOfStream_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_SeekableAfterEndOfStreamSplit.html]
+skip-if = true # bug 1182946
 [test_SeekableAfterEndOfStreamSplit_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_SeekableBeforeEndOfStream.html]
+skip-if = true # bug 1182946
 [test_SeekableBeforeEndOfStream_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_SeekableBeforeEndOfStreamSplit.html]
+skip-if = true # bug 1182946
 [test_SeekableBeforeEndOfStreamSplit_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_SeekTwice_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_SetModeThrows.html]
+skip-if = true # bug 1182946
 [test_SplitAppendDelay.html]
+skip-if = true # bug 1182946
 [test_SplitAppendDelay_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_SplitAppend.html]
+skip-if = true # bug 1182946
 [test_SplitAppend_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_TimestampOffset_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_TruncatedDuration.html]
+skip-if = true # bug 1182946
 [test_TruncatedDuration_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_WaitingOnMissingData.html]
 skip-if = true # Disabled due to bug 1124493 and friends. WebM MSE is deprioritized.
 [test_WaitingOnMissingData_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
--- a/dom/media/test/eme.js
+++ b/dom/media/test/eme.js
@@ -1,10 +1,21 @@
 const KEYSYSTEM_TYPE = "org.w3.clearkey";
 
+function IsMacOSSnowLeopardOrEarlier() {
+  var re = /Mac OS X (\d+)\.(\d+)/;
+  var ver = navigator.userAgent.match(re);
+  if (!ver || ver.length != 3) {
+    return false;
+  }
+  var major = ver[1] | 0;
+  var minor = ver[2] | 0;
+  return major == 10 && minor <= 6;
+}
+
 function bail(message)
 {
   return function(err) {
     if (err) {
       message +=  "; " + String(err)
     }
     ok(false, message);
     if (err) {
--- a/dom/media/test/test_eme_canvas_blocked.html
+++ b/dom/media/test/test_eme_canvas_blocked.html
@@ -41,14 +41,18 @@ function startTest(test, token)
   LoadTestWithManagedLoadToken(test, v, manager, token,
                                { onlyLoadFirstFragments:2, noEndOfStream:true });
 }
 
 function beginTest() {
   manager.runTests(gEMETests, startTest);
 }
 
-SimpleTest.waitForExplicitFinish();
-SetupEMEPref(beginTest);
+if (!IsMacOSSnowLeopardOrEarlier()) {
+  SimpleTest.waitForExplicitFinish();
+  SetupEMEPref(beginTest);
+} else {
+  todo(false, "Test disabled on this platform.");
+}
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/test/test_eme_non_mse_fails.html
+++ b/dom/media/test/test_eme_non_mse_fails.html
@@ -84,15 +84,19 @@ function startTest(test, token)
   TestSetMediaKeys(test, token + "_setMediaKeys");
   TestSetSrc(test, token + "_setSrc");
 }
 
 function beginTest() {
   manager.runTests(gEMENonMSEFailTests, startTest);
 }
 
-SimpleTest.waitForExplicitFinish();
-SetupEMEPref(beginTest);
+if (!IsMacOSSnowLeopardOrEarlier()) {
+  SimpleTest.waitForExplicitFinish();
+  SetupEMEPref(beginTest);
+} else {
+  todo(false, "Test disabled on this platform.");
+}
 </script>
 </pre>
 </body>
 </html>
 
--- a/dom/media/test/test_eme_persistent_sessions.html
+++ b/dom/media/test/test_eme_persistent_sessions.html
@@ -149,14 +149,18 @@ function startTest(test, token)
   LoadTestWithManagedLoadToken(test, v, manager, token,
                                { onlyLoadFirstFragments:2, noEndOfStream:true });
 }
 
 function beginTest() {
   manager.runTests(gEMETests.filter(t => t.sessionCount === 1), startTest);
 }
 
-SimpleTest.waitForExplicitFinish();
-SetupEMEPref(beginTest);
+if (!IsMacOSSnowLeopardOrEarlier()) {
+  SimpleTest.waitForExplicitFinish();
+  SetupEMEPref(beginTest);
+} else {
+  todo(false, "Test disabled on this platform.");
+}
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/test/test_eme_playback.html
+++ b/dom/media/test/test_eme_playback.html
@@ -106,14 +106,18 @@ function startTest(test, token)
     manager.finished(token);
   });
 }
 
 function beginTest() {
   manager.runTests(gEMETests, startTest);
 }
 
-SimpleTest.waitForExplicitFinish();
-SetupEMEPref(beginTest);
+if (!IsMacOSSnowLeopardOrEarlier()) {
+  SimpleTest.waitForExplicitFinish();
+  SetupEMEPref(beginTest);
+} else {
+  todo(false, "Test disabled on this platform.");
+}
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/test/test_eme_requestKeySystemAccess.html
+++ b/dom/media/test/test_eme_requestKeySystemAccess.html
@@ -287,15 +287,19 @@ var tests = [
     shouldPass: false
   },
 ];
 
 function beginTest() {
   Promise.all(tests.map(Test)).then(function() { SimpleTest.finish(); });
 }
 
-SimpleTest.waitForExplicitFinish();
-SetupEMEPref(beginTest);
+if (!IsMacOSSnowLeopardOrEarlier()) {
+  SimpleTest.waitForExplicitFinish();
+  SetupEMEPref(beginTest);
+} else {
+  todo(false, "Test disabled on this platform.");
+}
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/test/test_eme_stream_capture_blocked_case1.html
+++ b/dom/media/test/test_eme_stream_capture_blocked_case1.html
@@ -35,14 +35,18 @@ function startTest(test, token)
   LoadTestWithManagedLoadToken(test, v1, manager, case1token,
                                { onlyLoadFirstFragments:2, noEndOfStream:true });
 }
 
 function beginTest() {
   manager.runTests(gEMETests, startTest);
 }
 
-SimpleTest.waitForExplicitFinish();
-SetupEMEPref(beginTest);
+if (!IsMacOSSnowLeopardOrEarlier()) {
+  SimpleTest.waitForExplicitFinish();
+  SetupEMEPref(beginTest);
+} else {
+  todo(false, "Test disabled on this platform.");
+}
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/test/test_eme_stream_capture_blocked_case2.html
+++ b/dom/media/test/test_eme_stream_capture_blocked_case2.html
@@ -38,14 +38,18 @@ function startTest(test, token)
   LoadTestWithManagedLoadToken(test, v2, manager, case2token,
                                { onlyLoadFirstFragments:2, noEndOfStream:true });
 }
 
 function beginTest() {
   manager.runTests(gEMETests, startTest);
 }
 
-SimpleTest.waitForExplicitFinish();
-SetupEMEPref(beginTest);
+if (!IsMacOSSnowLeopardOrEarlier()) {
+  SimpleTest.waitForExplicitFinish();
+  SetupEMEPref(beginTest);
+} else {
+  todo(false, "Test disabled on this platform.");
+}
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/test/test_eme_stream_capture_blocked_case3.html
+++ b/dom/media/test/test_eme_stream_capture_blocked_case3.html
@@ -37,14 +37,18 @@ function startTest(test, token)
   LoadTestWithManagedLoadToken(test, v3, manager, case3token,
                                { onlyLoadFirstFragments:2, noEndOfStream:true });
 }
 
 function beginTest() {
   manager.runTests(gEMETests, startTest);
 }
 
-SimpleTest.waitForExplicitFinish();
-SetupEMEPref(beginTest);
+if (!IsMacOSSnowLeopardOrEarlier()) {
+  SimpleTest.waitForExplicitFinish();
+  SetupEMEPref(beginTest);
+} else {
+  todo(false, "Test disabled on this platform.");
+}
 </script>
 </pre>
 </body>
 </html>
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -125,26 +125,16 @@ mozilla::plugins::SetupBridge(uint32_t a
     }
     chromeParent->SetContentParent(aContentParent);
     if (!aForceBridgeNow && chromeParent->IsStartingAsync() &&
         PluginModuleChromeParent::DidInstantiate()) {
         // We'll handle the bridging asynchronously
         return true;
     }
     *rv = PPluginModule::Bridge(aContentParent, chromeParent);
-    if (NS_FAILED(*rv)) {
-#if defined(MOZ_CRASHREPORTER)
-        // We are going to abort due to the failure, lets note the cause
-        // in the report for diagnosing.
-        nsAutoCString error;
-        error.AppendPrintf("%X %d", *rv, chromeParent->GetIPCChannel()->GetChannelState__TotallyRacy());
-        CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("BridgePluginError"), error);
-#endif
-      return false;
-    }
     return true;
 }
 
 #ifdef MOZ_CRASHREPORTER_INJECTOR
 
 /**
  * Use for executing CreateToolhelp32Snapshot off main thread
  */
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -1256,16 +1256,18 @@ GLContext::InitWithPrefix(const char *pr
 
                 MarkUnsupported(GLFeature::get_integer64_indexed);
                 ClearSymbols(coreSymbols);
             }
         }
 
         if (IsSupported(GLFeature::gpu_shader4)) {
             SymLoadStruct gpuShader4Symbols[] = {
+                { (PRFuncPtr*) &mSymbols.fGetVertexAttribIiv, { "GetVertexAttribIiv", "GetVertexAttribIivEXT", nullptr } },
+                { (PRFuncPtr*) &mSymbols.fGetVertexAttribIuiv, { "GetVertexAttribIuiv", "GetVertexAttribIuivEXT", nullptr } },
                 { (PRFuncPtr*) &mSymbols.fVertexAttribI4i, { "VertexAttribI4i", "VertexAttribI4iEXT", nullptr } },
                 { (PRFuncPtr*) &mSymbols.fVertexAttribI4iv, { "VertexAttribI4iv","VertexAttribI4ivEXT", nullptr } },
                 { (PRFuncPtr*) &mSymbols.fVertexAttribI4ui, { "VertexAttribI4ui", "VertexAttribI4uiEXT", nullptr } },
                 { (PRFuncPtr*) &mSymbols.fVertexAttribI4uiv, { "VertexAttribI4uiv", "VertexAttribI4uivEXT", nullptr } },
                 { (PRFuncPtr*) &mSymbols.fVertexAttribIPointer, { "VertexAttribIPointer", "VertexAttribIPointerEXT", nullptr } },
                 { (PRFuncPtr*) &mSymbols.fUniform1ui,  { "Uniform1ui", "Uniform1uiEXT", nullptr } },
                 { (PRFuncPtr*) &mSymbols.fUniform2ui,  { "Uniform2ui", "Uniform2uiEXT", nullptr } },
                 { (PRFuncPtr*) &mSymbols.fUniform3ui,  { "Uniform3ui", "Uniform3uiEXT", nullptr } },
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -2371,16 +2371,32 @@ public:
         ASSERT_SYMBOL_PRESENT(fRenderbufferStorageMultisample);
         mSymbols.fRenderbufferStorageMultisample(target, samples, internalFormat, width, height);
         AFTER_GL_CALL;
     }
 
 // -----------------------------------------------------------------------------
 //  GL 3.0, GL ES 3.0 & EXT_gpu_shader4
 public:
+    void fGetVertexAttribIiv(GLuint index, GLenum pname, GLint* params)
+    {
+        ASSERT_SYMBOL_PRESENT(fGetVertexAttribIiv);
+        BEFORE_GL_CALL;
+        mSymbols.fGetVertexAttribIiv(index, pname, params);
+        AFTER_GL_CALL;
+    }
+
+    void fGetVertexAttribIuiv(GLuint index, GLenum pname, GLuint* params)
+    {
+        ASSERT_SYMBOL_PRESENT(fGetVertexAttribIuiv);
+        BEFORE_GL_CALL;
+        mSymbols.fGetVertexAttribIuiv(index, pname, params);
+        AFTER_GL_CALL;
+    }
+
     void fVertexAttribI4i(GLuint index, GLint x, GLint y, GLint z, GLint w)
     {
         BEFORE_GL_CALL;
         ASSERT_SYMBOL_PRESENT(fVertexAttribI4i);
         mSymbols.fVertexAttribI4i(index, x, y, z, w);
         AFTER_GL_CALL;
     }
 
--- a/gfx/gl/GLContextSymbols.h
+++ b/gfx/gl/GLContextSymbols.h
@@ -615,16 +615,20 @@ struct GLContextSymbols
     typedef void (GLAPIENTRY * PFNGLGETACTIVEUNIFORMBLOCKIVPROC) (GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint* params);
     PFNGLGETACTIVEUNIFORMBLOCKIVPROC fGetActiveUniformBlockiv;
     typedef void (GLAPIENTRY * PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC) (GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei* length, GLchar* uniformBlockName);
     PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC fGetActiveUniformBlockName;
     typedef void (GLAPIENTRY * PFNGLUNIFORMBLOCKBINDINGPROC) (GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding);
     PFNGLUNIFORMBLOCKBINDINGPROC fUniformBlockBinding;
 
     // EXT_gpu_shader4
+    typedef void (GLAPIENTRY * PFNGLGETVERTEXATTRIBIIVPROC) (GLuint index, GLenum pname, GLint* params);
+    PFNGLGETVERTEXATTRIBIIVPROC fGetVertexAttribIiv;
+    typedef void (GLAPIENTRY * PFNGLGETVERTEXATTRIBIUIVPROC) (GLuint index, GLenum pname, GLuint* params);
+    PFNGLGETVERTEXATTRIBIUIVPROC fGetVertexAttribIuiv;
     typedef void (GLAPIENTRY * PFNGLVERTEXATTRIBI4IPROC) (GLuint index, GLint x, GLint y, GLint z, GLint w);
     PFNGLVERTEXATTRIBI4IPROC fVertexAttribI4i;
     typedef void (GLAPIENTRY * PFNGLVERTEXATTRIBI4IVPROC) (GLuint index, const GLint* v);
     PFNGLVERTEXATTRIBI4IVPROC fVertexAttribI4iv;
     typedef void (GLAPIENTRY * PFNGLVERTEXATTRIBI4UIPROC) (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w);
     PFNGLVERTEXATTRIBI4UIPROC fVertexAttribI4ui;
     typedef void (GLAPIENTRY * PFNGLVERTEXATTRIBI4UIVPROC) (GLuint index, const GLuint* v);
     PFNGLVERTEXATTRIBI4UIVPROC fVertexAttribI4uiv;
--- a/gfx/layers/composite/TextureHost.cpp
+++ b/gfx/layers/composite/TextureHost.cpp
@@ -402,17 +402,17 @@ BufferTextureHost::~BufferTextureHost()
 
 void
 BufferTextureHost::UpdatedInternal(const nsIntRegion* aRegion)
 {
   ++mUpdateSerial;
   // If the last frame wasn't uploaded yet, and we -don't- have a partial update,
   // we still need to update the full surface.
   if (aRegion && !mNeedsFullUpdate) {
-    mMaybeUpdatedRegion = mMaybeUpdatedRegion.Or(mMaybeUpdatedRegion, *aRegion);
+    mMaybeUpdatedRegion.OrWith(*aRegion);
   } else {
     mNeedsFullUpdate = true;
   }
   if (GetFlags() & TextureFlags::IMMEDIATE_UPLOAD) {
     DebugOnly<bool> result = MaybeUpload(!mNeedsFullUpdate ? &mMaybeUpdatedRegion : nullptr);
     NS_WARN_IF_FALSE(result, "Failed to upload a texture");
   }
 }
--- a/ipc/ipdl/test/cxx/TestDataStructures.cpp
+++ b/ipc/ipdl/test/cxx/TestDataStructures.cpp
@@ -966,17 +966,17 @@ TestDataStructuresChild::Test18()
     RegionArray ra;
     // big enough to hopefully force a realloc to a different chunk of
     // memory on the receiving side, if the workaround isn't working
     // correctly.  But SetCapacity() here because we don't want to
     // crash on the sending side.
     ra.SetCapacity(nelements);
     for (int i = 0; i < nelements; ++i) {
         nsIntRegion r;
-        r = r.Or(nsIntRect(0, 0, 10, 10), nsIntRect(10, 10, 10, 10));
+        r.Or(nsIntRect(0, 0, 10, 10), nsIntRect(10, 10, 10, 10));
         ra.AppendElement(r);
     }
 
     if (!SendTest18(ra))
         fail("sending Test18");
 
     printf("  passed %s\n", __FUNCTION__);
 }
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -6,16 +6,17 @@
 
 #ifndef js_RootingAPI_h
 #define js_RootingAPI_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/GuardObjects.h"
 #include "mozilla/LinkedList.h"
+#include "mozilla/Move.h"
 #include "mozilla/TypeTraits.h"
 
 #include "jspubtd.h"
 
 #include "js/GCAPI.h"
 #include "js/HeapAPI.h"
 #include "js/TypeDecls.h"
 #include "js/Utility.h"
@@ -638,86 +639,90 @@ template <typename T>
 class MOZ_STACK_CLASS Rooted : public js::RootedBase<T>
 {
     static_assert(!mozilla::IsConvertible<T, StaticTraceable*>::value &&
                   !mozilla::IsConvertible<T, DynamicTraceable*>::value,
                   "Rooted takes pointer or Traceable types but not Traceable* type");
 
     /* Note: CX is a subclass of either ContextFriendFields or PerThreadDataFriendFields. */
     template <typename CX>
-    void init(CX* cx) {
+    void registerWithRootLists(CX* cx) {
         js::ThingRootKind kind = js::RootKind<T>::rootKind();
         this->stack = &cx->roots.stackRoots_[kind];
         this->prev = *stack;
         *stack = reinterpret_cast<Rooted<void*>*>(this);
     }
 
   public:
     explicit Rooted(JSContext* cx
                     MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : ptr(js::GCMethods<T>::initial())
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-        init(js::ContextFriendFields::get(cx));
+        registerWithRootLists(js::ContextFriendFields::get(cx));
     }
 
-    Rooted(JSContext* cx, const T& initial
+    template <typename S>
+    Rooted(JSContext* cx, S&& initial
            MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-      : ptr(initial)
+      : ptr(mozilla::Forward<S>(initial))
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-        init(js::ContextFriendFields::get(cx));
+        registerWithRootLists(js::ContextFriendFields::get(cx));
     }
 
     explicit Rooted(js::ContextFriendFields* cx
                     MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : ptr(js::GCMethods<T>::initial())
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-        init(cx);
+        registerWithRootLists(cx);
     }
 
-    Rooted(js::ContextFriendFields* cx, const T& initial
+    template <typename S>
+    Rooted(js::ContextFriendFields* cx, S&& initial
            MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-      : ptr(initial)
+      : ptr(mozilla::Forward<S>(initial))
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-        init(cx);
+        registerWithRootLists(cx);
     }
 
     explicit Rooted(js::PerThreadDataFriendFields* pt
                     MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : ptr(js::GCMethods<T>::initial())
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-        init(pt);
+        registerWithRootLists(pt);
     }
 
-    Rooted(js::PerThreadDataFriendFields* pt, const T& initial
+    template <typename S>
+    Rooted(js::PerThreadDataFriendFields* pt, S&& initial
            MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-      : ptr(initial)
+      : ptr(mozilla::Forward<S>(initial))
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-        init(pt);
+        registerWithRootLists(pt);
     }
 
     explicit Rooted(JSRuntime* rt
                     MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : ptr(js::GCMethods<T>::initial())
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-        init(js::PerThreadDataFriendFields::getMainThread(rt));
+        registerWithRootLists(js::PerThreadDataFriendFields::getMainThread(rt));
     }
 
-    Rooted(JSRuntime* rt, const T& initial
+    template <typename S>
+    Rooted(JSRuntime* rt, S&& initial
            MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-      : ptr(initial)
+      : ptr(mozilla::Forward<S>(initial))
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-        init(js::PerThreadDataFriendFields::getMainThread(rt));
+        registerWithRootLists(js::PerThreadDataFriendFields::getMainThread(rt));
     }
 
     ~Rooted() {
         MOZ_ASSERT(*stack == reinterpret_cast<Rooted<void*>*>(this));
         *stack = prev;
     }
 
     Rooted<T>* previous() { return reinterpret_cast<Rooted<T>*>(prev); }
new file mode 100644
--- /dev/null
+++ b/js/public/TraceableHashTable.h
@@ -0,0 +1,186 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef gc_HashTable_h
+#define gc_HashTable_h
+
+#include "js/HashTable.h"
+#include "js/RootingAPI.h"
+
+namespace js {
+
+template <typename> struct DefaultTracer;
+
+// A TraceableHashMap is a HashMap with an additional trace method that knows
+// how to visit all keys and values in the table. HashMaps that contain GC
+// pointers that must be traced to be kept alive will generally want to use
+// this TraceableHashMap specializeation in lieu of HashMap.
+//
+// Most types of GC pointers as keys and values can be traced with no extra
+// infrastructure.  For structs and non-gc-pointer members, ensure that there
+// is a specialization of DefaultTracer<T> with an appropriate trace method
+// available to handle the custom type.
+//
+// Note that although this HashMap's trace will deal correctly with moved keys,
+// it does not itself know when to barrier or trace keys. To function properly
+// it must either be used with Rooted, or barriered and traced manually.
+template <typename Key,
+          typename Value,
+          typename HashPolicy = DefaultHasher<Key>,
+          typename AllocPolicy = TempAllocPolicy,
+          typename KeyTraceFunc = DefaultTracer<Key>,
+          typename ValueTraceFunc = DefaultTracer<Value>>
+class TraceableHashMap : public HashMap<Key, Value, HashPolicy, AllocPolicy>,
+                         public JS::DynamicTraceable
+{
+    using Base = HashMap<Key, Value, HashPolicy, AllocPolicy>;
+
+  public:
+    explicit TraceableHashMap(AllocPolicy a = AllocPolicy()) : Base(a)  {}
+
+    void trace(JSTracer* trc) override {
+        if (!this->initialized())
+            return;
+        for (typename Base::Enum e(*this); !e.empty(); e.popFront()) {
+            ValueTraceFunc::trace(trc, &e.front().value(), "hashmap value");
+            Key key = e.front().key();
+            KeyTraceFunc::trace(trc, &key, "hashmap key");
+            if (key != e.front().key())
+                e.rekeyFront(key);
+        }
+    }
+
+    // TraceableHashMap is movable
+    TraceableHashMap(TraceableHashMap&& rhs) : Base(mozilla::Forward<TraceableHashMap>(rhs)) {}
+    void operator=(TraceableHashMap&& rhs) {
+        MOZ_ASSERT(this != &rhs, "self-move assignment is prohibited");
+        Base::operator=(mozilla::Forward<TraceableHashMap>(rhs));
+    }
+
+  private:
+    // TraceableHashMap is not copyable or assignable
+    TraceableHashMap(const TraceableHashMap& hm) = delete;
+    TraceableHashMap& operator=(const TraceableHashMap& hm) = delete;
+};
+
+template <typename Outer, typename... MapArgs>
+class TraceableHashMapOperations
+{
+    using Map = TraceableHashMap<MapArgs...>;
+    using Lookup = typename Map::Lookup;
+    using Ptr = typename Map::Ptr;
+    using AddPtr = typename Map::AddPtr;
+    using Range = typename Map::Range;
+    using Enum = typename Map::Enum;
+
+    const Map& map() const { return static_cast<const Outer*>(this)->extract(); }
+
+  public:
+    bool initialized() const                   { return map().initialized(); }
+    Ptr lookup(const Lookup& l) const          { return map().lookup(l); }
+    AddPtr lookupForAdd(const Lookup& l) const { return map().lookupForAdd(l); }
+    Range all() const                          { return map().all(); }
+    bool empty() const                         { return map().empty(); }
+    uint32_t count() const                     { return map().count(); }
+    size_t capacity() const                    { return map().capacity(); }
+    uint32_t generation() const                { return map().generation(); }
+    bool has(const Lookup& l) const            { return map().lookup(l).found(); }
+};
+
+template <typename Outer, typename... MapArgs>
+class MutableTraceableHashMapOperations
+  : public TraceableHashMapOperations<Outer, MapArgs...>
+{
+    using Map = TraceableHashMap<MapArgs...>;
+    using Lookup = typename Map::Lookup;
+    using Ptr = typename Map::Ptr;
+    using AddPtr = typename Map::AddPtr;
+    using Range = typename Map::Range;
+    using Enum = typename Map::Enum;
+
+    Map& map() { return static_cast<Outer*>(this)->extract(); }
+
+  public:
+    bool init(uint32_t len = 16) { return map().init(len); }
+    void clear()                 { map().clear(); }
+    void finish()                { map().finish(); }
+    void remove(Ptr p)           { map().remove(p); }
+
+    template<typename KeyInput, typename ValueInput>
+    bool add(AddPtr& p, KeyInput&& k, ValueInput&& v) {
+        return map().add(p, mozilla::Forward<KeyInput>(k), mozilla::Forward<ValueInput>(v));
+    }
+
+    template<typename KeyInput>
+    bool add(AddPtr& p, KeyInput&& k) {
+        return map().add(p, mozilla::Forward<KeyInput>(k), Map::Value());
+    }
+
+    template<typename KeyInput, typename ValueInput>
+    bool relookupOrAdd(AddPtr& p, KeyInput&& k, ValueInput&& v) {
+        return map().relookupOrAdd(p, k,
+                                   mozilla::Forward<KeyInput>(k),
+                                   mozilla::Forward<ValueInput>(v));
+    }
+
+    template<typename KeyInput, typename ValueInput>
+    bool put(KeyInput&& k, ValueInput&& v) {
+        return map().put(mozilla::Forward<KeyInput>(k), mozilla::Forward<ValueInput>(v));
+    }
+
+    template<typename KeyInput, typename ValueInput>
+    bool putNew(KeyInput&& k, ValueInput&& v) {
+        return map().putNew(mozilla::Forward<KeyInput>(k), mozilla::Forward<ValueInput>(v));
+    }
+};
+
+template <template <typename...> class TraceableHashMap, typename... MapArgs>
+class RootedBase<TraceableHashMap<MapArgs...>>
+  : public MutableTraceableHashMapOperations<JS::Rooted<TraceableHashMap<MapArgs...>>, MapArgs...>
+{
+    using Map = TraceableHashMap<MapArgs...>;
+
+    friend class TraceableHashMapOperations<JS::Rooted<Map>, MapArgs...>;
+    const Map& extract() const { return *static_cast<const JS::Rooted<Map>*>(this)->address(); }
+
+    friend class MutableTraceableHashMapOperations<JS::Rooted<Map>, MapArgs...>;
+    Map& extract() { return *static_cast<JS::Rooted<Map>*>(this)->address(); }
+};
+
+template <template <typename...> class TraceableHashMap, typename... MapArgs>
+class MutableHandleBase<TraceableHashMap<MapArgs...>>
+  : public MutableTraceableHashMapOperations<JS::MutableHandle<TraceableHashMap<MapArgs...>>,
+                                             MapArgs...>
+{
+    using Map = TraceableHashMap<MapArgs...>;
+
+    friend class TraceableHashMapOperations<JS::MutableHandle<Map>, MapArgs...>;
+    const Map& extract() const {
+        return *static_cast<const JS::MutableHandle<Map>*>(this)->address();
+    }
+
+    friend class MutableTraceableHashMapOperations<JS::MutableHandle<Map>, MapArgs...>;
+    Map& extract() { return *static_cast<JS::MutableHandle<Map>*>(this)->address(); }
+};
+
+template <template <typename...> class TraceableHashMap, typename... MapArgs>
+class HandleBase<TraceableHashMap<MapArgs...>>
+  : public TraceableHashMapOperations<JS::Handle<TraceableHashMap<MapArgs...>>, MapArgs...>
+{
+    using Map = TraceableHashMap<MapArgs...>;
+    friend class TraceableHashMapOperations<JS::Handle<Map>, MapArgs...>;
+    const Map& extract() const { return *static_cast<const JS::Handle<Map>*>(this)->address(); }
+};
+
+// The default implementation of DefaultTracer will leave alone POD types.
+template <typename T> struct DefaultTracer {
+    static_assert(mozilla::IsPod<T>::value, "non-pod types must not be ignored");
+    static void trace(JSTracer* trc, T* t, const char* name) {}
+};
+
+} /* namespace js */
+
+#endif /* gc_HashTable_h */
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -4037,44 +4037,32 @@ CType::Finalize(JSFreeOp* fop, JSObject*
   }
   default:
     // Nothing to do here.
     break;
   }
 }
 
 void
-TraceFieldInfoHash(JSTracer* trc, FieldInfoHash* fields)
-{
-  for (FieldInfoHash::Enum e(*fields); !e.empty(); e.popFront()) {
-    JSString* key = e.front().key();
-    JS_CallUnbarrieredStringTracer(trc, &key, "fieldName");
-    if (key != e.front().key())
-      e.rekeyFront(JS_ASSERT_STRING_IS_FLAT(key));
-    JS_CallObjectTracer(trc, &e.front().value().mType, "fieldType");
-  }
-}
-
-void
 CType::Trace(JSTracer* trc, JSObject* obj)
 {
   // Make sure our TypeCode slot is legit. If it's not, bail.
   Value slot = obj->as<NativeObject>().getSlot(SLOT_TYPECODE);
   if (slot.isUndefined())
     return;
 
   // The contents of our slots depends on what kind of type we are.
   switch (TypeCode(slot.toInt32())) {
   case TYPE_struct: {
     slot = obj->as<NativeObject>().getReservedSlot(SLOT_FIELDINFO);
     if (slot.isUndefined())
       return;
 
     FieldInfoHash* fields = static_cast<FieldInfoHash*>(slot.toPrivate());
-    TraceFieldInfoHash(trc, fields);
+    fields->trace(trc);
     break;
   }
   case TYPE_function: {
     // Check if we have a FunctionInfo.
     slot = obj->as<NativeObject>().getReservedSlot(SLOT_FNINFO);
     if (slot.isUndefined())
       return;
 
@@ -5473,41 +5461,16 @@ PostBarrierCallback(JSTracer* trc, JSStr
                     SystemAllocPolicy> UnbarrieredFieldInfoHash;
 
     UnbarrieredFieldInfoHash* table = reinterpret_cast<UnbarrieredFieldInfoHash*>(data);
     JSString* prior = key;
     JS_CallUnbarrieredStringTracer(trc, &key, "CType fieldName");
     table->rekeyIfMoved(JS_ASSERT_STRING_IS_FLAT(prior), JS_ASSERT_STRING_IS_FLAT(key));
 }
 
-// Holds a pointer to a FieldInfoHash while it is being constructed, tracing it
-// on GC and destroying it when it dies unless release() has been called first.
-class FieldInfoHolder : public JS::CustomAutoRooter
-{
-  public:
-    FieldInfoHolder(JSContext* cx, FieldInfoHash* fields)
-      : CustomAutoRooter(cx), fields_(fields) {}
-    ~FieldInfoHolder() {
-      delete fields_;
-    }
-    virtual void trace(JSTracer* trc) override {
-      if (fields_)
-        TraceFieldInfoHash(trc, fields_);
-    }
-    FieldInfoHash* operator->() { return fields_; }
-    operator FieldInfoHash*() { return fields_; }
-    FieldInfoHash* release() {
-      FieldInfoHash* result = fields_;
-      fields_ = nullptr;
-      return result;
-    }
-  private:
-    FieldInfoHash* fields_;
-};
-
 bool
 StructType::DefineInternal(JSContext* cx, JSObject* typeObj_, JSObject* fieldsObj_)
 {
   RootedObject typeObj(cx, typeObj_);
   RootedObject fieldsObj(cx, fieldsObj_);
 
   uint32_t len;
   ASSERT_OK(JS_GetArrayLength(cx, fieldsObj, &len));
@@ -5524,22 +5487,19 @@ StructType::DefineInternal(JSContext* cx
   RootedObject prototype(cx, JS_NewObjectWithGivenProto(cx, &sCDataProtoClass, dataProto));
   if (!prototype)
     return false;
 
   if (!JS_DefineProperty(cx, prototype, "constructor", typeObj,
                          JSPROP_READONLY | JSPROP_PERMANENT))
     return false;
 
-  // Create a FieldInfoHash to stash on the type object, and an array to root
-  // its constituents. (We cannot simply stash the hash in a reserved slot now
-  // to get GC safety for free, since if anything in this function fails we
-  // do not want to mutate 'typeObj'.)
-  FieldInfoHolder fields(cx, cx->new_<FieldInfoHash>());
-  if (!fields || !fields->init(len)) {
+  // Create a FieldInfoHash to stash on the type object.
+  Rooted<FieldInfoHash> fields(cx);
+  if (!fields.init(len)) {
     JS_ReportOutOfMemory(cx);
     return false;
   }
 
   // Process the field types.
   size_t structSize, structAlign;
   if (len != 0) {
     structSize = 0;
@@ -5551,17 +5511,17 @@ StructType::DefineInternal(JSContext* cx
         return false;
 
       RootedObject fieldType(cx, nullptr);
       Rooted<JSFlatString*> name(cx, ExtractStructField(cx, item, &fieldType));
       if (!name)
         return false;
 
       // Make sure each field name is unique
-      FieldInfoHash::AddPtr entryPtr = fields->lookupForAdd(name);
+      FieldInfoHash::AddPtr entryPtr = fields.lookupForAdd(name);
       if (entryPtr) {
         JS_ReportError(cx, "struct fields must have unique names");
         return false;
       }
 
       // Add the field to the StructType's 'prototype' property.
       AutoStableStringChars nameChars(cx);
       if (!nameChars.initTwoByte(cx, name))
@@ -5601,18 +5561,18 @@ StructType::DefineInternal(JSContext* cx
         return false;
       }
 
       // Add field name to the hash
       FieldInfo info;
       info.mType = fieldType;
       info.mIndex = i;
       info.mOffset = fieldOffset;
-      ASSERT_OK(fields->add(entryPtr, name, info));
-      JS_StoreStringPostBarrierCallback(cx, PostBarrierCallback, name, fields);
+      ASSERT_OK(fields.add(entryPtr, name, info));
+      JS_StoreStringPostBarrierCallback(cx, PostBarrierCallback, name, fields.address());
 
       structSize = fieldOffset + fieldSize;
 
       if (fieldAlign > structAlign)
         structAlign = fieldAlign;
     }
 
     // Pad the struct tail according to struct alignment.
@@ -5631,17 +5591,24 @@ StructType::DefineInternal(JSContext* cx
     structSize = 1;
     structAlign = 1;
   }
 
   RootedValue sizeVal(cx);
   if (!SizeTojsval(cx, structSize, &sizeVal))
     return false;
 
-  JS_SetReservedSlot(typeObj, SLOT_FIELDINFO, PrivateValue(fields.release()));
+  // Move the field hash to the heap and store it in the typeObj.
+  FieldInfoHash *heapHash = cx->new_<FieldInfoHash>(mozilla::Move(fields.get()));
+  if (!heapHash) {
+    JS_ReportOutOfMemory(cx);
+    return false;
+  }
+  MOZ_ASSERT(heapHash->initialized());
+  JS_SetReservedSlot(typeObj, SLOT_FIELDINFO, PrivateValue(heapHash));
 
   JS_SetReservedSlot(typeObj, SLOT_SIZE, sizeVal);
   JS_SetReservedSlot(typeObj, SLOT_ALIGN, Int32Value(structAlign));
   //if (!JS_FreezeObject(cx, prototype)0 // XXX fixme - see bug 541212!
   //  return false;
   JS_SetReservedSlot(typeObj, SLOT_PROTO, ObjectValue(*prototype));
   return true;
 }
--- a/js/src/ctypes/CTypes.h
+++ b/js/src/ctypes/CTypes.h
@@ -9,17 +9,17 @@
 #include "mozilla/UniquePtr.h"
 
 #include "ffi.h"
 #include "jsalloc.h"
 #include "jsprf.h"
 #include "prlink.h"
 
 #include "ctypes/typedefs.h"
-#include "js/HashTable.h"
+#include "js/TraceableHashTable.h"
 #include "js/Vector.h"
 #include "vm/String.h"
 
 namespace js {
 namespace ctypes {
 
 /*******************************************************************************
 ** Utility classes
@@ -274,17 +274,18 @@ struct FieldHashPolicy : DefaultHasher<J
 
     if (k->length() != l->length())
       return false;
 
     return EqualChars(k, l);
   }
 };
 
-typedef HashMap<JSFlatString*, FieldInfo, FieldHashPolicy, SystemAllocPolicy> FieldInfoHash;
+typedef TraceableHashMap<JSFlatString*, FieldInfo, FieldHashPolicy, SystemAllocPolicy>
+    FieldInfoHash;
 
 void
 TraceFieldInfoHash(JSTracer* trc, FieldInfoHash* fields);
 
 // Descriptor of ABI, return type, argument types, and variadicity for a
 // FunctionType.
 struct FunctionInfo
 {
@@ -528,11 +529,18 @@ namespace Int64 {
   bool IsInt64(JSObject* obj);
 } // namespace Int64
 
 namespace UInt64 {
   bool IsUInt64(JSObject* obj);
 } // namespace UInt64
 
 } // namespace ctypes
+
+template <> struct DefaultTracer<ctypes::FieldInfo> {
+    static void trace(JSTracer* trc, ctypes::FieldInfo* t, const char* name) {
+        JS_CallObjectTracer(trc, &t->mType, "fieldType");
+    }
+};
+
 } // namespace js
 
 #endif /* ctypes_CTypes_h */
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -35,26 +35,16 @@ using namespace js::gc;
 using mozilla::ArrayEnd;
 
 using JS::AutoGCRooter;
 
 typedef RootedValueMap::Range RootRange;
 typedef RootedValueMap::Entry RootEntry;
 typedef RootedValueMap::Enum RootEnum;
 
-// Note: the following two functions cannot be static as long as we are using
-// GCC 4.4, since it requires template function parameters to have external
-// linkage.
-
-void
-MarkPropertyDescriptorRoot(JSTracer* trc, JSPropertyDescriptor* pd, const char* name)
-{
-    pd->trace(trc);
-}
-
 // We cannot instantiate (even indirectly) the abstract JS::DynamicTraceable.
 // Instead we cast to a ConcreteTraceable, then upcast before calling trace so
 // that we get the implementation defined dynamically in the vtable.
 struct ConcreteTraceable : public JS::DynamicTraceable
 {
     void trace(JSTracer* trc) override {}
 };
 
@@ -90,18 +80,16 @@ MarkExactStackRootsAcrossTypes(T context
         trc, context, "exact-objectgroup");
     MarkExactStackRootList<JSString*>(trc, context, "exact-string");
     MarkExactStackRootList<JS::Symbol*>(trc, context, "exact-symbol");
     MarkExactStackRootList<jit::JitCode*>(trc, context, "exact-jitcode");
     MarkExactStackRootList<JSScript*>(trc, context, "exact-script");
     MarkExactStackRootList<LazyScript*>(trc, context, "exact-lazy-script");
     MarkExactStackRootList<jsid>(trc, context, "exact-id");
     MarkExactStackRootList<Value>(trc, context, "exact-value");
-    MarkExactStackRootList<JSPropertyDescriptor, MarkPropertyDescriptorRoot>(
-        trc, context, "JSPropertyDescriptor");
     MarkExactStackRootList<JS::StaticTraceable,
                            js::DispatchWrapper<JS::StaticTraceable>::TraceWrapped>(
         trc, context, "StaticTraceable");
     MarkExactStackRootList<ConcreteTraceable, MarkDynamicTraceable>(
         trc, context, "DynamicTraceable");
 }
 
 static void
@@ -198,28 +186,16 @@ AutoGCRooter::trace(JSTracer* trc)
       }
 
       case SCRIPTVECTOR: {
         AutoScriptVector::VectorImpl& vector = static_cast<AutoScriptVector*>(this)->vector;
         TraceRootRange(trc, vector.length(), vector.begin(), "js::AutoScriptVector.vector");
         return;
       }
 
-      case OBJU32HASHMAP: {
-        AutoObjectUnsigned32HashMap* self = static_cast<AutoObjectUnsigned32HashMap*>(this);
-        AutoObjectUnsigned32HashMap::HashMapImpl& map = self->map;
-        for (AutoObjectUnsigned32HashMap::Enum e(map); !e.empty(); e.popFront()) {
-            JSObject* key = e.front().key();
-            TraceRoot(trc, &key, "AutoObjectUnsignedHashMap key");
-            if (key != e.front().key())
-                e.rekeyFront(key);
-        }
-        return;
-      }
-
       case HASHABLEVALUE: {
         AutoHashableValueRooter* rooter = static_cast<AutoHashableValueRooter*>(this);
         rooter->trace(trc);
         return;
       }
 
       case IONMASM: {
         static_cast<js::jit::MacroAssembler::AutoRooter*>(this)->masm()->trace(trc);
--- a/js/src/gc/Tracer.h
+++ b/js/src/gc/Tracer.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef js_Tracer_h
 #define js_Tracer_h
 
 #include "jsfriendapi.h"
 
 #include "gc/Barrier.h"
+#include "js/TraceableHashTable.h"
 
 namespace js {
 
 // Internal Tracing API
 //
 // Tracing is an abstract visitation of each edge in a JS heap graph.[1] The
 // most common (and performance sensitive) use of this infrastructure is for GC
 // "marking" as part of the mark-and-sweep collector; however, this
@@ -125,11 +126,27 @@ namespace gc {
 // deep or infinite recursion.
 void
 TraceCycleCollectorChildren(JS::CallbackTracer* trc, Shape* shape);
 void
 TraceCycleCollectorChildren(JS::CallbackTracer* trc, ObjectGroup* group);
 
 } // namespace gc
 
+template <typename T>
+struct DefaultTracer<T*>
+{
+    static void trace(JSTracer* trc, T** t, const char* name) {
+        TraceManuallyBarrieredEdge(trc, t, name);
+    }
+};
+
+template <typename T>
+struct DefaultTracer<RelocatablePtr<T*>>
+{
+    static void trace(JSTracer* trc, RelocatablePtr<T*> t, const char* name) {
+        TraceEdge(trc, t, name);
+    }
+};
+
 } // namespace js
 
 #endif /* js_Tracer_h */
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -865,17 +865,17 @@ BaselineCompiler::emitTraceLoggerExit()
 
 void
 BaselineCompiler::emitProfilerEnterFrame()
 {
     // Store stack position to lastProfilingFrame variable, guarded by a toggled jump.
     // Starts off initially disabled.
     Label noInstrument;
     CodeOffsetLabel toggleOffset = masm.toggledJump(&noInstrument);
-    masm.profilerEnterFrame(BaselineStackReg, R0.scratchReg());
+    masm.profilerEnterFrame(masm.getStackPointer(), R0.scratchReg());
     masm.bind(&noInstrument);
 
     // Store the start offset in the appropriate location.
     MOZ_ASSERT(profilerEnterFrameToggleOffset_.offset() == 0);
     profilerEnterFrameToggleOffset_ = toggleOffset;
 }
 
 void
@@ -3775,17 +3775,17 @@ BaselineCompiler::emit_JSOP_RESUME()
     // If profiler instrumentation is on, update lastProfilingFrame on
     // current JitActivation
     {
         Register scratchReg = scratch2;
         Label skip;
         AbsoluteAddress addressOfEnabled(cx->runtime()->spsProfiler.addressOfEnabled());
         masm.branch32(Assembler::Equal, addressOfEnabled, Imm32(0), &skip);
         masm.loadPtr(AbsoluteAddress(cx->runtime()->addressOfProfilingActivation()), scratchReg);
-        masm.storePtr(BaselineStackReg,
+        masm.storePtr(masm.getStackPointer(),
                       Address(scratchReg, JitActivation::offsetOfLastProfilingFrame()));
         masm.bind(&skip);
     }
 
     // Construct BaselineFrame.
     masm.push(BaselineFrameReg);
     masm.moveStackPtrTo(BaselineFrameReg);
     masm.subFromStackPtr(Imm32(BaselineFrame::Size()));
--- a/js/src/jit/BaselineFrameInfo.h
+++ b/js/src/jit/BaselineFrameInfo.h
@@ -223,30 +223,30 @@ class FrameInfo
         return const_cast<StackValue*>(&stack[spIndex + index]);
     }
 
     inline void pop(StackAdjustment adjust = AdjustStack) {
         spIndex--;
         StackValue* popped = &stack[spIndex];
 
         if (adjust == AdjustStack && popped->kind() == StackValue::Stack)
-            masm.addPtr(Imm32(sizeof(Value)), BaselineStackReg);
+            masm.addToStackPtr(Imm32(sizeof(Value)));
 
         // Assert when anything uses this value.
         popped->reset();
     }
     inline void popn(uint32_t n, StackAdjustment adjust = AdjustStack) {
         uint32_t poppedStack = 0;
         for (uint32_t i = 0; i < n; i++) {
             if (peek(-1)->kind() == StackValue::Stack)
                 poppedStack++;
             pop(DontAdjustStack);
         }
         if (adjust == AdjustStack && poppedStack > 0)
-            masm.addPtr(Imm32(sizeof(Value) * poppedStack), BaselineStackReg);
+            masm.addToStackPtr(Imm32(sizeof(Value) * poppedStack));
     }
     inline void push(const Value& val) {
         StackValue* sv = rawPush();
         sv->setConstant(val);
     }
     inline void push(const ValueOperand& val, JSValueType knownType=JSVAL_TYPE_UNKNOWN) {
         StackValue* sv = rawPush();
         sv->setRegister(val, knownType);
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -3951,17 +3951,17 @@ ICGetElemNativeCompiler::generateStubCod
 #endif
 
     } else if (acctype_ == ICGetElemNativeStub::UnboxedProperty) {
         masm.load32(Address(ICStubReg, ICGetElemNativeSlotStub::offsetOfOffset()),
                     scratchReg);
         masm.loadUnboxedProperty(BaseIndex(objReg, scratchReg, TimesOne), unboxedType_,
                                  TypedOrValueRegister(R0));
         if (popR1)
-            masm.addPtr(ImmWord(sizeof(size_t)), BaselineStackReg);
+            masm.addToStackPtr(ImmWord(sizeof(size_t)));
     } else {
         MOZ_ASSERT(acctype_ == ICGetElemNativeStub::NativeGetter ||
                    acctype_ == ICGetElemNativeStub::ScriptedGetter);
         MOZ_ASSERT(kind == ICStub::GetElem_NativePrototypeCallNative ||
                    kind == ICStub::GetElem_NativePrototypeCallScripted);
 
         if (acctype_ == ICGetElemNativeStub::NativeGetter) {
             // If calling a native getter, there is no chance of failure now.
@@ -5637,17 +5637,17 @@ ICInNativeCompiler::generateStubCode(Mac
         // Shape guard holder. Use R0 scrachReg since on x86 there're not enough registers.
         Register holderReg = R0.scratchReg();
         masm.push(R0.scratchReg());
         masm.loadPtr(Address(ICStubReg, ICIn_NativePrototype::offsetOfHolder()),
                      holderReg);
         masm.loadPtr(Address(ICStubReg, ICIn_NativePrototype::offsetOfHolderShape()),
                      scratch);
         masm.branchTestObjShape(Assembler::NotEqual, holderReg, scratch, &failurePopR0Scratch);
-        masm.addPtr(Imm32(sizeof(size_t)), StackPointer);
+        masm.addToStackPtr(Imm32(sizeof(size_t)));
     }
 
     masm.moveValue(BooleanValue(true), R0);
 
     EmitReturnFromIC(masm);
 
     // Failure case - jump to next stub
     masm.bind(&failurePopR0Scratch);
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -3608,17 +3608,17 @@ CodeGenerator::generateArgumentsChecks(b
             // changing object groups.
             for (uint32_t i = info.startArgSlot(); i < info.endArgSlot(); i++) {
                 MParameter* param = rp->getOperand(i)->toParameter();
                 const TemporaryTypeSet* types = param->resultTypeSet();
                 if (!types || types->unknown())
                     continue;
 
                 Label skip;
-                Address addr(StackPointer, ArgToStackOffset((i - info.startArgSlot()) * sizeof(Value)));
+                Address addr(masm.getStackPointer(), ArgToStackOffset((i - info.startArgSlot()) * sizeof(Value)));
                 masm.branchTestObject(Assembler::NotEqual, addr, &skip);
                 Register obj = masm.extractObject(addr, temp);
                 masm.guardTypeSetMightBeIncomplete(types, obj, temp, &success);
                 masm.bind(&skip);
             }
 
             masm.assumeUnreachable("Argument check fail.");
             masm.bind(&success);
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -1628,17 +1628,17 @@ MacroAssembler::generateBailoutTail(Regi
         callWithABI(JS_FUNC_TO_DATA_PTR(void*, BailoutReportOverRecursed));
         jump(exceptionLabel());
     }
 
     bind(&baseline);
     {
         // Prepare a register set for use in this case.
         AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
-        MOZ_ASSERT(!regs.has(BaselineStackReg));
+        MOZ_ASSERT(!regs.has(getStackPointer()));
         regs.take(bailoutInfo);
 
         // Reset SP to the point where clobbering starts.
         loadStackPtr(Address(bailoutInfo, offsetof(BaselineBailoutInfo, incomingStack)));
 
         Register copyCur = regs.takeAny();
         Register copyEnd = regs.takeAny();
         Register temp = regs.takeAny();
@@ -1649,17 +1649,17 @@ MacroAssembler::generateBailoutTail(Regi
         {
             Label copyLoop;
             Label endOfCopy;
             bind(&copyLoop);
             branchPtr(Assembler::BelowOrEqual, copyCur, copyEnd, &endOfCopy);
             subPtr(Imm32(4), copyCur);
             subFromStackPtr(Imm32(4));
             load32(Address(copyCur, 0), temp);
-            store32(temp, Address(BaselineStackReg, 0));
+            store32(temp, Address(getStackPointer(), 0));
             jump(&copyLoop);
             bind(&endOfCopy);
         }
 
         // Enter exit frame for the FinishBailoutToBaseline call.
         loadPtr(Address(bailoutInfo, offsetof(BaselineBailoutInfo, resumeFramePtr)), temp);
         load32(Address(temp, BaselineFrame::reverseOffsetOfFrameSize()), temp);
         makeFrameDescriptor(temp, JitFrame_BaselineJS);
@@ -2690,17 +2690,17 @@ MacroAssembler::Push(JSValueType type, R
 {
     pushValue(type, reg);
     framePushed_ += sizeof(Value);
 }
 
 void
 MacroAssembler::PushValue(const Address& addr)
 {
-    MOZ_ASSERT(addr.base != StackPointer);
+    MOZ_ASSERT(addr.base != getStackPointer());
     pushValue(addr);
     framePushed_ += sizeof(Value);
 }
 
 void
 MacroAssembler::PushEmptyRooted(VMFunction::RootType rootType)
 {
     switch (rootType) {
--- a/js/src/jit/SharedIC.h
+++ b/js/src/jit/SharedIC.h
@@ -1007,25 +1007,30 @@ class ICStubCompiler
 
     // Some stubs need to emit SPS profiler updates.  This emits the guarding
     // jitcode for those stubs.  If profiling is not enabled, jumps to the
     // given label.
     void guardProfilingEnabled(MacroAssembler& masm, Register scratch, Label* skip);
 
     inline AllocatableGeneralRegisterSet availableGeneralRegs(size_t numInputs) const {
         AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
+#if defined(JS_CODEGEN_ARM)
         MOZ_ASSERT(!regs.has(BaselineStackReg));
-#if defined(JS_CODEGEN_ARM)
         MOZ_ASSERT(!regs.has(ICTailCallReg));
         regs.take(BaselineSecondScratchReg);
 #elif defined(JS_CODEGEN_MIPS)
+        MOZ_ASSERT(!regs.has(BaselineStackReg));
         MOZ_ASSERT(!regs.has(ICTailCallReg));
         MOZ_ASSERT(!regs.has(BaselineSecondScratchReg));
 #elif defined(JS_CODEGEN_ARM64)
+        MOZ_ASSERT(!regs.has(PseudoStackPointer));
+        MOZ_ASSERT(!regs.has(RealStackPointer));
         MOZ_ASSERT(!regs.has(ICTailCallReg));
+#else
+        MOZ_ASSERT(!regs.has(BaselineStackReg));
 #endif
         regs.take(BaselineFrameReg);
         regs.take(ICStubReg);
 #ifdef JS_CODEGEN_X64
         regs.take(ExtractTemp0);
         regs.take(ExtractTemp1);
 #endif
 
--- a/js/src/jit/arm64/Assembler-arm64.h
+++ b/js/src/jit/arm64/Assembler-arm64.h
@@ -68,18 +68,16 @@ static constexpr FloatRegister ReturnFlo
 static constexpr FloatRegister ScratchFloatReg = { FloatRegisters::d31 };
 
 static constexpr FloatRegister ReturnSimdReg = InvalidFloatReg;
 static constexpr FloatRegister ScratchSimdReg = InvalidFloatReg;
 
 // StackPointer is intentionally undefined on ARM64 to prevent misuse:
 //  using sp as a base register is only valid if sp % 16 == 0.
 static constexpr Register RealStackPointer = { Registers::sp };
-// TODO: We're not quite there yet.
-static constexpr Register StackPointer = { Registers::sp };
 
 static constexpr Register PseudoStackPointer = { Registers::x28 };
 static constexpr ARMRegister PseudoStackPointer64 = { Registers::x28, 64 };
 static constexpr ARMRegister PseudoStackPointer32 = { Registers::x28, 32 };
 
 // StackPointer for use by irregexp.
 static constexpr Register RegExpStackPointer = PseudoStackPointer;
 
--- a/js/src/jit/arm64/SharedICRegisters-arm64.h
+++ b/js/src/jit/arm64/SharedICRegisters-arm64.h
@@ -11,19 +11,18 @@
 
 namespace js {
 namespace jit {
 
 // Must be a callee-saved register for preservation around generateEnterJIT().
 static constexpr Register BaselineFrameReg = r23;
 static constexpr ARMRegister BaselineFrameReg64 = { BaselineFrameReg, 64 };
 
-// The BaselineStackReg cannot be sp, because that register is treated
-// as xzr/wzr during load/store operations.
-static constexpr Register BaselineStackReg = PseudoStackPointer;
+// BaselineStackReg is intentionally undefined on ARM64.
+// Refer to the comment next to the definition of RealStackPointer.
 
 // ValueOperands R0, R1, and R2.
 // R0 == JSReturnReg, and R2 uses registers not preserved across calls.
 // R1 value should be preserved across calls.
 static constexpr Register R0_ = r2;
 static constexpr Register R1_ = r19;
 static constexpr Register R2_ = r0;
 
--- a/js/src/jsapi-tests/testGCExactRooting.cpp
+++ b/js/src/jsapi-tests/testGCExactRooting.cpp
@@ -1,15 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * vim: set ts=8 sts=4 et sw=4 tw=99:
 */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "js/RootingAPI.h"
+#include "js/TraceableHashTable.h"
+
 #include "jsapi-tests/tests.h"
 
 BEGIN_TEST(testGCExactRooting)
 {
     JS::RootedObject rootCx(cx, JS_NewPlainObject(cx));
     JS::RootedObject rootRt(cx->runtime(), JS_NewPlainObject(cx));
 
     JS_GC(cx->runtime());
@@ -147,8 +150,83 @@ BEGIN_TEST(testGCRootedDynamicStructInte
     JS_GC(cx->runtime());
 
     JS::RootedObject obj(cx, container.obj());
     JS::RootedValue val(cx, StringValue(container.str()));
     CHECK(JS_SetProperty(cx, obj, "foo", val));
     return true;
 }
 END_TEST(testGCRootedDynamicStructInternalStackStorageAugmented)
+
+using MyHashMap = js::TraceableHashMap<js::Shape*, JSObject*>;
+
+BEGIN_TEST(testGCRootedHashMap)
+{
+    JS::Rooted<MyHashMap> map(cx, MyHashMap(cx));
+    CHECK(map.init(15));
+    CHECK(map.initialized());
+
+    for (size_t i = 0; i < 10; ++i) {
+        RootedObject obj(cx, JS_NewObject(cx, nullptr));
+        RootedValue val(cx, UndefinedValue());
+        char buffer[2];
+        buffer[0] = 'a' + i;
+        buffer[1] = '\0';
+        CHECK(JS_SetProperty(cx, obj, buffer, val));
+        CHECK(map.putNew(obj->as<NativeObject>().lastProperty(), obj));
+    }
+
+    JS_GC(rt);
+    JS_GC(rt);
+
+    for (auto r = map.all(); !r.empty(); r.popFront()) {
+        RootedObject obj(cx, r.front().value());
+        CHECK(obj->as<NativeObject>().lastProperty() == r.front().key());
+    }
+
+    return true;
+}
+END_TEST(testGCRootedHashMap)
+
+static bool
+FillMyHashMap(JSContext* cx, MutableHandle<MyHashMap> map)
+{
+    for (size_t i = 0; i < 10; ++i) {
+        RootedObject obj(cx, JS_NewObject(cx, nullptr));
+        RootedValue val(cx, UndefinedValue());
+        char buffer[2];
+        buffer[0] = 'a' + i;
+        buffer[1] = '\0';
+        if (!JS_SetProperty(cx, obj, buffer, val))
+            return false;
+        if (!map.putNew(obj->as<NativeObject>().lastProperty(), obj))
+            return false;
+    }
+    return true;
+}
+
+static bool
+CheckMyHashMap(JSContext* cx, Handle<MyHashMap> map)
+{
+    for (auto r = map.all(); !r.empty(); r.popFront()) {
+        RootedObject obj(cx, r.front().value());
+        if (obj->as<NativeObject>().lastProperty() != r.front().key())
+            return false;
+    }
+    return true;
+}
+
+BEGIN_TEST(testGCHandleHashMap)
+{
+    JS::Rooted<MyHashMap> map(cx, MyHashMap(cx));
+    CHECK(map.init(15));
+    CHECK(map.initialized());
+
+    CHECK(FillMyHashMap(cx, &map));
+
+    JS_GC(rt);
+    JS_GC(rt);
+
+    CHECK(CheckMyHashMap(cx, map));
+
+    return true;
+}
+END_TEST(testGCHandleHashMap)
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2502,30 +2502,29 @@ extern JS_PUBLIC_API(bool)
 JS_PreventExtensions(JSContext* cx, JS::HandleObject obj, JS::ObjectOpResult& result);
 
 extern JS_PUBLIC_API(JSObject*)
 JS_New(JSContext* cx, JS::HandleObject ctor, const JS::HandleValueArray& args);
 
 
 /*** Property descriptors ************************************************************************/
 
-struct JSPropertyDescriptor {
+struct JSPropertyDescriptor : public JS::StaticTraceable {
     JSObject* obj;
     unsigned attrs;
     JSGetterOp getter;
     JSSetterOp setter;
     JS::Value value;
 
     JSPropertyDescriptor()
       : obj(nullptr), attrs(0), getter(nullptr), setter(nullptr), value(JS::UndefinedValue())
     {}
 
+    static void trace(JSPropertyDescriptor* self, JSTracer* trc) { self->trace(trc); }
     void trace(JSTracer* trc);
-
-    static js::ThingRootKind rootKind() { return js::THING_ROOT_PROPERTY_DESCRIPTOR; }
 };
 
 namespace JS {
 
 template <typename Outer>
 class PropertyDescriptorOperations
 {
     const JSPropertyDescriptor* desc() const { return static_cast<const Outer*>(this)->extract(); }
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -661,29 +661,16 @@ CheckForInterrupt(JSContext* cx)
 }
 
 /************************************************************************/
 
 typedef JS::AutoVectorRooter<JSString*> AutoStringVector;
 typedef JS::AutoVectorRooter<PropertyName*> AutoPropertyNameVector;
 typedef JS::AutoVectorRooter<Shape*> AutoShapeVector;
 
-class AutoObjectUnsigned32HashMap : public AutoHashMapRooter<JSObject*, uint32_t>
-{
-  public:
-    explicit AutoObjectUnsigned32HashMap(JSContext* cx
-                                         MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-      : AutoHashMapRooter<JSObject*, uint32_t>(cx, OBJU32HASHMAP)
-    {
-        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-    }
-
-    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
-};
-
 /* AutoArrayRooter roots an external array of Values. */
 class AutoArrayRooter : private JS::AutoGCRooter
 {
   public:
     AutoArrayRooter(JSContext* cx, size_t len, Value* vec
                     MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : JS::AutoGCRooter(cx, len), array(vec)
     {
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -267,17 +267,16 @@ class JS_PUBLIC_API(AutoGCRooter)
         OBJVECTOR =   -14, /* js::AutoObjectVector */
         STRINGVECTOR =-15, /* js::AutoStringVector */
         SCRIPTVECTOR =-16, /* js::AutoScriptVector */
         NAMEVECTOR =  -17, /* js::AutoNameVector */
         HASHABLEVALUE=-18, /* js::HashableValue */
         IONMASM =     -19, /* js::jit::MacroAssembler */
         WRAPVECTOR =  -20, /* js::AutoWrapperVector */
         WRAPPER =     -21, /* js::AutoWrapperRooter */
-        OBJU32HASHMAP=-23, /* js::AutoObjectUnsigned32HashMap */
         JSONPARSER =  -25, /* js::JSONParser */
         CUSTOM =      -26  /* js::CustomAutoRooter */
     };
 
     static ptrdiff_t GetTag(const Value& value) { return VALVECTOR; }
     static ptrdiff_t GetTag(const jsid& id) { return IDVECTOR; }
     static ptrdiff_t GetTag(JSObject* obj) { return OBJVECTOR; }
     static ptrdiff_t GetTag(JSScript* script) { return SCRIPTVECTOR; }
@@ -320,18 +319,16 @@ enum ThingRootKind
     THING_ROOT_OBJECT_GROUP,
     THING_ROOT_STRING,
     THING_ROOT_SYMBOL,
     THING_ROOT_JIT_CODE,
     THING_ROOT_SCRIPT,
     THING_ROOT_LAZY_SCRIPT,
     THING_ROOT_ID,
     THING_ROOT_VALUE,
-    THING_ROOT_PROPERTY_DESCRIPTOR,
-    THING_ROOT_PROP_DESC,
     THING_ROOT_STATIC_TRACEABLE,
     THING_ROOT_DYNAMIC_TRACEABLE,
     THING_ROOT_LIMIT
 };
 
 template <typename T>
 struct RootKind;
 
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -117,16 +117,17 @@ EXPORTS.js += [
     '../public/Principals.h',
     '../public/ProfilingFrameIterator.h',
     '../public/ProfilingStack.h',
     '../public/Proxy.h',
     '../public/RequiredDefines.h',
     '../public/RootingAPI.h',
     '../public/SliceBudget.h',
     '../public/StructuredClone.h',
+    '../public/TraceableHashTable.h',
     '../public/TraceKind.h',
     '../public/TracingAPI.h',
     '../public/TrackedOptimizationInfo.h',
     '../public/TypeDecls.h',
     '../public/UbiNode.h',
     '../public/UbiNodeTraverse.h',
     '../public/Utility.h',
     '../public/Value.h',
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -1827,25 +1827,18 @@ js::IsDebugScopeSlow(ProxyObject* proxy)
     return proxy->handler() == &DebugScopeProxy::singleton;
 }
 
 /*****************************************************************************/
 
 /* static */ MOZ_ALWAYS_INLINE void
 DebugScopes::liveScopesPostWriteBarrier(JSRuntime* rt, LiveScopeMap* map, ScopeObject* key)
 {
-    // As above.  Otherwise, barriers could fire during GC when moving the
-    // value.
-    typedef HashMap<ScopeObject*,
-                    MissingScopeKey,
-                    DefaultHasher<ScopeObject*>,
-                    RuntimeAllocPolicy> UnbarrieredLiveScopeMap;
-    typedef gc::HashKeyRef<UnbarrieredLiveScopeMap, ScopeObject*> Ref;
     if (key && IsInsideNursery(key))
-        rt->gc.storeBuffer.putGeneric(Ref(reinterpret_cast<UnbarrieredLiveScopeMap*>(map), key));
+        rt->gc.storeBuffer.putGeneric(gc::HashKeyRef<LiveScopeMap, ScopeObject*>(map, key));
 }
 
 DebugScopes::DebugScopes(JSContext* cx)
  : proxiedScopes(cx),
    missingScopes(cx->runtime()),
    liveScopes(cx->runtime())
 {}
 
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -1516,28 +1516,33 @@ jit::JitActivation::clearRematerializedF
 jit::RematerializedFrame*
 jit::JitActivation::getRematerializedFrame(JSContext* cx, const JitFrameIterator& iter, size_t inlineDepth)
 {
     MOZ_ASSERT(iter.activation() == this);
     MOZ_ASSERT(iter.isIonScripted());
 
     if (!rematerializedFrames_) {
         rematerializedFrames_ = cx->new_<RematerializedFrameTable>(cx);
-        if (!rematerializedFrames_ || !rematerializedFrames_->init()) {
+        if (!rematerializedFrames_)
+            return nullptr;
+        if (!rematerializedFrames_->init()) {
             rematerializedFrames_ = nullptr;
+            ReportOutOfMemory(cx);
             return nullptr;
         }
     }
 
     uint8_t* top = iter.fp();
     RematerializedFrameTable::AddPtr p = rematerializedFrames_->lookupForAdd(top);
     if (!p) {
         RematerializedFrameVector empty(cx);
-        if (!rematerializedFrames_->add(p, top, Move(empty)))
+        if (!rematerializedFrames_->add(p, top, Move(empty))) {
+            ReportOutOfMemory(cx);
             return nullptr;
+        }
 
         // The unit of rematerialization is an uninlined frame and its inlined
         // frames. Since inlined frames do not exist outside of snapshots, it
         // is impossible to synchronize their rematerialized copies to
         // preserve identity. Therefore, we always rematerialize an uninlined
         // frame and all its inlined frames at once.
         InlineFrameIterator inlineIter(cx, &iter);
         MaybeReadFallback recover(cx, this, &iter);
--- a/js/src/vm/StructuredClone.cpp
+++ b/js/src/vm/StructuredClone.cpp
@@ -36,16 +36,17 @@
 
 #include "jsapi.h"
 #include "jscntxt.h"
 #include "jsdate.h"
 #include "jswrapper.h"
 
 #include "builtin/MapObject.h"
 #include "js/Date.h"
+#include "js/TraceableHashTable.h"
 #include "vm/SharedArrayObject.h"
 #include "vm/TypedArrayObject.h"
 #include "vm/WrapperObject.h"
 
 #include "jscntxtinlines.h"
 #include "jsobjinlines.h"
 
 using namespace js;
@@ -262,32 +263,36 @@ struct JSStructuredCloneReader {
 struct JSStructuredCloneWriter {
   public:
     explicit JSStructuredCloneWriter(JSContext* cx,
                                      const JSStructuredCloneCallbacks* cb,
                                      void* cbClosure,
                                      Value tVal)
         : out(cx), objs(out.context()),
           counts(out.context()), entries(out.context()),
-          memory(out.context()), callbacks(cb), closure(cbClosure),
-          transferable(out.context(), tVal), transferableObjects(out.context()) { }
+          memory(out.context(), CloneMemory(out.context())), callbacks(cb),
+          closure(cbClosure), transferable(out.context(), tVal), transferableObjects(out.context())
+    {}
 
     ~JSStructuredCloneWriter();
 
     bool init() { return memory.init() && parseTransferable() && writeTransferMap(); }
 
     bool write(HandleValue v);
 
     SCOutput& output() { return out; }
 
     bool extractBuffer(uint64_t** datap, size_t* sizep) {
         return out.extractBuffer(datap, sizep);
     }
 
   private:
+    JSStructuredCloneWriter() = delete;
+    JSStructuredCloneWriter(const JSStructuredCloneWriter&) = delete;
+
     JSContext* context() { return out.context(); }
 
     bool writeTransferMap();
 
     bool writeString(uint32_t tag, JSString* str);
     bool writeArrayBuffer(HandleObject obj);
     bool writeTypedArray(HandleObject obj);
     bool writeSharedArrayBuffer(HandleObject obj);
@@ -319,18 +324,18 @@ struct JSStructuredCloneWriter {
     // For JSObject: Propery IDs as value
     // For Map: Key followed by value.
     // For Set: Key
     AutoValueVector entries;
 
     // The "memory" list described in the HTML5 internal structured cloning algorithm.
     // memory is a superset of objs; items are never removed from Memory
     // until a serialization operation is finished
-    typedef AutoObjectUnsigned32HashMap CloneMemory;
-    CloneMemory memory;
+    using CloneMemory = TraceableHashMap<JSObject*, uint32_t>;
+    Rooted<CloneMemory> memory;
 
     // The user defined callbacks that will be used for cloning.
     const JSStructuredCloneCallbacks* callbacks;
 
     // Any value passed to JS_WriteStructuredClone.
     void* closure;
 
     // List of transferable objects
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -3490,18 +3490,18 @@ nsDisplayBoxShadowOuter::ComputeInvalida
     nscoord dontCare[8];
     bool hasBorderRadius = mFrame->GetBorderRadii(dontCare);
     if (hasBorderRadius) {
       // If we have rounded corners then we need to invalidate the frame area
       // too since we paint into it.
       oldShadow = geometry->mBounds;
       newShadow = GetBounds(aBuilder, &snap);
     } else {
-      oldShadow = oldShadow.Sub(geometry->mBounds, geometry->mBorderRect);
-      newShadow = newShadow.Sub(GetBounds(aBuilder, &snap), GetBorderRect());
+      oldShadow.Sub(geometry->mBounds, geometry->mBorderRect);
+      newShadow.Sub(GetBounds(aBuilder, &snap), GetBorderRect());
     }
     aInvalidRegion->Or(oldShadow, newShadow);
   }
 }
 
 
 void
 nsDisplayBoxShadowInner::Paint(nsDisplayListBuilder* aBuilder,
--- a/layout/style/test/mochitest.ini
+++ b/layout/style/test/mochitest.ini
@@ -32,25 +32,30 @@ support-files =
   visited-pref-iframe.html
   xbl_bindings.xml
 generated-files = css_properties.js
 
 [test_acid3_test46.html]
 [test_all_shorthand.html]
 [test_animations.html]
 skip-if = toolkit == 'android'
+[test_animations_async_tests.html]
+support-files = ../../reftests/fonts/Ahem.ttf file_animations_async_tests.html
 [test_animations_omta.html]
 [test_animations_omta_start.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # bug 1041017
 [test_animations_pausing.html]
 support-files = file_animations_pausing.html
 [test_animations_playbackrate.html]
 support-files = file_animations_playbackrate.html
 [test_any_dynamic.html]
 [test_at_rule_parse_serialize.html]
+[test_attribute_selector_eof_behavior.html]
+[test_background_blend_mode.html]
+[test_box_size_keywords.html]
 [test_bug73586.html]
 [test_bug74880.html]
 [test_bug98997.html]
 [test_bug160403.html]
 [test_bug200089.html]
 [test_bug221428.html]
 [test_bug229915.html]
 [test_bug302186.html]
@@ -85,30 +90,35 @@ skip-if = android_version == '18' # bug 
 support-files = bug453896_iframe.html
 [test_bug470769.html]
 [test_bug499655.html]
 [test_bug499655.xhtml]
 [test_bug511909.html]
 [test_bug517224.html]
 support-files = bug517224.sjs
 [test_bug524175.html]
+[test_bug525952.html]
 [test_bug534804.html]
 [test_bug573255.html]
 [test_bug580685.html]
 [test_bug621351.html]
 [test_bug635286.html]
 [test_bug652486.html]
 [test_bug657143.html]
 [test_bug664955.html]
 [test_bug667520.html]
 [test_bug645998.html]
 support-files = file_bug645998-1.css file_bug645998-2.css
 [test_bug716226.html]
+[test_bug732153.html]
+[test_bug732209.html]
+support-files = bug732209-css.sjs
 [test_bug765590.html]
 [test_bug771043.html]
+[test_bug795520.html]
 [test_bug798567.html]
 [test_bug798843_pref.html]
 [test_bug829816.html]
 [test_bug874919.html]
 support-files = file_bug829816.css
 [test_bug887741_at-rules_in_declaration_lists.html]
 [test_bug892929.html]
 [test_bug1055933.html]
@@ -120,94 +130,106 @@ support-files = file_bug1089417_iframe.h
 [test_ch_ex_no_infloops.html]
 [test_compute_data_with_start_struct.html]
 skip-if = toolkit == 'android'
 [test_computed_style.html]
 [test_computed_style_no_pseudo.html]
 [test_computed_style_prefs.html]
 [test_condition_text.html]
 [test_condition_text_assignment.html]
-[test_default_computed_style.html]
+[test_counter_descriptor_storage.html]
+[test_counter_style.html]
 [test_css_cross_domain.html]
 skip-if = toolkit == 'android' #bug 536603
 [test_css_eof_handling.html]
 [test_css_escape_api.html]
 [test_css_function_mismatched_parenthesis.html]
+[test_css_loader_crossorigin_data_url.html]
 [test_css_supports.html]
 [test_css_supports_variables.html]
 [test_default_bidi_css.html]
+[test_default_computed_style.html]
 [test_descriptor_storage.html]
 [test_descriptor_syntax_errors.html]
 [test_dont_use_document_colors.html]
 [test_dynamic_change_causing_reflow.html]
 [test_exposed_prop_accessors.html]
 [test_extra_inherit_initial.html]
+skip-if = (toolkit == 'gonk' && debug) # Bug 1186224
 [test_flexbox_align_self_auto.html]
 [test_flexbox_child_display_values.xhtml]
 [test_flexbox_flex_grow_and_shrink.html]
 [test_flexbox_flex_shorthand.html]
 [test_flexbox_layout.html]
 support-files = flexbox_layout_testcases.js
 [test_flexbox_min_size_auto.html]
 [test_flexbox_order.html]
 [test_flexbox_order_table.html]
 [test_flexbox_reflow_counts.html]
 [test_font_face_parser.html]
 [test_font_family_parsing.html]
 [test_font_feature_values_parsing.html]
 [test_font_loading_api.html]
 support-files = BitPattern.woff
 [test_garbage_at_end_of_declarations.html]
+[test_grid_container_shorthands.html]
 [test_grid_item_shorthands.html]
-[test_grid_container_shorthands.html]
 [test_grid_shorthand_serialization.html]
 [test_group_insertRule.html]
 [test_hover_quirk.html]
 [test_html_attribute_computed_values.html]
 [test_ident_escaping.html]
 [test_inherit_computation.html]
 skip-if = toolkit == 'android'
 [test_inherit_storage.html]
 [test_initial_computation.html]
 skip-if = toolkit == 'android'
 [test_initial_storage.html]
 [test_keyframes_rules.html]
+[test_load_events_on_stylesheets.html]
+[test_logical_properties.html]
+skip-if = (toolkit == 'gonk' && debug) # Bug 1186224
 [test_media_queries.html]
 skip-if = (toolkit == 'gonk' && debug) || android_version == '10' || android_version == '18' #debug-only failure; timed out #Android 2.3 and 4.3 aws only; bug 1030419
 [test_media_queries_dynamic.html]
 [test_media_queries_dynamic_xbl.html]
 [test_media_query_list.html]
 [test_moz_device_pixel_ratio.html]
 [test_namespace_rule.html]
 [test_of_type_selectors.xhtml]
+[test_page_parser.html]
 [test_parse_eof.html]
 [test_parse_ident.html]
 [test_parse_rule.html]
 [test_parse_url.html]
 [test_parser_diagnostics_unprintables.html]
 [test_pixel_lengths.html]
 [test_pointer-events.html]
+[test_position_float_display.html]
 [test_position_sticky.html]
 support-files = file_position_sticky.html
 [test_priority_preservation.html]
+[test_property_database.html]
 [test_property_syntax_errors.html]
+skip-if = (toolkit == 'gonk' && debug) # Bug 1186224
 [test_pseudoelement_state.html]
 skip-if = (toolkit == 'gonk' && debug) || e10s #debug-only failure
 [test_pseudoelement_parsing.html]
 [test_redundant_font_download.html]
 support-files = redundant_font_download.sjs
 [test_rem_unit.html]
 [test_root_node_display.html]
 [test_rule_insertion.html]
 skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) # b2g-debug(monospace and serif text have sufficiently different widths) b2g-desktop(monospace and serif text have sufficiently different widths)
 [test_rule_serialization.html]
 [test_rules_out_of_sheets.html]
 [test_selectors.html]
 skip-if = (toolkit == 'gonk' && debug) || toolkit == 'android' #bug 775227 #debug-only failure; timed out
 [test_selectors_on_anonymous_content.html]
+[test_setPropertyWithNull.html]
 [test_shorthand_property_getters.html]
 [test_specified_value_serialization.html]
 [test_style_attribute_quirks.html]
 [test_style_attribute_standards.html]
 [test_style_struct_copy_constructors.html]
 [test_supports_rules.html]
 [test_system_font_serialization.html]
 [test_text_decoration_shorthands.html]
@@ -216,66 +238,47 @@ skip-if = (toolkit == 'gonk' && debug) |
 [test_transitions_and_zoom.html]
 [test_transitions_cancel_near_end.html]
 skip-if = android_version == '10' #Android 2.3 aws only; bug 1030432
 [test_transitions_computed_values.html]
 [test_transitions_computed_value_combinations.html]
 [test_transitions_events.html]
 [test_transitions.html]
 skip-if = (android_version == '18' && debug) # bug 1159532
+[test_transitions_bug537151.html]
+[test_transitions_dynamic_changes.html]
 [test_transitions_per_property.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' #bug 775227 # b2g(times out, needs more time + various failures) b2g-debug(times out, needs more time + various failures) b2g-desktop(times out, needs more time + various failures)
 [test_transitions_step_functions.html]
-[test_transitions_dynamic_changes.html]
-[test_transitions_bug537151.html]
 [test_unclosed_parentheses.html]
 [test_unicode_range_loading.html]
 support-files = ../../reftests/fonts/markA.woff ../../reftests/fonts/markB.woff ../../reftests/fonts/markC.woff ../../reftests/fonts/markD.woff
 skip-if = (toolkit == "gtk2") || (toolkit == "gtk3") # bug 1056479
 [test_units_angle.html]
 [test_units_frequency.html]
 [test_units_length.html]
 [test_units_time.html]
 [test_unprefixing_service.html]
 support-files = unprefixing_service_iframe.html unprefixing_service_utils.js
 [test_unprefixing_service_prefs.html]
 support-files = unprefixing_service_iframe.html unprefixing_service_utils.js
 [test_value_cloning.html]
-skip-if = (toolkit == 'gonk' && debug) || toolkit == 'android' #bug 775227 #debug-only failure; timed out
+skip-if = toolkit == 'gonk' || toolkit == 'android' #bug 775227, # Bug 1186219 B2G memory explosion
 [test_value_computation.html]
-skip-if = (toolkit == 'gonk' && debug) || toolkit == 'android' #debug-only failure
+skip-if = toolkit == 'gonk' || toolkit == 'android' # Bug 1186219 B2G memory explosion
 [test_value_storage.html]
-skip-if = (toolkit == 'gonk' && debug) #debug-only failure
+skip-if = toolkit == 'gonk' # Bug 1186219 B2G memory explosion
 [test_variable_serialization_computed.html]
 [test_variable_serialization_specified.html]
 [test_variables.html]
 support-files = support/external-variable-url.css
 [test_video_object_fit.html]
 [test_viewport_units.html]
 [test_visited_image_loading.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT # b2g(bug 870262, :visited support) b2g-debug(bug 870262, :visited support) b2g-desktop(bug 870262, :visited support)
 [test_visited_image_loading_empty.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT # b2g(bug 870262, :visited support) b2g-debug(bug 870262, :visited support) b2g-desktop(bug 870262, :visited support)
 [test_visited_lying.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT # b2g(bug 870262, :visited support) b2g-debug(bug 870262, :visited support) b2g-desktop(bug 870262, :visited support)
 [test_visited_pref.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT # b2g(bug 870262, :visited support) b2g-debug(bug 870262, :visited support) b2g-desktop(bug 870262, :visited support)
 [test_visited_reftests.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT # b2g(bug 870262, :visited support) b2g-debug(bug 870262, :visited support) b2g-desktop(bug 870262, :visited support)
-[test_bug525952.html]
-[test_load_events_on_stylesheets.html]
-[test_logical_properties.html]
-[test_page_parser.html]
-[test_bug732153.html]
-[test_bug732209.html]
-support-files = bug732209-css.sjs
-[test_bug795520.html]
-[test_background_blend_mode.html]
-[test_property_database.html]
-[test_counter_style.html]
-[test_counter_descriptor_storage.html]
-[test_position_float_display.html]
-[test_animations_async_tests.html]
-support-files = ../../reftests/fonts/Ahem.ttf file_animations_async_tests.html
-[test_setPropertyWithNull.html]
-[test_attribute_selector_eof_behavior.html]
-[test_css_loader_crossorigin_data_url.html]
-[test_box_size_keywords.html]
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -261,19 +261,17 @@ pref("browser.ssl_override_behavior", 2)
 pref("browser.xul.error_pages.expert_bad_cert", false);
 
 // ordering of search engines in the engine list.
 pref("browser.search.order.1", "chrome://browser/locale/region.properties");
 pref("browser.search.order.2", "chrome://browser/locale/region.properties");
 pref("browser.search.order.3", "chrome://browser/locale/region.properties");
 
 // Market-specific search defaults
-// This is disabled globally, and then enabled for individual locales
-// in firefox-l10n.js (eg. it's enabled for en-US).
-pref("browser.search.geoSpecificDefaults", false);
+pref("browser.search.geoSpecificDefaults", true);
 pref("browser.search.geoSpecificDefaults.url", "https://search.services.mozilla.com/1/%APP%/%VERSION%/%CHANNEL%/%LOCALE%/%REGION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%");
 
 // US specific default (used as a fallback if the geoSpecificDefaults request fails).
 pref("browser.search.defaultenginename.US", "chrome://browser/locale/region.properties");
 pref("browser.search.order.US.1", "chrome://browser/locale/region.properties");
 pref("browser.search.order.US.2", "chrome://browser/locale/region.properties");
 pref("browser.search.order.US.3", "chrome://browser/locale/region.properties");
 
--- a/mobile/android/base/health/BrowserHealthRecorder.java
+++ b/mobile/android/base/health/BrowserHealthRecorder.java
@@ -258,16 +258,22 @@ public class BrowserHealthRecorder imple
 
     /**
      * If `startNewSession` is false, it means no new session should begin
      * (e.g., because we're about to restart, and we don't want to create
      * an orphan).
      */
     @Override
     public synchronized void onEnvironmentChanged(final boolean startNewSession, final String sessionEndReason) {
+        if (!(state == State.INITIALIZING ||
+              state == State.INITIALIZED)) {
+            Log.w(LOG_TAG, "Not initialized. Ignoring environment change. State is " + state);
+            return;
+        }
+
         final int previousEnv = this.env;
         this.env = -1;
         try {
             profileCache.completeInitialization();
         } catch (java.io.IOException e) {
             Log.e(LOG_TAG, "Error completing profile cache initialization.", e);
             this.state = State.INITIALIZATION_FAILED;
             return;
@@ -290,17 +296,17 @@ public class BrowserHealthRecorder imple
                 }
             }
         });
     }
 
     protected synchronized int ensureEnvironment() {
         if (!(state == State.INITIALIZING ||
               state == State.INITIALIZED)) {
-            throw new IllegalStateException("Not initialized.");
+            throw new IllegalStateException("Not initialized. State is " + state);
         }
 
         if (this.env != -1) {
             return this.env;
         }
         if (this.storage == null) {
             // Oh well.
             return -1;
@@ -561,20 +567,28 @@ public class BrowserHealthRecorder imple
                 if (desc != null && desc.valid) {
                     profileCache.setDistributionString(desc.id, desc.version);
                 }
 
                 // Now rebuild.
                 try {
                     profileCache.completeInitialization();
 
-                    if (state == State.INITIALIZING) {
-                        initializeStorage();
-                    } else {
-                        onEnvironmentChanged();
+                    synchronized (BrowserHealthRecorder.this) {
+                        switch (state) {
+                            case INITIALIZING:
+                                initializeStorage();
+                                break;
+                            case INITIALIZED:
+                                onEnvironmentChanged();
+                                break;
+                            default:
+                                Log.e(LOG_TAG, "Unexpected state " + state + " when handling late distribution.");
+                                return;
+                        }
                     }
                 } catch (Exception e) {
                     // Well, we tried.
                     Log.e(LOG_TAG, "Couldn't complete profile cache init.", e);
                 }
             }
         });
     }
--- a/mobile/android/locales/en-US/mobile-l10n.js
+++ b/mobile/android/locales/en-US/mobile-l10n.js
@@ -1,11 +1,7 @@
 # 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/.
 
 #filter substitution
 
-# LOCALIZATION NOTE: this preference is set to true for en-US specifically,
-# locales without this line have the setting set to false by default.
-pref("browser.search.geoSpecificDefaults", true);
-
 pref("general.useragent.locale", "@AB_CD@");
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -467,17 +467,17 @@ pref("media.mediasource.whitelist", true
 #else
 pref("media.mediasource.whitelist", false);
 #endif // RELEASE_BUILD
 
 pref("media.mediasource.mp4.enabled", true);
 pref("media.mediasource.webm.enabled", false);
 
 // Enable new MediaSource architecture.
-pref("media.mediasource.format-reader", false);
+pref("media.mediasource.format-reader", true);
 
 // Enable new MediaFormatReader architecture for webm in MSE
 pref("media.mediasource.format-reader.webm", false);
 // Enable new MediaFormatReader architecture for plain webm.
 pref("media.format-reader.webm", false);
 
 #ifdef MOZ_WEBSPEECH
 pref("media.webspeech.recognition.enable", false);
@@ -4101,16 +4101,18 @@ pref("image.onload.decode.limit", 0);
 
 // WebGL prefs
 #ifdef ANDROID
 // Disable MSAA on mobile.
 pref("gl.msaa-level", 0);
 #else
 pref("gl.msaa-level", 2);
 #endif
+pref("gl.require-hardware", false);
+
 pref("webgl.force-enabled", false);
 pref("webgl.disabled", false);
 pref("webgl.disable-angle", false);
 pref("webgl.min_capability_mode", false);
 pref("webgl.disable-extensions", false);
 pref("webgl.msaa-force", false);
 pref("webgl.prefer-16bpp", false);
 pref("webgl.default-no-alpha", false);
@@ -4119,17 +4121,19 @@ pref("webgl.lose-context-on-memory-press
 pref("webgl.can-lose-context-in-foreground", true);
 pref("webgl.restore-context-when-visible", true);
 pref("webgl.max-warnings-per-context", 32);
 pref("webgl.enable-draft-extensions", false);
 pref("webgl.enable-privileged-extensions", false);
 pref("webgl.bypass-shader-validation", false);
 pref("webgl.enable-prototype-webgl2", false);
 pref("webgl.disable-fail-if-major-performance-caveat", false);
-pref("gl.require-hardware", false);
+pref("webgl.disable-debug-renderer-info", false);
+pref("webgl.renderer-string-override", "");
+pref("webgl.vendor-string-override", "");
 
 #ifdef XP_WIN
 pref("webgl.angle.try-d3d11", true);
 pref("webgl.angle.force-d3d11", false);
 #endif
 
 #ifdef MOZ_WIDGET_GONK
 pref("gfx.gralloc.fence-with-readpixels", false);
--- a/netwerk/base/nsLoadGroup.cpp
+++ b/netwerk/base/nsLoadGroup.cpp
@@ -519,23 +519,24 @@ nsLoadGroup::AddRequest(nsIRequest *requ
     if (mIsCanceling) {
         LOG(("LOADGROUP [%x]: AddChannel() ABORTED because LoadGroup is"
              " being canceled!!\n", this));
 
         return NS_BINDING_ABORTED;
     }
 
     nsLoadFlags flags;
-    // if the request is the default load request or if the default
-    // load request is null, then the load group should inherit its
-    // load flags from the request.
-    if (mDefaultLoadRequest == request || !mDefaultLoadRequest)
-        rv = request->GetLoadFlags(&flags);
-    else
+    // if the request is the default load request or if the default load
+    // request is null, then the load group should inherit its load flags from
+    // the request, but also we need to enforce defaultLoadFlags.
+    if (mDefaultLoadRequest == request || !mDefaultLoadRequest) {
+        rv = MergeDefaultLoadFlags(request, flags);
+    } else {
         rv = MergeLoadFlags(request, flags);
+    }
     if (NS_FAILED(rv)) return rv;
     
     //
     // Add the request to the list of active requests...
     //
 
     RequestMapEntry *entry = static_cast<RequestMapEntry *>
         (PL_DHashTableAdd(&mRequests, request, fallible));
@@ -1053,38 +1054,63 @@ nsLoadGroup::TelemetryReportChannel(nsIT
     if (aDefaultRequest) {
         HTTP_REQUEST_HISTOGRAMS(PAGE)
     } else {
         HTTP_REQUEST_HISTOGRAMS(SUB)
     }
 #undef HTTP_REQUEST_HISTOGRAMS
 }
 
-nsresult nsLoadGroup::MergeLoadFlags(nsIRequest *aRequest, nsLoadFlags& outFlags)
+nsresult nsLoadGroup::MergeLoadFlags(nsIRequest *aRequest,
+                                     nsLoadFlags& outFlags)
 {
     nsresult rv;
     nsLoadFlags flags, oldFlags;
 
     rv = aRequest->GetLoadFlags(&flags);
-    if (NS_FAILED(rv)) 
+    if (NS_FAILED(rv)) {
         return rv;
+    }
 
     oldFlags = flags;
 
     // Inherit the following bits...
     flags |= (mLoadFlags & (LOAD_BACKGROUND |
                             LOAD_BYPASS_CACHE |
                             LOAD_FROM_CACHE |
                             VALIDATE_ALWAYS |
                             VALIDATE_ONCE_PER_SESSION |
                             VALIDATE_NEVER));
 
     // ... and force the default flags.
     flags |= mDefaultLoadFlags;
 
-    if (flags != oldFlags)
+    if (flags != oldFlags) {
         rv = aRequest->SetLoadFlags(flags);
+    }
 
     outFlags = flags;
     return rv;
 }
 
+nsresult nsLoadGroup::MergeDefaultLoadFlags(nsIRequest *aRequest,
+                                            nsLoadFlags& outFlags)
+{
+    nsresult rv;
+    nsLoadFlags flags, oldFlags;
+
+    rv = aRequest->GetLoadFlags(&flags);
+    if (NS_FAILED(rv)) {
+        return rv;
+    }
+
+    oldFlags = flags;
+    // ... and force the default flags.
+    flags |= mDefaultLoadFlags;
+
+    if (flags != oldFlags) {
+        rv = aRequest->SetLoadFlags(flags);
+    }
+    outFlags = flags;
+    return rv;
+}
+
 #undef LOG
--- a/netwerk/base/nsLoadGroup.h
+++ b/netwerk/base/nsLoadGroup.h
@@ -49,16 +49,17 @@ public:
     ////////////////////////////////////////////////////////////////////////////
     // nsLoadGroup methods:
 
     explicit nsLoadGroup(nsISupports* outer);
     virtual ~nsLoadGroup();
 
 protected:
     nsresult MergeLoadFlags(nsIRequest *aRequest, nsLoadFlags& flags);
+    nsresult MergeDefaultLoadFlags(nsIRequest *aRequest, nsLoadFlags& flags);
 
 private:
     void TelemetryReport();
     void TelemetryReportChannel(nsITimedChannel *timedChannel,
                                 bool defaultRequest);
 
 protected:
     uint32_t                        mForegroundCount;
--- a/netwerk/build/nsNetCID.h
+++ b/netwerk/build/nsNetCID.h
@@ -632,24 +632,30 @@
 #define NS_RESPROTOCOLHANDLER_CID                    \
 { /* e64f152a-9f07-11d3-8cda-0060b0fc14a3 */         \
     0xe64f152a,                                      \
     0x9f07,                                          \
     0x11d3,                                          \
     {0x8c, 0xda, 0x00, 0x60, 0xb0, 0xfc, 0x14, 0xa3} \
 }
 
-#define NS_RESURL_CID                    \
-{ /* ff8fe7ec-2f74-4408-b742-6b7a546029a8 */         \
-    0xff8fe7ec,                                      \
-    0x2f74,                                          \
-    0x4408,                                          \
-    {0xb7, 0x42, 0x6b, 0x7a, 0x54, 0x60, 0x29, 0xa8} \
+#define NS_EXTENSIONPROTOCOLHANDLER_CID              \
+{ /* aea16cd0-f020-4138-b068-0716c4a15b5a */         \
+    0xaea16cd0,                                      \
+    0xf020,                                          \
+    0x4138,                                          \
+    {0xb0, 0x68, 0x07, 0x16, 0xc4, 0xa1, 0x5b, 0x5a} \
 }
 
+#define NS_SUBSTITUTINGURL_CID                       \
+{ 0xdea9657c,                                        \
+  0x18cf,                                            \
+  0x4984,                                            \
+  { 0xbd, 0xe9, 0xcc, 0xef, 0x5d, 0x8a, 0xb4, 0x73 } \
+}
 
 /******************************************************************************
  * netwerk/protocol/file/ classes
  */
 
 #define NS_FILEPROTOCOLHANDLER_CID                   \
 { /* fbc81170-1f69-11d3-9344-00104ba0fd40 */         \
     0xfbc81170,                                      \
--- a/netwerk/build/nsNetModule.cpp
+++ b/netwerk/build/nsNetModule.cpp
@@ -267,18 +267,24 @@ namespace net {
   NS_GENERIC_FACTORY_CONSTRUCTOR(PackagedAppService)
 } // namespace net
 } // namespace mozilla
 #include "AppProtocolHandler.h"
 
 #ifdef NECKO_PROTOCOL_res
 // resource
 #include "nsResProtocolHandler.h"
+#include "ExtensionProtocolHandler.h"
+#include "SubstitutingProtocolHandler.h"
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsResProtocolHandler, Init)
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsResURL)
+
+namespace mozilla {
+NS_GENERIC_FACTORY_CONSTRUCTOR(ExtensionProtocolHandler)
+NS_GENERIC_FACTORY_CONSTRUCTOR(SubstitutingURL)
+} // namespace mozilla
 #endif
 
 #ifdef NECKO_PROTOCOL_device
 #include "nsDeviceProtocolHandler.h"
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsDeviceProtocolHandler)
 #endif
 
 #ifdef NECKO_PROTOCOL_viewsource
@@ -751,17 +757,18 @@ NS_DEFINE_NAMED_CID(NS_HTTPAUTHMANAGER_C
 NS_DEFINE_NAMED_CID(NS_HTTPCHANNELAUTHPROVIDER_CID);
 NS_DEFINE_NAMED_CID(NS_HTTPACTIVITYDISTRIBUTOR_CID);
 #endif // !NECKO_PROTOCOL_http
 #ifdef NECKO_PROTOCOL_ftp
 NS_DEFINE_NAMED_CID(NS_FTPPROTOCOLHANDLER_CID);
 #endif
 #ifdef NECKO_PROTOCOL_res
 NS_DEFINE_NAMED_CID(NS_RESPROTOCOLHANDLER_CID);
-NS_DEFINE_NAMED_CID(NS_RESURL_CID);
+NS_DEFINE_NAMED_CID(NS_EXTENSIONPROTOCOLHANDLER_CID);
+NS_DEFINE_NAMED_CID(NS_SUBSTITUTINGURL_CID);
 #endif
 NS_DEFINE_NAMED_CID(NS_ABOUTPROTOCOLHANDLER_CID);
 NS_DEFINE_NAMED_CID(NS_SAFEABOUTPROTOCOLHANDLER_CID);
 NS_DEFINE_NAMED_CID(NS_ABOUT_BLANK_MODULE_CID);
 NS_DEFINE_NAMED_CID(NS_NESTEDABOUTURI_CID);
 #ifdef NECKO_PROTOCOL_about
 #ifdef NS_BUILD_REFCNT_LOGGING
 NS_DEFINE_NAMED_CID(NS_ABOUT_BLOAT_MODULE_CID);
@@ -897,17 +904,18 @@ static const mozilla::Module::CIDEntry k
     { &kNS_HTTPCHANNELAUTHPROVIDER_CID, false, nullptr, mozilla::net::nsHttpChannelAuthProviderConstructor },
     { &kNS_HTTPACTIVITYDISTRIBUTOR_CID, false, nullptr, mozilla::net::nsHttpActivityDistributorConstructor },
 #endif // !NECKO_PROTOCOL_http
 #ifdef NECKO_PROTOCOL_ftp
     { &kNS_FTPPROTOCOLHANDLER_CID, false, nullptr, nsFtpProtocolHandlerConstructor },
 #endif
 #ifdef NECKO_PROTOCOL_res
     { &kNS_RESPROTOCOLHANDLER_CID, false, nullptr, nsResProtocolHandlerConstructor },
-    { &kNS_RESURL_CID, false, nullptr, nsResURLConstructor },
+    { &kNS_EXTENSIONPROTOCOLHANDLER_CID, false, nullptr, mozilla::ExtensionProtocolHandlerConstructor },
+    { &kNS_SUBSTITUTINGURL_CID, false, nullptr, mozilla::SubstitutingURLConstructor },
 #endif
     { &kNS_ABOUTPROTOCOLHANDLER_CID, false, nullptr, nsAboutProtocolHandlerConstructor },
     { &kNS_SAFEABOUTPROTOCOLHANDLER_CID, false, nullptr, nsSafeAboutProtocolHandlerConstructor },
     { &kNS_ABOUT_BLANK_MODULE_CID, false, nullptr, nsAboutBlank::Create },
     { &kNS_NESTEDABOUTURI_CID, false, nullptr, nsNestedAboutURIConstructor },
 #ifdef NECKO_PROTOCOL_about
 #ifdef NS_BUILD_REFCNT_LOGGING
     { &kNS_ABOUT_BLOAT_MODULE_CID, false, nullptr, nsAboutBloat::Create },
@@ -1052,16 +1060,17 @@ static const mozilla::Module::ContractID
     { NS_HTTPCHANNELAUTHPROVIDER_CONTRACTID, &kNS_HTTPCHANNELAUTHPROVIDER_CID },
     { NS_HTTPACTIVITYDISTRIBUTOR_CONTRACTID, &kNS_HTTPACTIVITYDISTRIBUTOR_CID },
 #endif // !NECKO_PROTOCOL_http
 #ifdef NECKO_PROTOCOL_ftp
     { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "ftp", &kNS_FTPPROTOCOLHANDLER_CID },
 #endif
 #ifdef NECKO_PROTOCOL_res
     { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "resource", &kNS_RESPROTOCOLHANDLER_CID },
+    { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "moz-extension", &kNS_EXTENSIONPROTOCOLHANDLER_CID },
 #endif
     { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "about", &kNS_ABOUTPROTOCOLHANDLER_CID },
     { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "moz-safe-about", &kNS_SAFEABOUTPROTOCOLHANDLER_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "blank", &kNS_ABOUT_BLANK_MODULE_CID },
 #ifdef NECKO_PROTOCOL_about
 #ifdef NS_BUILD_REFCNT_LOGGING
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "bloat", &kNS_ABOUT_BLOAT_MODULE_CID },
 #endif
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/res/ExtensionProtocolHandler.cpp
@@ -0,0 +1,16 @@
+/* -*- 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 "ExtensionProtocolHandler.h"
+
+namespace mozilla {
+
+NS_IMPL_QUERY_INTERFACE(ExtensionProtocolHandler, nsISubstitutingProtocolHandler,
+                        nsIProtocolHandler, nsISupportsWeakReference)
+NS_IMPL_ADDREF_INHERITED(ExtensionProtocolHandler, SubstitutingProtocolHandler)
+NS_IMPL_RELEASE_INHERITED(ExtensionProtocolHandler, SubstitutingProtocolHandler)
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/res/ExtensionProtocolHandler.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef ExtensionProtocolHandler_h___
+#define ExtensionProtocolHandler_h___
+
+#include "SubstitutingProtocolHandler.h"
+#include "nsWeakReference.h"
+
+namespace mozilla {
+
+class ExtensionProtocolHandler final : public nsISubstitutingProtocolHandler,
+                                       public mozilla::SubstitutingProtocolHandler,
+                                       public nsSupportsWeakReference
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_FORWARD_NSIPROTOCOLHANDLER(mozilla::SubstitutingProtocolHandler::)
+  NS_FORWARD_NSISUBSTITUTINGPROTOCOLHANDLER(mozilla::SubstitutingProtocolHandler::)
+
+  // In general a moz-extension URI is only loadable by chrome, but a whitelisted
+  // subset are web-accessible (see nsIAddonPolicyService).
+  ExtensionProtocolHandler()
+    : SubstitutingProtocolHandler("moz-extension", URI_STD | URI_DANGEROUS_TO_LOAD | URI_IS_LOCAL_RESOURCE)
+  {}
+
+protected:
+  ~ExtensionProtocolHandler() {}
+};
+
+} // namespace mozilla
+
+#endif /* ExtensionProtocolHandler_h___ */
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/res/SubstitutingProtocolHandler.cpp
@@ -0,0 +1,381 @@
+/* -*- 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/chrome/RegistryMessageUtils.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/unused.h"
+
+#include "SubstitutingProtocolHandler.h"
+#include "nsIIOService.h"
+#include "nsIFile.h"
+#include "nsNetCID.h"
+#include "nsNetUtil.h"
+#include "nsURLHelper.h"
+#include "nsEscape.h"
+
+using mozilla::dom::ContentParent;
+
+namespace mozilla {
+
+// Log module for Substituting Protocol logging. We keep the pre-existing module
+// name of "nsResProtocol" to avoid disruption.
+static PRLogModuleInfo *gResLog;
+
+static NS_DEFINE_CID(kSubstitutingURLCID, NS_SUBSTITUTINGURL_CID);
+
+//---------------------------------------------------------------------------------
+// SubstitutingURL : overrides nsStandardURL::GetFile to provide nsIFile resolution
+//---------------------------------------------------------------------------------
+
+nsresult
+SubstitutingURL::EnsureFile()
+{
+  nsAutoCString ourScheme;
+  nsresult rv = GetScheme(ourScheme);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Get the handler associated with this scheme. It would be nice to just
+  // pass this in when constructing SubstitutingURLs, but we need a generic
+  // factory constructor.
+  nsCOMPtr<nsIIOService> io = do_GetIOService(&rv);
+  nsCOMPtr<nsIProtocolHandler> handler;
+  rv = io->GetProtocolHandler(ourScheme.get(), getter_AddRefs(handler));
+  NS_ENSURE_SUCCESS(rv, rv);
+  nsCOMPtr<nsISubstitutingProtocolHandler> substHandler = do_QueryInterface(handler);
+  MOZ_ASSERT(substHandler);
+
+  nsAutoCString spec;
+  rv = substHandler->ResolveURI(this, spec);
+  if (NS_FAILED(rv))
+    return rv;
+
+  nsAutoCString scheme;
+  rv = net_ExtractURLScheme(spec, nullptr, nullptr, &scheme);
+  if (NS_FAILED(rv))
+    return rv;
+
+  // Bug 585869:
+  // In most cases, the scheme is jar if it's not file.
+  // Regardless, net_GetFileFromURLSpec should be avoided
+  // when the scheme isn't file.
+  if (!scheme.EqualsLiteral("file"))
+    return NS_ERROR_NO_INTERFACE;
+
+  return net_GetFileFromURLSpec(spec, getter_AddRefs(mFile));
+}
+
+/* virtual */ nsStandardURL*
+SubstitutingURL::StartClone()
+{
+  SubstitutingURL *clone = new SubstitutingURL();
+  return clone;
+}
+
+NS_IMETHODIMP
+SubstitutingURL::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
+{
+  *aClassIDNoAlloc = kSubstitutingURLCID;
+  return NS_OK;
+}
+
+SubstitutingProtocolHandler::SubstitutingProtocolHandler(const char* aScheme, uint32_t aFlags,
+                                                         bool aEnforceFileOrJar)
+  : mScheme(aScheme)
+  , mFlags(aFlags)
+  , mSubstitutions(16)
+  , mEnforceFileOrJar(aEnforceFileOrJar)
+{
+  nsresult rv;
+  mIOService = do_GetIOService(&rv);
+  MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv) && mIOService);
+
+  if (!gResLog) {
+    gResLog = PR_NewLogModule("nsResProtocol");
+  }
+}
+
+//
+// IPC marshalling.
+//
+
+struct EnumerateSubstitutionArg
+{
+  EnumerateSubstitutionArg(nsCString& aScheme, nsTArray<SubstitutionMapping>& aMappings)
+    : mScheme(aScheme), mMappings(aMappings) {}
+  nsCString& mScheme;
+  nsTArray<SubstitutionMapping>& mMappings;
+};
+
+static PLDHashOperator
+EnumerateSubstitution(const nsACString& aKey,
+                      nsIURI* aURI,
+                      void* aArg)
+{
+  auto arg = static_cast<EnumerateSubstitutionArg*>(aArg);
+  SerializedURI uri;
+  if (aURI) {
+    aURI->GetSpec(uri.spec);
+    aURI->GetOriginCharset(uri.charset);
+  }
+
+  SubstitutionMapping substitution = { arg->mScheme, nsCString(aKey), uri };
+  arg->mMappings.AppendElement(substitution);
+  return (PLDHashOperator)PL_DHASH_NEXT;
+}
+
+void
+SubstitutingProtocolHandler::CollectSubstitutions(InfallibleTArray<SubstitutionMapping>& aMappings)
+{
+  EnumerateSubstitutionArg arg(mScheme, aMappings);
+  mSubstitutions.EnumerateRead(&EnumerateSubstitution, &arg);
+}
+
+void
+SubstitutingProtocolHandler::SendSubstitution(const nsACString& aRoot, nsIURI* aBaseURI)
+{
+  if (GeckoProcessType_Content == XRE_GetProcessType()) {
+    return;
+  }
+
+  nsTArray<ContentParent*> parents;
+  ContentParent::GetAll(parents);
+  if (!parents.Length()) {
+    return;
+  }
+
+  SubstitutionMapping mapping;
+  mapping.scheme = mScheme;
+  mapping.path = aRoot;
+  if (aBaseURI) {
+    aBaseURI->GetSpec(mapping.resolvedURI.spec);
+    aBaseURI->GetOriginCharset(mapping.resolvedURI.charset);
+  }
+
+  for (uint32_t i = 0; i < parents.Length(); i++) {
+    unused << parents[i]->SendRegisterChromeItem(mapping);
+  }
+}
+
+//----------------------------------------------------------------------------
+// nsIProtocolHandler
+//----------------------------------------------------------------------------
+
+nsresult
+SubstitutingProtocolHandler::GetScheme(nsACString &result)
+{
+  result = mScheme;
+  return NS_OK;
+}
+
+nsresult
+SubstitutingProtocolHandler::GetDefaultPort(int32_t *result)
+{
+  *result = -1;
+  return NS_OK;
+}
+
+nsresult
+SubstitutingProtocolHandler::GetProtocolFlags(uint32_t *result)
+{
+  *result = mFlags;
+  return NS_OK;
+}
+
+nsresult
+SubstitutingProtocolHandler::NewURI(const nsACString &aSpec,
+                                    const char *aCharset,
+                                    nsIURI *aBaseURI,
+                                    nsIURI **result)
+{
+  nsresult rv;
+
+  nsRefPtr<SubstitutingURL> url = new SubstitutingURL();
+  if (!url)
+    return NS_ERROR_OUT_OF_MEMORY;
+
+  // unescape any %2f and %2e to make sure nsStandardURL coalesces them.
+  // Later net_GetFileFromURLSpec() will do a full unescape and we want to
+  // treat them the same way the file system will. (bugs 380994, 394075)
+  nsAutoCString spec;
+  const char *src = aSpec.BeginReading();
+  const char *end = aSpec.EndReading();
+  const char *last = src;
+
+  spec.SetCapacity(aSpec.Length()+1);
+  for ( ; src < end; ++src) {
+    if (*src == '%' && (src < end-2) && *(src+1) == '2') {
+      char ch = '\0';
+      if (*(src+2) == 'f' || *(src+2) == 'F') {
+        ch = '/';
+      } else if (*(src+2) == 'e' || *(src+2) == 'E') {
+        ch = '.';
+      }
+
+      if (ch) {
+        if (last < src) {
+          spec.Append(last, src-last);
+        }
+        spec.Append(ch);
+        src += 2;
+        last = src+1; // src will be incremented by the loop
+      }
+    }
+  }
+  if (last < src)
+    spec.Append(last, src-last);
+
+  rv = url->Init(nsIStandardURL::URLTYPE_STANDARD, -1, spec, aCharset, aBaseURI);
+  if (NS_SUCCEEDED(rv)) {
+    url.forget(result);
+  }
+  return rv;
+}
+
+nsresult
+SubstitutingProtocolHandler::NewChannel2(nsIURI* uri,
+                                         nsILoadInfo* aLoadInfo,
+                                         nsIChannel** result)
+{
+  NS_ENSURE_ARG_POINTER(uri);
+  nsAutoCString spec;
+  nsresult rv = ResolveURI(uri, spec);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIURI> newURI;
+  rv = NS_NewURI(getter_AddRefs(newURI), spec);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = NS_NewChannelInternal(result, newURI, aLoadInfo);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsLoadFlags loadFlags = 0;
+  (*result)->GetLoadFlags(&loadFlags);
+  (*result)->SetLoadFlags(loadFlags & ~nsIChannel::LOAD_REPLACE);
+  return (*result)->SetOriginalURI(uri);
+}
+
+nsresult
+SubstitutingProtocolHandler::NewChannel(nsIURI* uri, nsIChannel* *result)
+{
+  return NewChannel2(uri, nullptr, result);
+}
+
+nsresult
+SubstitutingProtocolHandler::AllowPort(int32_t port, const char *scheme, bool *_retval)
+{
+  // don't override anything.
+  *_retval = false;
+  return NS_OK;
+}
+
+//----------------------------------------------------------------------------
+// nsISubstitutingProtocolHandler
+//----------------------------------------------------------------------------
+
+nsresult
+SubstitutingProtocolHandler::SetSubstitution(const nsACString& root, nsIURI *baseURI)
+{
+  if (!baseURI) {
+    mSubstitutions.Remove(root);
+    SendSubstitution(root, baseURI);
+    return NS_OK;
+  }
+
+  // If baseURI isn't a same-scheme URI, we can set the substitution immediately.
+  nsAutoCString scheme;
+  nsresult rv = baseURI->GetScheme(scheme);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (!scheme.Equals(mScheme)) {
+    if (mEnforceFileOrJar && !scheme.EqualsLiteral("file") && !scheme.EqualsLiteral("jar")) {
+      NS_WARNING("Refusing to create substituting URI to non-file:// target");
+      return NS_ERROR_INVALID_ARG;
+    }
+
+    mSubstitutions.Put(root, baseURI);
+    SendSubstitution(root, baseURI);
+    return NS_OK;
+  }
+
+  // baseURI is a same-type substituting URI, let's resolve it first.
+  nsAutoCString newBase;
+  rv = ResolveURI(baseURI, newBase);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIURI> newBaseURI;
+  rv = mIOService->NewURI(newBase, nullptr, nullptr, getter_AddRefs(newBaseURI));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  mSubstitutions.Put(root, newBaseURI);
+  SendSubstitution(root, newBaseURI);
+  return NS_OK;
+}
+
+nsresult
+SubstitutingProtocolHandler::GetSubstitution(const nsACString& root, nsIURI **result)
+{
+  NS_ENSURE_ARG_POINTER(result);
+
+  if (mSubstitutions.Get(root, result))
+    return NS_OK;
+
+  return GetSubstitutionInternal(root, result);
+}
+
+nsresult
+SubstitutingProtocolHandler::HasSubstitution(const nsACString& root, bool *result)
+{
+  NS_ENSURE_ARG_POINTER(result);
+
+  *result = mSubstitutions.Get(root, nullptr);
+  return NS_OK;
+}
+
+nsresult
+SubstitutingProtocolHandler::ResolveURI(nsIURI *uri, nsACString &result)
+{
+  nsresult rv;
+
+  nsAutoCString host;
+  nsAutoCString path;
+
+  rv = uri->GetAsciiHost(host);
+  if (NS_FAILED(rv)) return rv;
+
+  rv = uri->GetPath(path);
+  if (NS_FAILED(rv)) return rv;
+
+  // Unescape the path so we can perform some checks on it.
+  nsAutoCString unescapedPath(path);
+  NS_UnescapeURL(unescapedPath);
+
+  // Don't misinterpret the filepath as an absolute URI.
+  if (unescapedPath.FindChar(':') != -1)
+    return NS_ERROR_MALFORMED_URI;
+
+  if (unescapedPath.FindChar('\\') != -1)
+    return NS_ERROR_MALFORMED_URI;
+
+  const char *p = path.get() + 1; // path always starts with a slash
+  NS_ASSERTION(*(p-1) == '/', "Path did not begin with a slash!");
+
+  if (*p == '/')
+    return NS_ERROR_MALFORMED_URI;
+
+  nsCOMPtr<nsIURI> baseURI;
+  rv = GetSubstitution(host, getter_AddRefs(baseURI));
+  if (NS_FAILED(rv)) return rv;
+
+  rv = baseURI->Resolve(nsDependentCString(p, path.Length()-1), result);
+
+  if (MOZ_LOG_TEST(gResLog, LogLevel::Debug)) {
+    nsAutoCString spec;
+    uri->GetAsciiSpec(spec);
+    MOZ_LOG(gResLog, LogLevel::Debug, ("%s\n -> %s\n", spec.get(), PromiseFlatCString(result).get()));
+  }
+  return rv;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/res/SubstitutingProtocolHandler.h
@@ -0,0 +1,83 @@
+/* -*- 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 SubstitutingProtocolHandler_h___
+#define SubstitutingProtocolHandler_h___
+
+#include "nsISubstitutingProtocolHandler.h"
+
+#include "nsInterfaceHashtable.h"
+#include "nsIOService.h"
+#include "nsStandardURL.h"
+#include "mozilla/chrome/RegistryMessageUtils.h"
+
+class nsIIOService;
+
+namespace mozilla {
+
+//
+// Base class for resource://-like substitution protocols.
+//
+// If you add a new protocol, make sure to change nsChromeRegistryChrome
+// to properly invoke CollectSubstitutions at the right time.
+class SubstitutingProtocolHandler
+{
+public:
+  SubstitutingProtocolHandler(const char* aScheme, uint32_t aFlags, bool aEnforceFileOrJar = true);
+
+  NS_INLINE_DECL_REFCOUNTING(SubstitutingProtocolHandler);
+  NS_DECL_NON_VIRTUAL_NSIPROTOCOLHANDLER;
+  NS_DECL_NON_VIRTUAL_NSISUBSTITUTINGPROTOCOLHANDLER;
+
+  void CollectSubstitutions(InfallibleTArray<SubstitutionMapping>& aResources);
+
+protected:
+  virtual ~SubstitutingProtocolHandler() {}
+
+  void SendSubstitution(const nsACString& aRoot, nsIURI* aBaseURI);
+
+  // Override this in the subclass to try additional lookups after checking
+  // mSubstitutions.
+  virtual nsresult GetSubstitutionInternal(const nsACString& aRoot, nsIURI** aResult)
+  {
+    *aResult = nullptr;
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  nsIIOService* IOService() { return mIOService; }
+
+private:
+  nsCString mScheme;
+  uint32_t mFlags;
+  nsInterfaceHashtable<nsCStringHashKey,nsIURI> mSubstitutions;
+  nsCOMPtr<nsIIOService> mIOService;
+
+  // In general, we expect the principal of a document loaded from a
+  // substituting URI to be a codebase principal for that URI (rather than
+  // a principal for whatever is underneath). However, this only works if
+  // the protocol handler for the underlying URI doesn't set an explicit
+  // owner (which chrome:// does, for example). So we want to require that
+  // substituting URIs only map to other URIs of the same type, or to
+  // file:// and jar:// URIs.
+  //
+  // Enforcing this for ye olde resource:// URIs could carry compat risks, so
+  // we just try to enforce it on new protocols going forward.
+  bool mEnforceFileOrJar;
+};
+
+// SubstitutingURL : overrides nsStandardURL::GetFile to provide nsIFile resolution
+class SubstitutingURL : public nsStandardURL
+{
+public:
+  SubstitutingURL() : nsStandardURL(true) {}
+  virtual nsStandardURL* StartClone();
+  virtual nsresult EnsureFile();
+  NS_IMETHOD GetClassIDNoAlloc(nsCID *aCID);
+};
+
+} // namespace mozilla
+
+#endif /* SubstitutingProtocolHandler_h___ */
--- a/netwerk/protocol/res/moz.build
+++ b/netwerk/protocol/res/moz.build
@@ -1,22 +1,25 @@
 # -*- 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/.
 
 XPIDL_SOURCES += [
     'nsIResProtocolHandler.idl',
+    'nsISubstitutingProtocolHandler.idl',
 ]
 
 XPIDL_MODULE = 'necko_res'
 
 SOURCES += [
+    'ExtensionProtocolHandler.cpp',
     'nsResProtocolHandler.cpp',
+    'SubstitutingProtocolHandler.cpp',
 ]
 
 FAIL_ON_WARNINGS = True
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
--- a/netwerk/protocol/res/nsIResProtocolHandler.idl
+++ b/netwerk/protocol/res/nsIResProtocolHandler.idl
@@ -1,45 +1,14 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "nsIProtocolHandler.idl"
+#include "nsISubstitutingProtocolHandler.idl"
 
 /**
  * Protocol handler interface for the resource:// protocol
  */
-[scriptable, uuid(067ca872-e947-4bd6-8946-a479cb6ba5dd)]
-interface nsIResProtocolHandler : nsIProtocolHandler
+[scriptable, uuid(241d34ac-9ed5-46d7-910c-7a9d914aa0c5)]
+interface nsIResProtocolHandler : nsISubstitutingProtocolHandler
 {
-    /**
-     * Sets the substitution for the root key:
-     *   resource://root/path ==> baseURI.resolve(path)
-     *
-     * A null baseURI removes the specified substitution.
-     *
-     * A root key should always be lowercase; however, this may not be
-     * enforced.
-     */
-    void setSubstitution(in ACString root, in nsIURI baseURI);
-
-    /**
-     * Gets the substitution for the root key.
-     *
-     * @throws NS_ERROR_NOT_AVAILABLE if none exists.
-     */
-    nsIURI getSubstitution(in ACString root);
-
-    /**
-     * Returns TRUE if the substitution exists and FALSE otherwise.
-     */
-    boolean hasSubstitution(in ACString root);
-
-    /**
-     * Utility function to resolve a resource URI.  A resolved URI is not 
-     * guaranteed to reference a resource that exists (ie. opening a channel to
-     * the resolved URI may fail).
-     *
-     * @throws NS_ERROR_NOT_AVAILABLE if resURI.host() is an unknown root key.
-     */
-    AUTF8String resolveURI(in nsIURI resURI);
 };
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/res/nsISubstitutingProtocolHandler.idl
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIProtocolHandler.idl"
+
+/**
+ * Protocol handler superinterface for a protocol which performs substitutions
+ * from URIs of its scheme to URIs of another scheme.
+ */
+[scriptable, uuid(154c64fd-a69e-4105-89f8-bd7dfe621372)]
+interface nsISubstitutingProtocolHandler : nsIProtocolHandler
+{
+  /**
+   * Sets the substitution for the root key:
+   *   resource://root/path ==> baseURI.resolve(path)
+   *
+   * A null baseURI removes the specified substitution.
+   *
+   * A root key should always be lowercase; however, this may not be
+   * enforced.
+   */
+  void setSubstitution(in ACString root, in nsIURI baseURI);
+
+  /**
+   * Gets the substitution for the root key.
+   *
+   * @throws NS_ERROR_NOT_AVAILABLE if none exists.
+   */
+  nsIURI getSubstitution(in ACString root);
+
+  /**
+   * Returns TRUE if the substitution exists and FALSE otherwise.
+   */
+  boolean hasSubstitution(in ACString root);
+
+  /**
+   * Utility function to resolve a substituted URI.  A resolved URI is not
+   * guaranteed to reference a resource that exists (ie. opening a channel to
+   * the resolved URI may fail).
+   *
+   * @throws NS_ERROR_NOT_AVAILABLE if resURI.host() is an unknown root key.
+   */
+  AUTF8String resolveURI(in nsIURI resURI);
+};
--- a/netwerk/protocol/res/nsResProtocolHandler.cpp
+++ b/netwerk/protocol/res/nsResProtocolHandler.cpp
@@ -16,118 +16,23 @@
 #include "nsEscape.h"
 
 #include "mozilla/Omnijar.h"
 
 using mozilla::dom::ContentParent;
 using mozilla::LogLevel;
 using mozilla::unused;
 
-static NS_DEFINE_CID(kResURLCID, NS_RESURL_CID);
-
-static nsResProtocolHandler *gResHandler = nullptr;
-
-//
-// Log module for Resource Protocol logging...
-//
-// To enable logging (see prlog.h for full details):
-//
-//    set NSPR_LOG_MODULES=nsResProtocol:5
-//    set NSPR_LOG_FILE=log.txt
-//
-// this enables LogLevel::Debug level information and places all output in
-// the file log.txt
-//
-static PRLogModuleInfo *gResLog;
-
 #define kAPP           NS_LITERAL_CSTRING("app")
 #define kGRE           NS_LITERAL_CSTRING("gre")
 
-//----------------------------------------------------------------------------
-// nsResURL : overrides nsStandardURL::GetFile to provide nsIFile resolution
-//----------------------------------------------------------------------------
-
-nsresult
-nsResURL::EnsureFile()
-{
-    nsresult rv;
-
-    NS_ENSURE_TRUE(gResHandler, NS_ERROR_NOT_AVAILABLE);
-
-    nsAutoCString spec;
-    rv = gResHandler->ResolveURI(this, spec);
-    if (NS_FAILED(rv))
-        return rv;
-
-    nsAutoCString scheme;
-    rv = net_ExtractURLScheme(spec, nullptr, nullptr, &scheme);
-    if (NS_FAILED(rv))
-        return rv;
-
-    // Bug 585869:
-    // In most cases, the scheme is jar if it's not file.
-    // Regardless, net_GetFileFromURLSpec should be avoided
-    // when the scheme isn't file.
-    if (!scheme.EqualsLiteral("file"))
-        return NS_ERROR_NO_INTERFACE;
-
-    rv = net_GetFileFromURLSpec(spec, getter_AddRefs(mFile));
-#ifdef DEBUG_bsmedberg
-    if (NS_SUCCEEDED(rv)) {
-        bool exists = true;
-        mFile->Exists(&exists);
-        if (!exists) {
-            printf("resource %s doesn't exist!\n", spec.get());
-        }
-    }
-#endif
-
-    return rv;
-}
-
-/* virtual */ nsStandardURL*
-nsResURL::StartClone()
-{
-    nsResURL *clone = new nsResURL();
-    return clone;
-}
-
-NS_IMETHODIMP 
-nsResURL::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
-{
-    *aClassIDNoAlloc = kResURLCID;
-    return NS_OK;
-}
-
-//----------------------------------------------------------------------------
-// nsResProtocolHandler <public>
-//----------------------------------------------------------------------------
-
-nsResProtocolHandler::nsResProtocolHandler()
-    : mSubstitutions(16)
-{
-    gResLog = PR_NewLogModule("nsResProtocol");
-
-    NS_ASSERTION(!gResHandler, "res handler already created!");
-    gResHandler = this;
-}
-
-nsResProtocolHandler::~nsResProtocolHandler()
-{
-    gResHandler = nullptr;
-}
-
 nsresult
 nsResProtocolHandler::Init()
 {
     nsresult rv;
-
-    mIOService = do_GetIOService(&rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
     nsAutoCString appURI, greURI;
     rv = mozilla::Omnijar::GetURIString(mozilla::Omnijar::APP, appURI);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = mozilla::Omnijar::GetURIString(mozilla::Omnijar::GRE, greURI);
     NS_ENSURE_SUCCESS(rv, rv);
 
     //
     // make resource:/// point to the application directory or omnijar
@@ -160,300 +65,38 @@ nsResProtocolHandler::Init()
     // but once I finish multiple chrome registration I'm not sure that it is needed
 
     // XXX dveditz: resource://pchrome/ defeats profile directory salting
     // if web content can load it. Tread carefully.
 
     return rv;
 }
 
-static PLDHashOperator
-EnumerateSubstitution(const nsACString& aKey,
-                      nsIURI* aURI,
-                      void* aArg)
-{
-    nsTArray<ResourceMapping>* resources =
-            static_cast<nsTArray<ResourceMapping>*>(aArg);
-    SerializedURI uri;
-    if (aURI) {
-        aURI->GetSpec(uri.spec);
-        aURI->GetOriginCharset(uri.charset);
-    }
-
-    ResourceMapping resource = {
-        nsCString(aKey), uri
-    };
-    resources->AppendElement(resource);
-    return (PLDHashOperator)PL_DHASH_NEXT;
-}
-
-void
-nsResProtocolHandler::CollectSubstitutions(InfallibleTArray<ResourceMapping>& aResources)
-{
-    mSubstitutions.EnumerateRead(&EnumerateSubstitution, &aResources);
-}
-
 //----------------------------------------------------------------------------
 // nsResProtocolHandler::nsISupports
 //----------------------------------------------------------------------------
 
-NS_IMPL_ISUPPORTS(nsResProtocolHandler,
-                  nsIResProtocolHandler,
-                  nsIProtocolHandler,
-                  nsISupportsWeakReference)
-
-//----------------------------------------------------------------------------
-// nsResProtocolHandler::nsIProtocolHandler
-//----------------------------------------------------------------------------
-
-NS_IMETHODIMP
-nsResProtocolHandler::GetScheme(nsACString &result)
-{
-    result.AssignLiteral("resource");
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsResProtocolHandler::GetDefaultPort(int32_t *result)
-{
-    *result = -1;        // no port for res: URLs
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsResProtocolHandler::GetProtocolFlags(uint32_t *result)
-{
-    // XXXbz Is this really true for all resource: URIs?  Could we
-    // somehow give different flags to some of them?
-    *result = URI_STD | URI_IS_UI_RESOURCE | URI_IS_LOCAL_RESOURCE;
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsResProtocolHandler::NewURI(const nsACString &aSpec,
-                             const char *aCharset,
-                             nsIURI *aBaseURI,
-                             nsIURI **result)
-{
-    nsresult rv;
-
-    nsRefPtr<nsResURL> resURL = new nsResURL();
-    if (!resURL)
-        return NS_ERROR_OUT_OF_MEMORY;
-
-    // unescape any %2f and %2e to make sure nsStandardURL coalesces them.
-    // Later net_GetFileFromURLSpec() will do a full unescape and we want to
-    // treat them the same way the file system will. (bugs 380994, 394075)
-    nsAutoCString spec;
-    const char *src = aSpec.BeginReading();
-    const char *end = aSpec.EndReading();
-    const char *last = src;
-
-    spec.SetCapacity(aSpec.Length()+1);
-    for ( ; src < end; ++src) {
-        if (*src == '%' && (src < end-2) && *(src+1) == '2') {
-           char ch = '\0';
-           if (*(src+2) == 'f' || *(src+2) == 'F')
-             ch = '/';
-           else if (*(src+2) == 'e' || *(src+2) == 'E')
-             ch = '.';
-
-           if (ch) {
-             if (last < src)
-               spec.Append(last, src-last);
-             spec.Append(ch);
-             src += 2;
-             last = src+1; // src will be incremented by the loop
-           }
-        }
-    }
-    if (last < src)
-      spec.Append(last, src-last);
-
-    rv = resURL->Init(nsIStandardURL::URLTYPE_STANDARD, -1, spec, aCharset, aBaseURI);
-    if (NS_SUCCEEDED(rv)) {
-        resURL.forget(result);
-    }
-    return rv;
-}
-
-NS_IMETHODIMP
-nsResProtocolHandler::NewChannel2(nsIURI* uri,
-                                  nsILoadInfo* aLoadInfo,
-                                  nsIChannel** result)
-{
-    NS_ENSURE_ARG_POINTER(uri);
-    nsAutoCString spec;
-    nsresult rv = ResolveURI(uri, spec);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsCOMPtr<nsIURI> newURI;
-    rv = NS_NewURI(getter_AddRefs(newURI), spec);
-    NS_ENSURE_SUCCESS(rv, rv);
+NS_IMPL_QUERY_INTERFACE(nsResProtocolHandler, nsIResProtocolHandler,
+                        nsISubstitutingProtocolHandler, nsIProtocolHandler,
+                        nsISupportsWeakReference)
+NS_IMPL_ADDREF_INHERITED(nsResProtocolHandler, SubstitutingProtocolHandler)
+NS_IMPL_RELEASE_INHERITED(nsResProtocolHandler, SubstitutingProtocolHandler)
 
-    rv = NS_NewChannelInternal(result,
-                               newURI,
-                               aLoadInfo);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsLoadFlags loadFlags = 0;
-    (*result)->GetLoadFlags(&loadFlags);
-    (*result)->SetLoadFlags(loadFlags & ~nsIChannel::LOAD_REPLACE);
-    return (*result)->SetOriginalURI(uri);
-}
-
-NS_IMETHODIMP
-nsResProtocolHandler::NewChannel(nsIURI* uri, nsIChannel* *result)
-{
-    return NewChannel2(uri, nullptr, result);
-}
-
-NS_IMETHODIMP 
-nsResProtocolHandler::AllowPort(int32_t port, const char *scheme, bool *_retval)
-{
-    // don't override anything.  
-    *_retval = false;
-    return NS_OK;
-}
-
-//----------------------------------------------------------------------------
-// nsResProtocolHandler::nsIResProtocolHandler
-//----------------------------------------------------------------------------
-
-static void
-SendResourceSubstitution(const nsACString& root, nsIURI* baseURI)
+nsresult
+nsResProtocolHandler::GetSubstitutionInternal(const nsACString& root, nsIURI **result)
 {
-    if (GeckoProcessType_Content == XRE_GetProcessType()) {
-        return;
-    }
-
-    ResourceMapping resourceMapping;
-    resourceMapping.resource = root;
-    if (baseURI) {
-        baseURI->GetSpec(resourceMapping.resolvedURI.spec);
-        baseURI->GetOriginCharset(resourceMapping.resolvedURI.charset);
-    }
-
-    nsTArray<ContentParent*> parents;
-    ContentParent::GetAll(parents);
-    if (!parents.Length()) {
-        return;
-    }
-
-    for (uint32_t i = 0; i < parents.Length(); i++) {
-        unused << parents[i]->SendRegisterChromeItem(resourceMapping);
-    }
-}
-
-NS_IMETHODIMP
-nsResProtocolHandler::SetSubstitution(const nsACString& root, nsIURI *baseURI)
-{
-    if (!baseURI) {
-        mSubstitutions.Remove(root);
-        SendResourceSubstitution(root, baseURI);
-        return NS_OK;
-    }
-
-    // If baseURI isn't a resource URI, we can set the substitution immediately.
-    nsAutoCString scheme;
-    nsresult rv = baseURI->GetScheme(scheme);
-    NS_ENSURE_SUCCESS(rv, rv);
-    if (!scheme.EqualsLiteral("resource")) {
-        mSubstitutions.Put(root, baseURI);
-        SendResourceSubstitution(root, baseURI);
-        return NS_OK;
-    }
-
-    // baseURI is a resource URI, let's resolve it first.
-    nsAutoCString newBase;
-    rv = ResolveURI(baseURI, newBase);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsCOMPtr<nsIURI> newBaseURI;
-    rv = mIOService->NewURI(newBase, nullptr, nullptr,
-                            getter_AddRefs(newBaseURI));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    mSubstitutions.Put(root, newBaseURI);
-    SendResourceSubstitution(root, newBaseURI);
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsResProtocolHandler::GetSubstitution(const nsACString& root, nsIURI **result)
-{
-    NS_ENSURE_ARG_POINTER(result);
-
-    if (mSubstitutions.Get(root, result))
-        return NS_OK;
-
     // try invoking the directory service for "resource:root"
 
     nsAutoCString key;
     key.AssignLiteral("resource:");
     key.Append(root);
 
     nsCOMPtr<nsIFile> file;
     nsresult rv = NS_GetSpecialDirectory(key.get(), getter_AddRefs(file));
     if (NS_FAILED(rv))
         return NS_ERROR_NOT_AVAILABLE;
         
-    rv = mIOService->NewFileURI(file, result);
+    rv = IOService()->NewFileURI(file, result);
     if (NS_FAILED(rv))
         return NS_ERROR_NOT_AVAILABLE;
 
     return NS_OK;
 }
-
-NS_IMETHODIMP
-nsResProtocolHandler::HasSubstitution(const nsACString& root, bool *result)
-{
-    NS_ENSURE_ARG_POINTER(result);
-
-    *result = mSubstitutions.Get(root, nullptr);
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsResProtocolHandler::ResolveURI(nsIURI *uri, nsACString &result)
-{
-    nsresult rv;
-
-    nsAutoCString host;
-    nsAutoCString path;
-
-    rv = uri->GetAsciiHost(host);
-    if (NS_FAILED(rv)) return rv;
-
-    rv = uri->GetPath(path);
-    if (NS_FAILED(rv)) return rv;
-
-    // Unescape the path so we can perform some checks on it.
-    nsAutoCString unescapedPath(path);
-    NS_UnescapeURL(unescapedPath);
-
-    // Don't misinterpret the filepath as an absolute URI.
-    if (unescapedPath.FindChar(':') != -1)
-        return NS_ERROR_MALFORMED_URI;
-
-    if (unescapedPath.FindChar('\\') != -1)
-        return NS_ERROR_MALFORMED_URI;
-
-    const char *p = path.get() + 1; // path always starts with a slash
-    NS_ASSERTION(*(p-1) == '/', "Path did not begin with a slash!");
-
-    if (*p == '/')
-        return NS_ERROR_MALFORMED_URI;
-
-    nsCOMPtr<nsIURI> baseURI;
-    rv = GetSubstitution(host, getter_AddRefs(baseURI));
-    if (NS_FAILED(rv)) return rv;
-
-    rv = baseURI->Resolve(nsDependentCString(p, path.Length()-1), result);
-
-    if (MOZ_LOG_TEST(gResLog, LogLevel::Debug)) {
-        nsAutoCString spec;
-        uri->GetAsciiSpec(spec);
-        MOZ_LOG(gResLog, LogLevel::Debug,
-               ("%s\n -> %s\n", spec.get(), PromiseFlatCString(result).get()));
-    }
-    return rv;
-}
--- a/netwerk/protocol/res/nsResProtocolHandler.h
+++ b/netwerk/protocol/res/nsResProtocolHandler.h
@@ -1,51 +1,40 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsResProtocolHandler_h___
 #define nsResProtocolHandler_h___
 
+#include "SubstitutingProtocolHandler.h"
+
 #include "nsIResProtocolHandler.h"
 #include "nsInterfaceHashtable.h"
 #include "nsWeakReference.h"
 #include "nsStandardURL.h"
 
-class nsIIOService;
-struct ResourceMapping;
-
-// nsResURL : overrides nsStandardURL::GetFile to provide nsIFile resolution
-class nsResURL : public nsStandardURL
+struct SubstitutionMapping;
+class nsResProtocolHandler final : public nsIResProtocolHandler,
+                                   public mozilla::SubstitutingProtocolHandler,
+                                   public nsSupportsWeakReference
 {
 public:
-    nsResURL() : nsStandardURL(true) {}
-    virtual nsStandardURL* StartClone();
-    virtual nsresult EnsureFile();
-    NS_IMETHOD GetClassIDNoAlloc(nsCID *aCID);
-};
-
-class nsResProtocolHandler final : public nsIResProtocolHandler, public nsSupportsWeakReference
-{
-public:
-    NS_DECL_THREADSAFE_ISUPPORTS
-    NS_DECL_NSIPROTOCOLHANDLER
+    NS_DECL_ISUPPORTS_INHERITED
     NS_DECL_NSIRESPROTOCOLHANDLER
 
-    nsResProtocolHandler();
+    NS_FORWARD_NSIPROTOCOLHANDLER(mozilla::SubstitutingProtocolHandler::)
+    NS_FORWARD_NSISUBSTITUTINGPROTOCOLHANDLER(mozilla::SubstitutingProtocolHandler::)
+
+    nsResProtocolHandler()
+      : SubstitutingProtocolHandler("resource", URI_STD | URI_IS_UI_RESOURCE | URI_IS_LOCAL_RESOURCE,
+                                    /* aEnforceFileOrJar = */ false)
+    {}
 
     nsresult Init();
 
-    void CollectSubstitutions(InfallibleTArray<ResourceMapping>& aResources);
-
-private:
-    virtual ~nsResProtocolHandler();
-
-    nsresult Init(nsIFile *aOmniJar);
-    nsresult AddSpecialDir(const char* aSpecialDir, const nsACString& aSubstitution);
-    nsInterfaceHashtable<nsCStringHashKey,nsIURI> mSubstitutions;
-    nsCOMPtr<nsIIOService> mIOService;
-
-    friend class nsResURL;
+protected:
+    nsresult GetSubstitutionInternal(const nsACString& aRoot, nsIURI** aResult) override;
+    virtual ~nsResProtocolHandler() {}
 };
 
 #endif /* nsResProtocolHandler_h___ */
--- a/netwerk/test/unit/test_predictor.js
+++ b/netwerk/test/unit/test_predictor.js
@@ -388,25 +388,27 @@ function registerObserver() {
 }
 
 function unregisterObserver() {
   Services.obs.removeObserver(observer, "predictor-reset-complete");
 }
 
 function run_test_real() {
   tests.forEach(add_test);
-  do_get_profile()
-  predictor = Cc["@mozilla.org/network/predictor;1"].getService(Ci.nsINetworkPredictor);
-
-  registerObserver();
+  do_get_profile();
 
   Services.prefs.setBoolPref("network.predictor.enabled", true);
   Services.prefs.setBoolPref("network.predictor.cleaned-up", true);
   Services.prefs.setBoolPref("browser.cache.use_new_backend_temp", true);
   Services.prefs.setIntPref("browser.cache.use_new_backend", 1);
+
+  predictor = Cc["@mozilla.org/network/predictor;1"].getService(Ci.nsINetworkPredictor);
+
+  registerObserver();
+
   do_register_cleanup(() => {
     Services.prefs.clearUserPref("network.predictor.preconnect-min-confidence");
     Services.prefs.clearUserPref("network.predictor.enabled");
     Services.prefs.clearUserPref("network.predictor.cleaned-up");
     Services.prefs.clearUserPref("browser.cache.use_new_backend_temp");
     Services.prefs.clearUserPref("browser.cache.use_new_backend");
   });
 
--- a/security/manager/ssl/CertBlocklist.cpp
+++ b/security/manager/ssl/CertBlocklist.cpp
@@ -1,9 +1,10 @@
 /* -*- Mode: C++; tab-width: 2; 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 "CertBlocklist.h"
 #include "mozilla/Base64.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/unused.h"
@@ -357,25 +358,16 @@ CertBlocklist::AddRevokedCertInternal(co
     mBlocklist.RemoveEntry(item);
     item.mIsCurrent = true;
   }
   mBlocklist.PutEntry(item);
 
   return NS_OK;
 }
 
-// Data needed for writing blocklist items out to the revocations file
-struct BlocklistSaveInfo
-{
-  IssuerTable issuerTable;
-  BlocklistStringSet issuers;
-  nsCOMPtr<nsIOutputStream> outputStream;
-  bool success;
-};
-
 // Write a line for a given string in the output stream
 nsresult
 WriteLine(nsIOutputStream* outputStream, const nsACString& string)
 {
   nsAutoCString line(string);
   line.Append('\n');
 
   const char* data = line.get();
@@ -392,91 +384,16 @@ WriteLine(nsIOutputStream* outputStream,
       return NS_ERROR_FAILURE;
     }
     length -= bytesWritten;
     data += bytesWritten;
   }
   return rv;
 }
 
-// sort blocklist items into lists of serials for each issuer
-PLDHashOperator
-ProcessBlocklistEntry(BlocklistItemKey* aHashKey, void* aUserArg)
-{
-  BlocklistSaveInfo* saveInfo = reinterpret_cast<BlocklistSaveInfo*>(aUserArg);
-  CertBlocklistItem item = aHashKey->GetKey();
-
-  if (!item.mIsCurrent) {
-    return PL_DHASH_NEXT;
-  }
-
-  nsAutoCString encDN;
-  nsAutoCString encOther;
-
-  nsresult rv = item.ToBase64(encDN, encOther);
-  if (NS_FAILED(rv)) {
-    saveInfo->success = false;
-    return PL_DHASH_STOP;
-  }
-
-  // If it's a subject / public key block, write it straight out
-  if (item.mItemMechanism == BlockBySubjectAndPubKey) {
-    WriteLine(saveInfo->outputStream, encDN);
-    WriteLine(saveInfo->outputStream, NS_LITERAL_CSTRING("\t") + encOther);
-    return PL_DHASH_NEXT;
-  }
-
-  // Otherwise, we have to group entries by issuer
-  saveInfo->issuers.PutEntry(encDN);
-  BlocklistStringSet* issuerSet = saveInfo->issuerTable.Get(encDN);
-  if (!issuerSet) {
-    issuerSet = new BlocklistStringSet();
-    saveInfo->issuerTable.Put(encDN, issuerSet);
-  }
-  issuerSet->PutEntry(encOther);
-  return PL_DHASH_NEXT;
-}
-
-// write serial data to the output stream
-PLDHashOperator
-WriteSerial(nsCStringHashKey* aHashKey, void* aUserArg)
-{
-  BlocklistSaveInfo* saveInfo = reinterpret_cast<BlocklistSaveInfo*>(aUserArg);
-
-  nsresult rv = WriteLine(saveInfo->outputStream,
-                          NS_LITERAL_CSTRING(" ") + aHashKey->GetKey());
-  if (NS_FAILED(rv)) {
-    saveInfo->success = false;
-    return PL_DHASH_STOP;
-  }
-  return PL_DHASH_NEXT;
-}
-
-// Write issuer data to the output stream
-PLDHashOperator
-WriteIssuer(nsCStringHashKey* aHashKey, void* aUserArg)
-{
-  BlocklistSaveInfo* saveInfo = reinterpret_cast<BlocklistSaveInfo*>(aUserArg);
-  nsAutoPtr<BlocklistStringSet> issuerSet;
-
-  saveInfo->issuerTable.RemoveAndForget(aHashKey->GetKey(), issuerSet);
-
-  nsresult rv = WriteLine(saveInfo->outputStream, aHashKey->GetKey());
-  if (NS_FAILED(rv)) {
-    return PL_DHASH_STOP;
-  }
-
-  issuerSet->EnumerateEntries(WriteSerial, saveInfo);
-  if (!saveInfo->success) {
-    saveInfo->success = false;
-    return PL_DHASH_STOP;
-  }
-  return PL_DHASH_NEXT;
-}
-
 // void saveEntries();
 // Store the blockist in a text file containing base64 encoded issuers and
 // serial numbers.
 //
 // Each item is stored on a separate line; each issuer is followed by its
 // revoked serial numbers, indented by one space.
 //
 // lines starting with a # character are ignored
@@ -497,46 +414,90 @@ CertBlocklist::SaveEntries()
 
   if (!mBackingFile) {
     // We allow this to succeed with no profile directory for tests
     MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
            ("CertBlocklist::SaveEntries no file in profile to write to"));
     return NS_OK;
   }
 
-  BlocklistSaveInfo saveInfo;
-  saveInfo.success = true;
-  rv = NS_NewAtomicFileOutputStream(getter_AddRefs(saveInfo.outputStream),
+  // Data needed for writing blocklist items out to the revocations file
+  IssuerTable issuerTable;
+  BlocklistStringSet issuers;
+  nsCOMPtr<nsIOutputStream> outputStream;
+
+  rv = NS_NewAtomicFileOutputStream(getter_AddRefs(outputStream),
                                     mBackingFile, -1, -1, 0);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  rv = WriteLine(saveInfo.outputStream,
+  rv = WriteLine(outputStream,
                  NS_LITERAL_CSTRING("# Auto generated contents. Do not edit."));
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  mBlocklist.EnumerateEntries(ProcessBlocklistEntry, &saveInfo);
-  if (!saveInfo.success) {
-    MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
-           ("CertBlocklist::SaveEntries writing revocation data failed"));
-    return NS_ERROR_FAILURE;
+  // Sort blocklist items into lists of serials for each issuer
+  for (auto iter = mBlocklist.Iter(); !iter.Done(); iter.Next()) {
+    CertBlocklistItem item = iter.Get()->GetKey();
+    if (!item.mIsCurrent) {
+      continue;
+    }
+
+    nsAutoCString encDN;
+    nsAutoCString encOther;
+
+    nsresult rv = item.ToBase64(encDN, encOther);
+    if (NS_FAILED(rv)) {
+      MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
+             ("CertBlocklist::SaveEntries writing revocation data failed"));
+      return NS_ERROR_FAILURE;
+    }
+
+    // If it's a subject / public key block, write it straight out
+    if (item.mItemMechanism == BlockBySubjectAndPubKey) {
+      WriteLine(outputStream, encDN);
+      WriteLine(outputStream, NS_LITERAL_CSTRING("\t") + encOther);
+      continue;
+    }
+
+    // Otherwise, we have to group entries by issuer
+    issuers.PutEntry(encDN);
+    BlocklistStringSet* issuerSet = issuerTable.Get(encDN);
+    if (!issuerSet) {
+      issuerSet = new BlocklistStringSet();
+      issuerTable.Put(encDN, issuerSet);
+    }
+    issuerSet->PutEntry(encOther);
   }
 
-  saveInfo.issuers.EnumerateEntries(WriteIssuer, &saveInfo);
-  if (!saveInfo.success) {
-    MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
-           ("CertBlocklist::SaveEntries writing revocation data failed"));
-    return NS_ERROR_FAILURE;
+  for (auto iter = issuers.Iter(); !iter.Done(); iter.Next()) {
+    nsCStringHashKey* hashKey = iter.Get();
+    nsAutoPtr<BlocklistStringSet> issuerSet;
+    issuerTable.RemoveAndForget(hashKey->GetKey(), issuerSet);
+
+    nsresult rv = WriteLine(outputStream, hashKey->GetKey());
+    if (NS_FAILED(rv)) {
+      break;
+    }
+
+    // Write serial data to the output stream
+    for (auto iter = issuerSet->Iter(); !iter.Done(); iter.Next()) {
+      nsresult rv = WriteLine(outputStream,
+                              NS_LITERAL_CSTRING(" ") + iter.Get()->GetKey());
+      if (NS_FAILED(rv)) {
+        MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
+               ("CertBlocklist::SaveEntries writing revocation data failed"));
+        return NS_ERROR_FAILURE;
+      }
+    }
   }
 
-  nsCOMPtr<nsISafeOutputStream> safeStream =
-      do_QueryInterface(saveInfo.outputStream);
+  nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(outputStream);
   NS_ASSERTION(safeStream, "expected a safe output stream!");
   if (!safeStream) {
     return NS_ERROR_FAILURE;
   }
   rv = safeStream->Finish();
   if (NS_FAILED(rv)) {
     MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
            ("CertBlocklist::SaveEntries saving revocation data failed"));
--- a/security/manager/ssl/nsCertOverrideService.cpp
+++ b/security/manager/ssl/nsCertOverrideService.cpp
@@ -1,11 +1,11 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
+/* -*- Mode: C++; tab-width: 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 "nsCertOverrideService.h"
 
 #include "NSSCertDBTrustDomain.h"
 #include "ScopedNSSTypes.h"
 #include "SharedSSLState.h"
@@ -158,36 +158,28 @@ nsCertOverrideService::Observe(nsISuppor
 
 void
 nsCertOverrideService::RemoveAllFromMemory()
 {
   ReentrantMonitorAutoEnter lock(monitor);
   mSettingsTable.Clear();
 }
 
-static PLDHashOperator
-RemoveTemporariesCallback(nsCertOverrideEntry *aEntry,
-                          void *aArg)
-{
-  if (aEntry && aEntry->mSettings.mIsTemporary) {
-    aEntry->mSettings.mCert = nullptr;
-    return PL_DHASH_REMOVE;
-  }
-
-  return PL_DHASH_NEXT;
-}
-
 void
 nsCertOverrideService::RemoveAllTemporaryOverrides()
 {
-  {
-    ReentrantMonitorAutoEnter lock(monitor);
-    mSettingsTable.EnumerateEntries(RemoveTemporariesCallback, nullptr);
-    // no need to write, as temporaries are never written to disk
+  ReentrantMonitorAutoEnter lock(monitor);
+  for (auto iter = mSettingsTable.Iter(); !iter.Done(); iter.Next()) {
+    nsCertOverrideEntry *entry = iter.Get();
+    if (entry->mSettings.mIsTemporary) {
+      entry->mSettings.mCert = nullptr;
+      iter.Remove();
+    }
   }
+  // no need to write, as temporaries are never written to disk
 }
 
 nsresult
 nsCertOverrideService::Read()
 {
   ReentrantMonitorAutoEnter lock(monitor);
 
   // If we don't have a profile, then we won't try to read any settings file.
@@ -265,54 +257,16 @@ nsCertOverrideService::Read()
                    nullptr, // don't have the cert
                    false, // not temporary
                    algo_string, fingerprint, bits, db_key);
   }
 
   return NS_OK;
 }
 
-static PLDHashOperator
-WriteEntryCallback(nsCertOverrideEntry *aEntry,
-                   void *aArg)
-{
-  static const char kTab[] = "\t";
-
-  nsIOutputStream *rawStreamPtr = (nsIOutputStream *)aArg;
-
-  uint32_t unused;
-
-  if (rawStreamPtr && aEntry)
-  {
-    const nsCertOverride &settings = aEntry->mSettings;
-    if (settings.mIsTemporary)
-      return PL_DHASH_NEXT;
-
-    nsAutoCString bits_string;
-    nsCertOverride::convertBitsToString(settings.mOverrideBits, 
-                                            bits_string);
-
-    rawStreamPtr->Write(aEntry->mHostWithPort.get(), aEntry->mHostWithPort.Length(), &unused);
-    rawStreamPtr->Write(kTab, sizeof(kTab) - 1, &unused);
-    rawStreamPtr->Write(settings.mFingerprintAlgOID.get(), 
-                        settings.mFingerprintAlgOID.Length(), &unused);
-    rawStreamPtr->Write(kTab, sizeof(kTab) - 1, &unused);
-    rawStreamPtr->Write(settings.mFingerprint.get(), 
-                        settings.mFingerprint.Length(), &unused);
-    rawStreamPtr->Write(kTab, sizeof(kTab) - 1, &unused);
-    rawStreamPtr->Write(bits_string.get(), 
-                        bits_string.Length(), &unused);
-    rawStreamPtr->Write(kTab, sizeof(kTab) - 1, &unused);
-    rawStreamPtr->Write(settings.mDBKey.get(), settings.mDBKey.Length(), &unused);
-    rawStreamPtr->Write(NS_LINEBREAK, NS_LINEBREAK_LEN, &unused);
-  }
-
-  return PL_DHASH_NEXT;
-}
-
 nsresult
 nsCertOverrideService::Write()
 {
   ReentrantMonitorAutoEnter lock(monitor);
 
   // If we don't have any profile, then we won't try to write any file
   if (!mSettingsFile) {
     return NS_OK;
@@ -340,18 +294,44 @@ nsCertOverrideService::Write()
       "# PSM Certificate Override Settings file" NS_LINEBREAK
       "# This is a generated file!  Do not edit." NS_LINEBREAK;
 
   /* see ::Read for file format */
 
   uint32_t unused;
   bufferedOutputStream->Write(kHeader, sizeof(kHeader) - 1, &unused);
 
-  nsIOutputStream *rawStreamPtr = bufferedOutputStream;
-  mSettingsTable.EnumerateEntries(WriteEntryCallback, rawStreamPtr);
+  static const char kTab[] = "\t";
+  for (auto iter = mSettingsTable.Iter(); !iter.Done(); iter.Next()) {
+    nsCertOverrideEntry *entry = iter.Get();
+
+    const nsCertOverride &settings = entry->mSettings;
+    if (settings.mIsTemporary) {
+      continue;
+    }
+
+    nsAutoCString bits_string;
+    nsCertOverride::convertBitsToString(settings.mOverrideBits, bits_string);
+
+    bufferedOutputStream->Write(entry->mHostWithPort.get(),
+                                entry->mHostWithPort.Length(), &unused);
+    bufferedOutputStream->Write(kTab, sizeof(kTab) - 1, &unused);
+    bufferedOutputStream->Write(settings.mFingerprintAlgOID.get(),
+                                settings.mFingerprintAlgOID.Length(), &unused);
+    bufferedOutputStream->Write(kTab, sizeof(kTab) - 1, &unused);
+    bufferedOutputStream->Write(settings.mFingerprint.get(),
+                                settings.mFingerprint.Length(), &unused);
+    bufferedOutputStream->Write(kTab, sizeof(kTab) - 1, &unused);
+    bufferedOutputStream->Write(bits_string.get(),
+                                bits_string.Length(), &unused);
+    bufferedOutputStream->Write(kTab, sizeof(kTab) - 1, &unused);
+    bufferedOutputStream->Write(settings.mDBKey.get(),
+                                settings.mDBKey.Length(), &unused);
+    bufferedOutputStream->Write(NS_LINEBREAK, NS_LINEBREAK_LEN, &unused);
+  }
 
   // All went ok. Maybe except for problems in Write(), but the stream detects
   // that for us
   nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(bufferedOutputStream);
   NS_ASSERTION(safeStream, "expected a safe output stream!");
   if (safeStream) {
     rv = safeStream->Finish();
     if (NS_FAILED(rv)) {
@@ -599,35 +579,26 @@ nsCertOverrideService::ClearValidityOver
     SSL_ClearSessionCache();
   } else {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   return NS_OK;
 }
 
-static PLDHashOperator
-CountPermanentEntriesCallback(nsCertOverrideEntry* aEntry, void* aArg)
-{
-  uint32_t* overrideCount = reinterpret_cast<uint32_t*>(aArg);
-  if (aEntry && !aEntry->mSettings.mIsTemporary) {
-    *overrideCount = *overrideCount + 1;
-    return PL_DHASH_NEXT;
-  }
-
-  return PL_DHASH_NEXT;
-}
-
 void
 nsCertOverrideService::CountPermanentOverrideTelemetry()
 {
   ReentrantMonitorAutoEnter lock(monitor);
   uint32_t overrideCount = 0;
-  mSettingsTable.EnumerateEntries(CountPermanentEntriesCallback,
-                                  &overrideCount);
+  for (auto iter = mSettingsTable.Iter(); !iter.Done(); iter.Next()) {
+    if (!iter.Get()->mSettings.mIsTemporary) {
+      overrideCount++;
+    }
+  }
   Telemetry::Accumulate(Telemetry::SSL_PERMANENT_CERT_ERROR_OVERRIDES,
                         overrideCount);
 }
 
 static bool
 matchesDBKey(nsIX509Cert *cert, const char *match_dbkey)
 {
   char *dbkey = nullptr;
@@ -670,143 +641,80 @@ matchesDBKey(nsIX509Cert *cert, const ch
     ++key1;
     ++key2;
   }
 
   PR_Free(dbkey);
   return !found_mismatch;
 }
 
-struct nsCertAndBoolsAndInt
-{
-  nsIX509Cert *cert;
-  bool aCheckTemporaries;
-  bool aCheckPermanents;
-  uint32_t counter;
-
-  SECOidTag mOidTagForStoringNewHashes;
-  nsCString mDottedOidForStoringNewHashes;
-};
-
-static PLDHashOperator
-FindMatchingCertCallback(nsCertOverrideEntry *aEntry,
-                         void *aArg)
-{
-  nsCertAndBoolsAndInt *cai = (nsCertAndBoolsAndInt *)aArg;
-
-  if (cai && aEntry)
-  {
-    const nsCertOverride &settings = aEntry->mSettings;
-    bool still_ok = true;
-
-    if ((settings.mIsTemporary && !cai->aCheckTemporaries)
-        ||
-        (!settings.mIsTemporary && !cai->aCheckPermanents)) {
-      still_ok = false;
-    }
-
-    if (still_ok && matchesDBKey(cai->cert, settings.mDBKey.get())) {
-      nsAutoCString cert_fingerprint;
-      nsresult rv = NS_ERROR_UNEXPECTED;
-      if (settings.mFingerprintAlgOID.Equals(cai->mDottedOidForStoringNewHashes)) {
-        rv = GetCertFingerprintByOidTag(cai->cert,
-               cai->mOidTagForStoringNewHashes, cert_fingerprint);
-      }
-      if (NS_SUCCEEDED(rv) &&
-          settings.mFingerprint.Equals(cert_fingerprint)) {
-        cai->counter++;
-      }
-    }
-  }
-
-  return PL_DHASH_NEXT;
-}
-
 NS_IMETHODIMP
 nsCertOverrideService::IsCertUsedForOverrides(nsIX509Cert *aCert, 
                                               bool aCheckTemporaries,
                                               bool aCheckPermanents,
                                               uint32_t *_retval)
 {
   NS_ENSURE_ARG(aCert);
   NS_ENSURE_ARG(_retval);
 
-  nsCertAndBoolsAndInt cai;
-  cai.cert = aCert;
-  cai.aCheckTemporaries = aCheckTemporaries;
-  cai.aCheckPermanents = aCheckPermanents;
-  cai.counter = 0;
-  cai.mOidTagForStoringNewHashes = mOidTagForStoringNewHashes;
-  cai.mDottedOidForStoringNewHashes = mDottedOidForStoringNewHashes;
-
+  uint32_t counter = 0;
   {
     ReentrantMonitorAutoEnter lock(monitor);
-    mSettingsTable.EnumerateEntries(FindMatchingCertCallback, &cai);
-  }
-  *_retval = cai.counter;
-  return NS_OK;
-}
-
-struct nsCertAndPointerAndCallback
-{
-  nsIX509Cert *cert;
-  void *userdata;
-  nsCertOverrideService::CertOverrideEnumerator enumerator;
-
-  SECOidTag mOidTagForStoringNewHashes;
-  nsCString mDottedOidForStoringNewHashes;
-};
+    for (auto iter = mSettingsTable.Iter(); !iter.Done(); iter.Next()) {
+      const nsCertOverride &settings = iter.Get()->mSettings;
+      bool still_ok = true;
 
-static PLDHashOperator
-EnumerateCertOverridesCallback(nsCertOverrideEntry *aEntry,
-                               void *aArg)
-{
-  nsCertAndPointerAndCallback *capac = (nsCertAndPointerAndCallback *)aArg;
+      if (( settings.mIsTemporary && !aCheckTemporaries) ||
+          (!settings.mIsTemporary && !aCheckPermanents)) {
+        still_ok = false;
+      }
 
-  if (capac && aEntry)
-  {
-    const nsCertOverride &settings = aEntry->mSettings;
-
-    if (!capac->cert) {
-      (*capac->enumerator)(settings, capac->userdata);
-    }
-    else {
-      if (matchesDBKey(capac->cert, settings.mDBKey.get())) {
+      if (still_ok && matchesDBKey(aCert, settings.mDBKey.get())) {
         nsAutoCString cert_fingerprint;
         nsresult rv = NS_ERROR_UNEXPECTED;
-        if (settings.mFingerprintAlgOID.Equals(capac->mDottedOidForStoringNewHashes)) {
-          rv = GetCertFingerprintByOidTag(capac->cert,
-                 capac->mOidTagForStoringNewHashes, cert_fingerprint);
+        if (settings.mFingerprintAlgOID.Equals(mDottedOidForStoringNewHashes)) {
+          rv = GetCertFingerprintByOidTag(aCert,
+                 mOidTagForStoringNewHashes, cert_fingerprint);
         }
         if (NS_SUCCEEDED(rv) &&
             settings.mFingerprint.Equals(cert_fingerprint)) {
-          (*capac->enumerator)(settings, capac->userdata);
+          counter++;
         }
       }
     }
   }
-
-  return PL_DHASH_NEXT;
+  *_retval = counter;
+  return NS_OK;
 }
 
-nsresult 
+nsresult
 nsCertOverrideService::EnumerateCertOverrides(nsIX509Cert *aCert,
-                         CertOverrideEnumerator enumerator,
+                         CertOverrideEnumerator aEnumerator,
                          void *aUserData)
 {
-  nsCertAndPointerAndCallback capac;
-  capac.cert = aCert;
-  capac.userdata = aUserData;
-  capac.enumerator = enumerator;
-  capac.mOidTagForStoringNewHashes = mOidTagForStoringNewHashes;
-  capac.mDottedOidForStoringNewHashes = mDottedOidForStoringNewHashes;
+  ReentrantMonitorAutoEnter lock(monitor);
+  for (auto iter = mSettingsTable.Iter(); !iter.Done(); iter.Next()) {
+    const nsCertOverride &settings = iter.Get()->mSettings;
 
-  {
-    ReentrantMonitorAutoEnter lock(monitor);
-    mSettingsTable.EnumerateEntries(EnumerateCertOverridesCallback, &capac);
+    if (!aCert) {
+      aEnumerator(settings, aUserData);
+    } else {
+      if (matchesDBKey(aCert, settings.mDBKey.get())) {
+        nsAutoCString cert_fingerprint;
+        nsresult rv = NS_ERROR_UNEXPECTED;
+        if (settings.mFingerprintAlgOID.Equals(mDottedOidForStoringNewHashes)) {
+          rv = GetCertFingerprintByOidTag(aCert,
+                 mOidTagForStoringNewHashes, cert_fingerprint);
+        }
+        if (NS_SUCCEEDED(rv) &&
+            settings.mFingerprint.Equals(cert_fingerprint)) {
+          aEnumerator(settings, aUserData);
+        }
+      }
+    }
   }
   return NS_OK;
 }
 
 void
 nsCertOverrideService::GetHostWithPort(const nsACString & aHostName, int32_t aPort, nsACString& _retval)
 {
   nsAutoCString hostPort(aHostName);
--- a/services/fxaccounts/FxAccountsCommon.js
+++ b/services/fxaccounts/FxAccountsCommon.js
@@ -171,16 +171,17 @@ exports.ERROR_MISSING_BODY_PARAMETERS   
 exports.ERROR_MISSING_CONTENT_LENGTH         = "MISSING_CONTENT_LENGTH";
 exports.ERROR_NO_TOKEN_SESSION               = "NO_TOKEN_SESSION";
 exports.ERROR_NO_SILENT_REFRESH_AUTH         = "NO_SILENT_REFRESH_AUTH";
 exports.ERROR_NOT_VALID_JSON_BODY            = "NOT_VALID_JSON_BODY";
 exports.ERROR_OFFLINE                        = "OFFLINE";
 exports.ERROR_PERMISSION_DENIED              = "PERMISSION_DENIED";
 exports.ERROR_REQUEST_BODY_TOO_LARGE         = "REQUEST_BODY_TOO_LARGE";
 exports.ERROR_SERVER_ERROR                   = "SERVER_ERROR";
+exports.ERROR_SYNC_DISABLED                  = "SYNC_DISABLED";
 exports.ERROR_TOO_MANY_CLIENT_REQUESTS       = "TOO_MANY_CLIENT_REQUESTS";
 exports.ERROR_SERVICE_TEMP_UNAVAILABLE       = "SERVICE_TEMPORARY_UNAVAILABLE";
 exports.ERROR_UI_ERROR                       = "UI_ERROR";
 exports.ERROR_UI_REQUEST                     = "UI_REQUEST";
 exports.ERROR_PARSE                          = "PARSE_ERROR";
 exports.ERROR_NETWORK                        = "NETWORK_ERROR";
 exports.ERROR_UNKNOWN                        = "UNKNOWN_ERROR";
 exports.ERROR_UNVERIFIED_ACCOUNT             = "UNVERIFIED_ACCOUNT";
--- a/services/fxaccounts/FxAccountsManager.jsm
+++ b/services/fxaccounts/FxAccountsManager.jsm
@@ -122,17 +122,25 @@ this.FxAccountsManager = {
     let client = this._getFxAccountsClient();
     return this._fxAccounts.getSignedInUser().then(
       user => {
         if ((!this._refreshing) && user) {
           return this._error(ERROR_ALREADY_SIGNED_IN_USER, {
             user: this._user
           });
         }
-        return client[aMethod](aEmail, aPassword);
+        let syncEnabled = false;
+        try {
+          syncEnabled = Services.prefs.getBoolPref("services.sync.enabled");
+        } catch(e) {
+          dump(e + "\n");
+        }
+        // XXX Refetch FxA credentials if services.sync.enabled preference
+        //     changes. Bug 1183103
+        return client[aMethod](aEmail, aPassword, syncEnabled);
       }
     ).then(
       user => {
         let error = this._getError(user);
         if (!user || !user.uid || !user.sessionToken || error) {
           return this._error(error ? error : ERROR_INTERNAL_INVALID_USER, {
             user: user
           });
@@ -572,13 +580,42 @@ this.FxAccountsManager = {
         }
         log.debug("No signed in user");
         if (aOptions && aOptions.silent) {
           return Promise.resolve(null);
         }
         return this._uiRequest(UI_REQUEST_SIGN_IN_FLOW, aAudience, principal);
       }
     );
+  },
+
+  getKeys: function() {
+    let syncEnabled = false;
+    try {
+      syncEnabled = Services.prefs.getBoolPref("services.sync.enabled");
+    } catch(e) {
+      dump("Sync is disabled, so you won't get the keys. " + e + "\n");
+    }
+
+    if (!syncEnabled) {
+      return Promise.reject(ERROR_SYNC_DISABLED);
+    }
+
+    return this.getAccount().then(
+      user => {
+        if (!user) {
+          log.debug("No signed in user");
+          return Promise.resolve(null);
+        }
+
+        if (!user.verified) {
+          return this._error(ERROR_UNVERIFIED_ACCOUNT, {
+            user: user
+          });
+        }
+
+        return this._fxAccounts.getKeys();
+      }
+    );
   }
-
 };
 
 FxAccountsManager.init();
--- a/services/fxaccounts/tests/xpcshell/test_manager.js
+++ b/services/fxaccounts/tests/xpcshell/test_manager.js
@@ -96,16 +96,17 @@ const kFxAccounts = fxAccounts;
 // and change it for a mock FxAccounts.
 FxAccountsManager._fxAccounts = {
   _reject: false,
   _getSignedInUserCalled: false,
   _setSignedInUserCalled: false,
 
   _error: 'error',
   _assertion: 'assertion',
+  _keys: 'keys',
   _signedInUser: null,
 
   _reset: function() {
     this._getSignedInUserCalled = false;
     this._setSignedInUserCalled = false;
     this._reject = false;
   },
 
@@ -134,16 +135,23 @@ FxAccountsManager._fxAccounts = {
   getSignedInUser: function() {
     this._getSignedInUserCalled = true;
     let deferred = Promise.defer();
     this._reject ? deferred.reject(this._error)
                  : deferred.resolve(this._signedInUser);
     return deferred.promise;
   },
 
+  getKeys: function() {
+    let deferred = Promise.defer();
+    this._reject ? deferred.reject(this._error)
+                 : deferred.resolve(this._keys);
+    return deferred.promise;
+  },
+
   resendVerificationEmail: function() {
     return this.getSignedInUser().then(data => {
       if (data) {
         return Promise.resolve(true);
       }
       throw new Error("Cannot resend verification email; no signed-in user");
     });
   },
@@ -894,8 +902,78 @@ add_test(function() {
   do_print("= fxaccounts:onlogout notification =");
   do_check_true(FxAccountsManager._activeSession != null);
   Services.obs.notifyObservers(null, ONLOGOUT_NOTIFICATION, null);
   do_execute_soon(function() {
     do_check_null(FxAccountsManager._activeSession);
     run_next_test();
   });
 });
+
+add_test(function(test_getKeys_sync_disabled) {
+  do_print("= getKeys sync disabled =");
+  Services.prefs.setBoolPref("services.sync.enabled", false);
+  FxAccountsManager.getKeys().then(
+    result => {
+      do_throw("Unexpected success");
+    },
+    error => {
+      do_check_eq(error, ERROR_SYNC_DISABLED);
+      Services.prefs.clearUserPref("services.sync.enabled");
+      run_next_test();
+    }
+  );
+});
+
+add_test(function(test_getKeys_no_session) {
+  do_print("= getKeys no session =");
+  Services.prefs.setBoolPref("services.sync.enabled", true);
+  FxAccountsManager._fxAccounts._signedInUser = null;
+  FxAccountsManager._activeSession = null;
+  FxAccountsManager.getKeys().then(
+    result => {
+      do_check_null(result);
+      FxAccountsManager._fxAccounts._reset();
+      Services.prefs.clearUserPref("services.sync.enabled");
+      run_next_test();
+    },
+    error => {
+      do_throw("Unexpected error: " + error);
+    }
+  );
+});
+
+add_test(function(test_getKeys_unverified_account) {
+  do_print("= getKeys unverified =");
+  Services.prefs.setBoolPref("services.sync.enabled", true);
+  FakeFxAccountsClient._verified = false;
+  FxAccountsManager.signIn("user@domain.org", "password").then(result => {
+    do_check_false(result.verified);
+    return FxAccountsManager.getKeys();
+  }).then(result => {
+      do_throw("Unexpected success");
+    },
+    error => {
+      do_check_eq(error.error, ERROR_UNVERIFIED_ACCOUNT);
+      FxAccountsManager._fxAccounts._reset();
+      Services.prefs.clearUserPref("services.sync.enabled");
+      FxAccountsManager.signOut().then(run_next_test)
+    }
+  );
+});
+
+add_test(function(test_getKeys_success) {
+  do_print("= getKeys success =");
+  Services.prefs.setBoolPref("services.sync.enabled", true);
+  FakeFxAccountsClient._verified = true;
+  FxAccountsManager.signIn("user@domain.org", "password").then(result => {
+    return FxAccountsManager.getKeys();
+  }).then(result => {
+      do_check_eq(result, FxAccountsManager._fxAccounts._keys);
+      FxAccountsManager._fxAccounts._reset();
+      Services.prefs.clearUserPref("services.sync.enabled");
+      run_next_test();
+    },
+    error => {
+      do_throw("Unexpected error " + error);
+    }
+  );
+});
--- a/services/sync/modules/engines/bookmarks.js
+++ b/services/sync/modules/engines/bookmarks.js
@@ -264,21 +264,34 @@ BookmarksEngine.prototype = {
 
           // Smart bookmarks map to their annotation value.
           let queryId;
           try {
             queryId = PlacesUtils.annotations.getItemAnnotation(
               id, SMART_BOOKMARKS_ANNO);
           } catch(ex) {}
 
-          if (queryId)
+          if (queryId) {
             key = "q" + queryId;
-          else
-            key = "b" + PlacesUtils.bookmarks.getBookmarkURI(id).spec + ":" +
-                  PlacesUtils.bookmarks.getItemTitle(id);
+          } else {
+            let uri;
+            try {
+              uri = PlacesUtils.bookmarks.getBookmarkURI(id);
+            } catch (ex) {
+              // Bug 1182366 - NS_ERROR_MALFORMED_URI here stops bookmarks sync.
+              this._log.warn("Deleting bookmark with invalid URI. id: " + id);
+              try {
+                PlacesUtils.bookmarks.removeItem(id);
+              } catch (ex) {
+                this._log.warn("Failed to delete invalid bookmark", ex);
+              }
+              continue;
+            }
+            key = "b" + uri.spec + ":" + PlacesUtils.bookmarks.getItemTitle(id);
+          }
           break;
         case PlacesUtils.bookmarks.TYPE_FOLDER:
           key = "f" + PlacesUtils.bookmarks.getItemTitle(id);
           break;
         case PlacesUtils.bookmarks.TYPE_SEPARATOR:
           key = "s" + PlacesUtils.bookmarks.getItemIndex(id);
           break;
         default:
--- a/testing/mozharness/configs/android/android_panda_talos_releng.py
+++ b/testing/mozharness/configs/android/android_panda_talos_releng.py
@@ -12,18 +12,16 @@ config = {
         "preflight_talos": "remotePerfConfigurator.py",
         "talos": "run_tests.py",
     },
     "retry_url":  "http://talos-bundles.pvt.build.mozilla.org/zips/retry.zip",
     "verify_path":  "/builds/sut_tools/verify.py",
     "install_app_path":  "/builds/sut_tools/installApp.py",
     "talos_from_code_url": "https://hg.mozilla.org/%s/raw-file/%s/testing/talos/talos_from_code.py",
     "talos_json_url": "https://hg.mozilla.org/%s/raw-file/%s/testing/talos/talos.json",
-    "datazilla_urls": ["https://datazilla.mozilla.org/talos"],
-    "datazilla_authfile": os.path.join(os.getcwd(), "oauth.txt"),
     #remotePerfConfigurator.py options
     "preflight_talos_options": [
         "-v", "-e", "%(app_name)s",
         "-t", "%(hostname)s",
         "--branchName=%(talos_branch)s",
         "--resultsServer=graphs.mozilla.org",
         "--resultsLink=/server/collect.cgi",
         "--noChrome",
--- a/testing/mozharness/configs/talos/linux_config.py
+++ b/testing/mozharness/configs/talos/linux_config.py
@@ -15,18 +15,16 @@ config = {
     "pip_index": False,
     "use_talos_json": True,
     "exes": {
         'python': PYTHON,
         'virtualenv': [PYTHON, '/tools/misc-python/virtualenv.py'],
     },
     "title": os.uname()[1].lower().split('.')[0],
     "results_url": "http://graphs.mozilla.org/server/collect.cgi",
-    "datazilla_urls": ["https://datazilla.mozilla.org/talos"],
-    "datazilla_authfile": os.path.join(os.getcwd(), "oauth.txt"),
     "default_actions": [
         "clobber",
         "read-buildbot-config",
         "download-and-extract",
         "clone-talos",
         "create-virtualenv",
         "install",
         "run-tests",
--- a/testing/mozharness/configs/talos/mac_config.py
+++ b/testing/mozharness/configs/talos/mac_config.py
@@ -25,18 +25,16 @@ config = {
     "pip_index": False,
     "use_talos_json": True,
     "exes": {
         'python': PYTHON,
         'virtualenv': [PYTHON, '/tools/misc-python/virtualenv.py'],
     },
     "title": os.uname()[1].lower().split('.')[0],
     "results_url": "http://graphs.mozilla.org/server/collect.cgi",
-    "datazilla_urls": ["https://datazilla.mozilla.org/talos"],
-    "datazilla_authfile": os.path.join(os.getcwd(), "oauth.txt"),
     "default_actions": [
         "clobber",
         "read-buildbot-config",
         "download-and-extract",
         "clone-talos",
         "create-virtualenv",
         "install",
         "run-tests",
--- a/testing/mozharness/configs/talos/windows_config.py
+++ b/testing/mozharness/configs/talos/windows_config.py
@@ -24,18 +24,16 @@ config = {
         'easy_install': ['%s/scripts/python' % VENV_PATH,
                          '%s/scripts/easy_install-2.7-script.py' % VENV_PATH],
         'mozinstall': ['%s/scripts/python' % VENV_PATH,
                        '%s/scripts/mozinstall-script.py' % VENV_PATH],
         'hg': 'c:/mozilla-build/hg/hg',
     },
     "title": socket.gethostname().split('.')[0],
     "results_url": "http://graphs.mozilla.org/server/collect.cgi",
-    "datazilla_urls": ["https://datazilla.mozilla.org/talos"],
-    "datazilla_authfile": os.path.join(os.getcwd(), "oauth.txt"),
     "default_actions": [
         "clobber",
         "read-buildbot-config",
         "download-and-extract",
         "clone-talos",
         "create-virtualenv",
         "install",
         "run-tests",
--- a/testing/mozharness/mozharness/mozilla/testing/talos.py
+++ b/testing/mozharness/mozharness/mozilla/testing/talos.py
@@ -439,23 +439,16 @@ class Talos(TestingMixin, MercurialScrip
         kw_options.update(kw)
         # talos expects tests to be in the format (e.g.) 'ts:tp5:tsvg'
         tests = kw_options.get('activeTests')
         if tests and not isinstance(tests, basestring):
             tests = ':'.join(tests)  # Talos expects this format
             kw_options['activeTests'] = tests
         for key, value in kw_options.items():
             options.extend(['--%s' % key, value])
-        # add datazilla results urls
-        for url in self.config.get('datazilla_urls', []):
-            options.extend(['--datazilla-url', url])
-        # add datazilla authfile
-        authfile = self.config.get('datazilla_authfile')
-        if authfile:
-            options.extend(['--authfile', authfile])
         # configure profiling options
         options.extend(self.query_sps_profile_options())
         # extra arguments
         if args is None:
             args = self.query_talos_options()
         options += args
 
         return options
--- a/testing/mozharness/scripts/android_panda_talos.py
+++ b/testing/mozharness/scripts/android_panda_talos.py
@@ -378,22 +378,16 @@ class PandaTalosTest(TestingMixin, Mercu
            talos_json_config['extra_options'].get('android'):
             for option in self.talos_json_config['extra_options']['android']:
                 options.append(option % {
                     'apk_path': self.apk_path })
 
         if self.config['%s_options' % suite_category]:
             for option in self.config['%s_options' % suite_category]:
                 options.append(option % str_format_values)
-            for url in self.config.get('datazilla_urls', []):
-                options.extend(['--datazilla-url', url])
-            # add datazilla authfile
-            authfile = self.config.get('datazilla_authfile')
-            if authfile:
-                options.extend(['--authfile', authfile])
             abs_base_cmd = base_cmd + options
             return abs_base_cmd
         else:
             self.warning("Suite options for %s could not be determined."
                          "\nIf you meant to have options for this suite, "
                          "please make sure they are specified in your "
                          "config under %s_options" %
                          (suite_category, suite_category))
--- a/testing/profiles/prefs_general.js
+++ b/testing/profiles/prefs_general.js
@@ -160,17 +160,17 @@ user_pref("layout.css.ruby.enabled", tru
 // Enable unicode-range for testing
 user_pref("layout.css.unicode-range.enabled", true);
 
 // Disable spammy layout warnings because they pollute test logs
 user_pref("layout.spammy_warnings.enabled", false);
 
 // Enable Media Source Extensions for testing
 user_pref("media.mediasource.mp4.enabled", true);
-user_pref("media.mediasource.webm.enabled", true);
+user_pref("media.mediasource.webm.enabled", false);
 
 // Enable mozContacts
 user_pref("dom.mozContacts.enabled", true);
 
 // Enable mozSettings
 user_pref("dom.mozSettings.enabled", true);
 
 // Make sure the disk cache doesn't get auto disabled
--- a/testing/web-platform/meta/media-source/SourceBuffer-abort-readyState.html.ini
+++ b/testing/web-platform/meta/media-source/SourceBuffer-abort-readyState.html.ini
@@ -1,10 +1,11 @@
 [SourceBuffer-abort-readyState.html]
   type: testharness
   prefs: [media.mediasource.enabled:true, media.mediasource.whitelist:false]
-  disabled:
-    if os == "linux": https://bugzilla.mozilla.org/show_bug.cgi?id=1105760
+  [SourceBuffer#abort() (video/webm; codecs="vorbis,vp8") : If the readyState attribute of the parent media source is not in the "open" state then throw an INVALID_STATE_ERR exception and abort these steps.]
+    expected: FAIL
+
   [SourceBuffer#abort() (video/mp4) : If the readyState attribute of the parent media source is not in the "open" state then throw an INVALID_STATE_ERR exception and abort these steps.]
     expected:
-      if not debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL
-      if debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
 
--- a/testing/web-platform/meta/media-source/SourceBuffer-abort-removed.html.ini
+++ b/testing/web-platform/meta/media-source/SourceBuffer-abort-removed.html.ini
@@ -1,8 +1,11 @@
 [SourceBuffer-abort-removed.html]
   type: testharness
   prefs: [media.mediasource.enabled:true, media.mediasource.whitelist:false]
+  [SourceBuffer#abort (video/webm; codecs="vorbis,vp8") : if this object has been removed from the sourceBuffers attribute of the parent media source, then throw an INVALID_STATE_ERR exception and abort these steps.]
+    expected: FAIL
+
   [SourceBuffer#abort (video/mp4) : if this object has been removed from the sourceBuffers attribute of the parent media source, then throw an INVALID_STATE_ERR exception and abort these steps.]
     expected:
-      if not debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL
-      if debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
 
--- a/testing/web-platform/meta/media-source/SourceBuffer-abort-updating.html.ini
+++ b/testing/web-platform/meta/media-source/SourceBuffer-abort-updating.html.ini
@@ -1,8 +1,11 @@
 [SourceBuffer-abort-updating.html]
   type: testharness
   prefs: [media.mediasource.enabled:true, media.mediasource.whitelist:false]
+  [SourceBuffer#abort() (video/webm; codecs="vorbis,vp8") : Check the algorithm when the updating attribute is true.]
+    expected: FAIL
+
   [SourceBuffer#abort() (video/mp4) : Check the algorithm when the updating attribute is true.]
     expected:
-      if not debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL
-      if debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
 
--- a/testing/web-platform/meta/media-source/SourceBuffer-abort.html.ini
+++ b/testing/web-platform/meta/media-source/SourceBuffer-abort.html.ini
@@ -1,8 +1,11 @@
 [SourceBuffer-abort.html]
   type: testharness
   prefs: [media.mediasource.enabled:true, media.mediasource.whitelist:false]
+  [SourceBuffer#abort() (video/webm; codecs="vorbis,vp8"): Check the values of appendWindowStart and appendWindowEnd.]
+    expected: FAIL
+
   [SourceBuffer#abort() (video/mp4): Check the values of appendWindowStart and appendWindowEnd.]
     expected:
-      if not debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL
-      if debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
 
--- a/testing/web-platform/meta/media-source/interfaces.html.ini
+++ b/testing/web-platform/meta/media-source/interfaces.html.ini
@@ -1,63 +1,4 @@
 [interfaces.html]
   type: testharness
   prefs: [media.mediasource.enabled:true, media.mediasource.whitelist:false]
-  [AudioTrack interface: attribute kind]
-    expected: FAIL
-
-  [AudioTrack interface: attribute language]
-    expected: FAIL
-
-  [AudioTrack interface: attribute sourceBuffer]
-    expected: FAIL
-
-  [VideoTrack interface: attribute kind]
-    expected: FAIL
-
-  [VideoTrack interface: attribute language]
-    expected: FAIL
-
-  [VideoTrack interface: attribute sourceBuffer]
-    expected: FAIL
-
-  [TextTrack interface: attribute kind]
-    expected: FAIL
-
-  [TextTrack interface: attribute language]
-    expected: FAIL
-
-  [TextTrack interface: attribute sourceBuffer]
-    expected: FAIL
-
-  [SourceBuffer interface: attribute audioTracks]
-    expected: FAIL
-
-  [SourceBuffer interface: attribute videoTracks]
-    expected: FAIL
-
-  [SourceBuffer interface: attribute textTracks]
-    expected: FAIL
-
-  [SourceBuffer interface: operation appendStream(Stream,unsigned long long)]
-    expected: FAIL
-
-  [SourceBuffer interface: sourceBuffer must inherit property "audioTracks" with the proper type (4)]
-    expected: FAIL
-
-  [SourceBuffer interface: sourceBuffer must inherit property "videoTracks" with the proper type (5)]
-    expected: FAIL
-
-  [SourceBuffer interface: sourceBuffer must inherit property "textTracks" with the proper type (6)]
-    expected: FAIL
-
-  [SourceBuffer interface: sourceBuffer must inherit property "appendStream" with the proper type (11)]
-    expected: FAIL
-
-  [SourceBuffer interface: calling appendStream(Stream,unsigned long long) on sourceBuffer with too few arguments must throw TypeError]
-    expected: FAIL
-
-  [VideoPlaybackQuality interface: attribute totalFrameDelay]
-    expected: FAIL
-
-  [VideoPlaybackQuality interface: video.getVideoPlaybackQuality() must inherit property "totalFrameDelay" with the proper type (4)]
-    expected: FAIL
-
+  expected: ERROR
--- a/testing/web-platform/meta/media-source/mediasource-addsourcebuffer.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-addsourcebuffer.html.ini
@@ -1,13 +1,39 @@
 [mediasource-addsourcebuffer.html]
   type: testharness
   prefs: [media.mediasource.enabled:true, media.mediasource.whitelist:false]
+  [Test addSourceBuffer() with Vorbis and VP8]
+    expected: FAIL
+
+  [Test addSourceBuffer() with Vorbis and VP8 in separate SourceBuffers]
+    expected: FAIL
+
+  [Test addSourceBuffer() in 'ended' state.]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Test addSourceBuffer() video only]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Test addSourceBuffer() audio only]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
   [Test addSourceBuffer() with AAC and H.264]
     expected:
       if os == "linux": FAIL
       if (os == "win") and (version == "5.1.2600"): FAIL
 
   [Test addSourceBuffer() with AAC and H.264 in separate SourceBuffers]
     expected:
       if os == "linux": FAIL
       if (os == "win") and (version == "5.1.2600"): FAIL
 
+  [Test addSourceBuffer() QuotaExceededError.]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
--- a/testing/web-platform/meta/media-source/mediasource-append-buffer.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-append-buffer.html.ini
@@ -1,6 +1,108 @@
 [mediasource-append-buffer.html]
   type: testharness
   prefs: [media.mediasource.enabled:true, media.mediasource.whitelist:false]
-  disabled:
-    if (os == "win") and (version != "5.1.2600"): https://bugzilla.mozilla.org/show_bug.cgi?id=1143650
-    if os == "mac": https://bugzilla.mozilla.org/show_bug.cgi?id=1143650
+  [Test SourceBuffer.appendBuffer() event dispatching.]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Test SourceBuffer.appendBuffer() call during a pending appendBuffer().]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Test SourceBuffer.abort() call during a pending appendBuffer().]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Test SourceBuffer.appendBuffer() triggering an 'ended' to 'open' transition.]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Test zero byte SourceBuffer.appendBuffer() call triggering an 'ended' to 'open' transition.]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Test MediaSource.removeSourceBuffer() call during a pending appendBuffer().]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Test set MediaSource.duration during a pending appendBuffer() for one of its SourceBuffers.]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Test MediaSource.endOfStream() during a pending appendBuffer() for one of its SourceBuffers.]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Test set SourceBuffer.timestampOffset during a pending appendBuffer().]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Test appending an empty ArrayBufferView.]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Test appending a neutered ArrayBufferView.]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Test appending an empty ArrayBuffer.]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Test appending a neutered ArrayBuffer.]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Test appendBuffer with partial init segments.]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Test appendBuffer with partial media segments.]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Test abort in the middle of an initialization segment.]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Test abort after removing sourcebuffer.]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Test abort after readyState is ended following init segment and media segment.]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Test abort after appendBuffer update ends.]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Test appending null.]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Test appending after removeSourceBuffer().]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
--- a/testing/web-platform/meta/media-source/mediasource-appendwindow.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-appendwindow.html.ini
@@ -1,3 +1,38 @@
 [mediasource-appendwindow.html]
   type: testharness
   prefs: [media.mediasource.enabled:true, media.mediasource.whitelist:false]
+  [Test correctly reset appendWindowStart and appendWindowEnd values]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Test set wrong values to appendWindowStart and appendWindowEnd.]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Test set correct values to appendWindowStart and appendWindowEnd.]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Test appendwindow throw error when mediasource object is not associated with a sourebuffer.]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Test set appendWindowStart and appendWindowEnd when source buffer updating.]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Test appendWindowStart and appendWindowEnd value after a sourceBuffer.abort().]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Test read appendWindowStart and appendWindowEnd initial values.]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
--- a/testing/web-platform/meta/media-source/mediasource-buffered.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-buffered.html.ini
@@ -1,20 +1,12 @@
 [mediasource-buffered.html]
   type: testharness
   prefs: [media.mediasource.enabled:true, media.mediasource.whitelist:false]
+  disabled:
+    if os == "linux": https://bugzilla.mozilla.org/show_bug.cgi?id=1182945
+    if (os == "win") and (version == "5.1.2600"): https://bugzilla.mozilla.org/show_bug.cgi?id=1182945
   [Demuxed content with different lengths]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
+    expected: FAIL
 
   [Muxed content with different lengths]
     expected: FAIL
 
-  [Demuxed content with an empty buffered range on one SourceBuffer]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Muxed content empty buffered ranges.]
-    expected:
-      if os == "linux": FAIL
-
--- a/testing/web-platform/meta/media-source/mediasource-closed.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-closed.html.ini
@@ -1,3 +1,28 @@
 [mediasource-closed.html]
   type: testharness
   prefs: [media.mediasource.enabled:true, media.mediasource.whitelist:false]
+  [Test addSourceBuffer() while closed.]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Test removeSourceBuffer() while closed.]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Test setting duration while open->closed.]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Test getting duration while open->closed.]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Test sourcebuffer.abort when closed.]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
--- a/testing/web-platform/meta/media-source/mediasource-config-change-mp4-av-audio-bitrate.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-config-change-mp4-av-audio-bitrate.html.ini
@@ -1,8 +1,4 @@
 [mediasource-config-change-mp4-av-audio-bitrate.html]
   type: testharness
   prefs: [media.mediasource.enabled:true, media.mediasource.whitelist:false]
-  [Tests mp4 audio bitrate changes in multiplexed content.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
+  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1130973
--- a/testing/web-platform/meta/media-source/mediasource-config-change-webm-a-bitrate.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-config-change-webm-a-bitrate.html.ini
@@ -1,4 +1,9 @@
 [mediasource-config-change-webm-a-bitrate.html]
   type: testharness
   prefs: [media.mediasource.enabled:true, media.mediasource.whitelist:false]
-  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1134888
+  disabled:
+    if os == "linux": https://bugzilla.mozilla.org/show_bug.cgi?id=1186261
+    if (os == "win") and (version == "5.1.2600"): https://bugzilla.mozilla.org/show_bug.cgi?id=1186261
+  [Tests webm audio-only bitrate changes.]
+    expected: FAIL
+
--- a/testing/web-platform/meta/media-source/mediasource-config-change-webm-av-audio-bitrate.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-config-change-webm-av-audio-bitrate.html.ini
@@ -1,3 +1,6 @@
 [mediasource-config-change-webm-av-audio-bitrate.html]
   type: testharness
   prefs: [media.mediasource.enabled:true, media.mediasource.whitelist:false]
+  [Tests webm audio bitrate changes in multiplexed content.]
+    expected: FAIL
+
--- a/testing/web-platform/meta/media-source/mediasource-config-change-webm-av-framesize.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-config-change-webm-av-framesize.html.ini
@@ -1,3 +1,9 @@
 [mediasource-config-change-webm-av-framesize.html]
   type: testharness
   prefs: [media.mediasource.enabled:true, media.mediasource.whitelist:false]
+  disabled:
+    if os == "linux": https://bugzilla.mozilla.org/show_bug.cgi?id=1186261
+    if (os == "win") and (version == "5.1.2600"): https://bugzilla.mozilla.org/show_bug.cgi?id=1186261
+  [Tests webm frame size changes in multiplexed content.]
+    expected: FAIL
+
--- a/testing/web-platform/meta/media-source/mediasource-config-change-webm-av-video-bitrate.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-config-change-webm-av-video-bitrate.html.ini
@@ -1,3 +1,6 @@
 [mediasource-config-change-webm-av-video-bitrate.html]
   type: testharness
   prefs: [media.mediasource.enabled:true, media.mediasource.whitelist:false]
+  [Tests webm video bitrate changes in multiplexed content.]
+    expected: FAIL
+
--- a/testing/web-platform/meta/media-source/mediasource-config-change-webm-v-bitrate.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-config-change-webm-v-bitrate.html.ini
@@ -1,3 +1,9 @@
 [mediasource-config-change-webm-v-bitrate.html]
   type: testharness
   prefs: [media.mediasource.enabled:true, media.mediasource.whitelist:false]
+  disabled:
+    if os == "linux": https://bugzilla.mozilla.org/show_bug.cgi?id=1186261
+    if (os == "win") and (version == "5.1.2600"): https://bugzilla.mozilla.org/show_bug.cgi?id=1186261
+  [Tests webm video-only bitrate changes.]
+    expected: FAIL
+
--- a/testing/web-platform/meta/media-source/mediasource-config-change-webm-v-framerate.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-config-change-webm-v-framerate.html.ini
@@ -1,3 +1,9 @@
 [mediasource-config-change-webm-v-framerate.html]
   type: testharness
   prefs: [media.mediasource.enabled:true, media.mediasource.whitelist:false]
+  disabled:
+    if os == "linux": https://bugzilla.mozilla.org/show_bug.cgi?id=1186261
+    if (os == "win") and (version == "5.1.2600"): https://bugzilla.mozilla.org/show_bug.cgi?id=1186261
+  [Tests webm video-only frame rate changes.]
+    expected: FAIL
+
--- a/testing/web-platform/meta/media-source/mediasource-config-change-webm-v-framesize.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-config-change-webm-v-framesize.html.ini
@@ -1,3 +1,9 @@
 [mediasource-config-change-webm-v-framesize.html]
   type: testharness
   prefs: [media.mediasource.enabled:true, media.mediasource.whitelist:false]
+  disabled:
+    if os == "linux": https://bugzilla.mozilla.org/show_bug.cgi?id=1186261
+    if (os == "win") and (version == "5.1.2600"): https://bugzilla.mozilla.org/show_bug.cgi?id=1186261
+  [Tests webm video-only frame size changes.]
+    expected: FAIL
+
--- a/testing/web-platform/meta/media-source/mediasource-duration-boundaryconditions.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-duration-boundaryconditions.html.ini
@@ -1,5 +1,68 @@
 [mediasource-duration-boundaryconditions.html]
   type: testharness
   prefs: [media.mediasource.enabled:true, media.mediasource.whitelist:false]
-  disabled:
-    if debug: Unstable
+  [Set duration to 2^31 - 1]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Set duration to 1]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Set duration to Number.MAX_VALUE]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Set duration to Number.MIN_VALUE]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Set duration to Number.MAX_VALUE - 1]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Set duration to Number.MIN_VALUE - 1]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Set duration to Number.POSITIVE_INFINITY]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Set duration to Number.NEGATIVE_INFINITY]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Set duration to lowest value.]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Set duration to a negative double.]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Set duration to a positive double.]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Set duration to zero]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
+  [Set duration to NaN]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
--- a/testing/web-platform/meta/media-source/mediasource-duration.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-duration.html.ini
@@ -6,12 +6,10 @@
 
   [Test appendBuffer completes previous seek to truncated duration]
     disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1148224
 
   [Test endOfStream completes previous seek to truncated duration]
     disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1148224
 
   [Test setting same duration multiple times does not fire duplicate durationchange]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
+    expected: FAIL
 
--- a/testing/web-platform/meta/media-source/mediasource-getvideoplaybackquality.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-getvideoplaybackquality.html.ini
@@ -1,4 +1,8 @@
 [mediasource-getvideoplaybackquality.html]
   type: testharness
   prefs: [media.mediasource.enabled:true, media.mediasource.whitelist:false]
-  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467
+  [Test HTMLVideoElement.getVideoPlaybackQuality() with MediaSource API]
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
--- a/testing/web-platform/meta/media-source/mediasource-is-type-supported.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-is-type-supported.html.ini
@@ -1,94 +1,69 @@
 [mediasource-is-type-supported.html]
   type: testharness
   prefs: [media.mediasource.enabled:true, media.mediasource.whitelist:false]
-  [Test invalid MIME format "video/webm"]
+  [Test valid WebM type "video/webm;codecs="vp8""]
     expected: FAIL
 
-  [Test invalid MIME format "video/webm;"]
-    expected: FAIL
-
-  [Test invalid MIME format "video/webm;codecs"]
+  [Test valid WebM type "video/webm;codecs="vorbis""]
     expected: FAIL
 
-  [Test invalid MIME format "video/webm;codecs="]
+  [Test valid WebM type "video/webm;codecs="vp8,vorbis""]
     expected: FAIL
 
-  [Test invalid MIME format "video/webm;codecs=""]
+  [Test valid WebM type "video/webm;codecs="vorbis, vp8""]
     expected: FAIL
 
-  [Test invalid MIME format "video/webm;codecs="""]
-    expected: FAIL
-
-  [Test invalid mismatch between major type and codec ID "audio/webm;codecs="vp8""]
+  [Test valid WebM type "audio/webm;codecs="vorbis""]
     expected: FAIL
 
   [Test valid WebM type "AUDIO/WEBM;CODECS="vorbis""]
     expected: FAIL
 
+  [Test valid MP4 type "audio/mp4;codecs="mp4a.67""]
+    expected: FAIL
+
   [Test valid MP4 type "video/mp4;codecs="avc1.4d001e""]
     expected:
-      if not debug and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86") and (bits == 32): FAIL
-      if not debug and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86_64") and (bits == 64): FAIL
-      if not debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL
-      if debug and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86_64") and (bits == 64): FAIL
-      if debug and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86") and (bits == 32): FAIL
-      if debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
 
   [Test valid MP4 type "video/mp4;codecs="avc1.42001e""]
     expected:
-      if not debug and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86") and (bits == 32): FAIL
-      if not debug and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86_64") and (bits == 64): FAIL
-      if not debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL
-      if debug and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86_64") and (bits == 64): FAIL
-      if debug and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86") and (bits == 32): FAIL
-      if debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
 
   [Test valid MP4 type "audio/mp4;codecs="mp4a.40.2""]
     expected:
-      if not debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL
-      if debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
 
   [Test valid MP4 type "audio/mp4;codecs="mp4a.40.5""]
     expected:
-      if not debug and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86") and (bits == 32): FAIL
-      if not debug and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86_64") and (bits == 64): FAIL
-      if not debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL
-      if debug and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86_64") and (bits == 64): FAIL
-      if debug and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86") and (bits == 32): FAIL
-      if debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
 
   [Test valid MP4 type "audio/mp4;codecs="mp4a.67""]
-    expected: FAIL
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
 
   [Test valid MP4 type "video/mp4;codecs="mp4a.40.2""]
     expected:
-      if not debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL
-      if debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
 
   [Test valid MP4 type "video/mp4;codecs="avc1.4d001e,mp4a.40.2""]
     expected:
-      if not debug and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86") and (bits == 32): FAIL
-      if not debug and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86_64") and (bits == 64): FAIL
-      if not debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL
-      if debug and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86_64") and (bits == 64): FAIL
-      if debug and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86") and (bits == 32): FAIL
-      if debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
 
   [Test valid MP4 type "video/mp4;codecs="mp4a.40.2 , avc1.4d001e ""]
     expected:
-      if not debug and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86") and (bits == 32): FAIL
-      if not debug and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86_64") and (bits == 64): FAIL
-      if not debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL
-      if debug and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86_64") and (bits == 64): FAIL
-      if debug and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86") and (bits == 32): FAIL
-      if debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
 
   [Test valid MP4 type "video/mp4;codecs="avc1.4d001e,mp4a.40.5""]
     expected:
-      if not debug and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86") and (bits == 32): FAIL
-      if not debug