Merge autoland to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Thu, 05 Jan 2017 17:24:15 -0800
changeset 456715 53193729a50b40c00889b1fa07df8dffab7781f9
parent 456417 b548da4e16f067e5b69349376e37b2db97983cf7 (current diff)
parent 456714 50ab4998bee758ab818fd1d415d43ccd8d7f81f4 (diff)
child 456716 a14094edbad78fc1d16e8d4c57902537cf286fd1
child 458300 580669e178e88220ff1bc5503e2681d20f639fc0
push id40575
push userjwwang@mozilla.com
push dateFri, 06 Jan 2017 02:27:46 +0000
reviewersmerge
milestone53.0a1
Merge autoland to central, a=merge MozReview-Commit-ID: FuAgy3YQ0De
devtools/client/shared/widgets/Chart.jsm
toolkit/mozapps/extensions/amIWebInstallListener.idl
toolkit/mozapps/extensions/amIWebInstaller.idl
toolkit/mozapps/extensions/amWebInstallListener.js
toolkit/mozapps/extensions/test/xpinstall/amosigned2.xpi
toolkit/mozapps/extensions/test/xpinstall/browser_cancel.js
toolkit/mozapps/extensions/test/xpinstall/browser_signed_multiple.js
toolkit/mozapps/extensions/test/xpinstall/browser_signed_naming.js
toolkit/mozapps/extensions/test/xpinstall/signed2.xpi
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -2,16 +2,17 @@
 
 module.exports = {
   // When adding items to this file please check for effects on sub-directories.
   "plugins": [
     "mozilla"
   ],
   "rules": {
     "mozilla/import-globals": "warn",
+    "mozilla/no-import-into-var-and-global": "error",
 
     // No (!foo in bar) or (!object instanceof Class)
     "no-unsafe-negation": "error",
   },
   "env": {
     "es6": true
   },
   "parserOptions": {
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -444,17 +444,16 @@
 #ifndef DISABLE_MOZ_RIL_GEOLOC
 #endif
 #endif // MOZ_WIDGET_GONK && MOZ_B2G_RIL
 
 #ifndef MOZ_WIDGET_GONK
 @RESPATH@/components/addonManager.js
 @RESPATH@/components/amContentHandler.js
 @RESPATH@/components/amInstallTrigger.js
-@RESPATH@/components/amWebInstallListener.js
 
 @RESPATH@/components/OopCommandLine.js
 @RESPATH@/components/CommandLine.js
 #endif
 @RESPATH@/components/extensions.manifest
 @RESPATH@/components/nsBlocklistService.js
 @RESPATH@/components/BootstrapCommandLine.js
 
--- a/browser/base/content/aboutTabCrashed.xhtml
+++ b/browser/base/content/aboutTabCrashed.xhtml
@@ -68,17 +68,17 @@
               <input type="checkbox" id="emailMe"/>
               <label for="emailMe">&tabCrashed.emailMe;</label>
             </div>
             <input type="text" id="email" placeholder="&tabCrashed.emailPlaceholder;"/>
           </li>
         </ul>
 
         <div id="requestAutoSubmit" hidden="true">
-          <h2>&tabCrashed.requestAutoSubmit;</h2>
+          <h2>&tabCrashed.requestAutoSubmit2;</h2>
           <div class="checkbox-with-label">
             <input type="checkbox" id="autoSubmit"/>
             <label for="autoSubmit">&tabCrashed.autoSubmit;</label>
           </div>
         </div>
       </div>
 
       <p id="reportSent">&tabCrashed.reportSent;</p>
--- a/browser/base/content/browser-addons.js
+++ b/browser/base/content/browser-addons.js
@@ -210,17 +210,17 @@ const gXPInstallObserver = {
 
     Services.telemetry
             .getHistogramById("SECURITY_UI")
             .add(Ci.nsISecurityUITelemetry.WARNING_CONFIRM_ADDON_INSTALL);
   },
 
   observe(aSubject, aTopic, aData) {
     var brandBundle = document.getElementById("bundle_brand");
-    var installInfo = aSubject.QueryInterface(Components.interfaces.amIWebInstallInfo);
+    var installInfo = aSubject.wrappedJSObject;
     var browser = installInfo.browser;
 
     // Make sure the browser is still alive.
     if (!browser || gBrowser.browsers.indexOf(browser) == -1)
       return;
 
     const anchorID = "addons-notification-icon";
     var messageString, action;
--- a/browser/base/content/browser-gestureSupport.js
+++ b/browser/base/content/browser-gestureSupport.js
@@ -1146,18 +1146,18 @@ var gHistorySwipeAnimation = {
     let url = "";
     try {
       url = URL.createObjectURL(aBlob);
       img.onload = function() {
         URL.revokeObjectURL(url);
       };
     } finally {
       img.src = url;
-      return img;
     }
+    return img;
   },
 
   /**
    * Scales the background of a given box element (which uses a given snapshot
    * as background) based on a given scale factor.
    * @param aSnapshot
    *        The snapshot that is used as background of aBox.
    * @param aScale
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -113,17 +113,18 @@ var StarUI = {
 
         switch (aEvent.keyCode) {
           case KeyEvent.DOM_VK_ESCAPE:
             this.panel.hidePopup();
             break;
           case KeyEvent.DOM_VK_RETURN:
             if (aEvent.target.classList.contains("expander-up") ||
                 aEvent.target.classList.contains("expander-down") ||
-                aEvent.target.id == "editBMPanel_newFolderButton") {
+                aEvent.target.id == "editBMPanel_newFolderButton" ||
+                aEvent.target.id == "editBookmarkPanelRemoveButton") {
               // XXX Why is this necessary? The defaultPrevented check should
               //    be enough.
               break;
             }
             this.panel.hidePopup();
             break;
           // This case is for catching character-generating keypresses
           case 0:
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -6659,18 +6659,18 @@ var gIdentityHandler = {
     delete this._identityPopup;
     return this._identityPopup = document.getElementById("identity-popup");
   },
   get _identityBox() {
     delete this._identityBox;
     return this._identityBox = document.getElementById("identity-box");
   },
   get _identityPopupMultiView() {
-    delete _identityPopupMultiView;
-    return document.getElementById("identity-popup-multiView");
+    delete this._identityPopupMultiView;
+    return this._identityPopupMultiView = document.getElementById("identity-popup-multiView");
   },
   get _identityPopupContentHosts() {
     delete this._identityPopupContentHosts;
     let selector = ".identity-popup-headline.host";
     return this._identityPopupContentHosts = [
       ...this._identityPopupMultiView._mainView.querySelectorAll(selector),
       ...document.querySelectorAll(selector)
     ];
--- a/browser/base/content/sync/customize.xul
+++ b/browser/base/content/sync/customize.xul
@@ -34,17 +34,17 @@
 #ifdef XP_UNIX
                  value="&syncCustomizeUnix.description;"
 #else
                  value="&syncCustomize.description;"
 #endif
                  />
 
   <vbox align="start">
-      <checkbox label="&engine.tabs.label;"
+      <checkbox label="&engine.tabs.label2;"
                 accesskey="&engine.tabs.accesskey;"
                 preference="engine.tabs"/>
       <checkbox label="&engine.bookmarks.label;"
                 accesskey="&engine.bookmarks.accesskey;"
                 preference="engine.bookmarks"/>
       <checkbox label="&engine.passwords.label;"
                 accesskey="&engine.passwords.accesskey;"
                 preference="engine.passwords"/>
--- a/browser/base/content/sync/setup.xul
+++ b/browser/base/content/sync/setup.xul
@@ -387,17 +387,17 @@
             <checkbox label="&engine.prefs.label;"
                       accesskey="&engine.prefs.accesskey;"
                       id="engine.prefs"
                       checked="true"/>
             <checkbox label="&engine.history.label;"
                       accesskey="&engine.history.accesskey;"
                       id="engine.history"
                       checked="true"/>
-            <checkbox label="&engine.tabs.label;"
+            <checkbox label="&engine.tabs.label2;"
                       accesskey="&engine.tabs.accesskey;"
                       id="engine.tabs"
                       checked="true"/>
           </vbox>
         </row>
       </rows>
     </grid>
     </groupbox>
--- a/browser/base/content/test/general/browser_bookmark_popup.js
+++ b/browser/base/content/test/general/browser_bookmark_popup.js
@@ -249,11 +249,35 @@ add_task(function* ctrl_d_edit_bookmark_
     shouldAutoClose: true,
     popupHideFn() {
       document.getElementById("editBookmarkPanelRemoveButton").click();
     },
     isBookmarkRemoved: true,
   });
 });
 
+add_task(function* enter_on_remove_bookmark_should_remove_bookmark() {
+  if (AppConstants.platform == "macosx") {
+    // "Full Keyboard Access" is disabled by default, and thus doesn't allow
+    // keyboard navigation to the "Remove Bookmarks" button by default.
+    return;
+  }
+
+  yield test_bookmarks_popup({
+    isNewBookmark: true,
+    popupShowFn(browser) {
+      EventUtils.synthesizeKey("D", {accelKey: true}, window);
+    },
+    shouldAutoClose: true,
+    popupHideFn() {
+      while (!document.activeElement ||
+             document.activeElement.id != "editBookmarkPanelRemoveButton") {
+        EventUtils.sendChar("VK_TAB", window);
+      }
+      EventUtils.sendChar("VK_RETURN", window);
+    },
+    isBookmarkRemoved: true,
+  });
+});
+
 registerCleanupFunction(function() {
   delete StarUI._closePanelQuickForTesting;
 })
--- a/browser/base/content/test/general/browser_bug553455.js
+++ b/browser/base/content/test/general/browser_bug553455.js
@@ -465,56 +465,16 @@ function test_restartless() {
     Services.perms.remove(makeURI("http://example.com/"), "install");
 
     let closePromise = waitForNotificationClose();
     gBrowser.removeTab(gBrowser.selectedTab);
     yield closePromise;
   });
 },
 
-function test_multiple() {
-  return Task.spawn(function* () {
-    let pm = Services.perms;
-    pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
-
-    let progressPromise = waitForProgressNotification();
-    let dialogPromise = waitForInstallDialog();
-    let triggers = encodeURIComponent(JSON.stringify({
-      "Unsigned XPI": "amosigned.xpi",
-      "Restartless XPI": "restartless.xpi"
-    }));
-    BrowserTestUtils.openNewForegroundTab(gBrowser, TESTROOT + "installtrigger.html?" + triggers);
-    let panel = yield progressPromise;
-    let installDialog = yield dialogPromise;
-
-    let notificationPromise = waitForNotification("addon-install-restart");
-    acceptInstallDialog(installDialog);
-    yield notificationPromise;
-
-    let notification = panel.childNodes[0];
-    is(notification.button.label, "Restart Now", "Should have seen the right button");
-    is(notification.getAttribute("label"),
-       "2 add-ons will be installed after you restart " + gApp + ".",
-       "Should have seen the right message");
-
-    let installs = yield getInstalls();
-    is(installs.length, 1, "Should be one pending install");
-    installs[0].cancel();
-
-    let addon = yield new Promise(resolve => {
-      AddonManager.getAddonByID("restartless-xpi@tests.mozilla.org", function(result) {
-        resolve(result);
-      });
-    });
-    addon.uninstall();
-    Services.perms.remove(makeURI("http://example.com/"), "install");
-    yield removeTab();
-  });
-},
-
 function test_sequential() {
   return Task.spawn(function* () {
     // This test is only relevant if using the new doorhanger UI
     // TODO: this subtest is disabled until multiple notification prompts are
     // reworked in bug 1188152
     if (true || !Preferences.get("xpinstall.customConfirmationUI", false)) {
       return;
     }
@@ -581,74 +541,16 @@ function test_sequential() {
     Services.perms.remove(makeURI("http://example.com"), "install");
     let closePromise = waitForNotificationClose();
     cancelInstallDialog(installDialog);
     yield closePromise;
     yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
   });
 },
 
-function test_someUnverified() {
-  return Task.spawn(function* () {
-    // This test is only relevant if using the new doorhanger UI and allowing
-    // unsigned add-ons
-    if (!Preferences.get("xpinstall.customConfirmationUI", false) ||
-        Preferences.get("xpinstall.signatures.required", true) ||
-        REQUIRE_SIGNING) {
-      return;
-    }
-    let pm = Services.perms;
-    pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
-
-    let progressPromise = waitForProgressNotification();
-    let dialogPromise = waitForInstallDialog();
-    let triggers = encodeURIComponent(JSON.stringify({
-      "Extension XPI": "restartless-unsigned.xpi",
-      "Theme XPI": "theme.xpi"
-    }));
-    BrowserTestUtils.openNewForegroundTab(gBrowser, TESTROOT + "installtrigger.html?" + triggers);
-    yield progressPromise;
-    let installDialog = yield dialogPromise;
-
-    let notification = document.getElementById("addon-install-confirmation-notification");
-    let message = notification.getAttribute("label");
-    is(message, "Caution: This site would like to install 2 add-ons in " + gApp +
-       ", some of which are unverified. Proceed at your own risk.",
-       "Should see the right message");
-
-    let container = document.getElementById("addon-install-confirmation-content");
-    is(container.childNodes.length, 2, "Should be two items listed");
-    is(container.childNodes[0].firstChild.getAttribute("value"), "XPI Test", "Should have the right add-on");
-    is(container.childNodes[0].lastChild.getAttribute("class"),
-       "addon-install-confirmation-unsigned", "Should have the unverified marker");
-    is(container.childNodes[1].firstChild.getAttribute("value"), "Theme Test", "Should have the right add-on");
-    is(container.childNodes[1].childNodes.length, 1, "Shouldn't have the unverified marker");
-
-    let notificationPromise = waitForNotification("addon-install-restart");
-    acceptInstallDialog(installDialog);
-    yield notificationPromise;
-
-    let [addon, theme] = yield new Promise(resolve => {
-      AddonManager.getAddonsByIDs(["restartless-xpi@tests.mozilla.org",
-                                  "theme-xpi@tests.mozilla.org"],
-                                  function(addons) {
-        resolve(addons);
-      });
-    });
-    addon.uninstall();
-    // Installing a new theme tries to switch to it, switch back to the
-    // default theme.
-    theme.userDisabled = true;
-    theme.uninstall();
-
-    Services.perms.remove(makeURI("http://example.com/"), "install");
-    yield removeTab();
-  });
-},
-
 function test_allUnverified() {
   return Task.spawn(function* () {
     // This test is only relevant if using the new doorhanger UI and allowing
     // unsigned add-ons
     if (!Preferences.get("xpinstall.customConfirmationUI", false) ||
         Preferences.get("xpinstall.signatures.required", true) ||
         REQUIRE_SIGNING) {
       return;
--- a/browser/base/content/test/popupNotifications/browser.ini
+++ b/browser/base/content/test/popupNotifications/browser.ini
@@ -11,12 +11,14 @@ skip-if = (os == "linux" && (debug || as
 [browser_popupNotification_3.js]
 skip-if = (os == "linux" && (debug || asan))
 [browser_popupNotification_4.js]
 skip-if = (os == "linux" && (debug || asan))
 [browser_popupNotification_5.js]
 skip-if = (os == "linux" && (debug || asan))
 [browser_popupNotification_checkbox.js]
 skip-if = (os == "linux" && (debug || asan))
+[browser_popupNotification_keyboard.js]
+skip-if = (os == "linux" && (debug || asan))
 [browser_popupNotification_no_anchors.js]
 skip-if = (os == "linux" && (debug || asan))
 [browser_reshow_in_background.js]
 skip-if = (os == "linux" && (debug || asan))
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/popupNotifications/browser_popupNotification_keyboard.js
@@ -0,0 +1,53 @@
+/* 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/. */
+
+function test() {
+  waitForExplicitFinish();
+
+  ok(PopupNotifications, "PopupNotifications object exists");
+  ok(PopupNotifications.panel, "PopupNotifications panel exists");
+
+  setup();
+}
+
+var tests = [
+  // Test that for persistent notifications,
+  // the secondary action is triggered by pressing the escape key.
+  { id: "Test#1",
+    run() {
+      this.notifyObj = new BasicNotification(this.id);
+      this.notifyObj.options.persistent = true;
+      showNotification(this.notifyObj);
+    },
+    onShown(popup) {
+      checkPopup(popup, this.notifyObj);
+      EventUtils.synthesizeKey("VK_ESCAPE", {});
+    },
+    onHidden(popup) {
+      ok(!this.notifyObj.mainActionClicked, "mainAction was not clicked");
+      ok(this.notifyObj.secondaryActionClicked, "secondaryAction was clicked");
+      ok(!this.notifyObj.dismissalCallbackTriggered, "dismissal callback wasn't triggered");
+      ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered");
+    }
+  },
+  // Test that for non-persistent notifications, the escape key dismisses the notification.
+  { id: "Test#2",
+    *run() {
+      yield waitForWindowReadyForPopupNotifications(window);
+      this.notifyObj = new BasicNotification(this.id);
+      this.notification = showNotification(this.notifyObj);
+    },
+    onShown(popup) {
+      checkPopup(popup, this.notifyObj);
+      EventUtils.synthesizeKey("VK_ESCAPE", {});
+    },
+    onHidden(popup) {
+      ok(!this.notifyObj.mainActionClicked, "mainAction was not clicked");
+      ok(!this.notifyObj.secondaryActionClicked, "secondaryAction was not clicked");
+      ok(this.notifyObj.dismissalCallbackTriggered, "dismissal callback triggered");
+      ok(!this.notifyObj.removedCallbackTriggered, "removed callback was not triggered");
+      this.notification.remove();
+    }
+  },
+];
--- a/browser/base/content/test/urlbar/browser.ini
+++ b/browser/base/content/test/urlbar/browser.ini
@@ -31,17 +31,16 @@ subsuite = clipboard
 [browser_bug562649.js]
 [browser_bug623155.js]
 support-files =
   redirect_bug623155.sjs
 [browser_bug783614.js]
 [browser_canonizeURL.js]
 [browser_dragdropURL.js]
 [browser_locationBarCommand.js]
-skip-if = true # bug 917535, bug 1289765
 [browser_locationBarExternalLoad.js]
 [browser_moz_action_link.js]
 [browser_pasteAndGo.js]
 subsuite = clipboard
 [browser_removeUnsafeProtocolsFromURLBarPaste.js]
 subsuite = clipboard
 [browser_search_favicon.js]
 [browser_tabMatchesInAwesomebar.js]
--- a/browser/components/preferences/in-content/advanced.js
+++ b/browser/components/preferences/in-content/advanced.js
@@ -223,17 +223,17 @@ var gAdvancedPane = {
     return checkbox.checked ? 1 : 0;
   },
 
   /**
    * When the user toggles the layers.acceleration.disabled pref,
    * sync its new value to the gfx.direct2d.disabled pref too.
    */
   updateHardwareAcceleration() {
-    if (AppConstants.platform = "win") {
+    if (AppConstants.platform == "win") {
       var fromPref = document.getElementById("layers.acceleration.disabled");
       var toPref = document.getElementById("gfx.direct2d.disabled");
       toPref.value = fromPref.value;
     }
   },
 
   // DATA CHOICES TAB
 
--- a/browser/components/preferences/in-content/sync.xul
+++ b/browser/components/preferences/in-content/sync.xul
@@ -111,17 +111,17 @@
                       preference="engine.prefs"/>
           </richlistitem>
           <richlistitem>
             <checkbox label="&engine.history.label;"
                       accesskey="&engine.history.accesskey;"
                       preference="engine.history"/>
           </richlistitem>
           <richlistitem>
-            <checkbox label="&engine.tabs.label;"
+            <checkbox label="&engine.tabs.label2;"
                       accesskey="&engine.tabs.accesskey;"
                       preference="engine.tabs"/>
           </richlistitem>
         </richlistbox>
       </vbox>
     </groupbox>
 
     <groupbox class="syncGroupBox">
@@ -249,18 +249,18 @@
                   <vbox><image id="fxaLoginRejectedWarning"/></vbox>
                   <description flex="1">
                     &signedInUnverified.beforename.label;
                     <label id="fxaEmailAddress2"/>
                     &signedInUnverified.aftername.label;
                   </description>
                 </hbox>
                 <hbox class="fxaAccountBoxButtons">
-                  <button id="verifyFxaAccount" accesskey="&verify.accesskey;">&verify.label;</button>
-                  <button id="unverifiedUnlinkFxaAccount" accesskey="&forget.accesskey;">&forget.label;</button>
+                  <button id="verifyFxaAccount" label="&verify.label;" accesskey="&verify.accesskey;"></button>
+                  <button id="unverifiedUnlinkFxaAccount" label="&forget.label;" accesskey="&forget.accesskey;"></button>
                 </hbox>
               </vbox>
             </hbox>
 
             <!-- logged in locally but server rejected credentials -->
             <hbox id="fxaLoginRejected" class="fxaAccountBox">
               <vbox>
                 <image id="fxaProfileImage"/>
@@ -270,28 +270,28 @@
                   <vbox><image id="fxaLoginRejectedWarning"/></vbox>
                   <description flex="1">
                     &signedInLoginFailure.beforename.label;
                     <label id="fxaEmailAddress3"/>
                     &signedInLoginFailure.aftername.label;
                   </description>
                 </hbox>
                 <hbox class="fxaAccountBoxButtons">
-                  <button id="rejectReSignIn" accessky="&signIn.accesskey;">&signIn.label;</button>
-                  <button id="rejectUnlinkFxaAccount" accesskey="&forget.accesskey;">&forget.label;</button>
+                  <button id="rejectReSignIn" label="&signIn.label;" accesskey="&signIn.accesskey;"></button>
+                  <button id="rejectUnlinkFxaAccount" label="&forget.label;" accesskey="&forget.accesskey;"></button>
                 </hbox>
               </vbox>
             </hbox>
           </deck>
         </groupbox>
         <groupbox id="syncOptions">
           <caption><label>&signedIn.engines.label;</label></caption>
           <hbox id="fxaSyncEngines">
             <vbox align="start" flex="1">
-              <checkbox label="&engine.tabs.label;"
+              <checkbox label="&engine.tabs.label2;"
                         accesskey="&engine.tabs.accesskey;"
                         preference="engine.tabs"/>
               <checkbox label="&engine.bookmarks.label;"
                         accesskey="&engine.bookmarks.accesskey;"
                         preference="engine.bookmarks"/>
               <checkbox label="&engine.passwords.label;"
                         accesskey="&engine.passwords.accesskey;"
                         preference="engine.passwords"/>
--- a/browser/components/search/test/browser.ini
+++ b/browser/components/search/test/browser.ini
@@ -18,16 +18,18 @@ support-files =
 [browser_addEngine.js]
 [browser_amazon.js]
 [browser_amazon_behavior.js]
 [browser_bing.js]
 [browser_bing_behavior.js]
 [browser_contextmenu.js]
 [browser_contextSearchTabPosition.js]
 skip-if = os == "mac" # bug 967013
+[browser_ddg.js]
+[browser_ddg_behavior.js]
 [browser_google.js]
 [browser_google_codes.js]
 [browser_google_behavior.js]
 [browser_healthreport.js]
 [browser_hiddenOneOffs_cleanup.js]
 [browser_hiddenOneOffs_diacritics.js]
 [browser_oneOffContextMenu.js]
 [browser_oneOffContextMenu_setDefault.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/search/test/browser_ddg.js
@@ -0,0 +1,108 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ * Test DuckDuckGo search plugin URLs
+ */
+
+"use strict";
+
+const BROWSER_SEARCH_PREF = "browser.search.";
+
+function test() {
+  let engine = Services.search.getEngineByName("DuckDuckGo");
+  ok(engine, "DuckDuckGo");
+
+  let base = "https://duckduckgo.com/?q=foo";
+  let url;
+
+  // Test search URLs (including purposes).
+  url = engine.getSubmission("foo").uri.spec;
+  is(url, base + "&t=ffsb", "Check search URL for 'foo'");
+  url = engine.getSubmission("foo", null, "contextmenu").uri.spec;
+  is(url, base + "&t=ffcm", "Check context menu search URL for 'foo'");
+  url = engine.getSubmission("foo", null, "keyword").uri.spec;
+  is(url, base + "&t=ffab", "Check keyword search URL for 'foo'");
+  url = engine.getSubmission("foo", null, "searchbar").uri.spec;
+  is(url, base + "&t=ffsb", "Check search bar search URL for 'foo'");
+  url = engine.getSubmission("foo", null, "homepage").uri.spec;
+  is(url, base + "&t=ffhp", "Check homepage search URL for 'foo'");
+  url = engine.getSubmission("foo", null, "newtab").uri.spec;
+  is(url, base + "&t=ffnt", "Check newtab search URL for 'foo'");
+
+  // Check search suggestion URL.
+  url = engine.getSubmission("foo", "application/x-suggestions+json").uri.spec;
+  is(url, "https://ac.duckduckgo.com/ac/?q=foo&type=list", "Check search suggestion URL for 'foo'");
+
+  // Check all other engine properties.
+  const EXPECTED_ENGINE = {
+    name: "DuckDuckGo",
+    alias: null,
+    description: "Search DuckDuckGo",
+    searchForm: "https://duckduckgo.com/?q=&t=ffsb",
+    hidden: false,
+    wrappedJSObject: {
+      queryCharset: "UTF-8",
+      "_iconURL": "data:image/icon;base64,AAABAAIAEBAAAAEAIABoBAAAJgAAACAgAAABACAAqBAAAI4EAAAoAAAAEAAAACAAAAABACAAAAAAAAAEAAATCwAAEwsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA11RgALs6oACbQ9wAj0v8AI9L/ACfQ9wAu0agANdUYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzzN4CNdL/oK/z//////////////////////+jsPv/BDXX/wAz0t4AAAAAAAAAAAAAAAAAAAAAAAAAAAAyzvNSduD//////8jK/v+P+Lf/IbQL/17RPP+J3Y//wOKX//////9YeuX/ADLO8wAAAAAAAAAAAAAAAAAw091piOX/8/X9/1Fx5P9xhu//WOWZ/0W9Lv9Lwjn/J8BB/xyDAP9bdfL/9fP//2mI5v8AMNPdAAAAAAc610YRQ9f//////0Zr4P8AGdD/sb32////////////wrv//wAh1/8MPab/ACPc/05r4///////EkPX/wc610YANtWkrr/y/6S48P8AJ9L/AB3R/+/w/v///////////3+D7f8AQeL/AYTw/wFr5/8AMNb/p7Tv/6698v8AM9WkADLW//////8yXt//AC3V/wAw1/////////////z///8A0P7/AKb1/wWI7P8AuPf/AJ3w/zZW3P//////ADHV/wAx2P//////AzrZ/wAu1/84ZOL////////////e////AND//wC1+f8Atff/AZbv/wY62f8ELNf//////wAw1/8AMtn//////wAw2f8ALNn/kKrz////+//cwbH////////////R////Rcb8/wDO/f8A/P//AHzo//////8AMNj/ADXa//////8vXuL/ACna/4yq9///79T/jUkg/9i+r///////r2Q0/7Cozv8BKdr/AirY/zdZ4P//////ADTa/wI72tOuv/T/prr0/wAl2v+JqPb//7yW/+bUxv/9+/n////u//W+n/+Op/L/ADPd/wAv2v+ru/T/r7/0/wI72tMLQd1DEEjg//////9Cbef/ADng///////////////////////R3///AC3g/wAy3v9SeOn//////xFI4P8LQd1DAAAAAAM64PNmiuz/9/j//2mN7f/m7P3///////////9Cb+n/ACXd/wAt3v9rju3//////2iL7P8DOuDzAAAAAAAAAAAAAAAAAT3g/0p16f//////3OT8/3OS7v8AKt3/ACPc/zhn5/+xw/b//////0956v8CPeD/AAAAAAAAAAAAAAAAAAAAAAAAAAAEPODzBUDh/5uz8//7/f7/////////////////prz0/wtF4v8FQeDzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtF5kYDQOOkADrj/wA44v8AOeP/ADzk/wVB46QPReZGAAAAAAAAAAAAAAAAAAAAAPAPAADgBwAAwAMAAIABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIABAADAAwAA4AcAAPAPAAAoAAAAIAAAAEAAAAABACAAAAAAAAAQAAATCwAAEwsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAChIzyAnRNFwJ0TQryND0d8nRNH/J0TR/ydE0f8nRNH/I0PR3ydE0K8nRNFwKEjPIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAChE00AlRdK/J0XS/ydF0v8nRdL/XXPd/11z3f94i+P/k6Lp/5Oi6f9rf+D/NVDV/ydF0v8nRdL/JUXSvyhE00AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBAzxAnRNOvJ0XT/ydF0/8lRdK/KEXSYOvu+6/+/v6//v7+v/39/c////////////7+/r/J0fOAKEXSYCVF0r8nRdP/J0XT/ydE068gQM8QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlRdUwJ0bT7ydG0/8nRtHPKETTQAAAAADHx8dA2vHhn5TYpN/o9+z/////////////////8PL83ydG0o8lRdUwAAAAAChE00AnRtHPJ0bT/ydG0+8lRdUwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKEXVYCdG1P8nRtT/KEbTgAAAAAAmRtZQI0PU38jIyP/F6s//Rrtk/0a7ZP9/yIr/c796/4vLkv+JpNf/M3Kq/zyWh/8zeKTfJkbWUAAAAAAoRtOAJ0bU/ydG1P8oRdVgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACVF1TAnR9X/J0fV/yhF1WAgQM8QJ0fTrydH1f9CW8//2tra/6Pdsv9Gu2T/Rrtk/0WzWv9Gu2T/Rrtk/0a7ZP9Gu2T/Rrtk/z6egP8nR9X/J0fTryBAzxAoRdVgJ0fV/ydH1f8lRdUwAAAAAAAAAAAAAAAAAAAAAAAAAAAgQM8QJ0fV7ydH1f8oSNVgIEDPECdH1c8nR9X/J0fV/1xwyf/t7e3/o92y/0a7ZP9Gu2T/Ra5U/0a7ZP9Gu2T/Rrtk/0a7ZP9Gu2T/Pp6A/ydH1f8nR9X/J0fVzyBAzxAoSNVgJ0fV/ydH1e8gQM8QAAAAAAAAAAAAAAAAAAAAACdH1q8nR9b/KEjVgCBQzxAnR9bPJ0fW/ydH1v8nR9b/gIzB//r6+v+j3bL/Rrtk/13Ed/+i26//ruG7/z6egf8+noH/Rrtk/0a7ZP86kI//J0fW/ydH1v8nR9b/J0fWzyBQzxAoSNWAJ0fW/ydH1q8AAAAAAAAAAAAAAAAoSNdAJkjW/yZH1s8AAAAAJEfWryZI1v8mSNb/JkjW/yZI1v+jqsT//////+j37P/R7tj////////////W3ff/JkjW/yZI1v8uZbr/PJeI/zJzrP8mSNb/JkjW/yZI1v8mSNb/JEfWrwAAAAAmR9bPJkjW/yhI10AAAAAAAAAAACVI1r8mSNf/KEjXQCZJ1lAmSNf/JkjX/yZI1/8mSNf/JkjX/9HR0f///////////////////////////5Ok6/8mSNf/JkjX/yZI1/8mSNf/JkjX/yZI1/8mSNf/JkjX/yZI1/8mSNf/JknWUChI10AmSNf/JUjWvwAAAAAoSNcgJknY/yZH2M8AAAAAI0nY3yZJ2P8mSdj/JknY/yZJ2P9KZM//39/f////////////////////////////XHfi/yZJ2P8mSdj/JknY/yZJ2P8mSdj/JknY/yZJ2P8mSdj/JknY/yZJ2P8jSdjfAAAAACZH2M8mSdj/KEjXICdJ2HAmSdj/JUjXYCVK2jAmSdj/JknY/yZJ2P8mSdj/JknY/2V4yf/t7e3///////////////////////////9cd+L/HXTj/xSf7/8Nwfj/CdL8/wnS/P8J0vz/ELDz/xt85v8mSdj/JknY/yZJ2P8lStowJUjXYCZJ2P8nSdhwJErZryZK2f8oSNcgJUnajyZK2f8mStn/JkrZ/yZK2f8mStn/iJPA////////////////////////////0ff+/xjV/P8J0vz/Drn1/xiO6/8Yjuv/GI7r/xCw8/8Lyvr/CdL8/xmF6P8mStn/JkrZ/yVJ2o8oSNcgJkrZ/yRK2a8jStrfI0rZ3wAAAAAlSdq/Jkra/yZK2v8mStr/Jkra/yZK2v+xtsf///////////////////////////8o2Pz/CdL8/wvK+v8mStr/Jkra/yZK2v8mStr/Jkra/yZK2v8iW97/Jkra/yZK2v8mStr/JUnavwAAAAAjStnfI0ra3yZK2v8lSdq/AAAAACZH2O8mStr/Jkra/yZK2v8mStr/L1HY/9HR0f///////////////////////////yjY/P8J0vz/CdL8/xCw9P8QsPT/ELD0/xSf7/8ddeX/Jkra/yZK2v8mStr/Jkra/yZK2v8mR9jvAAAAACVJ2r8mStr/Jkvb/yVJ2r8AAAAAJkvb/yZL2/8mS9v/Jkvb/yZL2/9KZtL/4+Pj////////////////////////////4Pn//0fd/f8J0vz/CdL8/wnS/P8J0vz/CdL8/wnS/P8Lyvr/Fpfu/yJc3/8mS9v/Jkvb/yZL2/8AAAAAJUnavyZL2/8mS9z/JUncvwAAAAAmS9z/Jkvc/yZL3P8mS9z/Jkvc/26AyP/x8fH//////////////////////////////////////9H3/v/C9P7/o+7+/2fa+/8Oufb/CdL8/wnS/P8J0vz/CdL8/xiP7P8mS9z/Jkvc/wAAAAAlSdy/Jkvc/yZM3P8lTNy/AAAAACZJ2e8mTNz/Jkzc/yZM3P8mTNz/iJTB////////////qnth/5VaOf/x6eX///////////////////////Hp5f/x6eX/ydL2/yZM3P8kVN7/G37o/xKo8v8QsfT/HXbm/yZM3P8mSdnvAAAAACVM3L8mTNz/I0vc3yZJ2u8AAAAAJUzevyZM3f8mTN3/Jkzd/yZM3f+fqc3///////////+VWjn/v5yI/+re1///////////////////////jk8s/7iRe//J0vb/Jkzd/yZM3f8mTN3/Jkzd/yZM3f8mTN3/Jkzd/yVM3r8AAAAAI0vc3yNL3N8kTd2vJk3d/yhQ3yAlTd2PJk3d/yZN3f8mTd3/Jk3d/6St0v////////////Hp5f/q3tf///////////////////////////+xhm7/49PK/6Cx8P8mTd3/Jk3d/yZN3f8mTd3/Jk3d/yZN3f8mTd3/JU3djyhQ3yAmTd3/JE3drydN33AmTd7/J03fcCVK3zAmTd7/Jk3e/yZN3v8mTd7/pK7S///////Sp5r/////////////////////////////////////////////////T27k/yZN3v8mTd7/Jk3e/yZN3v8mTd7/Jk3e/yZN3v8lSt8wJ03fcCZN3v8nTd9wKFDfICZO3/8mTt3PAAAAACVN3r8mTt//Jk7f/yZO3/+EltX//////+fRyv/SqaD/59LO///////////////////////at63/vIBy/7Glxf8mTt//Jk7f/yZO3/8mTt//Jk7f/yZO3/8mTt//JU3evwAAAAAmTt3PJk7f/yhQ3yAAAAAAJE/dryZO3/8oUN9AKFDfQCZO3/8mTt//Jk7f/zhb2v/o6/T/////////////////////////////////////////////////XHrn/yZO3/8mTt//Jk7f/yZO3/8mTt//Jk7f/yZO3/8oUN9AKFDfQCZO3/8kT92vAAAAAAAAAAAoUN9AJk7g/yZO4M8AAAAAJk/hnyZO4P8mTuD/Jk7g/05v5v/k6fv//////////////////////////////////////3eR7P8mTuD/Jk7g/yZO4P8mTuD/Jk7g/yZO4P8mTuD/Jk/hnwAAAAAmTuDPJk7g/yhQ30AAAAAAAAAAAAAAAAAjT+GfJU/h/yVO4Y8gUN8QIk7gzyVP4f8lT+H/SWnW/0lp1v+bq+H/8fHx/////////////////6Cy8v9OcOb/JU/h/yVP4f8lT+H/JU/h/yVP4f8lT+H/JU/h/yJO4M8gUN8QJU7hjyVP4f8jT+GfAAAAAAAAAAAAAAAAAAAAACBQ3xAlTOHvJU/h/yVQ4mAgUN8QIk7hzyVP4f+ktOv///////////////////////H0/f9phur/JU/h/yVP4f8lT+H/JU/h/yVP4f8lT+H/JU/h/yVP4f8iTuHPIFDfECVQ4mAlT+H/JUzh7yBQ3xAAAAAAAAAAAAAAAAAAAAAAAAAAACVQ3zAlUOLvJVDi/yVQ4mAgUN8QI1Din4mb2//J0/j/ydP4/6299P93ku3/M1vk/yVQ4v8lUOL/JVDi/yVQ4v8lUOL/JVDi/yVQ4v8lUOL/I1DinyBQ3xAlUOJgJVDi/yVQ4u8lUN8wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACVQ5DAlUOLvJVDi/yVQ4o8AAAAAJFDjQCVQ4r8lUOL/JVDi/yVQ4v8lUOL/JVDi/yVQ4v8lUOL/JVDi/yVQ4v8lUOL/JVDivyRQ40AAAAAAJVDijyVQ4v8lUOLvJVDkMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACVQ5DAjUeTfJVHj/yNR5N8kUONAAAAAACVQ5DAmUuOAJVHivyNR5N8lUeP/JVHj/yNR5N8lUeK/JlLjgCVQ5DAAAAAAJFDjQCNR5N8lUeP/I1Hk3yVQ5DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBQ3xAjUuSfJVHk/yVR5P8jUeTfJFLkcChQ5yAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoUOcgJFLkcCNR5N8lUeT/JVHk/yNS5J8gUN8QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkUONAI1LknyVS5P8lUuT/JVLk/yVS5O8lUeS/JVHkvyVR5L8lUeS/JVLk7yVS5P8lUuT/JVLk/yRS468kUONAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIFDfECVS5GAjUuWfIlPlzyVS5f8lUuX/JVLl/yVS5f8iU+XPI1LlnyVS5GAgUN8QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/AA///AAD//AAAP/ggBB/wgAEP4AAAB8AAAAPAAAADiAAAEYAAAAEQAAAIAAAAAAAAAAAgAAAEIAAABCAAAAQgAAAEIAAABCAAAAQAAAAAAAAAABAAAAiAAAABiAAAEcAAAAPAAAAD4AAAB/CAAQ/4IAQf/AfgP/8AAP//wAP/",
+      _urls : [
+        {
+          type: "text/html",
+          method: "GET",
+          template: "https://duckduckgo.com/",
+          params: [
+            {
+              name: "q",
+              value: "{searchTerms}",
+              purpose: undefined,
+            },
+            {
+              name: "t",
+              value: "ffcm",
+              purpose: "contextmenu",
+            },
+            {
+              name: "t",
+              value: "ffab",
+              purpose:"keyword",
+            },
+            {
+              name: "t",
+              value: "ffsb",
+              purpose: "searchbar",
+            },
+            {
+              name: "t",
+              value: "ffhp",
+              purpose: "homepage",
+            },
+            {
+              name: "t",
+              value: "ffnt",
+              purpose: "newtab",
+            },
+          ],
+          mozparams: {},
+        },
+        {
+          type: "application/x-suggestions+json",
+          method: "GET",
+          template: "https://ac.duckduckgo.com/ac/",
+          params: [
+            {
+              name: "q",
+              value: "{searchTerms}",
+              purpose: undefined,
+            },
+            {
+              name: "type",
+              value: "list",
+              purpose: undefined,
+            },
+          ],
+        },
+      ],
+    },
+  };
+
+  isSubObjectOf(EXPECTED_ENGINE, engine, "DuckDuckGo");
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/search/test/browser_ddg_behavior.js
@@ -0,0 +1,165 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ * Test DuckDuckGo search plugin URLs
+ */
+
+"use strict";
+
+const BROWSER_SEARCH_PREF = "browser.search.";
+
+
+function test() {
+  let engine = Services.search.getEngineByName("DuckDuckGo");
+  ok(engine, "DuckDuckGo is installed");
+
+  let previouslySelectedEngine = Services.search.currentEngine;
+  Services.search.currentEngine = engine;
+  engine.alias = "d";
+
+  let base = "https://duckduckgo.com/?q=foo";
+  let url;
+
+  // Test search URLs (including purposes).
+  url = engine.getSubmission("foo").uri.spec;
+  is(url, base + "&t=ffsb", "Check search URL for 'foo'");
+
+  waitForExplicitFinish();
+
+  var gCurrTest;
+  var gTests = [
+    {
+      name: "context menu search",
+      searchURL: base + "&t=ffcm",
+      run() {
+        // Simulate a contextmenu search
+        // FIXME: This is a bit "low-level"...
+        BrowserSearch.loadSearch("foo", false, "contextmenu");
+      }
+    },
+    {
+      name: "keyword search",
+      searchURL: base + "&t=ffab",
+      run() {
+        gURLBar.value = "? foo";
+        gURLBar.focus();
+        EventUtils.synthesizeKey("VK_RETURN", {});
+      }
+    },
+    {
+      name: "keyword search with alias",
+      searchURL: base + "&t=ffab",
+      run() {
+        gURLBar.value = "d foo";
+        gURLBar.focus();
+        EventUtils.synthesizeKey("VK_RETURN", {});
+      }
+    },
+    {
+      name: "search bar search",
+      searchURL: base + "&t=ffsb",
+      run() {
+        let sb = BrowserSearch.searchBar;
+        sb.focus();
+        sb.value = "foo";
+        registerCleanupFunction(function() {
+          sb.value = "";
+        });
+        EventUtils.synthesizeKey("VK_RETURN", {});
+      }
+    },
+    {
+      name: "new tab search",
+      searchURL: base + "&t=ffnt",
+      run() {
+        function doSearch(doc) {
+          // Re-add the listener, and perform a search
+          gBrowser.addProgressListener(listener);
+          doc.getElementById("newtab-search-text").value = "foo";
+          doc.getElementById("newtab-search-submit").click();
+        }
+
+        // load about:newtab, but remove the listener first so it doesn't
+        // get in the way
+        gBrowser.removeProgressListener(listener);
+        gBrowser.loadURI("about:newtab");
+        info("Waiting for about:newtab load");
+        tab.linkedBrowser.addEventListener("load", function load(loadEvent) {
+          if (loadEvent.originalTarget != tab.linkedBrowser.contentDocumentAsCPOW ||
+              loadEvent.target.location.href == "about:blank") {
+            info("skipping spurious load event");
+            return;
+          }
+          tab.linkedBrowser.removeEventListener("load", load, true);
+
+          // Observe page setup
+          let win = gBrowser.contentWindowAsCPOW;
+          if (win.gSearch.currentEngineName ==
+              Services.search.currentEngine.name) {
+            doSearch(win.document);
+          } else {
+            info("Waiting for newtab search init");
+            win.addEventListener("ContentSearchService", function done(contentSearchServiceEvent) {
+              info("Got newtab search event " + contentSearchServiceEvent.detail.type);
+              if (contentSearchServiceEvent.detail.type == "State") {
+                win.removeEventListener("ContentSearchService", done);
+                // Let gSearch respond to the event before continuing.
+                executeSoon(() => doSearch(win.document));
+              }
+            });
+          }
+        }, true);
+      }
+    }
+  ];
+
+  function nextTest() {
+    if (gTests.length) {
+      gCurrTest = gTests.shift();
+      info("Running : " + gCurrTest.name);
+      executeSoon(gCurrTest.run);
+    } else {
+      finish();
+    }
+  }
+
+  let tab = gBrowser.selectedTab = gBrowser.addTab();
+
+  let listener = {
+    onStateChange: function onStateChange(webProgress, req, flags, status) {
+      info("onStateChange");
+      // Only care about top-level document starts
+      let docStart = Ci.nsIWebProgressListener.STATE_IS_DOCUMENT |
+                     Ci.nsIWebProgressListener.STATE_START;
+      if (!(flags & docStart) || !webProgress.isTopLevel)
+        return;
+
+      if (req.originalURI.spec == "about:blank")
+        return;
+
+      info("received document start");
+
+      ok(req instanceof Ci.nsIChannel, "req is a channel");
+      is(req.originalURI.spec, gCurrTest.searchURL, "search URL was loaded");
+      info("Actual URI: " + req.URI.spec);
+
+      req.cancel(Components.results.NS_ERROR_FAILURE);
+
+      executeSoon(nextTest);
+    }
+  }
+
+  registerCleanupFunction(function() {
+    engine.alias = undefined;
+    gBrowser.removeProgressListener(listener);
+    gBrowser.removeTab(tab);
+    Services.search.currentEngine = previouslySelectedEngine;
+  });
+
+  tab.linkedBrowser.addEventListener("load", function load() {
+    tab.linkedBrowser.removeEventListener("load", load, true);
+    gBrowser.addProgressListener(listener);
+    nextTest();
+  }, true);
+}
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -407,17 +407,16 @@
 @RESPATH@/components/NetworkGeolocationProvider.js
 @RESPATH@/components/extensions.manifest
 @RESPATH@/components/EditorUtils.manifest
 @RESPATH@/components/EditorUtils.js
 @RESPATH@/components/addonManager.js
 @RESPATH@/components/amContentHandler.js
 @RESPATH@/components/amInstallTrigger.js
 @RESPATH@/components/amWebAPI.js
-@RESPATH@/components/amWebInstallListener.js
 @RESPATH@/components/nsBlocklistService.js
 @RESPATH@/components/nsBlocklistServiceContent.js
 #ifdef MOZ_UPDATER
 @RESPATH@/components/nsUpdateService.manifest
 @RESPATH@/components/nsUpdateService.js
 @RESPATH@/components/nsUpdateServiceStub.js
 #endif
 @RESPATH@/components/nsUpdateTimerManager.manifest
--- a/browser/locales/en-US/chrome/browser/aboutTabCrashed.dtd
+++ b/browser/locales/en-US/chrome/browser/aboutTabCrashed.dtd
@@ -21,10 +21,10 @@
 <!ENTITY tabCrashed.requestHelpMessage "Crash reports help us diagnose problems and make &brandShortName; better.">
 <!ENTITY tabCrashed.requestReport "Report this tab">
 <!ENTITY tabCrashed.sendReport2 "Send a crash report for the tab you are viewing">
 <!ENTITY tabCrashed.commentPlaceholder2 "Optional comments (comments are publicly visible)">
 <!ENTITY tabCrashed.includeURL2 "Include page URL with this crash report">
 <!ENTITY tabCrashed.emailPlaceholder "Enter your email address here">
 <!ENTITY tabCrashed.emailMe "Email me when more information is available">
 <!ENTITY tabCrashed.reportSent "Crash report already submitted; thank you for helping make &brandShortName; better!">
-<!ENTITY tabCrashed.requestAutoSubmit "Request background tabs">
+<!ENTITY tabCrashed.requestAutoSubmit2 "Report background tabs">
 <!ENTITY tabCrashed.autoSubmit "Update preferences to automatically submit backlogged crash reports (and get fewer messages like this from us in the future)">
\ No newline at end of file
--- a/browser/locales/en-US/chrome/browser/preferences/sync.dtd
+++ b/browser/locales/en-US/chrome/browser/preferences/sync.dtd
@@ -20,17 +20,17 @@
 <!ENTITY myRecoveryKey.label          "My Recovery Key">
 <!ENTITY resetSync2.label             "Reset Sync…">
 
 <!ENTITY pairDevice.label             "Pair a Device">
 
 <!ENTITY syncMy.label               "Sync My">
 <!ENTITY engine.bookmarks.label     "Bookmarks">
 <!ENTITY engine.bookmarks.accesskey "m">
-<!ENTITY engine.tabs.label          "Tabs">
+<!ENTITY engine.tabs.label2         "Open Tabs">
 <!ENTITY engine.tabs.accesskey      "T">
 <!ENTITY engine.history.label       "History">
 <!ENTITY engine.history.accesskey   "r">
 <!ENTITY engine.passwords.label     "Passwords">
 <!ENTITY engine.passwords.accesskey "P">
 <!ENTITY engine.prefs.label         "Preferences">
 <!ENTITY engine.prefs.accesskey     "S">
 <!ENTITY engine.addons.label        "Add-ons">
--- a/browser/locales/en-US/chrome/browser/syncCustomize.dtd
+++ b/browser/locales/en-US/chrome/browser/syncCustomize.dtd
@@ -12,16 +12,16 @@
 <!--
   These engine names are the same as in browser/preferences/sync.dtd except
   for the last two that are marked as being specific to Desktop browsers.
 -->
 <!ENTITY engine.bookmarks.label           "Bookmarks">
 <!ENTITY engine.bookmarks.accesskey       "m">
 <!ENTITY engine.history.label             "History">
 <!ENTITY engine.history.accesskey         "r">
-<!ENTITY engine.tabs.label                "Tabs">
+<!ENTITY engine.tabs.label2               "Open Tabs">
 <!ENTITY engine.tabs.accesskey            "T">
 <!ENTITY engine.passwords.label           "Passwords">
 <!ENTITY engine.passwords.accesskey       "P">
 <!ENTITY engine.addons.label              "Desktop Add-ons">
 <!ENTITY engine.addons.accesskey          "A">
 <!ENTITY engine.prefs.label               "Desktop Preferences">
 <!ENTITY engine.prefs.accesskey           "S">
--- a/browser/locales/en-US/chrome/browser/syncSetup.dtd
+++ b/browser/locales/en-US/chrome/browser/syncSetup.dtd
@@ -80,17 +80,17 @@
 <!-- Sync Options -->
 <!ENTITY setup.optionsPage.title      "Sync Options">
 <!ENTITY syncDeviceName.label       "Device Name:">
 <!ENTITY syncDeviceName.accesskey   "c">
 
 <!ENTITY syncMy.label               "Sync My">
 <!ENTITY engine.bookmarks.label     "Bookmarks">
 <!ENTITY engine.bookmarks.accesskey "m">
-<!ENTITY engine.tabs.label          "Tabs">
+<!ENTITY engine.tabs.label2         "Open Tabs">
 <!ENTITY engine.tabs.accesskey      "T">
 <!ENTITY engine.history.label       "History">
 <!ENTITY engine.history.accesskey   "r">
 <!ENTITY engine.passwords.label     "Passwords">
 <!ENTITY engine.passwords.accesskey "P">
 <!ENTITY engine.prefs.label         "Preferences">
 <!ENTITY engine.prefs.accesskey     "S">
 <!ENTITY engine.addons.label        "Add-ons">
--- a/browser/modules/test/browser_UnsubmittedCrashHandler.js
+++ b/browser/modules/test/browser_UnsubmittedCrashHandler.js
@@ -1,23 +1,23 @@
 "use strict";
 
 /**
  * This suite tests the "unsubmitted crash report" notification
  * that is seen when we detect pending crash reports on startup.
  */
 
 const { UnsubmittedCrashHandler } =
-  Cu.import("resource:///modules/ContentCrashHandlers.jsm", this);
+  Cu.import("resource:///modules/ContentCrashHandlers.jsm", {});
 const { FileUtils } =
-  Cu.import("resource://gre/modules/FileUtils.jsm", this);
+  Cu.import("resource://gre/modules/FileUtils.jsm", {});
 const { makeFakeAppDir }  =
-  Cu.import("resource://testing-common/AppData.jsm", this);
+  Cu.import("resource://testing-common/AppData.jsm", {});
 const { OS } =
-  Cu.import("resource://gre/modules/osfile.jsm", this);
+  Cu.import("resource://gre/modules/osfile.jsm", {});
 
 const DAY = 24 * 60 * 60 * 1000; // milliseconds
 const SERVER_URL = "http://example.com/browser/toolkit/crashreporter/test/browser/crashreport.sjs";
 
 /**
  * Returns the directly where the browsing is storing the
  * pending crash reports.
  *
--- a/devtools/client/netmonitor/netmonitor-controller.js
+++ b/devtools/client/netmonitor/netmonitor-controller.js
@@ -20,18 +20,16 @@ const { configureStore } = require("./st
 const Actions = require("./actions/index");
 const { getDisplayedRequestById } = require("./selectors/index");
 const { Prefs } = require("./prefs");
 
 XPCOMUtils.defineConstant(window, "EVENTS", EVENTS);
 XPCOMUtils.defineConstant(window, "ACTIVITY_TYPE", ACTIVITY_TYPE);
 XPCOMUtils.defineConstant(window, "Editor", Editor);
 XPCOMUtils.defineConstant(window, "Prefs", Prefs);
-XPCOMUtils.defineLazyModuleGetter(window, "Chart",
-  "resource://devtools/client/shared/widgets/Chart.jsm");
 
 // Initialize the global Redux store
 window.gStore = configureStore();
 
 /**
  * Object defining the network monitor controller components.
  */
 var NetMonitorController = {
@@ -706,16 +704,22 @@ NetworkEventsHandler.prototype = {
    *        The long string grip containing the corresponding actor.
    *        If you pass in a plain string (by accident or because you're lazy),
    *        then a promise of the same string is simply returned.
    * @return object Promise
    *         A promise that is resolved when the full string contents
    *         are available, or rejected if something goes wrong.
    */
   getString: function (stringGrip) {
+    // FIXME: this.webConsoleClient will be undefined in mochitest,
+    // so we return string instantly to skip undefined error
+    if (typeof stringGrip === "string") {
+      return Promise.resolve(stringGrip);
+    }
+
     return this.webConsoleClient.getString(stringGrip);
   }
 };
 
 /**
  * Convenient way of emitting events from the panel window.
  */
 EventEmitter.decorate(window);
--- a/devtools/client/netmonitor/netmonitor.xul
+++ b/devtools/client/netmonitor/netmonitor.xul
@@ -279,24 +279,23 @@
           </tabbox>
         </deck>
       </hbox>
 
     </vbox>
 
     <html:div id="network-statistics-view">
       <html:div id="network-statistics-toolbar"
-               class="devtools-toolbar">
+                class="devtools-toolbar">
         <html:div xmlns="http://www.w3.org/1999/xhtml"
                   id="react-statistics-back-hook"/>
       </html:div>
-      <box id="network-statistics-charts"
-           class="devtools-responsive-container"
-           flex="1">
-        <vbox id="primed-cache-chart" pack="center" flex="1"/>
-        <splitter id="network-statistics-view-splitter"
-                  class="devtools-side-splitter"/>
-        <vbox id="empty-cache-chart" pack="center" flex="1"/>
-      </box>
+      <html:div id="network-statistics-charts"
+                class="devtools-responsive-container">
+        <html:div id="primed-cache-chart"/>
+        <html:div id="network-statistics-view-splitter"
+                  class="split-box devtools-side-splitter"/>
+        <html:div id="empty-cache-chart"/>
+      </html:div>
     </html:div>
   </deck>
 
 </window>
--- a/devtools/client/netmonitor/requests-menu-view.js
+++ b/devtools/client/netmonitor/requests-menu-view.js
@@ -203,56 +203,54 @@ RequestsMenuView.prototype = {
   updateRequest: Task.async(function* (id, data) {
     const action = Actions.updateRequest(id, data, true);
     yield this.store.dispatch(action);
 
     let { responseContent, requestPostData } = action.data;
 
     if (responseContent && responseContent.content) {
       let request = getRequestById(this.store.getState(), action.id);
-      let { text, encoding } = responseContent.content;
       if (request) {
         let { mimeType } = request;
+        let { text, encoding } = responseContent.content;
+        let response = yield gNetwork.getString(text);
+        let payload = {};
 
-        // Fetch response data if the response is an image (to display thumbnail)
         if (mimeType.includes("image/")) {
-          let responseBody = yield gNetwork.getString(text);
-          const dataUri = formDataURI(mimeType, encoding, responseBody);
-          yield this.store.dispatch(Actions.updateRequest(
-            action.id,
-            { responseContentDataUri: dataUri },
-            true
-          ));
+          payload.responseContentDataUri = formDataURI(mimeType, encoding, response);
+        }
+
+        if (mimeType.includes("text/")) {
+          responseContent.content.text = response;
+          payload.responseContent = responseContent;
+        }
+
+        yield this.store.dispatch(Actions.updateRequest(action.id, payload, true));
+
+        if (mimeType.includes("image/")) {
           window.emit(EVENTS.RESPONSE_IMAGE_THUMBNAIL_DISPLAYED);
-        // Fetch response text only if the response is html, but not all text/*
-        } else if (mimeType.includes("text/html") && typeof text !== "string") {
-          let responseBody = yield gNetwork.getString(text);
-          responseContent.content.text = responseBody;
-          responseContent = Object.assign({}, responseContent);
-          yield this.store.dispatch(Actions.updateRequest(
-            action.id,
-            { responseContent },
-            true
-          ));
         }
       }
     }
 
     // Search the POST data upload stream for request headers and add
     // them as a separate property, different from the classic headers.
     if (requestPostData && requestPostData.postData) {
       let { text } = requestPostData.postData;
       let postData = yield gNetwork.getString(text);
       const headers = CurlUtils.getHeadersFromMultipartText(postData);
       const headersSize = headers.reduce((acc, { name, value }) => {
         return acc + name.length + value.length + 2;
       }, 0);
-      yield this.store.dispatch(Actions.updateRequest(action.id, {
-        requestHeadersFromUploadStream: { headers, headersSize }
-      }, true));
+      let payload = {};
+      requestPostData.postData.text = postData;
+      payload.requestPostData = Object.assign({}, requestPostData);
+      payload.requestHeadersFromUploadStream = { headers, headersSize };
+
+      yield this.store.dispatch(Actions.updateRequest(action.id, payload, true));
     }
   }),
 
   /**
    * Disable batched updates. Used by tests.
    */
   set lazyUpdate(value) {
     this.store.dispatch(Actions.batchEnable(value));
--- a/devtools/client/netmonitor/selectors/requests.js
+++ b/devtools/client/netmonitor/selectors/requests.js
@@ -88,32 +88,35 @@ const getDisplayedRequestsSummary = crea
     return {
       count: requests.size,
       bytes: totalBytes,
       millis: totalMillis,
     };
   }
 );
 
+const getSelectedRequest = createSelector(
+  state => state.requests,
+  requests => {
+    if (!requests.selectedId) {
+      return null;
+    }
+
+    return requests.requests.find(r => r.id === requests.selectedId);
+  }
+);
+
 function getRequestById(state, id) {
   return state.requests.requests.find(r => r.id === id);
 }
 
 function getDisplayedRequestById(state, id) {
   return getDisplayedRequests(state).find(r => r.id === id);
 }
 
-function getSelectedRequest(state) {
-  if (!state.requests.selectedId) {
-    return null;
-  }
-
-  return getRequestById(state, state.requests.selectedId);
-}
-
 module.exports = {
-  getSortedRequests,
+  getDisplayedRequestById,
   getDisplayedRequests,
   getDisplayedRequestsSummary,
   getRequestById,
-  getDisplayedRequestById,
   getSelectedRequest,
+  getSortedRequests,
 };
--- a/devtools/client/netmonitor/statistics-view.js
+++ b/devtools/client/netmonitor/statistics-view.js
@@ -2,28 +2,25 @@
  * 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/. */
 
 /* eslint-disable mozilla/reject-some-requires */
 /* globals $, window, document */
 
 "use strict";
 
-const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
 const { PluralForm } = require("devtools/shared/plural-form");
 const { Filters } = require("./filter-predicates");
 const { L10N } = require("./l10n");
 const { EVENTS } = require("./events");
 const { DOM } = require("devtools/client/shared/vendor/react");
 const { button } = DOM;
 const ReactDOM = require("devtools/client/shared/vendor/react-dom");
 const Actions = require("./actions/index");
-
-XPCOMUtils.defineLazyModuleGetter(this, "Chart",
-  "resource://devtools/client/shared/widgets/Chart.jsm");
+const { Chart } = require("devtools/client/shared/widgets/Chart");
 
 const REQUEST_TIME_DECIMALS = 2;
 const CONTENT_SIZE_DECIMALS = 2;
 
 // px
 const NETWORK_ANALYSIS_PIE_CHART_DIAMETER = 200;
 
 /**
@@ -33,16 +30,17 @@ function StatisticsView() {
 }
 
 StatisticsView.prototype = {
   /**
    * Initialization function, called when the statistics view is started.
    */
   initialize: function (store) {
     this.store = store;
+    this.Chart = Chart;
     this._backButton = $("#react-statistics-back-hook");
 
     let backStr = L10N.getStr("netmonitor.backButton");
     ReactDOM.render(button({
       id: "network-statistics-back-button",
       className: "devtools-toolbarbutton",
       "data-text-only": "true",
       title: backStr,
@@ -144,28 +142,28 @@ StatisticsView.prototype = {
   },
 
   /**
    * Adds a specific chart to this container.
    *
    * @param object
    *        An object containing all or some the following properties:
    *          - id: either "#primed-cache-chart" or "#empty-cache-chart"
-   *          - title/data/strings/totals/sorted: @see Chart.jsm for details
+   *          - title/data/strings/totals/sorted: @see Chart.js for details
    */
   _createChart: function ({ id, title, data, strings, totals, sorted }) {
     let container = $(id);
 
     // Nuke all existing charts of the specified type.
     while (container.hasChildNodes()) {
       container.firstChild.remove();
     }
 
     // Create a new chart.
-    let chart = Chart.PieTable(document, {
+    let chart = this.Chart.PieTable(document, {
       diameter: NETWORK_ANALYSIS_PIE_CHART_DIAMETER,
       title: L10N.getStr(title),
       data: data,
       strings: strings,
       totals: totals,
       sorted: sorted
     });
 
@@ -175,17 +173,17 @@ StatisticsView.prototype = {
       this.store.dispatch(Actions.openStatistics(false));
     });
 
     container.appendChild(chart.node);
   },
 
   /**
    * Sanitizes the data source used for creating charts, to follow the
-   * data format spec defined in Chart.jsm.
+   * data format spec defined in Chart.js.
    *
    * @param array items
    *        A collection of request items used as the data source for the chart.
    * @param boolean emptyCache
    *        True if the cache is considered enabled, false for disabled.
    */
   _sanitizeChartDataSource: function (items, emptyCache) {
     let data = [
--- a/devtools/client/netmonitor/test/browser.ini
+++ b/devtools/client/netmonitor/test/browser.ini
@@ -38,16 +38,17 @@ support-files =
   html_curl-utils.html
   sjs_content-type-test-server.sjs
   sjs_cors-test-server.sjs
   sjs_https-redirect-test-server.sjs
   sjs_hsts-test-server.sjs
   sjs_simple-test-server.sjs
   sjs_sorting-test-server.sjs
   sjs_status-codes-test-server.sjs
+  sjs_truncate-test-server.sjs
   test-image.png
   service-workers/status-codes.html
   service-workers/status-codes-service-worker.js
   !/devtools/client/framework/test/shared-head.js
 
 [browser_net_aaa_leaktest.js]
 [browser_net_accessibility-01.js]
 [browser_net_accessibility-02.js]
@@ -148,9 +149,10 @@ skip-if = true # Redundant for React/Red
 [browser_net_statistics-02.js]
 [browser_net_statistics-03.js]
 [browser_net_status-codes.js]
 [browser_net_streaming-response.js]
 [browser_net_throttle.js]
 [browser_net_timeline_ticks.js]
 skip-if = true # TODO: fix the test
 [browser_net_timing-division.js]
+[browser_net_truncate.js]
 [browser_net_persistent_logs.js]
--- a/devtools/client/netmonitor/test/browser_net_charts-01.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-01.js
@@ -6,17 +6,18 @@
 /**
  * Makes sure Pie Charts have the right internal structure.
  */
 
 add_task(function* () {
   let { monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
-  let { document, Chart } = monitor.panelWin;
+  let { document, NetMonitorView } = monitor.panelWin;
+  const { Chart } = NetMonitorView.Statistics;
 
   let pie = Chart.Pie(document, {
     width: 100,
     height: 100,
     data: [{
       size: 1,
       label: "foo"
     }, {
--- a/devtools/client/netmonitor/test/browser_net_charts-02.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-02.js
@@ -9,17 +9,18 @@
  */
 
 add_task(function* () {
   let { L10N } = require("devtools/client/netmonitor/l10n");
 
   let { monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
-  let { document, Chart } = monitor.panelWin;
+  let { document, NetMonitorView } = monitor.panelWin;
+  let { Chart } = NetMonitorView.Statistics;
 
   let pie = Chart.Pie(document, {
     data: null,
     width: 100,
     height: 100
   });
 
   let node = pie.node;
--- a/devtools/client/netmonitor/test/browser_net_charts-03.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-03.js
@@ -8,17 +8,18 @@
  */
 
 add_task(function* () {
   let { L10N } = require("devtools/client/netmonitor/l10n");
 
   let { monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
-  let { document, Chart } = monitor.panelWin;
+  let { document, NetMonitorView } = monitor.panelWin;
+  let { Chart } = NetMonitorView.Statistics;
 
   let table = Chart.Table(document, {
     title: "Table title",
     data: [{
       label1: 1,
       label2: 11.1
     }, {
       label1: 2,
@@ -43,64 +44,64 @@ add_task(function* () {
   let rows = grid.querySelectorAll(".table-chart-row");
   let sums = node.querySelectorAll(".table-chart-summary-label");
 
   ok(node.classList.contains("table-chart-container") &&
      node.classList.contains("generic-chart-container"),
     "A table chart container was created successfully.");
 
   ok(title, "A title node was created successfully.");
-  is(title.getAttribute("value"), "Table title",
+  is(title.textContent, "Table title",
     "The title node displays the correct text.");
 
   is(rows.length, 3, "There should be 3 table chart rows created.");
 
   ok(rows[0].querySelector(".table-chart-row-box.chart-colored-blob"),
     "A colored blob exists for the firt row.");
-  is(rows[0].querySelectorAll("label")[0].getAttribute("name"), "label1",
+  is(rows[0].querySelectorAll("span")[0].getAttribute("name"), "label1",
     "The first column of the first row exists.");
-  is(rows[0].querySelectorAll("label")[1].getAttribute("name"), "label2",
+  is(rows[0].querySelectorAll("span")[1].getAttribute("name"), "label2",
     "The second column of the first row exists.");
-  is(rows[0].querySelectorAll("label")[0].getAttribute("value"), "1",
+  is(rows[0].querySelectorAll("span")[0].textContent, "1",
     "The first column of the first row displays the correct text.");
-  is(rows[0].querySelectorAll("label")[1].getAttribute("value"), "11.1foo",
+  is(rows[0].querySelectorAll("span")[1].textContent, "11.1foo",
     "The second column of the first row displays the correct text.");
 
   ok(rows[1].querySelector(".table-chart-row-box.chart-colored-blob"),
     "A colored blob exists for the second row.");
-  is(rows[1].querySelectorAll("label")[0].getAttribute("name"), "label1",
+  is(rows[1].querySelectorAll("span")[0].getAttribute("name"), "label1",
     "The first column of the second row exists.");
-  is(rows[1].querySelectorAll("label")[1].getAttribute("name"), "label2",
+  is(rows[1].querySelectorAll("span")[1].getAttribute("name"), "label2",
     "The second column of the second row exists.");
-  is(rows[1].querySelectorAll("label")[0].getAttribute("value"), "2",
+  is(rows[1].querySelectorAll("span")[0].textContent, "2",
     "The first column of the second row displays the correct text.");
-  is(rows[1].querySelectorAll("label")[1].getAttribute("value"), "12.2bar",
+  is(rows[1].querySelectorAll("span")[1].textContent, "12.2bar",
     "The second column of the first row displays the correct text.");
 
   ok(rows[2].querySelector(".table-chart-row-box.chart-colored-blob"),
     "A colored blob exists for the third row.");
-  is(rows[2].querySelectorAll("label")[0].getAttribute("name"), "label1",
+  is(rows[2].querySelectorAll("span")[0].getAttribute("name"), "label1",
     "The first column of the third row exists.");
-  is(rows[2].querySelectorAll("label")[1].getAttribute("name"), "label2",
+  is(rows[2].querySelectorAll("span")[1].getAttribute("name"), "label2",
     "The second column of the third row exists.");
-  is(rows[2].querySelectorAll("label")[0].getAttribute("value"), "3",
+  is(rows[2].querySelectorAll("span")[0].textContent, "3",
     "The first column of the third row displays the correct text.");
-  is(rows[2].querySelectorAll("label")[1].getAttribute("value"), "13.3baz",
+  is(rows[2].querySelectorAll("span")[1].textContent, "13.3baz",
     "The second column of the third row displays the correct text.");
 
   is(sums.length, 2, "There should be 2 total summaries created.");
 
   is(totals.querySelectorAll(".table-chart-summary-label")[0].getAttribute("name"),
     "label1",
     "The first sum's type is correct.");
-  is(totals.querySelectorAll(".table-chart-summary-label")[0].getAttribute("value"),
+  is(totals.querySelectorAll(".table-chart-summary-label")[0].textContent,
     "Hello 6",
     "The first sum's value is correct.");
 
   is(totals.querySelectorAll(".table-chart-summary-label")[1].getAttribute("name"),
     "label2",
     "The second sum's type is correct.");
-  is(totals.querySelectorAll(".table-chart-summary-label")[1].getAttribute("value"),
+  is(totals.querySelectorAll(".table-chart-summary-label")[1].textContent,
     "World 36.60",
     "The second sum's value is correct.");
 
   yield teardown(monitor);
 });
--- a/devtools/client/netmonitor/test/browser_net_charts-04.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-04.js
@@ -9,17 +9,18 @@
  */
 
 add_task(function* () {
   let { L10N } = require("devtools/client/netmonitor/l10n");
 
   let { monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
-  let { document, Chart } = monitor.panelWin;
+  let { document, NetMonitorView } = monitor.panelWin;
+  let { Chart } = NetMonitorView.Statistics;
 
   let table = Chart.Table(document, {
     title: "Table title",
     data: null,
     totals: {
       label1: value => "Hello " + L10N.numberWithDecimals(value, 2),
       label2: value => "World " + L10N.numberWithDecimals(value, 2)
     }
@@ -32,44 +33,44 @@ add_task(function* () {
   let rows = grid.querySelectorAll(".table-chart-row");
   let sums = node.querySelectorAll(".table-chart-summary-label");
 
   ok(node.classList.contains("table-chart-container") &&
      node.classList.contains("generic-chart-container"),
     "A table chart container was created successfully.");
 
   ok(title, "A title node was created successfully.");
-  is(title.getAttribute("value"), "Table title",
+  is(title.textContent, "Table title",
     "The title node displays the correct text.");
 
   is(rows.length, 1, "There should be 1 table chart row created.");
 
   ok(rows[0].querySelector(".table-chart-row-box.chart-colored-blob"),
     "A colored blob exists for the firt row.");
-  is(rows[0].querySelectorAll("label")[0].getAttribute("name"), "size",
+  is(rows[0].querySelectorAll("span")[0].getAttribute("name"), "size",
     "The first column of the first row exists.");
-  is(rows[0].querySelectorAll("label")[1].getAttribute("name"), "label",
+  is(rows[0].querySelectorAll("span")[1].getAttribute("name"), "label",
     "The second column of the first row exists.");
-  is(rows[0].querySelectorAll("label")[0].getAttribute("value"), "",
+  is(rows[0].querySelectorAll("span")[0].textContent, "",
     "The first column of the first row displays the correct text.");
-  is(rows[0].querySelectorAll("label")[1].getAttribute("value"),
+  is(rows[0].querySelectorAll("span")[1].textContent,
     L10N.getStr("tableChart.loading"),
     "The second column of the first row displays the correct text.");
 
   is(sums.length, 2,
     "There should be 2 total summaries created.");
 
   is(totals.querySelectorAll(".table-chart-summary-label")[0].getAttribute("name"),
     "label1",
     "The first sum's type is correct.");
-  is(totals.querySelectorAll(".table-chart-summary-label")[0].getAttribute("value"),
+  is(totals.querySelectorAll(".table-chart-summary-label")[0].textContent,
     "Hello 0",
     "The first sum's value is correct.");
 
   is(totals.querySelectorAll(".table-chart-summary-label")[1].getAttribute("name"),
     "label2",
     "The second sum's type is correct.");
-  is(totals.querySelectorAll(".table-chart-summary-label")[1].getAttribute("value"),
+  is(totals.querySelectorAll(".table-chart-summary-label")[1].textContent,
     "World 0",
     "The second sum's value is correct.");
 
   yield teardown(monitor);
 });
--- a/devtools/client/netmonitor/test/browser_net_charts-05.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-05.js
@@ -8,17 +8,18 @@
  */
 
 add_task(function* () {
   let { L10N } = require("devtools/client/netmonitor/l10n");
 
   let { monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
-  let { document, Chart } = monitor.panelWin;
+  let { document, NetMonitorView } = monitor.panelWin;
+  let { Chart } = NetMonitorView.Statistics;
 
   let chart = Chart.PieTable(document, {
     title: "Table title",
     data: [{
       size: 1,
       label: 11.1
     }, {
       size: 2,
--- a/devtools/client/netmonitor/test/browser_net_charts-06.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-06.js
@@ -8,17 +8,18 @@
  */
 
 add_task(function* () {
   let { L10N } = require("devtools/client/netmonitor/l10n");
 
   let { monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
-  let { document, Chart } = monitor.panelWin;
+  let { document, NetMonitorView } = monitor.panelWin;
+  let { Chart } = NetMonitorView.Statistics;
 
   let pie = Chart.Pie(document, {
     data: [],
     width: 100,
     height: 100
   });
 
   let node = pie.node;
--- a/devtools/client/netmonitor/test/browser_net_charts-07.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-07.js
@@ -8,17 +8,18 @@
  */
 
 add_task(function* () {
   let { L10N } = require("devtools/client/netmonitor/l10n");
 
   let { monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
-  let { document, Chart } = monitor.panelWin;
+  let { document, NetMonitorView } = monitor.panelWin;
+  let { Chart } = NetMonitorView.Statistics;
 
   let table = Chart.Table(document, {
     data: [],
     totals: {
       label1: value => "Hello " + L10N.numberWithDecimals(value, 2),
       label2: value => "World " + L10N.numberWithDecimals(value, 2)
     }
   });
@@ -28,36 +29,36 @@ add_task(function* () {
   let totals = node.querySelector(".table-chart-totals");
   let rows = grid.querySelectorAll(".table-chart-row");
   let sums = node.querySelectorAll(".table-chart-summary-label");
 
   is(rows.length, 1, "There should be 1 table chart row created.");
 
   ok(rows[0].querySelector(".table-chart-row-box.chart-colored-blob"),
     "A colored blob exists for the firt row.");
-  is(rows[0].querySelectorAll("label")[0].getAttribute("name"), "size",
+  is(rows[0].querySelectorAll("span")[0].getAttribute("name"), "size",
     "The first column of the first row exists.");
-  is(rows[0].querySelectorAll("label")[1].getAttribute("name"), "label",
+  is(rows[0].querySelectorAll("span")[1].getAttribute("name"), "label",
     "The second column of the first row exists.");
-  is(rows[0].querySelectorAll("label")[0].getAttribute("value"), "",
+  is(rows[0].querySelectorAll("span")[0].textContent, "",
     "The first column of the first row displays the correct text.");
-  is(rows[0].querySelectorAll("label")[1].getAttribute("value"),
+  is(rows[0].querySelectorAll("span")[1].textContent,
     L10N.getStr("tableChart.unavailable"),
     "The second column of the first row displays the correct text.");
 
   is(sums.length, 2, "There should be 2 total summaries created.");
 
   is(totals.querySelectorAll(".table-chart-summary-label")[0].getAttribute("name"),
     "label1",
     "The first sum's type is correct.");
-  is(totals.querySelectorAll(".table-chart-summary-label")[0].getAttribute("value"),
+  is(totals.querySelectorAll(".table-chart-summary-label")[0].textContent,
     "Hello 0",
     "The first sum's value is correct.");
 
   is(totals.querySelectorAll(".table-chart-summary-label")[1].getAttribute("name"),
     "label2",
     "The second sum's type is correct.");
-  is(totals.querySelectorAll(".table-chart-summary-label")[1].getAttribute("value"),
+  is(totals.querySelectorAll(".table-chart-summary-label")[1].textContent,
     "World 0",
     "The second sum's value is correct.");
 
   yield teardown(monitor);
 });
--- a/devtools/client/netmonitor/test/browser_net_icon-preview.js
+++ b/devtools/client/netmonitor/test/browser_net_icon-preview.js
@@ -12,16 +12,18 @@ add_task(function* () {
 
   let { tab, monitor } = yield initNetMonitor(CONTENT_TYPE_WITHOUT_CACHE_URL);
   info("Starting test... ");
 
   let { $, $all, EVENTS, ACTIVITY_TYPE, NetMonitorView, NetMonitorController,
         gStore } = monitor.panelWin;
   let { RequestsMenu } = NetMonitorView;
 
+  RequestsMenu.lazyUpdate = false;
+
   let wait = waitForEvents();
   yield performRequests();
   yield wait;
 
   info("Checking the image thumbnail when all items are shown.");
   checkImageThumbnail();
 
   gStore.dispatch(Actions.sortBy("size"));
--- a/devtools/client/netmonitor/test/browser_net_status-codes.js
+++ b/devtools/client/netmonitor/test/browser_net_status-codes.js
@@ -52,17 +52,17 @@ add_task(function* () {
       // request #2
       method: "GET",
       uri: STATUS_CODES_SJS + "?sts=300",
       details: {
         status: 303,
         statusText: "See Other",
         type: "plain",
         fullMimeType: "text/plain; charset=utf-8",
-        size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 0),
+        size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 22),
         time: true
       }
     },
     {
       // request #3
       method: "GET",
       uri: STATUS_CODES_SJS + "?sts=400",
       details: {
--- a/devtools/client/netmonitor/test/browser_net_streaming-response.js
+++ b/devtools/client/netmonitor/test/browser_net_streaming-response.js
@@ -46,19 +46,19 @@ add_task(function* () {
     document.querySelectorAll("#details-pane tab")[3]);
 
   yield panelWin.once(panelWin.EVENTS.RESPONSE_BODY_DISPLAYED);
   let editor = yield NetMonitorView.editor("#response-content-textarea");
 
   // the hls-m3u8 part
   testEditorContent(editor, REQUESTS[0]);
 
+  wait = panelWin.once(panelWin.EVENTS.RESPONSE_BODY_DISPLAYED);
   RequestsMenu.selectedIndex = 1;
-  yield panelWin.once(panelWin.EVENTS.TAB_UPDATED);
-  yield panelWin.once(panelWin.EVENTS.RESPONSE_BODY_DISPLAYED);
+  yield wait;
 
   // the mpeg-dash part
   testEditorContent(editor, REQUESTS[1]);
 
   return teardown(monitor);
 
   function testEditorContent(e, [ fmt, textRe, mode ]) {
     ok(e.getText().match(textRe),
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/test/browser_net_truncate.js
@@ -0,0 +1,44 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * Verifies that truncated response bodies still have the correct reported size.
+ */
+
+function test() {
+  let { L10N } = require("devtools/client/netmonitor/l10n");
+  const { RESPONSE_BODY_LIMIT } = require("devtools/shared/webconsole/network-monitor");
+
+  const URL = EXAMPLE_URL + "sjs_truncate-test-server.sjs?limit=" + RESPONSE_BODY_LIMIT;
+
+  // Another slow test on Linux debug.
+  requestLongerTimeout(2);
+
+  initNetMonitor(URL).then(({ tab, monitor }) => {
+    info("Starting test... ");
+
+    let { NetMonitorView } = monitor.panelWin;
+    let { RequestsMenu } = NetMonitorView;
+
+    RequestsMenu.lazyUpdate = false;
+
+    waitForNetworkEvents(monitor, 1)
+      .then(() => teardown(monitor))
+      .then(finish);
+
+    monitor.panelWin.once(monitor.panelWin.EVENTS.RECEIVED_RESPONSE_CONTENT, () => {
+      let requestItem = RequestsMenu.getItemAtIndex(0);
+
+      verifyRequestItemTarget(RequestsMenu, requestItem, "GET", URL, {
+        type: "plain",
+        fullMimeType: "text/plain; charset=utf-8",
+        transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeMB", 2),
+        size: L10N.getFormatStrWithNumbers("networkMenu.sizeMB", 2),
+      });
+    });
+
+    tab.linkedBrowser.reload();
+  });
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/test/sjs_truncate-test-server.sjs
@@ -0,0 +1,18 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function handleRequest(request, response) {
+  let params = request.queryString.split("&");
+  let limit = (params.filter((s) => s.includes("limit="))[0] || "").split("=")[1];
+
+  response.setStatusLine(request.httpVersion, 200, "Och Aye");
+
+  response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
+  response.setHeader("Pragma", "no-cache");
+  response.setHeader("Expires", "0");
+  response.setHeader("Content-Type", "text/plain; charset=utf-8", false);
+
+  response.write("x".repeat(2 * parseInt(limit, 10)));
+
+  response.write("Hello world!");
+}
--- a/devtools/client/shared/components/tree/tree-view.js
+++ b/devtools/client/shared/components/tree/tree-view.js
@@ -120,16 +120,23 @@ define(function (require, exports, modul
 
     getInitialState: function () {
       return {
         expandedNodes: this.props.expandedNodes,
         columns: ensureDefaultColumn(this.props.columns)
       };
     },
 
+    componentWillReceiveProps: function (nextProps) {
+      let { expandedNodes } = nextProps;
+      this.setState(Object.assign({}, this.state, {
+        expandedNodes,
+      }));
+    },
+
     // Node expand/collapse
 
     toggle: function (nodePath) {
       let nodes = this.state.expandedNodes;
       if (this.isExpanded(nodePath)) {
         nodes.delete(nodePath);
       } else {
         nodes.add(nodePath);
rename from devtools/client/shared/widgets/Chart.jsm
rename to devtools/client/shared/widgets/Chart.js
--- a/devtools/client/shared/widgets/Chart.jsm
+++ b/devtools/client/shared/widgets/Chart.js
@@ -1,37 +1,25 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
-const Cu = Components.utils;
-
 const NET_STRINGS_URI = "devtools/client/locales/netmonitor.properties";
 const SVG_NS = "http://www.w3.org/2000/svg";
 const PI = Math.PI;
 const TAU = PI * 2;
 const EPSILON = 0.0000001;
 const NAMED_SLICE_MIN_ANGLE = TAU / 8;
 const NAMED_SLICE_TEXT_DISTANCE_RATIO = 1.9;
 const HOVERED_SLICE_TRANSLATE_DISTANCE_RATIO = 20;
 
-const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
-const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
 const EventEmitter = require("devtools/shared/event-emitter");
 const { LocalizationHelper } = require("devtools/shared/l10n");
-
-this.EXPORTED_SYMBOLS = ["Chart"];
-
-/**
- * Localization convenience methods.
- */
-var L10N = new LocalizationHelper(NET_STRINGS_URI);
+const L10N = new LocalizationHelper(NET_STRINGS_URI);
 
 /**
  * A factory for creating charts.
  * Example usage: let myChart = Chart.Pie(document, { ... });
  */
 var Chart = {
   Pie: createPieChart,
   Table: createTableChart,
@@ -100,34 +88,35 @@ function PieTableChart(node, pie, table)
  *          - sorted: a flag specifying if the `data` should be sorted
  *                    ascending by `size`.
  * @return PieTableChart
  *         A pie+table chart proxy instance, which emits the following events:
  *           - "mouseover", when the mouse enters a slice or a row
  *           - "mouseout", when the mouse leaves a slice or a row
  *           - "click", when the mouse enters a slice or a row
  */
-function createPieTableChart(document, { title, diameter, data, strings, totals, sorted }) {
+function createPieTableChart(document,
+                             { title, diameter, data, strings, totals, sorted }) {
   if (data && sorted) {
     data = data.slice().sort((a, b) => +(a.size < b.size));
   }
 
   let pie = Chart.Pie(document, {
     width: diameter,
     data: data
   });
 
   let table = Chart.Table(document, {
     title: title,
     data: data,
     strings: strings,
     totals: totals
   });
 
-  let container = document.createElement("hbox");
+  let container = document.createElement("div");
   container.className = "pie-table-chart-container";
   container.appendChild(pie.node);
   container.appendChild(table.node);
 
   let proxy = new PieTableChart(container, pie, table);
 
   pie.on("click", (event, item) => {
     proxy.emit(event, item);
@@ -202,21 +191,21 @@ function createPieChart(document, { data
   radius = radius || (width + height) / 4;
   let isPlaceholder = false;
 
   // Filter out very small sizes, as they'll just render invisible slices.
   data = data ? data.filter(e => e.size > EPSILON) : null;
 
   // If there's no data available, display an empty placeholder.
   if (!data) {
-    data = loadingPieChartData;
+    data = loadingPieChartData();
     isPlaceholder = true;
   }
   if (!data.length) {
-    data = emptyPieChartData;
+    data = emptyPieChartData();
     isPlaceholder = true;
   }
 
   let container = document.createElementNS(SVG_NS, "svg");
   container.setAttribute("class", "generic-chart-container pie-chart-container");
   container.setAttribute("pack", "center");
   container.setAttribute("flex", "1");
   container.setAttribute("width", width);
@@ -340,100 +329,103 @@ function createPieChart(document, { data
  */
 function createTableChart(document, { title, data, strings, totals }) {
   strings = strings || {};
   totals = totals || {};
   let isPlaceholder = false;
 
   // If there's no data available, display an empty placeholder.
   if (!data) {
-    data = loadingTableChartData;
+    data = loadingTableChartData();
     isPlaceholder = true;
   }
   if (!data.length) {
-    data = emptyTableChartData;
+    data = emptyTableChartData();
     isPlaceholder = true;
   }
 
-  let container = document.createElement("vbox");
+  let container = document.createElement("div");
   container.className = "generic-chart-container table-chart-container";
   container.setAttribute("pack", "center");
   container.setAttribute("flex", "1");
   container.setAttribute("rows", data.length);
   container.setAttribute("placeholder", isPlaceholder);
+  container.setAttribute("style", "-moz-box-orient: vertical");
 
   let proxy = new TableChart(container);
 
-  let titleNode = document.createElement("label");
+  let titleNode = document.createElement("span");
   titleNode.className = "plain table-chart-title";
-  titleNode.setAttribute("value", title);
+  titleNode.textContent = title;
   container.appendChild(titleNode);
 
-  let tableNode = document.createElement("vbox");
+  let tableNode = document.createElement("div");
   tableNode.className = "plain table-chart-grid";
+  tableNode.setAttribute("style", "-moz-box-orient: vertical");
   container.appendChild(tableNode);
 
   for (let rowInfo of data) {
-    let rowNode = document.createElement("hbox");
+    let rowNode = document.createElement("div");
     rowNode.className = "table-chart-row";
     rowNode.setAttribute("align", "center");
 
-    let boxNode = document.createElement("hbox");
+    let boxNode = document.createElement("div");
     boxNode.className = "table-chart-row-box chart-colored-blob";
     boxNode.setAttribute("name", rowInfo.label);
     rowNode.appendChild(boxNode);
 
     for (let [key, value] of Object.entries(rowInfo)) {
       let index = data.indexOf(rowInfo);
       let stringified = strings[key] ? strings[key](value, index) : value;
-      let labelNode = document.createElement("label");
+      let labelNode = document.createElement("span");
       labelNode.className = "plain table-chart-row-label";
       labelNode.setAttribute("name", key);
-      labelNode.setAttribute("value", stringified);
+      labelNode.textContent = stringified;
       rowNode.appendChild(labelNode);
     }
 
     proxy.rows.set(rowInfo, rowNode);
     delegate(proxy, ["click", "mouseover", "mouseout"], rowNode, rowInfo);
     tableNode.appendChild(rowNode);
   }
 
-  let totalsNode = document.createElement("vbox");
+  let totalsNode = document.createElement("div");
   totalsNode.className = "table-chart-totals";
+  totalsNode.setAttribute("style", "-moz-box-orient: vertical");
 
   for (let [key, value] of Object.entries(totals)) {
     let total = data.reduce((acc, e) => acc + e[key], 0);
-    let stringified = totals[key] ? totals[key](total || 0) : total;
-    let labelNode = document.createElement("label");
+    let stringified = value ? value(total || 0) : total;
+    let labelNode = document.createElement("span");
     labelNode.className = "plain table-chart-summary-label";
     labelNode.setAttribute("name", key);
-    labelNode.setAttribute("value", stringified);
+    labelNode.textContent = stringified;
     totalsNode.appendChild(labelNode);
   }
 
   container.appendChild(totalsNode);
 
   return proxy;
 }
 
-XPCOMUtils.defineLazyGetter(this, "loadingPieChartData", () => {
+function loadingPieChartData() {
   return [{ size: 1, label: L10N.getStr("pieChart.loading") }];
-});
+}
 
-XPCOMUtils.defineLazyGetter(this, "emptyPieChartData", () => {
+function emptyPieChartData() {
   return [{ size: 1, label: L10N.getStr("pieChart.unavailable") }];
-});
+}
 
-XPCOMUtils.defineLazyGetter(this, "loadingTableChartData", () => {
+function loadingTableChartData() {
   return [{ size: "", label: L10N.getStr("tableChart.loading") }];
-});
+}
 
-XPCOMUtils.defineLazyGetter(this, "emptyTableChartData", () => {
+function emptyTableChartData() {
   return [{ size: "", label: L10N.getStr("tableChart.unavailable") }];
-});
+}
 
 /**
  * Delegates DOM events emitted by an nsIDOMNode to an EventEmitter proxy.
  *
  * @param EventEmitter emitter
  *        The event emitter proxy instance.
  * @param array events
  *        An array of events, e.g. ["mouseover", "mouseout"].
@@ -442,8 +434,10 @@ XPCOMUtils.defineLazyGetter(this, "empty
  * @param any args
  *        The arguments passed when emitting events through the proxy.
  */
 function delegate(emitter, events, node, args) {
   for (let event of events) {
     node.addEventListener(event, emitter.emit.bind(emitter, event, args));
   }
 }
+
+exports.Chart = Chart;
--- a/devtools/client/shared/widgets/moz.build
+++ b/devtools/client/shared/widgets/moz.build
@@ -7,17 +7,17 @@
 DIRS += [
     'tooltip',
 ]
 
 DevToolsModules(
     'AbstractTreeItem.jsm',
     'BarGraphWidget.js',
     'BreadcrumbsWidget.jsm',
-    'Chart.jsm',
+    'Chart.js',
     'CubicBezierPresets.js',
     'CubicBezierWidget.js',
     'FastListWidget.js',
     'FilterWidget.js',
     'FlameGraph.js',
     'Graphs.js',
     'GraphsWorker.js',
     'LineGraphWidget.js',
--- a/devtools/client/sourceeditor/editor.js
+++ b/devtools/client/sourceeditor/editor.js
@@ -252,21 +252,23 @@ Editor.prototype = {
    *
    * This method is asynchronous and returns a promise.
    */
   appendTo: function (el, env) {
     let def = promise.defer();
     let cm = editors.get(this);
 
     if (!env) {
-      env = el.ownerDocument.createElementNS(XUL_NS, "iframe");
+      env = el.ownerDocument.createElementNS(el.namespaceURI, "iframe");
+
+      if (el.namespaceURI === XUL_NS) {
+        env.flex = 1;
+      }
     }
 
-    env.flex = 1;
-
     if (cm) {
       throw new Error("You can append an editor only once.");
     }
 
     let onLoad = () => {
       let win = env.contentWindow.wrappedJSObject;
 
       if (!this.config.themeSwitching) {
--- a/devtools/client/themes/netmonitor.css
+++ b/devtools/client/themes/netmonitor.css
@@ -1137,17 +1137,33 @@
 
 .treeTable .treeRow.hasChildren > .treeLabelCell > .treeLabel:hover {
   cursor: default;
   text-decoration: none;
 }
 
 /*
  * FIXME: normal html block element cannot fill outer XUL element
- * This workaround should be removed after sidebar is migrated to react
+ * This workaround should be removed after netmonitor is migrated to react
  */
 #react-preview-tabpanel-hook,
 #react-security-tabpanel-hook,
-#react-timings-tabpanel-hook {
+#react-timings-tabpanel-hook,
+#network-statistics-charts,
+#primed-cache-chart,
+#empty-cache-chart {
   display: -moz-box;
-  -moz-box-orient: vertical;
   -moz-box-flex: 1;
 }
+
+/* For vbox */
+#react-preview-tabpanel-hook,
+#react-security-tabpanel-hook,
+#react-timings-tabpanel-hook,
+#primed-cache-chart,
+#empty-cache-chart {
+  -moz-box-orient: vertical;
+}
+
+#primed-cache-chart,
+#empty-cache-chart {
+  -moz-box-pack: center;
+}
--- a/devtools/shared/gcli/commands/csscoverage.js
+++ b/devtools/shared/gcli/commands/csscoverage.js
@@ -5,17 +5,17 @@
 "use strict";
 
 const domtemplate = require("gcli/util/domtemplate");
 const csscoverage = require("devtools/shared/fronts/csscoverage");
 const l10n = csscoverage.l10n;
 
 loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
 
-loader.lazyImporter(this, "Chart", "resource://devtools/client/shared/widgets/Chart.jsm");
+const { Chart } = require("devtools/client/shared/widgets/Chart");
 
 /**
  * The commands/converters for GCLI
  */
 exports.items = [
   {
     name: "csscoverage",
     hidden: true,
--- a/devtools/shared/webconsole/network-monitor.js
+++ b/devtools/shared/webconsole/network-monitor.js
@@ -32,16 +32,18 @@ const PR_UINT32_MAX = 4294967295;
 // HTTP status codes.
 const HTTP_MOVED_PERMANENTLY = 301;
 const HTTP_FOUND = 302;
 const HTTP_SEE_OTHER = 303;
 const HTTP_TEMPORARY_REDIRECT = 307;
 
 // The maximum number of bytes a NetworkResponseListener can hold: 1 MB
 const RESPONSE_BODY_LIMIT = 1048576;
+// Exported for testing.
+exports.RESPONSE_BODY_LIMIT = RESPONSE_BODY_LIMIT;
 
 /**
  * Check if a given network request should be logged by a network monitor
  * based on the specified filters.
  *
  * @param nsIHttpChannel channel
  *        Request to check.
  * @param filters
@@ -593,17 +595,17 @@ NetworkResponseListener.prototype = {
    *        from the cache.
    */
   _onComplete: function (data) {
     let response = {
       mimeType: "",
       text: data || "",
     };
 
-    response.size = response.text.length;
+    response.size = this.bodySize;
     response.transferredSize = this.transferredSize;
 
     try {
       response.mimeType = this.request.contentType;
     } catch (ex) {
       // Ignore.
     }
 
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -5095,17 +5095,17 @@ CanvasRenderingContext2D::DrawDirectlyTo
   uint32_t modifiedFlags = aImage.mDrawingFlags | imgIContainer::FLAG_CLAMP;
 
   CSSIntSize sz(scaledImageSize.width, scaledImageSize.height); // XXX hmm is scaledImageSize really in CSS pixels?
   SVGImageContext svgContext(sz, Nothing(), CurrentState().globalAlpha);
 
   auto result = aImage.mImgContainer->
     Draw(context, scaledImageSize,
          ImageRegion::Create(gfxRect(aSrc.x, aSrc.y, aSrc.width, aSrc.height)),
-         aImage.mWhichFrame, SamplingFilter::GOOD, Some(svgContext), modifiedFlags);
+         aImage.mWhichFrame, SamplingFilter::GOOD, Some(svgContext), modifiedFlags, 1.0);
 
   if (result != DrawResult::SUCCESS) {
     NS_WARNING("imgIContainer::Draw failed");
   }
 }
 
 void
 CanvasRenderingContext2D::SetGlobalCompositeOperation(const nsAString& aOp,
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -411,16 +411,18 @@ MediaDecoder::MediaDecoder(MediaDecoderO
   , INIT_CANONICAL(mLogicallySeeking, false)
   , INIT_CANONICAL(mSameOriginMedia, false)
   , INIT_CANONICAL(mMediaPrincipalHandle, PRINCIPAL_HANDLE_NONE)
   , INIT_CANONICAL(mPlaybackBytesPerSecond, 0.0)
   , INIT_CANONICAL(mPlaybackRateReliable, true)
   , INIT_CANONICAL(mDecoderPosition, 0)
   , INIT_CANONICAL(mIsVisible, !aOwner->IsHidden())
   , mTelemetryReported(false)
+  , mIsMediaElement(!!aOwner->GetMediaElement())
+  , mElement(aOwner->GetMediaElement())
 {
   MOZ_ASSERT(NS_IsMainThread());
   MediaMemoryTracker::AddMediaDecoder(this);
 
   mAudioChannel = AudioChannelService::GetDefaultAudioChannel();
   mResourceCallback->Connect(this);
 
   //
@@ -1645,16 +1647,18 @@ MediaMemoryTracker::CollectReports(nsIHa
 
   return NS_OK;
 }
 
 MediaDecoderOwner*
 MediaDecoder::GetOwner() const
 {
   MOZ_ASSERT(NS_IsMainThread());
+  // Check object lifetime when mOwner points to a media element.
+  MOZ_DIAGNOSTIC_ASSERT(!mOwner || !mIsMediaElement || mElement);
   // mOwner is valid until shutdown.
   return mOwner;
 }
 
 void
 MediaDecoder::ConstructMediaTracks()
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -37,16 +37,17 @@
 
 class nsIStreamListener;
 class nsIPrincipal;
 
 namespace mozilla {
 
 namespace dom {
 class Promise;
+class HTMLMediaElement;
 }
 
 class VideoFrameContainer;
 class MediaDecoderStateMachine;
 
 enum class MediaEventType : int8_t;
 
 // GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
@@ -840,13 +841,17 @@ private:
   void NotifyBytesConsumed(int64_t aBytes, int64_t aOffset);
 
   // Called by nsChannelToPipeListener or MediaResource when the
   // download has ended. Called on the main thread only. aStatus is
   // the result from OnStopRequest.
   void NotifyDownloadEnded(nsresult aStatus);
 
   bool mTelemetryReported;
+
+  // Used to debug how mOwner becomes a dangling pointer in bug 1326294.
+  bool mIsMediaElement;
+  WeakPtr<dom::HTMLMediaElement> mElement;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -1993,21 +1993,25 @@ DecodingState::Enter()
 
   if (mMaster->CheckIfDecodeComplete()) {
     SetState<CompletedState>();
     return;
   }
 
   mOnAudioPopped = AudioQueue().PopEvent().Connect(
     OwnerThread(), [this] () {
-    mMaster->DispatchAudioDecodeTaskIfNeeded();
+    if (mMaster->IsAudioDecoding() && !mMaster->HaveEnoughDecodedAudio()) {
+      mMaster->EnsureAudioDecodeTaskQueued();
+    }
   });
   mOnVideoPopped = VideoQueue().PopEvent().Connect(
     OwnerThread(), [this] () {
-    mMaster->DispatchVideoDecodeTaskIfNeeded();
+    if (mMaster->IsVideoDecoding() && !mMaster->HaveEnoughDecodedVideo()) {
+      mMaster->EnsureVideoDecodeTaskQueued();
+    }
   });
 
   mMaster->UpdateNextFrameStatus(MediaDecoderOwner::NEXT_FRAME_AVAILABLE);
 
   mDecodeStartTime = TimeStamp::Now();
 
   MaybeStopPrerolling();
 
@@ -2357,17 +2361,16 @@ MediaDecoderStateMachine::MediaDecoderSt
   mAudioCaptured(false),
   mMinimizePreroll(false),
   mSentLoadedMetadataEvent(false),
   mSentFirstFrameLoadedEvent(false),
   mVideoDecodeSuspended(false),
   mVideoDecodeSuspendTimer(mTaskQueue),
   mOutputStreamManager(new OutputStreamManager()),
   mResource(aDecoder->GetResource()),
-  mAudioOffloading(false),
   INIT_MIRROR(mBuffered, TimeIntervals()),
   INIT_MIRROR(mEstimatedDuration, NullableTimeUnit()),
   INIT_MIRROR(mExplicitDuration, Maybe<double>()),
   INIT_MIRROR(mPlayState, MediaDecoder::PLAY_STATE_LOADING),
   INIT_MIRROR(mNextPlayState, MediaDecoder::PLAY_STATE_PAUSED),
   INIT_MIRROR(mVolume, 1.0),
   INIT_MIRROR(mPreservesPitch, true),
   INIT_MIRROR(mSameOriginMedia, false),
@@ -2494,29 +2497,23 @@ MediaDecoderStateMachine::GetDecodedAudi
     // overlap between 2 adjacent audio samples or when we are playing
     // a chained ogg file.
     return std::max<int64_t>(mDecodedAudioEndTime - GetClock(), 0);
   }
   // MediaSink not started. All audio samples are in the queue.
   return AudioQueue().Duration();
 }
 
-bool MediaDecoderStateMachine::HaveEnoughDecodedAudio()
+bool
+MediaDecoderStateMachine::HaveEnoughDecodedAudio()
 {
   MOZ_ASSERT(OnTaskQueue());
-
-  int64_t ampleAudioUSecs = mAmpleAudioThresholdUsecs * mPlaybackRate;
-  if (AudioQueue().GetSize() == 0 ||
-      GetDecodedAudioDuration() < ampleAudioUSecs) {
-    return false;
-  }
-
-  // MDSM will ensure buffering level is high enough for playback speed at 1x
-  // at which the DecodedStream is playing.
-  return true;
+  auto ampleAudioUSecs = mAmpleAudioThresholdUsecs * mPlaybackRate;
+  return AudioQueue().GetSize() > 0 &&
+         GetDecodedAudioDuration() >= ampleAudioUSecs;
 }
 
 bool MediaDecoderStateMachine::HaveEnoughDecodedVideo()
 {
   MOZ_ASSERT(OnTaskQueue());
 
   if (VideoQueue().GetSize() == 0) {
     return false;
@@ -2738,34 +2735,29 @@ void MediaDecoderStateMachine::MaybeStar
   MOZ_ASSERT(mState == DECODER_STATE_DECODING ||
              mState == DECODER_STATE_COMPLETED);
 
   if (IsPlaying()) {
     // Logging this case is really spammy - don't do it.
     return;
   }
 
-  bool playStatePermits = mPlayState == MediaDecoder::PLAY_STATE_PLAYING;
-  if (!playStatePermits || mAudioOffloading) {
-    DECODER_LOG("Not starting playback [playStatePermits: %d, "
-                "mAudioOffloading: %d]",
-                playStatePermits, mAudioOffloading);
+  if (mPlayState != MediaDecoder::PLAY_STATE_PLAYING) {
+    DECODER_LOG("Not starting playback [mPlayState=%d]", mPlayState.Ref());
     return;
   }
 
   DECODER_LOG("MaybeStartPlayback() starting playback");
   mOnPlaybackEvent.Notify(MediaEventType::PlaybackStarted);
   StartMediaSink();
 
   if (!IsPlaying()) {
     mMediaSink->SetPlaying(true);
     MOZ_ASSERT(IsPlaying());
   }
-
-  DispatchDecodeTasksIfNeeded();
 }
 
 void MediaDecoderStateMachine::UpdatePlaybackPositionInternal(int64_t aTime)
 {
   MOZ_ASSERT(OnTaskQueue());
   SAMPLE_LOG("UpdatePlaybackPositionInternal(%lld)", aTime);
 
   mCurrentPosition = aTime;
@@ -3012,25 +3004,16 @@ MediaDecoderStateMachine::DispatchDecode
     EnsureAudioDecodeTaskQueued();
   }
   if (needToDecodeVideo) {
     EnsureVideoDecodeTaskQueued();
   }
 }
 
 void
-MediaDecoderStateMachine::DispatchAudioDecodeTaskIfNeeded()
-{
-  MOZ_ASSERT(OnTaskQueue());
-  if (!IsShutdown() && NeedToDecodeAudio()) {
-    EnsureAudioDecodeTaskQueued();
-  }
-}
-
-void
 MediaDecoderStateMachine::EnsureAudioDecodeTaskQueued()
 {
   MOZ_ASSERT(OnTaskQueue());
   MOZ_ASSERT(mState != DECODER_STATE_SEEKING);
   MOZ_ASSERT(mState != DECODER_STATE_DECODING_FIRSTFRAME);
 
   SAMPLE_LOG("EnsureAudioDecodeTaskQueued isDecoding=%d status=%s",
               IsAudioDecoding(), AudioRequestStatus());
@@ -3074,25 +3057,16 @@ MediaDecoderStateMachine::RequestAudioDa
         SAMPLE_LOG("OnAudioNotDecoded aError=%u", aError.Code());
         mAudioDataRequest.Complete();
         mStateObj->HandleAudioNotDecoded(aError);
       })
   );
 }
 
 void
-MediaDecoderStateMachine::DispatchVideoDecodeTaskIfNeeded()
-{
-  MOZ_ASSERT(OnTaskQueue());
-  if (!IsShutdown() && NeedToDecodeVideo()) {
-    EnsureVideoDecodeTaskQueued();
-  }
-}
-
-void
 MediaDecoderStateMachine::EnsureVideoDecodeTaskQueued()
 {
   MOZ_ASSERT(OnTaskQueue());
   MOZ_ASSERT(mState != DECODER_STATE_SEEKING);
   MOZ_ASSERT(mState != DECODER_STATE_DECODING_FIRSTFRAME);
 
   SAMPLE_LOG("EnsureVideoDecodeTaskQueued isDecoding=%d status=%s",
              IsVideoDecoding(), VideoRequestStatus());
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -203,28 +203,16 @@ public:
   {
     RefPtr<MediaDecoderStateMachine> self = this;
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self, aEndTime] () {
       self->mFragmentEndTime = aEndTime;
     });
     OwnerThread()->Dispatch(r.forget());
   }
 
-  void DispatchAudioOffloading(bool aAudioOffloading)
-  {
-    RefPtr<MediaDecoderStateMachine> self = this;
-    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
-      if (self->mAudioOffloading != aAudioOffloading) {
-        self->mAudioOffloading = aAudioOffloading;
-        self->ScheduleStateMachine();
-      }
-    });
-    OwnerThread()->Dispatch(r.forget());
-  }
-
   // Drop reference to mResource. Only called during shutdown dance.
   void BreakCycles() {
     MOZ_ASSERT(NS_IsMainThread());
     mResource = nullptr;
   }
 
   TimedMetadataEventSource& TimedMetadataEvent() {
     return mMetadataManager.TimedMetadataEvent();
@@ -443,19 +431,16 @@ protected:
 
   // Dispatches a LoadedMetadataEvent.
   // This is threadsafe and can be called on any thread.
   // The decoder monitor must be held.
   void EnqueueLoadedMetadataEvent();
 
   void EnqueueFirstFrameLoadedEvent();
 
-  void DispatchAudioDecodeTaskIfNeeded();
-  void DispatchVideoDecodeTaskIfNeeded();
-
   // Dispatch a task to decode audio if there is not.
   void EnsureAudioDecodeTaskQueued();
 
   // Dispatch a task to decode video if there is not.
   void EnsureVideoDecodeTaskQueued();
 
   // Start a task to decode audio.
   void RequestAudioData();
@@ -736,20 +721,16 @@ private:
   MediaEventProducerExc<nsAutoPtr<MediaInfo>,
                         MediaDecoderEventVisibility> mFirstFrameLoadedEvent;
 
   MediaEventProducer<MediaEventType> mOnPlaybackEvent;
   MediaEventProducer<MediaResult> mOnPlaybackErrorEvent;
 
   MediaEventProducer<DecoderDoctorEvent> mOnDecoderDoctorEvent;
 
-  // True if audio is offloading.
-  // Playback will not start when audio is offloading.
-  bool mAudioOffloading;
-
   void OnCDMProxyReady(RefPtr<CDMProxy> aProxy);
   void OnCDMProxyNotReady();
   RefPtr<CDMProxy> mCDMProxy;
   MozPromiseRequestHolder<MediaDecoder::CDMProxyPromise> mCDMProxyPromise;
 
 private:
   // The buffered range. Mirrored from the decoder thread.
   Mirror<media::TimeIntervals> mBuffered;
--- a/gfx/vr/gfxVROculus.cpp
+++ b/gfx/vr/gfxVROculus.cpp
@@ -109,18 +109,18 @@ static pfn_ovr_GetMirrorTextureBufferGL 
 
 #ifdef HAVE_64BIT_BUILD
 #define BUILD_BITS 64
 #else
 #define BUILD_BITS 32
 #endif
 
 #define OVR_PRODUCT_VERSION 1
-#define OVR_MAJOR_VERSION   3
-#define OVR_MINOR_VERSION   1
+#define OVR_MAJOR_VERSION   1
+#define OVR_MINOR_VERSION   10
 
 static bool
 InitializeOculusCAPI()
 {
   static PRLibrary *ovrlib = nullptr;
 
   if (!ovrlib) {
     nsTArray<nsCString> libSearchPaths;
--- a/gfx/vr/ovr_capi_dynamic.h
+++ b/gfx/vr/ovr_capi_dynamic.h
@@ -248,52 +248,61 @@ typedef enum {
   OVR_FORMAT_B8G8R8A8_UNORM_SRGB,
   OVR_FORMAT_B8G8R8X8_UNORM,
   OVR_FORMAT_B8G8R8X8_UNORM_SRGB,
   OVR_FORMAT_R16G16B16A16_FLOAT,
   OVR_FORMAT_D16_UNORM,
   OVR_FORMAT_D24_UNORM_S8_UINT,
   OVR_FORMAT_D32_FLOAT,
   OVR_FORMAT_D32_FLOAT_S8X24_UINT,
+  OVR_FORMAT_BC1_UNORM,
+  OVR_FORMAT_BC1_UNORM_SRGB,
+  OVR_FORMAT_BC2_UNORM,
+  OVR_FORMAT_BC2_UNORM_SRGB,
+  OVR_FORMAT_BC3_UNORM,
+  OVR_FORMAT_BC3_UNORM_SRGB,
+  OVR_FORMAT_BC6H_UF16,
+  OVR_FORMAT_BC6H_SF16,
+  OVR_FORMAT_BC7_UNORM,
+  OVR_FORMAT_BC7_UNORM_SRGB,
+  OVR_FORMAT_R11G11B10_FLOAT,
   OVR_FORMAT_ENUMSIZE = 0x7fffffff
 } ovrTextureFormat;
 
 typedef enum {
   ovrTextureMisc_None,
   ovrTextureMisc_DX_Typeless = 0x0001,
   ovrTextureMisc_AllowGenerateMips = 0x0002,
+  ovrTextureMisc_ProtectedContent = 0x0004,
   ovrTextureMisc_EnumSize = 0x7fffffff
 } ovrTextureFlags;
 
 typedef struct {
   ovrTextureType Type;
   ovrTextureFormat Format;
   int ArraySize;
   int Width;
   int Height;
   int MipLevels;
   int SampleCount;
   ovrBool StaticImage;
   unsigned int MiscFlags;
   unsigned int BindFlags;
 } ovrTextureSwapChainDesc;
 
-typedef struct
-{
+typedef struct {
   ovrTextureFormat Format;
   int Width;
   int Height;
   unsigned int MiscFlags;
 } ovrMirrorTextureDesc;
 
 typedef void* ovrTextureSwapChain;
 typedef struct ovrMirrorTextureData* ovrMirrorTexture;
 
-
-
 typedef enum {
   ovrButton_A = 0x00000001,
   ovrButton_B = 0x00000002,
   ovrButton_RThumb = 0x00000004,
   ovrButton_RShoulder = 0x00000008,
   ovrButton_RMask = ovrButton_A | ovrButton_B | ovrButton_RThumb | ovrButton_RShoulder,
   ovrButton_X = 0x00000100,
   ovrButton_Y = 0x00000200,
@@ -312,58 +321,95 @@ typedef enum {
   ovrButton_Private = ovrButton_VolUp | ovrButton_VolDown | ovrButton_Home,
   ovrButton_EnumSize = 0x7fffffff
 } ovrButton;
 
 typedef enum {
   ovrTouch_A = ovrButton_A,
   ovrTouch_B = ovrButton_B,
   ovrTouch_RThumb = ovrButton_RThumb,
+  ovrTouch_RThumbRest = 0x00000008,
   ovrTouch_RIndexTrigger = 0x00000010,
-  ovrTouch_RButtonMask = ovrTouch_A | ovrTouch_B | ovrTouch_RThumb | ovrTouch_RIndexTrigger,
+  ovrTouch_RButtonMask = ovrTouch_A | ovrTouch_B | ovrTouch_RThumb | ovrTouch_RThumbRest | ovrTouch_RIndexTrigger,
   ovrTouch_X = ovrButton_X,
   ovrTouch_Y = ovrButton_Y,
   ovrTouch_LThumb = ovrButton_LThumb,
+  ovrTouch_LThumbRest = 0x00000800,
   ovrTouch_LIndexTrigger = 0x00001000,
-  ovrTouch_LButtonMask = ovrTouch_X | ovrTouch_Y | ovrTouch_LThumb | ovrTouch_LIndexTrigger,
+  ovrTouch_LButtonMask = ovrTouch_X | ovrTouch_Y | ovrTouch_LThumb | ovrTouch_LThumbRest | ovrTouch_LIndexTrigger,
   ovrTouch_RIndexPointing = 0x00000020,
   ovrTouch_RThumbUp = 0x00000040,
   ovrTouch_RPoseMask = ovrTouch_RIndexPointing | ovrTouch_RThumbUp,
   ovrTouch_LIndexPointing = 0x00002000,
   ovrTouch_LThumbUp = 0x00004000,
   ovrTouch_LPoseMask = ovrTouch_LIndexPointing | ovrTouch_LThumbUp,
   ovrTouch_EnumSize = 0x7fffffff
 } ovrTouch;
 
+typedef struct OVR_ALIGNAS(OVR_PTR_SIZE) {
+  int SampleRateHz;
+  int SampleSizeInBytes;
+  int QueueMinSizeToAvoidStarvation;
+  int SubmitMinSamples;
+  int SubmitMaxSamples;
+  int SubmitOptimalSamples;
+} ovrTouchHapticsDesc;
+
 typedef enum {
   ovrControllerType_None = 0x00,
   ovrControllerType_LTouch = 0x01,
   ovrControllerType_RTouch = 0x02,
   ovrControllerType_Touch = 0x03,
   ovrControllerType_Remote = 0x04,
   ovrControllerType_XBox = 0x10,
   ovrControllerType_Active = 0xff,
   ovrControllerType_EnumSize = 0x7fffffff
 } ovrControllerType;
 
 typedef enum {
+  ovrHapticsBufferSubmit_Enqueue
+} ovrHapticsBufferSubmitMode;
+
+typedef struct {
+  const void* Samples;
+  int SamplesCount;
+  ovrHapticsBufferSubmitMode SubmitMode;
+} ovrHapticsBuffer;
+
+typedef struct {
+  int RemainingQueueSpace;
+  int SamplesQueued;
+} ovrHapticsPlaybackState;
+
+typedef enum {
+  ovrTrackedDevice_HMD = 0x0001,
+  ovrTrackedDevice_LTouch = 0x0002,
+  ovrTrackedDevice_RTouch = 0x0004,
+  ovrTrackedDevice_Touch = 0x0006,
+  ovrTrackedDevice_All = 0xFFFF,
+} ovrTrackedDeviceType;
+
+typedef enum {
   ovrHand_Left = 0,
   ovrHand_Right = 1,
   ovrHand_Count = 2,
   ovrHand_EnumSize = 0x7fffffff
 } ovrHandType;
 
 typedef struct {
   double TimeInSeconds;
   unsigned int Buttons;
   unsigned int Touches;
   float IndexTrigger[ovrHand_Count];
   float HandTrigger[ovrHand_Count];
   ovrVector2f Thumbstick[ovrHand_Count];
   ovrControllerType ControllerType;
+  float IndexTriggerNoDeadzone[ovrHand_Count];
+  float HandTriggerNoDeadzone[ovrHand_Count];
+  ovrVector2f ThumbstickNoDeadzone[ovrHand_Count];
 } ovrInputState;
 
 typedef enum {
   ovrInit_Debug          = 0x00000001,
   ovrInit_RequestVersion = 0x00000004,
   ovrinit_WritableBits   = 0x00ffffff,
   ovrInit_EnumSize       = 0x7fffffff
 } ovrInitFlags;
--- a/image/ClippedImage.cpp
+++ b/image/ClippedImage.cpp
@@ -93,23 +93,25 @@ private:
 
 class DrawSingleTileCallback : public gfxDrawingCallback
 {
 public:
   DrawSingleTileCallback(ClippedImage* aImage,
                          const nsIntSize& aSize,
                          const Maybe<SVGImageContext>& aSVGContext,
                          uint32_t aWhichFrame,
-                         uint32_t aFlags)
+                         uint32_t aFlags,
+                         float aOpacity)
     : mImage(aImage)
     , mSize(aSize)
     , mSVGContext(aSVGContext)
     , mWhichFrame(aWhichFrame)
     , mFlags(aFlags)
     , mDrawResult(DrawResult::NOT_READY)
+    , mOpacity(aOpacity)
   {
     MOZ_ASSERT(mImage, "Must have an image to clip");
   }
 
   virtual bool operator()(gfxContext* aContext,
                           const gfxRect& aFillRect,
                           const SamplingFilter aSamplingFilter,
                           const gfxMatrix& aTransform)
@@ -117,30 +119,32 @@ public:
     MOZ_ASSERT(aTransform.IsIdentity(),
                "Caller is probably CreateSamplingRestrictedDrawable, "
                "which should not happen");
 
     // Draw the image. |gfxCallbackDrawable| always calls this function with
     // arguments that guarantee we never tile.
     mDrawResult =
       mImage->DrawSingleTile(aContext, mSize, ImageRegion::Create(aFillRect),
-                             mWhichFrame, aSamplingFilter, mSVGContext, mFlags);
+                             mWhichFrame, aSamplingFilter, mSVGContext, mFlags,
+                             mOpacity);
 
     return true;
   }
 
   DrawResult GetDrawResult() { return mDrawResult; }
 
 private:
   RefPtr<ClippedImage>        mImage;
   const nsIntSize               mSize;
   const Maybe<SVGImageContext>& mSVGContext;
   const uint32_t                mWhichFrame;
   const uint32_t                mFlags;
   DrawResult                    mDrawResult;
+  float                         mOpacity;
 };
 
 ClippedImage::ClippedImage(Image* aImage,
                            nsIntRect aClip,
                            const Maybe<nsSize>& aSVGViewportSize)
   : ImageWrapper(aImage)
   , mClip(aClip)
 {
@@ -253,17 +257,17 @@ ClippedImage::GetIntrinsicRatio(nsSize* 
 }
 
 NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
 ClippedImage::GetFrame(uint32_t aWhichFrame,
                        uint32_t aFlags)
 {
   DrawResult result;
   RefPtr<SourceSurface> surface;
-  Tie(result, surface) = GetFrameInternal(mClip.Size(), Nothing(), aWhichFrame, aFlags);
+  Tie(result, surface) = GetFrameInternal(mClip.Size(), Nothing(), aWhichFrame, aFlags, 1.0);
   return surface.forget();
 }
 
 NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
 ClippedImage::GetFrameAtSize(const IntSize& aSize,
                              uint32_t aWhichFrame,
                              uint32_t aFlags)
 {
@@ -271,17 +275,18 @@ ClippedImage::GetFrameAtSize(const IntSi
   // but right now we just fall back to the intrinsic size.
   return GetFrame(aWhichFrame, aFlags);
 }
 
 Pair<DrawResult, RefPtr<SourceSurface>>
 ClippedImage::GetFrameInternal(const nsIntSize& aSize,
                                const Maybe<SVGImageContext>& aSVGContext,
                                uint32_t aWhichFrame,
-                               uint32_t aFlags)
+                               uint32_t aFlags,
+                               float aOpacity)
 {
   if (!ShouldClip()) {
     RefPtr<SourceSurface> surface = InnerImage()->GetFrame(aWhichFrame, aFlags);
     return MakePair(surface ? DrawResult::SUCCESS : DrawResult::NOT_READY,
                     Move(surface));
   }
 
   float frameToDraw = InnerImage()->GetFrameIndex(aWhichFrame);
@@ -297,17 +302,18 @@ ClippedImage::GetFrameInternal(const nsI
       return MakePair(DrawResult::TEMPORARY_ERROR, RefPtr<SourceSurface>());
     }
 
     RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(target);
     MOZ_ASSERT(ctx); // already checked the draw target above
 
     // Create our callback.
     RefPtr<DrawSingleTileCallback> drawTileCallback =
-      new DrawSingleTileCallback(this, aSize, aSVGContext, aWhichFrame, aFlags);
+      new DrawSingleTileCallback(this, aSize, aSVGContext, aWhichFrame, aFlags,
+                                 aOpacity);
     RefPtr<gfxDrawable> drawable =
       new gfxCallbackDrawable(drawTileCallback, aSize);
 
     // Actually draw. The callback will end up invoking DrawSingleTile.
     gfxUtils::DrawPixelSnapped(ctx, drawable, aSize,
                                ImageRegion::Create(aSize),
                                SurfaceFormat::B8G8R8A8,
                                SamplingFilter::LINEAR,
@@ -366,60 +372,63 @@ MustCreateSurface(gfxContext* aContext,
 
 NS_IMETHODIMP_(DrawResult)
 ClippedImage::Draw(gfxContext* aContext,
                    const nsIntSize& aSize,
                    const ImageRegion& aRegion,
                    uint32_t aWhichFrame,
                    SamplingFilter aSamplingFilter,
                    const Maybe<SVGImageContext>& aSVGContext,
-                   uint32_t aFlags)
+                   uint32_t aFlags,
+                   float aOpacity)
 {
   if (!ShouldClip()) {
     return InnerImage()->Draw(aContext, aSize, aRegion, aWhichFrame,
-                              aSamplingFilter, aSVGContext, aFlags);
+                              aSamplingFilter, aSVGContext, aFlags, aOpacity);
   }
 
   // Check for tiling. If we need to tile then we need to create a
   // gfxCallbackDrawable to handle drawing for us.
   if (MustCreateSurface(aContext, aSize, aRegion, aFlags)) {
     // Create a temporary surface containing a single tile of this image.
     // GetFrame will call DrawSingleTile internally.
     DrawResult result;
     RefPtr<SourceSurface> surface;
     Tie(result, surface) =
-      GetFrameInternal(aSize, aSVGContext, aWhichFrame, aFlags);
+      GetFrameInternal(aSize, aSVGContext, aWhichFrame, aFlags, aOpacity);
     if (!surface) {
       MOZ_ASSERT(result != DrawResult::SUCCESS);
       return result;
     }
 
     // Create a drawable from that surface.
     RefPtr<gfxSurfaceDrawable> drawable =
       new gfxSurfaceDrawable(surface, aSize);
 
     // Draw.
     gfxUtils::DrawPixelSnapped(aContext, drawable, aSize, aRegion,
-                               SurfaceFormat::B8G8R8A8, aSamplingFilter);
+                               SurfaceFormat::B8G8R8A8, aSamplingFilter,
+                               aOpacity);
 
     return result;
   }
 
   return DrawSingleTile(aContext, aSize, aRegion, aWhichFrame,
-                        aSamplingFilter, aSVGContext, aFlags);
+                        aSamplingFilter, aSVGContext, aFlags, aOpacity);
 }
 
 DrawResult
 ClippedImage::DrawSingleTile(gfxContext* aContext,
                              const nsIntSize& aSize,
                              const ImageRegion& aRegion,
                              uint32_t aWhichFrame,
                              SamplingFilter aSamplingFilter,
                              const Maybe<SVGImageContext>& aSVGContext,
-                             uint32_t aFlags)
+                             uint32_t aFlags,
+                             float aOpacity)
 {
   MOZ_ASSERT(!MustCreateSurface(aContext, aSize, aRegion, aFlags),
              "Shouldn't need to create a surface");
 
   gfxRect clip(mClip.x, mClip.y, mClip.width, mClip.height);
   nsIntSize size(aSize), innerSize(aSize);
   bool needScale = false;
   if (mSVGViewportSize && !mSVGViewportSize->IsEmpty()) {
@@ -465,17 +474,17 @@ ClippedImage::DrawSingleTile(gfxContext*
 
     return SVGImageContext(vSize,
                            aOldContext.GetPreserveAspectRatio());
   };
 
   return InnerImage()->Draw(aContext, size, region,
                             aWhichFrame, aSamplingFilter,
                             aSVGContext.map(unclipViewport),
-                            aFlags);
+                            aFlags, aOpacity);
 }
 
 NS_IMETHODIMP
 ClippedImage::RequestDiscard()
 {
   // We're very aggressive about discarding.
   mCachedSurface = nullptr;
 
--- a/image/ClippedImage.h
+++ b/image/ClippedImage.h
@@ -48,17 +48,18 @@ public:
     GetImageContainer(layers::LayerManager* aManager,
                       uint32_t aFlags) override;
   NS_IMETHOD_(DrawResult) Draw(gfxContext* aContext,
                                const nsIntSize& aSize,
                                const ImageRegion& aRegion,
                                uint32_t aWhichFrame,
                                gfx::SamplingFilter aSamplingFilter,
                                const Maybe<SVGImageContext>& aSVGContext,
-                               uint32_t aFlags) override;
+                               uint32_t aFlags,
+                               float aOpacity) override;
   NS_IMETHOD RequestDiscard() override;
   NS_IMETHOD_(Orientation) GetOrientation() override;
   NS_IMETHOD_(nsIntRect) GetImageSpaceInvalidationRect(const nsIntRect& aRect)
     override;
   nsIntSize OptimalImageSizeForDest(const gfxSize& aDest,
                                     uint32_t aWhichFrame,
                                     gfx::SamplingFilter aSamplingFilter,
                                     uint32_t aFlags) override;
@@ -69,25 +70,27 @@ protected:
 
   virtual ~ClippedImage();
 
 private:
   Pair<DrawResult, RefPtr<SourceSurface>>
     GetFrameInternal(const nsIntSize& aSize,
                      const Maybe<SVGImageContext>& aSVGContext,
                      uint32_t aWhichFrame,
-                     uint32_t aFlags);
+                     uint32_t aFlags,
+                     float aOpacity);
   bool ShouldClip();
   DrawResult DrawSingleTile(gfxContext* aContext,
                             const nsIntSize& aSize,
                             const ImageRegion& aRegion,
                             uint32_t aWhichFrame,
                             gfx::SamplingFilter aSamplingFilter,
                             const Maybe<SVGImageContext>& aSVGContext,
-                            uint32_t aFlags);
+                            uint32_t aFlags,
+                            float aOpacity);
 
   // If we are forced to draw a temporary surface, we cache it here.
   UniquePtr<ClippedImageCachedSurface> mCachedSurface;
 
   nsIntRect        mClip;            // The region to clip to.
   Maybe<bool>      mShouldClip;      // Memoized ShouldClip() if present.
   Maybe<nsIntSize> mSVGViewportSize; // If we're clipping a VectorImage, this
                                      // is the size of viewport of that image.
--- a/image/DynamicImage.cpp
+++ b/image/DynamicImage.cpp
@@ -184,17 +184,18 @@ DynamicImage::GetFrameAtSize(const IntSi
     gfxWarning() <<
       "DynamicImage::GetFrame failed in CreateOffscreenContentDrawTarget";
     return nullptr;
   }
   RefPtr<gfxContext> context = gfxContext::CreateOrNull(dt);
   MOZ_ASSERT(context); // already checked the draw target above
 
   auto result = Draw(context, aSize, ImageRegion::Create(aSize),
-                     aWhichFrame, SamplingFilter::POINT, Nothing(), aFlags);
+                     aWhichFrame, SamplingFilter::POINT, Nothing(), aFlags,
+                     1.0);
 
   return result == DrawResult::SUCCESS ? dt->Snapshot() : nullptr;
 }
 
 NS_IMETHODIMP_(bool)
 DynamicImage::WillDrawOpaqueNow()
 {
   return false;
@@ -214,39 +215,42 @@ DynamicImage::GetImageContainer(LayerMan
 
 NS_IMETHODIMP_(DrawResult)
 DynamicImage::Draw(gfxContext* aContext,
                    const nsIntSize& aSize,
                    const ImageRegion& aRegion,
                    uint32_t aWhichFrame,
                    SamplingFilter aSamplingFilter,
                    const Maybe<SVGImageContext>& aSVGContext,
-                   uint32_t aFlags)
+                   uint32_t aFlags,
+                   float aOpacity)
 {
   MOZ_ASSERT(!aSize.IsEmpty(), "Unexpected empty size");
 
   IntSize drawableSize(mDrawable->Size());
 
   if (aSize == drawableSize) {
     gfxUtils::DrawPixelSnapped(aContext, mDrawable, drawableSize, aRegion,
-                               SurfaceFormat::B8G8R8A8, aSamplingFilter);
+                               SurfaceFormat::B8G8R8A8, aSamplingFilter,
+                               aOpacity);
     return DrawResult::SUCCESS;
   }
 
   gfxSize scale(double(aSize.width) / drawableSize.width,
                 double(aSize.height) / drawableSize.height);
 
   ImageRegion region(aRegion);
   region.Scale(1.0 / scale.width, 1.0 / scale.height);
 
   gfxContextMatrixAutoSaveRestore saveMatrix(aContext);
   aContext->Multiply(gfxMatrix::Scaling(scale.width, scale.height));
 
   gfxUtils::DrawPixelSnapped(aContext, mDrawable, drawableSize, region,
-                             SurfaceFormat::B8G8R8A8, aSamplingFilter);
+                             SurfaceFormat::B8G8R8A8, aSamplingFilter,
+                             aOpacity);
   return DrawResult::SUCCESS;
 }
 
 NS_IMETHODIMP
 DynamicImage::StartDecoding(uint32_t aFlags)
 {
   return NS_OK;
 }
--- a/image/FrozenImage.cpp
+++ b/image/FrozenImage.cpp
@@ -73,20 +73,21 @@ FrozenImage::GetImageContainer(layers::L
 
 NS_IMETHODIMP_(DrawResult)
 FrozenImage::Draw(gfxContext* aContext,
                   const nsIntSize& aSize,
                   const ImageRegion& aRegion,
                   uint32_t /* aWhichFrame - ignored */,
                   SamplingFilter aSamplingFilter,
                   const Maybe<SVGImageContext>& aSVGContext,
-                  uint32_t aFlags)
+                  uint32_t aFlags,
+                  float aOpacity)
 {
   return InnerImage()->Draw(aContext, aSize, aRegion, FRAME_FIRST,
-                            aSamplingFilter, aSVGContext, aFlags);
+                            aSamplingFilter, aSVGContext, aFlags, aOpacity);
 }
 
 NS_IMETHODIMP_(void)
 FrozenImage::RequestRefresh(const TimeStamp& aTime)
 {
   // Do nothing.
 }
 
--- a/image/FrozenImage.h
+++ b/image/FrozenImage.h
@@ -47,17 +47,18 @@ public:
     GetImageContainer(layers::LayerManager* aManager,
                       uint32_t aFlags) override;
   NS_IMETHOD_(DrawResult) Draw(gfxContext* aContext,
                                const nsIntSize& aSize,
                                const ImageRegion& aRegion,
                                uint32_t aWhichFrame,
                                gfx::SamplingFilter aSamplingFilter,
                                const Maybe<SVGImageContext>& aSVGContext,
-                               uint32_t aFlags) override;
+                               uint32_t aFlags,
+                               float aOpacity) override;
   NS_IMETHOD_(void) RequestRefresh(const TimeStamp& aTime) override;
   NS_IMETHOD GetAnimationMode(uint16_t* aAnimationMode) override;
   NS_IMETHOD SetAnimationMode(uint16_t aAnimationMode) override;
   NS_IMETHOD ResetAnimation() override;
   NS_IMETHOD_(float) GetFrameIndex(uint32_t aWhichFrame) override;
 
 protected:
   explicit FrozenImage(Image* aImage) : ImageWrapper(aImage) { }
--- a/image/ImageWrapper.cpp
+++ b/image/ImageWrapper.cpp
@@ -204,20 +204,21 @@ ImageWrapper::GetImageContainer(LayerMan
 
 NS_IMETHODIMP_(DrawResult)
 ImageWrapper::Draw(gfxContext* aContext,
                    const nsIntSize& aSize,
                    const ImageRegion& aRegion,
                    uint32_t aWhichFrame,
                    SamplingFilter aSamplingFilter,
                    const Maybe<SVGImageContext>& aSVGContext,
-                   uint32_t aFlags)
+                   uint32_t aFlags,
+                   float aOpacity)
 {
   return mInnerImage->Draw(aContext, aSize, aRegion, aWhichFrame,
-                           aSamplingFilter, aSVGContext, aFlags);
+                           aSamplingFilter, aSVGContext, aFlags, aOpacity);
 }
 
 NS_IMETHODIMP
 ImageWrapper::StartDecoding(uint32_t aFlags)
 {
   return mInnerImage->StartDecoding(aFlags);
 }
 
--- a/image/OrientedImage.cpp
+++ b/image/OrientedImage.cpp
@@ -258,22 +258,23 @@ OrientedImage::OrientationMatrix(const n
 
 NS_IMETHODIMP_(DrawResult)
 OrientedImage::Draw(gfxContext* aContext,
                     const nsIntSize& aSize,
                     const ImageRegion& aRegion,
                     uint32_t aWhichFrame,
                     SamplingFilter aSamplingFilter,
                     const Maybe<SVGImageContext>& aSVGContext,
-                    uint32_t aFlags)
+                    uint32_t aFlags,
+                    float aOpacity)
 {
   if (mOrientation.IsIdentity()) {
     return InnerImage()->Draw(aContext, aSize, aRegion,
                               aWhichFrame, aSamplingFilter,
-                              aSVGContext, aFlags);
+                              aSVGContext, aFlags, aOpacity);
   }
 
   // Update the image size to match the image's coordinate system. (This could
   // be done using TransformBounds but since it's only a size a swap is enough.)
   nsIntSize size(aSize);
   if (mOrientation.SwapsWidthAndHeight()) {
     swap(size.width, size.height);
   }
@@ -295,18 +296,20 @@ OrientedImage::Draw(gfxContext* aContext
     CSSIntSize viewportSize(aOldContext.GetViewportSize());
     if (mOrientation.SwapsWidthAndHeight()) {
       swap(viewportSize.width, viewportSize.height);
     }
     return SVGImageContext(viewportSize,
                            aOldContext.GetPreserveAspectRatio());
   };
 
-  return InnerImage()->Draw(aContext, size, region, aWhichFrame, aSamplingFilter,
-                            aSVGContext.map(orientViewport), aFlags);
+  return InnerImage()->Draw(aContext, size, region, aWhichFrame,
+                            aSamplingFilter,
+                            aSVGContext.map(orientViewport), aFlags,
+                            aOpacity);
 }
 
 nsIntSize
 OrientedImage::OptimalImageSizeForDest(const gfxSize& aDest,
                                        uint32_t aWhichFrame,
                                        SamplingFilter aSamplingFilter,
                                        uint32_t aFlags)
 {
--- a/image/OrientedImage.h
+++ b/image/OrientedImage.h
@@ -44,17 +44,18 @@ public:
     GetImageContainer(layers::LayerManager* aManager,
                       uint32_t aFlags) override;
   NS_IMETHOD_(DrawResult) Draw(gfxContext* aContext,
                                const nsIntSize& aSize,
                                const ImageRegion& aRegion,
                                uint32_t aWhichFrame,
                                gfx::SamplingFilter aSamplingFilter,
                                const Maybe<SVGImageContext>& aSVGContext,
-                               uint32_t aFlags) override;
+                               uint32_t aFlags,
+                               float aOpacity) override;
   NS_IMETHOD_(nsIntRect) GetImageSpaceInvalidationRect(
                                            const nsIntRect& aRect) override;
   nsIntSize OptimalImageSizeForDest(const gfxSize& aDest,
                                     uint32_t aWhichFrame,
                                     gfx::SamplingFilter aSamplingFilter,
                                     uint32_t aFlags) override;
 
 protected:
--- a/image/RasterImage.cpp
+++ b/image/RasterImage.cpp
@@ -1306,17 +1306,18 @@ RasterImage::CanDownscaleDuringDecode(co
 }
 
 DrawResult
 RasterImage::DrawInternal(DrawableSurface&& aSurface,
                           gfxContext* aContext,
                           const IntSize& aSize,
                           const ImageRegion& aRegion,
                           SamplingFilter aSamplingFilter,
-                          uint32_t aFlags)
+                          uint32_t aFlags,
+                          float aOpacity)
 {
   gfxContextMatrixAutoSaveRestore saveMatrix(aContext);
   ImageRegion region(aRegion);
   bool frameIsFinished = aSurface->IsFinished();
 
   // By now we may have a frame with the requested size. If not, we need to
   // adjust the drawing parameters accordingly.
   IntSize finalSize = aSurface->GetImageSize();
@@ -1325,17 +1326,17 @@ RasterImage::DrawInternal(DrawableSurfac
     gfx::Size scale(double(aSize.width) / finalSize.width,
                     double(aSize.height) / finalSize.height);
     aContext->Multiply(gfxMatrix::Scaling(scale.width, scale.height));
     region.Scale(1.0 / scale.width, 1.0 / scale.height);
 
     couldRedecodeForBetterFrame = CanDownscaleDuringDecode(aSize, aFlags);
   }
 
-  if (!aSurface->Draw(aContext, region, aSamplingFilter, aFlags)) {
+  if (!aSurface->Draw(aContext, region, aSamplingFilter, aFlags, aOpacity)) {
     RecoverFromInvalidFrames(aSize, aFlags);
     return DrawResult::TEMPORARY_ERROR;
   }
   if (!frameIsFinished) {
     return DrawResult::INCOMPLETE;
   }
   if (couldRedecodeForBetterFrame) {
     return DrawResult::WRONG_SIZE;
@@ -1346,17 +1347,18 @@ RasterImage::DrawInternal(DrawableSurfac
 //******************************************************************************
 NS_IMETHODIMP_(DrawResult)
 RasterImage::Draw(gfxContext* aContext,
                   const IntSize& aSize,
                   const ImageRegion& aRegion,
                   uint32_t aWhichFrame,
                   SamplingFilter aSamplingFilter,
                   const Maybe<SVGImageContext>& /*aSVGContext - ignored*/,
-                  uint32_t aFlags)
+                  uint32_t aFlags,
+                  float aOpacity)
 {
   if (aWhichFrame > FRAME_MAX_VALUE) {
     return DrawResult::BAD_ARGS;
   }
 
   if (mError) {
     return DrawResult::BAD_IMAGE;
   }
@@ -1392,17 +1394,17 @@ RasterImage::Draw(gfxContext* aContext,
     }
     return DrawResult::NOT_READY;
   }
 
   bool shouldRecordTelemetry = !mDrawStartTime.IsNull() &&
                                surface->IsFinished();
 
   auto result = DrawInternal(Move(surface), aContext, aSize,
-                             aRegion, aSamplingFilter, flags);
+                             aRegion, aSamplingFilter, flags, aOpacity);
 
   if (shouldRecordTelemetry) {
       TimeDuration drawLatency = TimeStamp::Now() - mDrawStartTime;
       Telemetry::Accumulate(Telemetry::IMAGE_DECODE_ON_DRAW_LATENCY,
                             int32_t(drawLatency.ToMicroseconds()));
       mDrawStartTime = TimeStamp();
   }
 
--- a/image/RasterImage.h
+++ b/image/RasterImage.h
@@ -297,17 +297,18 @@ private:
                                    uint32_t aFlags,
                                    PlaybackType aPlaybackType);
 
   DrawResult DrawInternal(DrawableSurface&& aFrameRef,
                           gfxContext* aContext,
                           const nsIntSize& aSize,
                           const ImageRegion& aRegion,
                           gfx::SamplingFilter aSamplingFilter,
-                          uint32_t aFlags);
+                          uint32_t aFlags,
+                          float aOpacity);
 
   Pair<DrawResult, RefPtr<gfx::SourceSurface>>
     GetFrameInternal(const gfx::IntSize& aSize,
                      uint32_t aWhichFrame,
                      uint32_t aFlags);
 
   Pair<DrawResult, RefPtr<layers::Image>>
     GetCurrentImage(layers::ImageContainer* aContainer, uint32_t aFlags);
--- a/image/VectorImage.cpp
+++ b/image/VectorImage.cpp
@@ -745,17 +745,18 @@ VectorImage::GetFrameAtSize(const IntSiz
     NS_ERROR("Could not create a DrawTarget");
     return nullptr;
   }
 
   RefPtr<gfxContext> context = gfxContext::CreateOrNull(dt);
   MOZ_ASSERT(context); // already checked the draw target above
 
   auto result = Draw(context, aSize, ImageRegion::Create(aSize),
-                     aWhichFrame, SamplingFilter::POINT, Nothing(), aFlags);
+                     aWhichFrame, SamplingFilter::POINT, Nothing(), aFlags,
+                     1.0);
 
   return result == DrawResult::SUCCESS ? dt->Snapshot() : nullptr;
 }
 
 NS_IMETHODIMP_(bool)
 VectorImage::IsImageContainerAvailable(LayerManager* aManager, uint32_t aFlags)
 {
   return false;
@@ -771,26 +772,27 @@ VectorImage::GetImageContainer(LayerMana
 struct SVGDrawingParameters
 {
   SVGDrawingParameters(gfxContext* aContext,
                        const nsIntSize& aSize,
                        const ImageRegion& aRegion,
                        SamplingFilter aSamplingFilter,
                        const Maybe<SVGImageContext>& aSVGContext,
                        float aAnimationTime,
-                       uint32_t aFlags)
+                       uint32_t aFlags,
+                       float aOpacity)
     : context(aContext)
     , size(aSize.width, aSize.height)
     , region(aRegion)
     , samplingFilter(aSamplingFilter)
     , svgContext(aSVGContext)
     , viewportSize(aSize)
     , animationTime(aAnimationTime)
     , flags(aFlags)
-    , opacity(aSVGContext ? aSVGContext->GetGlobalOpacity() : 1.0)
+    , opacity(aSVGContext ? aSVGContext->GetGlobalOpacity() : aOpacity)
   {
     if (aSVGContext) {
       CSSIntSize sz = aSVGContext->GetViewportSize();
       viewportSize = nsIntSize(sz.width, sz.height); // XXX losing unit
     }
   }
 
   gfxContext*                   context;
@@ -807,17 +809,18 @@ struct SVGDrawingParameters
 //******************************************************************************
 NS_IMETHODIMP_(DrawResult)
 VectorImage::Draw(gfxContext* aContext,
                   const nsIntSize& aSize,
                   const ImageRegion& aRegion,
                   uint32_t aWhichFrame,
                   SamplingFilter aSamplingFilter,
                   const Maybe<SVGImageContext>& aSVGContext,
-                  uint32_t aFlags)
+                  uint32_t aFlags,
+                  float aOpacity)
 {
   if (aWhichFrame > FRAME_MAX_VALUE) {
     return DrawResult::BAD_ARGS;
   }
 
   if (!aContext) {
     return DrawResult::BAD_ARGS;
   }
@@ -862,17 +865,17 @@ VectorImage::Draw(gfxContext* aContext,
   float animTime =
     (aWhichFrame == FRAME_FIRST) ? 0.0f
                                  : mSVGDocumentWrapper->GetCurrentTime();
   AutoSVGRenderingState autoSVGState(svgContext, animTime,
                                      mSVGDocumentWrapper->GetRootSVGElem());
 
 
   SVGDrawingParameters params(aContext, aSize, aRegion, aSamplingFilter,
-                              svgContext, animTime, aFlags);
+                              svgContext, animTime, aFlags, aOpacity);
 
   // If we have an prerasterized version of this image that matches the
   // drawing parameters, use that.
   RefPtr<gfxDrawable> svgDrawable = LookupCachedSurface(params);
   if (svgDrawable) {
     Show(svgDrawable, params);
     return DrawResult::SUCCESS;
   }
--- a/image/imgFrame.cpp
+++ b/image/imgFrame.cpp
@@ -534,17 +534,18 @@ imgFrame::SurfaceForDrawing(bool        
   aRegion = aRegion.Intersect(available);
   IntSize availableSize(mDecoded.width, mDecoded.height);
 
   return SurfaceWithFormat(new gfxSurfaceDrawable(aSurface, availableSize),
                            mFormat);
 }
 
 bool imgFrame::Draw(gfxContext* aContext, const ImageRegion& aRegion,
-                    SamplingFilter aSamplingFilter, uint32_t aImageFlags)
+                    SamplingFilter aSamplingFilter, uint32_t aImageFlags,
+                    float aOpacity)
 {
   PROFILER_LABEL("imgFrame", "Draw",
     js::ProfileEntry::Category::GRAPHICS);
 
   MOZ_ASSERT(NS_IsMainThread());
   NS_ASSERTION(!aRegion.Rect().IsEmpty(), "Drawing empty region!");
   NS_ASSERTION(!aRegion.IsRestricted() ||
                !aRegion.Rect().Intersect(aRegion.Restriction()).IsEmpty(),
@@ -576,17 +577,17 @@ bool imgFrame::Draw(gfxContext* aContext
 
   ImageRegion region(aRegion);
   SurfaceWithFormat surfaceResult =
     SurfaceForDrawing(doPartialDecode, doTile, region, surf);
 
   if (surfaceResult.IsValid()) {
     gfxUtils::DrawPixelSnapped(aContext, surfaceResult.mDrawable,
                                imageRect.Size(), region, surfaceResult.mFormat,
-                               aSamplingFilter, aImageFlags);
+                               aSamplingFilter, aImageFlags, aOpacity);
   }
   return true;
 }
 
 nsresult
 imgFrame::ImageUpdated(const nsIntRect& aUpdateRect)
 {
   MonitorAutoLock lock(mMonitor);
--- a/image/imgFrame.h
+++ b/image/imgFrame.h
@@ -252,17 +252,18 @@ public:
    * volatile buffer to be freed.
    *
    * It is an error to call this without already holding a RawAccessFrameRef to
    * this imgFrame.
    */
   void SetRawAccessOnly();
 
   bool Draw(gfxContext* aContext, const ImageRegion& aRegion,
-            SamplingFilter aSamplingFilter, uint32_t aImageFlags);
+            SamplingFilter aSamplingFilter, uint32_t aImageFlags,
+            float aOpacity);
 
   nsresult ImageUpdated(const nsIntRect& aUpdateRect);
 
   /**
    * Mark this imgFrame as completely decoded, and set final options.
    *
    * You must always call either Finish() or Abort() before releasing the last
    * RawAccessFrameRef pointing to an imgFrame.
--- a/image/imgIContainer.idl
+++ b/image/imgIContainer.idl
@@ -402,17 +402,18 @@ interface imgIContainer : nsISupports
    */
   [noscript, notxpcom] DrawResult
   draw(in gfxContext aContext,
        [const] in nsIntSize aSize,
        [const] in ImageRegion aRegion,
        in uint32_t aWhichFrame,
        in SamplingFilter aSamplingFilter,
        [const] in MaybeSVGImageContext aSVGContext,
-       in uint32_t aFlags);
+       in uint32_t aFlags,
+       in float aOpacity);
 
   /*
    * Ensures that an image is decoding. Calling this function guarantees that
    * the image will at some point fire off decode notifications. Images that
    * can be decoded "quickly" according to some heuristic will be decoded
    * synchronously.
    *
    * @param aFlags Flags of the FLAG_* variety. Only FLAG_ASYNC_NOTIFY
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -5573,23 +5573,22 @@ DarkenColor(nscolor aColor)
 static bool
 ShouldDarkenColors(nsPresContext* aPresContext)
 {
   return !aPresContext->GetBackgroundColorDraw() &&
          !aPresContext->GetBackgroundImageDraw();
 }
 
 nscolor
-nsLayoutUtils::GetColor(nsIFrame* aFrame, nsCSSPropertyID aProperty)
-{
-  nscolor color = aFrame->GetVisitedDependentColor(aProperty);
+nsLayoutUtils::DarkenColorIfNeeded(nsIFrame* aFrame, nscolor aColor)
+{
   if (ShouldDarkenColors(aFrame->PresContext())) {
-    color = DarkenColor(color);
-  }
-  return color;
+    return DarkenColor(aColor);
+  }
+  return aColor;
 }
 
 gfxFloat
 nsLayoutUtils::GetSnappedBaselineY(nsIFrame* aFrame, gfxContext* aContext,
                                    nscoord aY, nscoord aAscent)
 {
   gfxFloat appUnitsPerDevUnit = aFrame->PresContext()->AppUnitsPerDevPixel();
   gfxFloat baseline = gfxFloat(aY) + aAscent;
@@ -6458,17 +6457,18 @@ DrawImageInternal(gfxContext&           
                   imgIContainer*         aImage,
                   const SamplingFilter   aSamplingFilter,
                   const nsRect&          aDest,
                   const nsRect&          aFill,
                   const nsPoint&         aAnchor,
                   const nsRect&          aDirty,
                   const SVGImageContext* aSVGContext,
                   uint32_t               aImageFlags,
-                  ExtendMode             aExtendMode = ExtendMode::CLAMP)
+                  ExtendMode             aExtendMode = ExtendMode::CLAMP,
+                  float                  aOpacity = 1.0)
 {
   DrawResult result = DrawResult::SUCCESS;
 
   aImageFlags |= imgIContainer::FLAG_ASYNC_NOTIFY;
 
   if (aPresContext->Type() == nsPresContext::eContext_Print) {
     // We want vector images to be passed on as vector commands, not a raster
     // image.
@@ -6499,17 +6499,17 @@ DrawImageInternal(gfxContext&           
     Maybe<SVGImageContext> svgContext = ToMaybe(aSVGContext);
     if (!svgContext) {
       // Use the default viewport.
       svgContext = Some(SVGImageContext(params.svgViewportSize, Nothing()));
     }
 
     result = aImage->Draw(destCtx, params.size, params.region,
                           imgIContainer::FRAME_CURRENT, aSamplingFilter,
-                          svgContext, aImageFlags);
+                          svgContext, aImageFlags, aOpacity);
 
   }
 
   return result;
 }
 
 /* static */ DrawResult
 nsLayoutUtils::DrawSingleUnscaledImage(gfxContext&          aContext,
@@ -6671,39 +6671,42 @@ nsLayoutUtils::DrawBackgroundImage(gfxCo
                                    const CSSIntSize&   aImageSize,
                                    SamplingFilter      aSamplingFilter,
                                    const nsRect&       aDest,
                                    const nsRect&       aFill,
                                    const nsSize&       aRepeatSize,
                                    const nsPoint&      aAnchor,
                                    const nsRect&       aDirty,
                                    uint32_t            aImageFlags,
-                                   ExtendMode          aExtendMode)
+                                   ExtendMode          aExtendMode,
+                                   float               aOpacity)
 {
   PROFILER_LABEL("layout", "nsLayoutUtils::DrawBackgroundImage",
                  js::ProfileEntry::Category::GRAPHICS);
 
   SVGImageContext svgContext(aImageSize, Nothing());
 
   /* Fast path when there is no need for image spacing */
   if (aRepeatSize.width == aDest.width && aRepeatSize.height == aDest.height) {
     return DrawImageInternal(aContext, aPresContext, aImage,
                              aSamplingFilter, aDest, aFill, aAnchor,
-                             aDirty, &svgContext, aImageFlags, aExtendMode);
+                             aDirty, &svgContext, aImageFlags, aExtendMode,
+                             aOpacity);
   }
 
   nsPoint firstTilePos = aDest.TopLeft() +
                          nsPoint(NSToIntFloor(float(aFill.x - aDest.x) / aRepeatSize.width) * aRepeatSize.width,
                                  NSToIntFloor(float(aFill.y - aDest.y) / aRepeatSize.height) * aRepeatSize.height);
   for (int32_t i = firstTilePos.x; i < aFill.XMost(); i += aRepeatSize.width) {
     for (int32_t j = firstTilePos.y; j < aFill.YMost(); j += aRepeatSize.height) {
       nsRect dest(i, j, aDest.width, aDest.height);
       DrawResult result = DrawImageInternal(aContext, aPresContext, aImage, aSamplingFilter,
                                             dest, dest, aAnchor, aDirty, &svgContext,
-                                            aImageFlags, ExtendMode::CLAMP);
+                                            aImageFlags, ExtendMode::CLAMP,
+                                            aOpacity);
       if (result != DrawResult::SUCCESS) {
         return result;
       }
     }
   }
 
   return DrawResult::SUCCESS;
 }
@@ -6712,21 +6715,23 @@ nsLayoutUtils::DrawBackgroundImage(gfxCo
 nsLayoutUtils::DrawImage(gfxContext&         aContext,
                          nsPresContext*      aPresContext,
                          imgIContainer*      aImage,
                          const SamplingFilter aSamplingFilter,
                          const nsRect&       aDest,
                          const nsRect&       aFill,
                          const nsPoint&      aAnchor,
                          const nsRect&       aDirty,
-                         uint32_t            aImageFlags)
+                         uint32_t            aImageFlags,
+                         float               aOpacity)
 {
   return DrawImageInternal(aContext, aPresContext, aImage,
                            aSamplingFilter, aDest, aFill, aAnchor,
-                           aDirty, nullptr, aImageFlags);
+                           aDirty, nullptr, aImageFlags, ExtendMode::CLAMP,
+                           aOpacity);
 }
 
 /* static */ nsRect
 nsLayoutUtils::GetWholeImageDestination(const nsSize& aWholeImageSize,
                                         const nsRect& aImageSourceArea,
                                         const nsRect& aDestArea)
 {
   double scaleX = double(aDestArea.width)/aImageSourceArea.width;
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -1541,18 +1541,28 @@ public:
   // Implement nsIFrame::GetPrefISize in terms of nsIFrame::AddInlinePrefISize
   static nscoord PrefISizeFromInline(nsIFrame* aFrame,
                                      nsRenderingContext* aRenderingContext);
 
   // Implement nsIFrame::GetMinISize in terms of nsIFrame::AddInlineMinISize
   static nscoord MinISizeFromInline(nsIFrame* aFrame,
                                     nsRenderingContext* aRenderingContext);
 
-  // Get a suitable foreground color for painting aProperty for aFrame.
-  static nscolor GetColor(nsIFrame* aFrame, nsCSSPropertyID aProperty);
+  // Get a suitable foreground color for painting aColor for aFrame.
+  static nscolor DarkenColorIfNeeded(nsIFrame* aFrame, nscolor aColor);
+
+  // Get a suitable foreground color for painting aField for aFrame.
+  // Type of aFrame is made a template parameter because nsIFrame is not
+  // a complete type in the header. Type-safety is not harmed given that
+  // DarkenColorIfNeeded requires an nsIFrame pointer.
+  template<typename Frame, typename T, typename S>
+  static nscolor GetColor(Frame* aFrame, T S::* aField) {
+    nscolor color = aFrame->GetVisitedDependentColor(aField);
+    return DarkenColorIfNeeded(aFrame, color);
+  }
 
   // Get a baseline y position in app units that is snapped to device pixels.
   static gfxFloat GetSnappedBaselineY(nsIFrame* aFrame, gfxContext* aContext,
                                       nscoord aY, nscoord aAscent);
   // Ditto for an x position (for vertical text). Note that for vertical-rl
   // writing mode, the ascent value should be negated by the caller.
   static gfxFloat GetSnappedBaselineX(nsIFrame* aFrame, gfxContext* aContext,
                                       nscoord aX, nscoord aAscent);
@@ -1750,17 +1760,18 @@ public:
                                         const CSSIntSize&   aImageSize,
                                         SamplingFilter      aSamplingFilter,
                                         const nsRect&       aDest,
                                         const nsRect&       aFill,
                                         const nsSize&       aRepeatSize,
                                         const nsPoint&      aAnchor,
                                         const nsRect&       aDirty,
                                         uint32_t            aImageFlags,
-                                        ExtendMode          aExtendMode);
+                                        ExtendMode          aExtendMode,
+                                        float               aOpacity);
 
   /**
    * Draw an image.
    * See https://wiki.mozilla.org/Gecko:Image_Snapping_and_Rendering
    *   @param aRenderingContext Where to draw the image, set up with an
    *                            appropriate scale and transform for drawing in
    *                            app units.
    *   @param aImage            The image.
@@ -1775,17 +1786,18 @@ public:
   static DrawResult DrawImage(gfxContext&         aContext,
                               nsPresContext*      aPresContext,
                               imgIContainer*      aImage,
                               const SamplingFilter aSamplingFilter,
                               const nsRect&       aDest,
                               const nsRect&       aFill,
                               const nsPoint&      aAnchor,
                               const nsRect&       aDirty,
-                              uint32_t            aImageFlags);
+                              uint32_t            aImageFlags,
+                              float               aOpacity = 1.0);
 
   static inline void InitDashPattern(StrokeOptions& aStrokeOptions,
                                      uint8_t aBorderStyle) {
     if (aBorderStyle == NS_STYLE_BORDER_STYLE_DOTTED) {
       static Float dot[] = { 1.f, 1.f };
       aStrokeOptions.mDashLength = MOZ_ARRAY_LENGTH(dot);
       aStrokeOptions.mDashPattern = dot;
     } else if (aBorderStyle == NS_STYLE_BORDER_STYLE_DASHED) {
--- a/layout/generic/TextOverflow.cpp
+++ b/layout/generic/TextOverflow.cpp
@@ -210,17 +210,17 @@ PaintTextShadowCallback(nsRenderingConte
            PaintTextToContext(aCtx, aShadowOffset);
 }
 
 void
 nsDisplayTextOverflowMarker::Paint(nsDisplayListBuilder* aBuilder,
                                    nsRenderingContext*   aCtx)
 {
   nscolor foregroundColor = nsLayoutUtils::
-    GetColor(mFrame, eCSSProperty__webkit_text_fill_color);
+    GetColor(mFrame, &nsStyleText::mWebkitTextFillColor);
 
   // Paint the text-shadows for the overflow marker
   nsLayoutUtils::PaintTextShadow(mFrame, aCtx, mRect, mVisibleRect,
                                  foregroundColor, PaintTextShadowCallback,
                                  (void*)this);
   aCtx->ThebesContext()->SetColor(gfx::Color::FromABGR(foregroundColor));
   PaintTextToContext(aCtx, nsPoint(0, 0));
 }
--- a/layout/generic/nsBulletFrame.cpp
+++ b/layout/generic/nsBulletFrame.cpp
@@ -311,17 +311,17 @@ nsBulletFrame::PaintBullet(nsRenderingCo
              PresContext(),
              imageCon, nsLayoutUtils::GetSamplingFilterForFrame(this),
              dest + aPt, aDirtyRect, nullptr, aFlags);
       }
     }
   }
 
   ColorPattern color(ToDeviceColor(
-                       nsLayoutUtils::GetColor(this, eCSSProperty_color)));
+    nsLayoutUtils::GetColor(this, &nsStyleColor::mColor)));
 
   DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
   int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel();
 
   switch (listStyleType->GetStyle()) {
   case NS_STYLE_LIST_STYLE_NONE:
     break;
 
@@ -418,17 +418,17 @@ nsBulletFrame::PaintBullet(nsRenderingCo
     break;
 
   default:
     {
       DrawTargetAutoDisableSubpixelAntialiasing
         disable(aRenderingContext.GetDrawTarget(), aDisableSubpixelAA);
 
       aRenderingContext.ThebesContext()->SetColor(
-        Color::FromABGR(nsLayoutUtils::GetColor(this, eCSSProperty_color)));
+        Color::FromABGR(nsLayoutUtils::GetColor(this, &nsStyleColor::mColor)));
 
       RefPtr<nsFontMetrics> fm =
         nsLayoutUtils::GetFontMetricsForFrame(this, GetFontSizeInflation());
       nsAutoString text;
       GetListItemText(text);
       WritingMode wm = GetWritingMode();
       nscoord ascent = wm.IsLineInverted()
                          ? fm->MaxDescent() : fm->MaxAscent();
--- a/layout/generic/nsColumnSetFrame.cpp
+++ b/layout/generic/nsColumnSetFrame.cpp
@@ -78,17 +78,17 @@ nsColumnSetFrame::PaintColumnRule(nsRend
     ruleStyle = colStyle->mColumnRuleStyle;
 
   nsPresContext* presContext = PresContext();
   nscoord ruleWidth = colStyle->GetComputedColumnRuleWidth();
   if (!ruleWidth)
     return;
 
   nscolor ruleColor =
-    GetVisitedDependentColor(eCSSProperty_column_rule_color);
+    GetVisitedDependentColor(&nsStyleColumn::mColumnRuleColor);
 
   // In order to re-use a large amount of code, we treat the column rule as a border.
   // We create a new border style object and fill in all the details of the column rule as
   // the left border. PaintBorder() does all the rendering for us, so we not
   // only save an enormous amount of code but we'll support all the line styles that
   // we support on borders!
   nsStyleBorder border(presContext);
   Sides skipSides;
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -722,18 +722,19 @@ public:
     const nsStyle##name_ * Style##name_ () const {                            \
       NS_ASSERTION(mStyleContext, "No style context found!");                 \
       return mStyleContext->Style##name_ ();                                  \
     }
   #include "nsStyleStructList.h"
   #undef STYLE_STRUCT
 
   /** Also forward GetVisitedDependentColor to the style context */
-  nscolor GetVisitedDependentColor(nsCSSPropertyID aProperty)
-    { return mStyleContext->GetVisitedDependentColor(aProperty); }
+  template<typename T, typename S>
+  nscolor GetVisitedDependentColor(T S::* aField)
+    { return mStyleContext->GetVisitedDependentColor(aField); }
 
   /**
    * These methods are to access any additional style contexts that
    * the frame may be holding. These are contexts that are children
    * of the frame's primary context and are NOT used as style contexts
    * for any child frames. These contexts also MUST NOT have any child 
    * contexts whatsoever. If you need to insert style contexts into the
    * style tree, then you should create pseudo element frames to own them
--- a/layout/generic/nsPluginFrame.cpp
+++ b/layout/generic/nsPluginFrame.cpp
@@ -336,18 +336,18 @@ nsPluginFrame::PrepForDrawing(nsIWidget 
 
     RegisterPluginForGeometryUpdates();
 
     // Here we set the background color for this widget because some plugins will use 
     // the child window background color when painting. If it's not set, it may default to gray
     // Sometimes, a frame doesn't have a background color or is transparent. In this
     // case, walk up the frame tree until we do find a frame with a background color
     for (nsIFrame* frame = this; frame; frame = frame->GetParent()) {
-      nscolor bgcolor =
-        frame->GetVisitedDependentColor(eCSSProperty_background_color);
+      nscolor bgcolor = frame->
+        GetVisitedDependentColor(&nsStyleBackground::mBackgroundColor);
       if (NS_GET_A(bgcolor) > 0) {  // make sure we got an actual color
         mWidget->SetBackgroundColor(bgcolor);
         break;
       }
     }
   } else {
     // Changing to windowless mode changes the NPWindow geometry.
     FixupWindow(GetContentRectRelativeToSelf().Size());
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -3747,24 +3747,24 @@ nsTextPaintStyle::GetTextColor()
     if (!mResolveColors)
       return NS_SAME_AS_FOREGROUND_COLOR;
 
     const nsStyleSVG* style = mFrame->StyleSVG();
     switch (style->mFill.Type()) {
       case eStyleSVGPaintType_None:
         return NS_RGBA(0, 0, 0, 0);
       case eStyleSVGPaintType_Color:
-        return nsLayoutUtils::GetColor(mFrame, eCSSProperty_fill);
+        return nsLayoutUtils::GetColor(mFrame, &nsStyleSVG::mFill);
       default:
         NS_ERROR("cannot resolve SVG paint to nscolor");
         return NS_RGBA(0, 0, 0, 255);
     }
   }
 
-  return nsLayoutUtils::GetColor(mFrame, eCSSProperty__webkit_text_fill_color);
+  return nsLayoutUtils::GetColor(mFrame, &nsStyleText::mWebkitTextFillColor);
 }
 
 bool
 nsTextPaintStyle::GetSelectionColors(nscolor* aForeColor,
                                      nscolor* aBackColor)
 {
   NS_ASSERTION(aForeColor, "aForeColor is null");
   NS_ASSERTION(aBackColor, "aBackColor is null");
@@ -3848,18 +3848,18 @@ void
 nsTextPaintStyle::InitCommonColors()
 {
   if (mInitCommonColors)
     return;
 
   nsIFrame* bgFrame =
     nsCSSRendering::FindNonTransparentBackgroundFrame(mFrame);
   NS_ASSERTION(bgFrame, "Cannot find NonTransparentBackgroundFrame.");
-  nscolor bgColor =
-    bgFrame->GetVisitedDependentColor(eCSSProperty_background_color);
+  nscolor bgColor = bgFrame->
+    GetVisitedDependentColor(&nsStyleBackground::mBackgroundColor);
 
   nscolor defaultBgColor = mPresContext->DefaultBackgroundColor();
   mFrameBackgroundColor = NS_ComposeColors(defaultBgColor, bgColor);
 
   mSystemFieldForegroundColor =
     LookAndFeel::GetColor(LookAndFeel::eColorID__moz_fieldtext);
   mSystemFieldBackgroundColor =
     LookAndFeel::GetColor(LookAndFeel::eColorID__moz_field);
@@ -3946,19 +3946,19 @@ nsTextPaintStyle::InitSelectionColorsAnd
     RefPtr<nsStyleContext> sc = nullptr;
     sc = mPresContext->StyleSet()->
       ProbePseudoElementStyle(selectionElement,
                               CSSPseudoElementType::mozSelection,
                               mFrame->StyleContext());
     // Use -moz-selection pseudo class.
     if (sc) {
       mSelectionBGColor =
-        sc->GetVisitedDependentColor(eCSSProperty_background_color);
+        sc->GetVisitedDependentColor(&nsStyleBackground::mBackgroundColor);
       mSelectionTextColor =
-        sc->GetVisitedDependentColor(eCSSProperty__webkit_text_fill_color);
+        sc->GetVisitedDependentColor(&nsStyleText::mWebkitTextFillColor);
       mHasSelectionShadow =
         nsRuleNode::HasAuthorSpecifiedRules(sc,
                                             NS_AUTHOR_SPECIFIED_TEXT_SHADOW,
                                             true);
       if (mHasSelectionShadow) {
         mSelectionShadow = sc->StyleText()->mTextShadow;
       }
       return true;
@@ -3984,26 +3984,24 @@ nsTextPaintStyle::InitSelectionColorsAnd
   }
 
   mSelectionTextColor =
     LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectForeground);
 
   if (mResolveColors) {
     // On MacOS X, we don't exchange text color and BG color.
     if (mSelectionTextColor == NS_DONT_CHANGE_COLOR) {
-      nsCSSPropertyID property = mFrame->IsSVGText()
-                               ? eCSSProperty_fill
-                               : eCSSProperty__webkit_text_fill_color;
-      nscoord frameColor = mFrame->GetVisitedDependentColor(property);
+      nscolor frameColor = mFrame->IsSVGText()
+        ? mFrame->GetVisitedDependentColor(&nsStyleSVG::mFill)
+        : mFrame->GetVisitedDependentColor(&nsStyleText::mWebkitTextFillColor);
       mSelectionTextColor = EnsureDifferentColors(frameColor, mSelectionBGColor);
     } else if (mSelectionTextColor == NS_CHANGE_COLOR_IF_SAME_AS_BG) {
-      nsCSSPropertyID property = mFrame->IsSVGText()
-                               ? eCSSProperty_fill
-                               : eCSSProperty__webkit_text_fill_color;
-      nscolor frameColor = mFrame->GetVisitedDependentColor(property);
+      nscolor frameColor = mFrame->IsSVGText()
+        ? mFrame->GetVisitedDependentColor(&nsStyleSVG::mFill)
+        : mFrame->GetVisitedDependentColor(&nsStyleText::mWebkitTextFillColor);
       if (frameColor == mSelectionBGColor) {
         mSelectionTextColor =
           LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectForegroundCustom);
       }
     } else {
       EnsureSufficientContrast(&mSelectionTextColor, &mSelectionBGColor);
     }
   } else {
@@ -5308,17 +5306,17 @@ nsTextFrame::GetTextDecorations(
     const uint8_t textDecorations = styleText->mTextDecorationLine;
 
     if (!useOverride &&
         (NS_STYLE_TEXT_DECORATION_LINE_OVERRIDE_ALL & textDecorations)) {
       // This handles the <a href="blah.html"><font color="green">La
       // la la</font></a> case. The link underline should be green.
       useOverride = true;
       overrideColor =
-        nsLayoutUtils::GetColor(f, eCSSProperty_text_decoration_color);
+        nsLayoutUtils::GetColor(f, &nsStyleTextReset::mTextDecorationColor);
     }
 
     nsBlockFrame* fBlock = nsLayoutUtils::GetAsBlock(f);
     const bool firstBlock = !nearestBlockFound && fBlock;
 
     // Not updating positions once we hit a parent block is equivalent to
     // the CSS 2.1 spec that blocks should propagate decorations down to their
     // children (albeit the style should be preserved)
@@ -5362,20 +5360,21 @@ nsTextFrame::GetTextDecorations(
         // XXX We might want to do something with text-decoration-color when
         //     painting SVG text, but it's not clear what we should do.  We
         //     at least need SVG text decorations to paint with 'fill' if
         //     text-decoration-color has its initial value currentColor.
         //     We could choose to interpret currentColor as "currentFill"
         //     for SVG text, and have e.g. text-decoration-color:red to
         //     override the fill paint of the decoration.
         color = aColorResolution == eResolvedColors ?
-                  nsLayoutUtils::GetColor(f, eCSSProperty_fill) :
+                  nsLayoutUtils::GetColor(f, &nsStyleSVG::mFill) :
                   NS_SAME_AS_FOREGROUND_COLOR;
       } else {
-        color = nsLayoutUtils::GetColor(f, eCSSProperty_text_decoration_color);
+        color = nsLayoutUtils::
+          GetColor(f, &nsStyleTextReset::mTextDecorationColor);
       }
 
       bool swapUnderlineAndOverline = vertical && IsUnderlineRight(f);
       const uint8_t kUnderline =
         swapUnderlineAndOverline ? NS_STYLE_TEXT_DECORATION_LINE_OVERLINE :
                                    NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE;
       const uint8_t kOverline =
         swapUnderlineAndOverline ? NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE :
@@ -6556,17 +6555,17 @@ nsTextFrame::DrawEmphasisMarks(gfxContex
 {
   const EmphasisMarkInfo* info = Properties().Get(EmphasisMarkProperty());
   if (!info) {
     return;
   }
 
   bool isTextCombined = StyleContext()->IsTextCombined();
   nscolor color = aDecorationOverrideColor ? *aDecorationOverrideColor :
-    nsLayoutUtils::GetColor(this, eCSSProperty_text_emphasis_color);
+    nsLayoutUtils::GetColor(this, &nsStyleText::mTextEmphasisColor);
   aContext->SetColor(Color::FromABGR(color));
   gfxPoint pt;
   if (!isTextCombined) {
     pt = aTextBaselinePt;
   } else {
     MOZ_ASSERT(aWM.IsVertical());
     pt = aFramePt;
     if (aWM.IsVerticalRL()) {
--- a/layout/mathml/nsMathMLChar.cpp
+++ b/layout/mathml/nsMathMLChar.cpp
@@ -2051,17 +2051,17 @@ nsMathMLChar::PaintForeground(nsPresCont
     // Set default context to the parent context
     styleContext = parentContext;
   }
 
   RefPtr<gfxContext> thebesContext = aRenderingContext.ThebesContext();
 
   // Set color ...
   nscolor fgColor = styleContext->
-    GetVisitedDependentColor(eCSSProperty__webkit_text_fill_color);
+    GetVisitedDependentColor(&nsStyleText::mWebkitTextFillColor);
   if (aIsSelected) {
     // get color to use for selection from the look&feel object
     fgColor = LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectForeground,
                                     fgColor);
   }
   thebesContext->SetColor(Color::FromABGR(fgColor));
   thebesContext->Save();
   nsRect r = mRect + aPt;
--- a/layout/mathml/nsMathMLFrame.cpp
+++ b/layout/mathml/nsMathMLFrame.cpp
@@ -362,17 +362,17 @@ void nsDisplayMathMLBar::Paint(nsDisplay
 {
   // paint the bar with the current text color
   DrawTarget* drawTarget = aCtx->GetDrawTarget();
   Rect rect =
     NSRectToNonEmptySnappedRect(mRect + ToReferenceFrame(),
                                 mFrame->PresContext()->AppUnitsPerDevPixel(),
                                 *drawTarget);
   ColorPattern color(ToDeviceColor(
-    mFrame->GetVisitedDependentColor(eCSSProperty__webkit_text_fill_color)));
+    mFrame->GetVisitedDependentColor(&nsStyleText::mWebkitTextFillColor)));
   drawTarget->FillRect(rect, color);
 }
 
 void
 nsMathMLFrame::DisplayBar(nsDisplayListBuilder* aBuilder,
                           nsIFrame* aFrame, const nsRect& aRect,
                           const nsDisplayListSet& aLists) {
   if (!aFrame->StyleVisibility()->IsVisible() || aRect.IsEmpty())
--- a/layout/mathml/nsMathMLmencloseFrame.cpp
+++ b/layout/mathml/nsMathMLmencloseFrame.cpp
@@ -775,17 +775,17 @@ void nsDisplayNotation::Paint(nsDisplayL
 
   Float strokeWidth = presContext->AppUnitsToGfxUnits(mThickness);
 
   Rect rect = NSRectToRect(mRect + ToReferenceFrame(),
                            presContext->AppUnitsPerDevPixel());
   rect.Deflate(strokeWidth / 2.f);
 
   ColorPattern color(ToDeviceColor(
-    mFrame->GetVisitedDependentColor(eCSSProperty__webkit_text_fill_color)));
+    mFrame->GetVisitedDependentColor(&nsStyleText::mWebkitTextFillColor)));
 
   StrokeOptions strokeOptions(strokeWidth);
 
   switch(mType)
   {
     case NOTATION_CIRCLE: {
       RefPtr<Path> ellipse =
         MakePathForEllipse(aDrawTarget, rect.Center(), rect.Size());
--- a/layout/mathml/nsMathMLmfracFrame.cpp
+++ b/layout/mathml/nsMathMLmfracFrame.cpp
@@ -624,17 +624,17 @@ void nsDisplayMathMLSlash::Paint(nsDispl
   DrawTarget& aDrawTarget = *aCtx->GetDrawTarget();
 
   // get the gfxRect
   nsPresContext* presContext = mFrame->PresContext();
   Rect rect = NSRectToRect(mRect + ToReferenceFrame(),
                            presContext->AppUnitsPerDevPixel());
 
   ColorPattern color(ToDeviceColor(
-    mFrame->GetVisitedDependentColor(eCSSProperty__webkit_text_fill_color)));
+    mFrame->GetVisitedDependentColor(&nsStyleText::mWebkitTextFillColor)));
 
   // draw the slash as a parallelogram
   Point delta = Point(presContext->AppUnitsToGfxUnits(mThickness), 0);
   RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder();
   if (mRTL) {
     builder->MoveTo(rect.TopLeft());
     builder->LineTo(rect.TopLeft() + delta);
     builder->LineTo(rect.BottomRight());
--- a/layout/painting/nsCSSRendering.cpp
+++ b/layout/painting/nsCSSRendering.cpp
@@ -666,18 +666,18 @@ nsCSSRendering::PaintBorder(nsPresContex
     return PaintBorderWithStyleBorder(aPresContext, aRenderingContext, aForFrame,
                                       aDirtyRect, aBorderArea, *styleBorder,
                                       aStyleContext, aFlags, aSkipSides);
   }
 
   nsStyleBorder newStyleBorder(*styleBorder);
 
   NS_FOR_CSS_SIDES(side) {
-    nscolor color = aStyleContext->GetVisitedDependentColor(
-      nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color)[side]);
+    nscolor color = aStyleContext->
+      GetVisitedDependentColor(nsStyleBorder::BorderColorFieldFor(side));
     newStyleBorder.mBorderColor[side] = StyleComplexColor::FromColor(color);
   }
   return PaintBorderWithStyleBorder(aPresContext, aRenderingContext, aForFrame,
                                     aDirtyRect, aBorderArea, newStyleBorder,
                                     aStyleContext, aFlags, aSkipSides);
 }
 
 Maybe<nsCSSBorderRenderer>
@@ -698,18 +698,18 @@ nsCSSRendering::CreateBorderRenderer(nsP
                                                aForFrame, aDirtyRect,
                                                aBorderArea, *styleBorder,
                                                aStyleContext, aSkipSides);
   }
 
   nsStyleBorder newStyleBorder(*styleBorder);
 
   NS_FOR_CSS_SIDES(side) {
-    nscolor color = aStyleContext->GetVisitedDependentColor(
-      nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color)[side]);
+    nscolor color = aStyleContext->
+      GetVisitedDependentColor(nsStyleBorder::BorderColorFieldFor(side));
     newStyleBorder.mBorderColor[side] = StyleComplexColor::FromColor(color);
   }
   return CreateBorderRendererWithStyleBorder(aPresContext, aDrawTarget,
                                              aForFrame, aDirtyRect, aBorderArea,
                                              newStyleBorder, aStyleContext,
                                              aSkipSides);
 }
 
@@ -729,18 +729,18 @@ ConstructBorderRenderer(nsPresContext* a
   // Get our style context's color struct.
   const nsStyleColor* ourColor = aStyleContext->StyleColor();
 
   // In NavQuirks mode we want to use the parent's context as a starting point
   // for determining the background color.
   bool quirks = aPresContext->CompatibilityMode() == eCompatibility_NavQuirks;
   nsIFrame* bgFrame = nsCSSRendering::FindNonTransparentBackgroundFrame(aForFrame, quirks);
   nsStyleContext* bgContext = bgFrame->StyleContext();
-  nscolor bgColor =
-    bgContext->GetVisitedDependentColor(eCSSProperty_background_color);
+  nscolor bgColor = bgContext->
+    GetVisitedDependentColor(&nsStyleBackground::mBackgroundColor);
 
   // Compute the outermost boundary of the area that might be painted.
   // Same coordinate space as aBorderArea & aBGClipRect.
   nsRect joinedBorderArea =
     ::BoxDecorationRectForBorder(aForFrame, aBorderArea, aSkipSides, &aStyleBorder);
   RectCornerRadii bgRadii;
   ::GetRadii(aForFrame, aStyleBorder, aBorderArea, joinedBorderArea, &bgRadii);
 
@@ -968,18 +968,18 @@ nsCSSRendering::PaintOutline(nsPresConte
   if (width == 0 && outlineStyle != NS_STYLE_BORDER_STYLE_AUTO) {
     // Empty outline
     return;
   }
 
   nsIFrame* bgFrame = nsCSSRendering::FindNonTransparentBackgroundFrame
     (aForFrame, false);
   nsStyleContext* bgContext = bgFrame->StyleContext();
-  nscolor bgColor =
-    bgContext->GetVisitedDependentColor(eCSSProperty_background_color);
+  nscolor bgColor = bgContext->
+    GetVisitedDependentColor(&nsStyleBackground::mBackgroundColor);
 
   nsRect innerRect;
   if (
 #ifdef MOZ_XUL
       aStyleContext->GetPseudoType() == CSSPseudoElementType::XULTree
 #else
       false
 #endif
@@ -1036,17 +1036,17 @@ nsCSSRendering::PaintOutline(nsPresConte
   }
 
   uint8_t outlineStyles[4] = { outlineStyle, outlineStyle,
                                outlineStyle, outlineStyle };
 
   // This handles treating the initial color as 'currentColor'; if we
   // ever want 'invert' back we'll need to do a bit of work here too.
   nscolor outlineColor =
-    aStyleContext->GetVisitedDependentColor(eCSSProperty_outline_color);
+    aStyleContext->GetVisitedDependentColor(&nsStyleOutline::mOutlineColor);
   nscolor outlineColors[4] = { outlineColor,
                                outlineColor,
                                outlineColor,
                                outlineColor };
 
   // convert the border widths
   Float outlineWidths[4] = { Float(width / twipsPerPixel),
                              Float(width / twipsPerPixel),
@@ -1773,47 +1773,51 @@ nsCSSRendering::PaintBoxShadowInner(nsPr
 
 /* static */
 nsCSSRendering::PaintBGParams
 nsCSSRendering::PaintBGParams::ForAllLayers(nsPresContext& aPresCtx,
                                             nsRenderingContext& aRenderingCtx,
                                             const nsRect& aDirtyRect,
                                             const nsRect& aBorderArea,
                                             nsIFrame *aFrame,
-                                            uint32_t aPaintFlags)
+                                            uint32_t aPaintFlags,
+                                            float aOpacity)
 {
   MOZ_ASSERT(aFrame);
 
-  PaintBGParams result(aPresCtx, aRenderingCtx, aDirtyRect, aBorderArea, aFrame,
-    aPaintFlags, -1, CompositionOp::OP_OVER);
+  PaintBGParams result(aPresCtx, aRenderingCtx, aDirtyRect, aBorderArea,
+                       aFrame, aPaintFlags, -1, CompositionOp::OP_OVER,
+                       aOpacity);
 
   return result;
 }
 
 /* static */
 nsCSSRendering::PaintBGParams
 nsCSSRendering::PaintBGParams::ForSingleLayer(nsPresContext& aPresCtx,
                                               nsRenderingContext& aRenderingCtx,
                                               const nsRect& aDirtyRect,
                                               const nsRect& aBorderArea,
                                               nsIFrame *aFrame,
                                               uint32_t aPaintFlags,
                                               int32_t aLayer,
-                                              CompositionOp aCompositionOp)
+                                              CompositionOp aCompositionOp,
+                                              float aOpacity)
 {
   MOZ_ASSERT(aFrame && (aLayer != -1));
 
-  PaintBGParams result(aPresCtx, aRenderingCtx, aDirtyRect, aBorderArea, aFrame,
-    aPaintFlags, aLayer, aCompositionOp);
+  PaintBGParams result(aPresCtx, aRenderingCtx, aDirtyRect, aBorderArea,
+                       aFrame, aPaintFlags, aLayer, aCompositionOp,
+                       aOpacity);
 
   return result;
 }
 
 DrawResult
-nsCSSRendering::PaintBackground(const PaintBGParams& aParams)
+nsCSSRendering::PaintStyleImageLayer(const PaintBGParams& aParams)
 {
   PROFILER_LABEL("nsCSSRendering", "PaintBackground",
     js::ProfileEntry::Category::GRAPHICS);
 
   NS_PRECONDITION(aParams.frame,
                   "Frame is expected to be provided to PaintBackground");
 
   nsStyleContext *sc;
@@ -1830,17 +1834,17 @@ nsCSSRendering::PaintBackground(const Pa
     nsIContent* content = aParams.frame->GetContent();
     if (!content || content->GetParent()) {
       return DrawResult::SUCCESS;
     }
 
     sc = aParams.frame->StyleContext();
   }
 
-  return PaintBackgroundWithSC(aParams, sc, *aParams.frame->StyleBorder());
+  return PaintStyleImageLayerWithSC(aParams, sc, *aParams.frame->StyleBorder());
 }
 
 static bool
 IsOpaqueBorderEdge(const nsStyleBorder& aBorder, mozilla::Side aSide)
 {
   if (aBorder.GetComputedBorder().Side(aSide) == 0)
     return true;
   switch (aBorder.GetBorderStyle(aSide)) {
@@ -2235,18 +2239,18 @@ nsCSSRendering::DetermineBackgroundColor
       aFrame->HonorPrintBackgroundSettings()) {
     aDrawBackgroundImage = aPresContext->GetBackgroundImageDraw();
     aDrawBackgroundColor = aPresContext->GetBackgroundColorDraw();
   }
 
   const nsStyleBackground *bg = aStyleContext->StyleBackground();
   nscolor bgColor;
   if (aDrawBackgroundColor) {
-    bgColor =
-      aStyleContext->GetVisitedDependentColor(eCSSProperty_background_color);
+    bgColor = aStyleContext->
+      GetVisitedDependentColor(&nsStyleBackground::mBackgroundColor);
     if (NS_GET_A(bgColor) == 0) {
       aDrawBackgroundColor = false;
     }
   } else {
     // If GetBackgroundColorDraw() is false, we are still expected to
     // draw color in the background of any frame that's not completely
     // transparent, but we are expected to use white instead of whatever
     // color was specified.
@@ -2777,17 +2781,18 @@ void
 nsCSSRendering::PaintGradient(nsPresContext* aPresContext,
                               nsRenderingContext& aRenderingContext,
                               nsStyleGradient* aGradient,
                               const nsRect& aDirtyRect,
                               const nsRect& aDest,
                               const nsRect& aFillArea,
                               const nsSize& aRepeatSize,
                               const CSSIntRect& aSrc,
-                              const nsSize& aIntrinsicSize)
+                              const nsSize& aIntrinsicSize,
+                              float aOpacity)
 {
   PROFILER_LABEL("nsCSSRendering", "PaintGradient",
     js::ProfileEntry::Category::GRAPHICS);
 
   Telemetry::AutoTimer<Telemetry::GRADIENT_DURATION, Telemetry::Microsecond> gradientTimer;
   if (aDest.IsEmpty() || aFillArea.IsEmpty()) {
     return;
   }
@@ -3126,16 +3131,17 @@ nsCSSRendering::PaintGradient(nsPresCont
   // which is a lookup table used to evaluate the gradient. This surface can use
   // much memory (ram and/or GPU ram) and can be expensive to create. So we cache it.
   // The cache key correlates 1:1 with the arguments for CreateGradientStops (also the implied backend type)
   // Note that GradientStop is a simple struct with a stop value (while GradientStops has the surface).
   nsTArray<gfx::GradientStop> rawStops(stops.Length());
   rawStops.SetLength(stops.Length());
   for(uint32_t i = 0; i < stops.Length(); i++) {
     rawStops[i].color = stops[i].mColor;
+    rawStops[i].color.a *= aOpacity;
     rawStops[i].offset = stopScale * (stops[i].mPosition - stopOrigin);
   }
   RefPtr<mozilla::gfx::GradientStops> gs =
     gfxGradientCache::GetOrCreateGradientStops(ctx->GetDrawTarget(),
                                                rawStops,
                                                isRepeat ? gfx::ExtendMode::REPEAT : gfx::ExtendMode::CLAMP);
   gradientPattern->SetColorStops(gs);
 
@@ -3201,16 +3207,17 @@ nsCSSRendering::PaintGradient(nsPresCont
       ctx->Rectangle(fillRect);
 
       gfxRect dirtyFillRect = fillRect.Intersect(dirtyAreaToFill);
       gfxRect fillRectRelativeToTile = dirtyFillRect - tileRect.TopLeft();
       Color edgeColor;
       if (aGradient->mShape == NS_STYLE_GRADIENT_SHAPE_LINEAR && !isRepeat &&
           RectIsBeyondLinearGradientEdge(fillRectRelativeToTile, matrix, stops,
                                          gradientStart, gradientEnd, &edgeColor)) {
+        edgeColor.a = aOpacity;
         ctx->SetColor(edgeColor);
       } else {
         ctx->SetMatrix(
           ctx->CurrentMatrix().Copy().Translate(tileRect.TopLeft()));
         ctx->SetPattern(gradientPattern);
       }
       ctx->Fill();
       ctx->SetMatrix(ctm);
@@ -3238,19 +3245,19 @@ DetermineCompositionOp(const nsCSSRender
 
     return nsCSSRendering::GetGFXCompositeMode(layer.mComposite);
   }
 
   return nsCSSRendering::GetGFXBlendMode(layer.mBlendMode);
 }
 
 DrawResult
-nsCSSRendering::PaintBackgroundWithSC(const PaintBGParams& aParams,
-                                      nsStyleContext *aBackgroundSC,
-                                      const nsStyleBorder& aBorder)
+nsCSSRendering::PaintStyleImageLayerWithSC(const PaintBGParams& aParams,
+                                           nsStyleContext *aBackgroundSC,
+                                           const nsStyleBorder& aBorder)
 {
   NS_PRECONDITION(aParams.frame,
                   "Frame is expected to be provided to PaintBackground");
 
   // If we're drawing all layers, aCompositonOp is ignored, so make sure that
   // it was left at its default value.
   MOZ_ASSERT_IF(aParams.layer == -1,
                 aParams.compositionOp == CompositionOp::OP_OVER);
@@ -3443,22 +3450,22 @@ nsCSSRendering::PaintBackgroundWithSC(co
           if (co != CompositionOp::OP_OVER) {
             NS_ASSERTION(ctx->CurrentOp() == CompositionOp::OP_OVER,
                          "It is assumed the initial op is OP_OVER, when it is "
                          "restored later");
             ctx->SetOp(co);
           }
 
           result &=
-            state.mImageRenderer.DrawBackground(&aParams.presCtx,
-                                                aParams.renderingCtx,
-                                                state.mDestArea, state.mFillArea,
-                                                state.mAnchor + paintBorderArea.TopLeft(),
-                                                clipState.mDirtyRect,
-                                                state.mRepeatSize);
+            state.mImageRenderer.DrawLayer(&aParams.presCtx,
+                                           aParams.renderingCtx,
+                                           state.mDestArea, state.mFillArea,
+                                           state.mAnchor + paintBorderArea.TopLeft(),
+                                           clipState.mDirtyRect,
+                                           state.mRepeatSize, aParams.opacity);
 
           if (co != CompositionOp::OP_OVER) {
             ctx->SetOp(CompositionOp::OP_OVER);
           }
         }
       }
     }
   }
@@ -5640,17 +5647,18 @@ RGBALuminanceOperation(uint8_t *aData,
 DrawResult
 nsImageRenderer::Draw(nsPresContext*       aPresContext,
                       nsRenderingContext&  aRenderingContext,
                       const nsRect&        aDirtyRect,
                       const nsRect&        aDest,
                       const nsRect&        aFill,
                       const nsPoint&       aAnchor,
                       const nsSize&        aRepeatSize,
-                      const CSSIntRect&    aSrc)
+                      const CSSIntRect&    aSrc,
+                      float                aOpacity)
 {
   if (!IsReady()) {
     NS_NOTREACHED("Ensure PrepareImage() has returned true before calling me");
     return DrawResult::TEMPORARY_ERROR;
   }
   if (aDest.IsEmpty() || aFill.IsEmpty() ||
       mSize.width <= 0 || mSize.height <= 0) {
     return DrawResult::SUCCESS;
@@ -5691,41 +5699,43 @@ nsImageRenderer::Draw(nsPresContext*    
       result =
         nsLayoutUtils::DrawBackgroundImage(*ctx,
                                            aPresContext,
                                            mImageContainer, imageSize,
                                            samplingFilter,
                                            aDest, aFill, aRepeatSize,
                                            aAnchor, aDirtyRect,
                                            ConvertImageRendererToDrawFlags(mFlags),
-                                           mExtendMode);
+                                           mExtendMode, aOpacity);
       break;
     }
     case eStyleImageType_Gradient:
     {
       nsCSSRendering::PaintGradient(aPresContext, aRenderingContext,
                                     mGradientData, aDirtyRect,
-                                    aDest, aFill, aRepeatSize, aSrc, mSize);
+                                    aDest, aFill, aRepeatSize, aSrc, mSize,
+                                    aOpacity);
       break;
     }
     case eStyleImageType_Element:
     {
       RefPtr<gfxDrawable> drawable = DrawableForElement(aDest,
                                                           aRenderingContext);
       if (!drawable) {
         NS_WARNING("Could not create drawable for element");
         return DrawResult::TEMPORARY_ERROR;
       }
 
       nsCOMPtr<imgIContainer> image(ImageOps::CreateFromDrawable(drawable));
       result =
         nsLayoutUtils::DrawImage(*ctx,
                                  aPresContext, image,
                                  samplingFilter, aDest, aFill, aAnchor, aDirtyRect,
-                                 ConvertImageRendererToDrawFlags(mFlags));
+                                 ConvertImageRendererToDrawFlags(mFlags),
+                                 aOpacity);
       break;
     }
     case eStyleImageType_Null:
     default:
       break;
   }
 
   if (!tmpDTRect.IsEmpty()) {
@@ -5781,38 +5791,40 @@ nsImageRenderer::DrawableForElement(cons
   NS_ASSERTION(mImageElementSurface.GetSourceSurface(), "Surface should be ready.");
   RefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(
                                 mImageElementSurface.GetSourceSurface().get(),
                                 mImageElementSurface.mSize);
   return drawable.forget();
 }
 
 DrawResult
-nsImageRenderer::DrawBackground(nsPresContext*       aPresContext,
-                                nsRenderingContext&  aRenderingContext,
-                                const nsRect&        aDest,
-                                const nsRect&        aFill,
-                                const nsPoint&       aAnchor,
-                                const nsRect&        aDirty,
-                                const nsSize&        aRepeatSize)
+nsImageRenderer::DrawLayer(nsPresContext*       aPresContext,
+                           nsRenderingContext&  aRenderingContext,
+                           const nsRect&        aDest,
+                           const nsRect&        aFill,
+                           const nsPoint&       aAnchor,
+                           const nsRect&        aDirty,
+                           const nsSize&        aRepeatSize,
+                           float                aOpacity)
 {
   if (!IsReady()) {
     NS_NOTREACHED("Ensure PrepareImage() has returned true before calling me");
     return DrawResult::TEMPORARY_ERROR;
   }
   if (aDest.IsEmpty() || aFill.IsEmpty() ||
       mSize.width <= 0 || mSize.height <= 0) {
     return DrawResult::SUCCESS;
   }
 
   return Draw(aPresContext, aRenderingContext,
               aDirty, aDest, aFill, aAnchor, aRepeatSize,
               CSSIntRect(0, 0,
                          nsPresContext::AppUnitsToIntCSSPixels(mSize.width),
-                         nsPresContext::AppUnitsToIntCSSPixels(mSize.height)));
+                         nsPresContext::AppUnitsToIntCSSPixels(mSize.height)),
+              aOpacity);
 }
 
 /**
  * Compute the size and position of the master copy of the image. I.e., a single
  * tile used to fill the dest rect.
  * aFill The destination rect to be filled
  * aHFill and aVFill are the repeat patterns for the component -
  * NS_STYLE_BORDER_IMAGE_REPEAT_* - i.e., how a tiling unit is used to fill aFill
@@ -5995,17 +6007,17 @@ nsImageRenderer::DrawBorderImageComponen
     nsRect tile = ComputeTile(fillRect, aHFill, aVFill, aUnitSize, repeatSize);
     CSSIntSize imageSize(srcRect.width, srcRect.height);
     return nsLayoutUtils::DrawBackgroundImage(*aRenderingContext.ThebesContext(),
                                               aPresContext,
                                               subImage, imageSize, samplingFilter,
                                               tile, fillRect, repeatSize,
                                               tile.TopLeft(), aDirtyRect,
                                               drawFlags,
-                                              ExtendMode::CLAMP);
+                                              ExtendMode::CLAMP, 1.0);
   }
 
   nsSize repeatSize(aFill.Size());
   nsRect fillRect(aFill);
   nsRect destTile = RequiresScaling(fillRect, aHFill, aVFill, aUnitSize)
                   ? ComputeTile(fillRect, aHFill, aVFill, aUnitSize, repeatSize)
                   : fillRect;
   return Draw(aPresContext, aRenderingContext, aDirtyRect, destTile,
--- a/layout/painting/nsCSSRendering.h
+++ b/layout/painting/nsCSSRendering.h
@@ -142,17 +142,17 @@ public:
    */
   bool PrepareImage();
 
   /**
    * The three Compute*Size functions correspond to the sizing algorthms and
    * definitions from the CSS Image Values and Replaced Content spec. See
    * http://dev.w3.org/csswg/css-images-3/#sizing .
    */
-   
+
   /**
    * Compute the intrinsic size of the image as defined in the CSS Image Values
    * spec. The intrinsic size is the unscaled size which the image would ideally
    * like to be in app units.
    */
   mozilla::CSSSizeOrRatio ComputeIntrinsicSize();
 
   /**
@@ -204,27 +204,28 @@ public:
    * Set this image's preferred size. This will be its intrinsic size where
    * specified and the default size where it is not. Used as the unscaled size
    * when rendering the image.
    */
   void SetPreferredSize(const mozilla::CSSSizeOrRatio& aIntrinsicSize,
                         const nsSize& aDefaultSize);
 
   /**
-   * Draws the image to the target rendering context using background-specific
-   * arguments.
+   * Draws the image to the target rendering context using
+   * {background|mask}-specific arguments.
    * @see nsLayoutUtils::DrawImage() for parameters.
    */
-  DrawResult DrawBackground(nsPresContext*       aPresContext,
-                            nsRenderingContext&  aRenderingContext,
-                            const nsRect&        aDest,
-                            const nsRect&        aFill,
-                            const nsPoint&       aAnchor,
-                            const nsRect&        aDirty,
-                            const nsSize&        aRepeatSize);
+  DrawResult DrawLayer(nsPresContext*       aPresContext,
+                       nsRenderingContext&  aRenderingContext,
+                       const nsRect&        aDest,
+                       const nsRect&        aFill,
+                       const nsPoint&       aAnchor,
+                       const nsRect&        aDirty,
+                       const nsSize&        aRepeatSize,
+                       float                aOpacity);
 
   /**
    * Draw the image to a single component of a border-image style rendering.
    * aFill The destination rect to be drawn into
    * aSrc is the part of the image to be rendered into a tile (aUnitSize in
    * aFill), if aSrc and the dest tile are different sizes, the image will be
    * scaled to map aSrc onto the dest tile.
    * aHFill and aVFill are the repeat patterns for the component -
@@ -276,17 +277,18 @@ private:
    */
   DrawResult Draw(nsPresContext*       aPresContext,
                   nsRenderingContext&  aRenderingContext,
                   const nsRect&        aDirtyRect,
                   const nsRect&        aDest,
                   const nsRect&        aFill,
                   const nsPoint&       aAnchor,
                   const nsSize&        aRepeatSize,
-                  const mozilla::CSSIntRect& aSrc);
+                  const mozilla::CSSIntRect& aSrc,
+                  float                aOpacity = 1.0);
 
   /**
    * Helper method for creating a gfxDrawable from mPaintServerFrame or
    * mImageElementSurface.
    * Requires mType is eStyleImageType_Element.
    * Returns null if we cannot create the drawable.
    */
   already_AddRefed<gfxDrawable> DrawableForElement(const nsRect& aImageRect,
@@ -472,17 +474,18 @@ struct nsCSSRendering {
   static void PaintGradient(nsPresContext* aPresContext,
                             nsRenderingContext& aRenderingContext,
                             nsStyleGradient* aGradient,
                             const nsRect& aDirtyRect,
                             const nsRect& aDest,
                             const nsRect& aFill,
                             const nsSize& aRepeatSize,
                             const mozilla::CSSIntRect& aSrc,
-                            const nsSize& aIntrinsiceSize);
+                            const nsSize& aIntrinsiceSize,
+                            float aOpacity = 1.0);
 
   /**
    * Find the frame whose background style should be used to draw the
    * canvas background. aForFrame must be the frame for the root element
    * whose background style should be used. This function will return
    * aForFrame unless the <body> background should be propagated, in
    * which case we return the frame associated with the <body>'s background.
    */
@@ -629,69 +632,75 @@ struct nsCSSRendering {
     nsRect borderArea;
     nsIFrame* frame;
     uint32_t paintFlags;
     nsRect* bgClipRect = nullptr;
     int32_t layer;                  // -1 means painting all layers; other
                                     // value means painting one specific
                                     // layer only.
     CompositionOp compositionOp;
+    float opacity;
 
     static PaintBGParams ForAllLayers(nsPresContext& aPresCtx,
                                       nsRenderingContext& aRenderingCtx,
                                       const nsRect& aDirtyRect,
                                       const nsRect& aBorderArea,
                                       nsIFrame *aFrame,
-                                      uint32_t aPaintFlags);
+                                      uint32_t aPaintFlags,
+                                      float aOpacity = 1.0);
     static PaintBGParams ForSingleLayer(nsPresContext& aPresCtx,
                                         nsRenderingContext& aRenderingCtx,
                                         const nsRect& aDirtyRect,
                                         const nsRect& aBorderArea,
                                         nsIFrame *aFrame,
                                         uint32_t aPaintFlags,
                                         int32_t aLayer,
-                                        CompositionOp aCompositionOp  = CompositionOp::OP_OVER);
+                                        CompositionOp aCompositionOp  = CompositionOp::OP_OVER,
+                                        float aOpacity = 1.0);
 
   private:
     PaintBGParams(nsPresContext& aPresCtx,
                   nsRenderingContext& aRenderingCtx,
                   const nsRect& aDirtyRect,
                   const nsRect& aBorderArea,
                   nsIFrame* aFrame,
                   uint32_t aPaintFlags,
                   int32_t aLayer,
-                  CompositionOp aCompositionOp)
+                  CompositionOp aCompositionOp,
+                  float aOpacity)
      : presCtx(aPresCtx),
        renderingCtx(aRenderingCtx),
        dirtyRect(aDirtyRect),
        borderArea(aBorderArea),
        frame(aFrame),
        paintFlags(aPaintFlags),
        layer(aLayer),
-       compositionOp(aCompositionOp) {}
+       compositionOp(aCompositionOp),
+       opacity(aOpacity) {}
   };
 
-  static DrawResult PaintBackground(const PaintBGParams& aParams);
+  static DrawResult PaintStyleImageLayer(const PaintBGParams& aParams);
 
 
   /**
-   * Same as |PaintBackground|, except using the provided style structs.
+   * Same as |PaintStyleImageLayer|, except using the provided style structs.
    * This short-circuits the code that ensures that the root element's
-   * background is drawn on the canvas.
-   * The aLayer parameter allows you to paint a single layer of the background.
+   * {background|mask} is drawn on the canvas.
+   * The aLayer parameter allows you to paint a single layer of the
+   * {background|mask}.
    * The default value for aLayer, -1, means that all layers will be painted.
    * The background color will only be painted if the back-most layer is also
-   * being painted.
+   * being painted and (aParams.paintFlags & PAINTBG_MASK_IMAGE) is false.
    * aCompositionOp is only respected if a single layer is specified (aLayer != -1).
    * If all layers are painted, the image layer's blend mode (or the mask
    * layer's composition mode) will be used.
    */
-  static DrawResult PaintBackgroundWithSC(const PaintBGParams& aParams,
-                                          nsStyleContext *mBackgroundSC,
-                                          const nsStyleBorder& aBorder);
+  static DrawResult PaintStyleImageLayerWithSC(const PaintBGParams& aParams,
+                                               nsStyleContext *mBackgroundSC,
+                                               const nsStyleBorder& aBorder);
 
   /**
    * Returns the rectangle covered by the given background layer image, taking
    * into account background positioning, sizing, and repetition, but not
    * clipping.
    */
   static nsRect GetBackgroundLayerRect(nsPresContext* aPresContext,
                                        nsIFrame* aForFrame,
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -3244,17 +3244,17 @@ nsDisplayBackgroundImage::PaintInternal(
   nsCSSRendering::PaintBGParams params =
     nsCSSRendering::PaintBGParams::ForSingleLayer(*mFrame->PresContext(),
                                                   *aCtx,
                                                   aBounds, mBackgroundRect,
                                                   mFrame, flags, mLayer,
                                                   CompositionOp::OP_OVER);
   params.bgClipRect = aClipRect;
   image::DrawResult result =
-    nsCSSRendering::PaintBackground(params);
+    nsCSSRendering::PaintStyleImageLayer(params);
 
   if (clip == StyleGeometryBox::Text) {
     ctx->PopGroupAndBlend();
   }
 
   nsDisplayBackgroundGeometry::UpdateDrawResult(this, result);
 }
 
@@ -7346,21 +7346,19 @@ bool nsDisplayMask::ShouldPaintOnMaskLay
 {
   if (!aManager->IsCompositingCheap()) {
     return false;
   }
 
   nsSVGUtils::MaskUsage maskUsage;
   nsSVGUtils::DetermineMaskUsage(mFrame, mHandleOpacity, maskUsage);
 
-  // XXX Bug 1323912. nsSVGIntegrationUtils::PaintMask can not handle opacity
-  // correctly. Turn it off before bug fixed.
   // XXX Temporary disable paint clip-path onto mask before figure out
   // performance regression(bug 1325550).
-  if (maskUsage.opacity != 1.0 || maskUsage.shouldApplyClipPath) {
+  if (maskUsage.shouldApplyClipPath) {
     return false;
   }
 
   if (!nsSVGIntegrationUtils::IsMaskResourceReady(mFrame)) {
     return false;
   }
 
   if (gfxPrefs::DrawMaskLayer()) {
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/masking/mask-opacity-1d.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>CSS Masking: mask-image: mask with opacity</title>
+    <link rel="author" title="CJ Ku" href="mailto:cku@mozilla.com">
+    <link rel="author" title="Mozilla" href="https://www.mozilla.org">
+    <link rel="help" href="https://www.w3.org/TR/css-masking-1/#the-mask-image">
+    <link rel="match" href="mask-opacity-1-ref.html">
+    <meta name="assert" content="Test checks whether apply opacity to masked element correctly or not.">
+    <svg height="0">
+      <mask id="myMask" x="0" y="0" width="100" height="100" >
+        <rect x="0" y="50" width="100" height="50" style="stroke:none; fill: #ffffff"/>
+      </mask>
+    </svg>
+    <style type="text/css">
+      div {
+        position: absolute;
+        left: 10px;
+        top: 10px;
+        background-color: rgb(0,0,255);
+        width: 100px;
+        height: 100px;
+        mask-image: url(#myMask), url(#myMask);
+        opacity: 0.5;
+      }
+    </style>
+  </head>
+  <body>
+    <div></div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/masking/mask-opacity-1e.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>CSS Masking: mask-image: mask with opacity</title>
+    <link rel="author" title="CJ Ku" href="mailto:cku@mozilla.com">
+    <link rel="author" title="Mozilla" href="https://www.mozilla.org">
+    <link rel="help" href="https://www.w3.org/TR/css-masking-1/#the-mask-image">
+    <link rel="match" href="mask-opacity-1-ref.html">
+    <meta name="assert" content="Test checks whether apply opacity to masked element correctly or not.">
+    <style type="text/css">
+      #outter {
+        position: absolute;
+        left: 10px;
+        top: 10px;
+        width: 100px;
+        height: 100px;
+        mask-image: url(support/blue-100x50-transparent-100x50.png),
+                    url(support/blue-100x50-transparent-100x50.png);
+        opacity: 0.5;
+      }
+
+      #inner {
+        width: 100px;
+        height: 100px;
+        box-sizing:border-box;
+        background-color: blue;
+        border: 1px solid transparent;
+        will-change: transform;
+      }
+    </style>
+  </head>
+  <body>
+    <div id="outter"><div id="inner"></div></div>
+  </body>
+</html>
--- a/layout/reftests/w3c-css/submitted/masking/reftest.list
+++ b/layout/reftests/w3c-css/submitted/masking/reftest.list
@@ -106,10 +106,12 @@ fuzzy-if(winWidget,9,98) == clip-path-ge
 == clip-path-localRef-1.html clip-path-localRef-1-ref.html
 
 default-preferences
 
 # mask with opacity test cases
 fuzzy(1,5000) == mask-opacity-1a.html mask-opacity-1-ref.html
 fuzzy(1,5000) == mask-opacity-1b.html mask-opacity-1-ref.html
 fuzzy(1,5000) == mask-opacity-1c.html mask-opacity-1-ref.html
+fuzzy(1,5000) == mask-opacity-1d.html mask-opacity-1-ref.html
+fuzzy(1,5000) == mask-opacity-1e.html mask-opacity-1-ref.html
 
 == clip-path-mix-blend-mode-1.html clip-path-mix-blend-mode-1-ref.html
\ No newline at end of file
--- a/layout/style/StyleAnimationValue.cpp
+++ b/layout/style/StyleAnimationValue.cpp
@@ -1967,22 +1967,20 @@ AddCSSValuePercentNumber(const uint32_t 
   // aCoeff2 is 0, then we'll return the value halfway between 1 and
   // aValue1, rather than the value halfway between 0 and aValue1.
   // Note that we do something similar in AddTransformScale().
   float result = (n1 - aInitialVal) * aCoeff1 + (n2 - aInitialVal) * aCoeff2;
   aResult.SetFloatValue(RestrictValue(aValueRestrictions, result + aInitialVal),
                         eCSSUnit_Number);
 }
 
-// Multiplies |aValue| color by |aDilutionRation|.
+// Multiplies |aValue| color by |aDilutionRatio|.
 static nscolor
 DiluteColor(const RGBAColorData& aValue, double aDilutionRatio)
 {
-  MOZ_ASSERT(aDilutionRatio >= 0.0 && aDilutionRatio <= 1.0,
-             "Dilution ratio should be in [0, 1]");
   float resultA = aValue.mA * aDilutionRatio;
   return resultA <= 0.0 ? NS_RGBA(0, 0, 0, 0)
                         : aValue.WithAlpha(resultA).ToColor();
 }
 
 // Clamped AddWeightedColors.
 static nscolor
 AddWeightedColorsAndClamp(double aCoeff1, const RGBAColorData& aValue1,
new file mode 100644
--- /dev/null
+++ b/layout/style/crashtests/1328535-1.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<html>
+<style>
+@keyframes anim {
+  from { box-shadow: none; }
+  to { box-shadow: rgba(120, 120, 120, 0.5) 10px 10px 10px 0px; }
+}
+#target {
+  width: 100px; height: 100px;
+  /*
+   * Use negative delay to shift to the point that the cubic-bezier function
+   * produces a value out of range of [0, 1].
+   */
+  animation: anim 1s -0.02s cubic-bezier(0, -0.5, 0, 0) paused;
+}
+</style>
+<div id="target"></div>
--- a/layout/style/crashtests/crashtests.list
+++ b/layout/style/crashtests/crashtests.list
@@ -161,8 +161,9 @@ asserts-if(stylo,2) pref(dom.animations-
 asserts-if(stylo,1) pref(dom.animations-api.core.enabled,true) load 1290994-3.html # bug 1324690
 load 1290994-4.html
 load 1314531.html
 load 1315889-1.html
 load 1315894-1.html
 skip-if(stylo) load 1319072-1.html # bug 1323733
 HTTP load 1320423-1.html
 asserts-if(stylo,5-9) load 1321357-1.html # bug 1324669
+load 1328535-1.html
new file mode 100644
--- /dev/null
+++ b/layout/style/nsCSSVisitedDependentPropList.h
@@ -0,0 +1,35 @@
+/* -*- 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/. */
+
+/* a list of style struct's member variables which can be visited-dependent */
+
+/* This file contains the list of all style structs and fields that can
+ * be visited-dependent. Each entry is defined as a STYLE_STRUCT macro
+ * with the following parameters:
+ * - 'name_' the name of the style struct
+ * - 'fields_' the list of member variables in the style struct that can
+ *   be visited-dependent
+ *
+ * Users of this list should define a macro STYLE_STRUCT(name_, fields_)
+ * before including this file.
+ *
+ * Note that, currently, there is a restriction that all fields in a
+ * style struct must have the same type.
+ */
+
+STYLE_STRUCT(Color, (mColor))
+STYLE_STRUCT(Background, (mBackgroundColor))
+STYLE_STRUCT(Border, (mBorderTopColor,
+                      mBorderRightColor,
+                      mBorderBottomColor,
+                      mBorderLeftColor))
+STYLE_STRUCT(Outline, (mOutlineColor))
+STYLE_STRUCT(Column, (mColumnRuleColor))
+STYLE_STRUCT(Text, (mTextEmphasisColor,
+                    mWebkitTextFillColor,
+                    mWebkitTextStrokeColor))
+STYLE_STRUCT(TextReset, (mTextDecorationColor))
+STYLE_STRUCT(SVG, (mFill, mStroke))
--- a/layout/style/nsStyleContext.cpp
+++ b/layout/style/nsStyleContext.cpp
@@ -20,17 +20,16 @@
 #include "nsIStyleRule.h"
 
 #include "nsCOMPtr.h"
 #include "nsStyleSet.h"
 #include "nsIPresShell.h"
 
 #include "nsRuleNode.h"
 #include "nsStyleContext.h"
-#include "mozilla/StyleAnimationValue.h"
 #include "GeckoProfiler.h"
 #include "nsIDocument.h"
 #include "nsPrintfCString.h"
 #include "RubyUtils.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ArenaObjectID.h"
 #include "mozilla/StyleSetHandle.h"
 #include "mozilla/StyleSetHandleInlines.h"
@@ -1164,92 +1163,28 @@ nsStyleContext::CalcStyleDifferenceInter
     // Both style contexts have a style-if-visited.
     bool change = false;
 
     // NB: Calling Peek on |this|, not |thisVis|, since callers may look
     // at a struct on |this| without looking at the same struct on
     // |thisVis| (including this function if we skip one of these checks
     // due to change being true already or due to the old style context
     // not having a style-if-visited), but not the other way around.
-    if (PeekStyleColor()) {
-      if (thisVis->StyleColor()->mColor !=
-          otherVis->StyleColor()->mColor) {
-        change = true;
-      }
-    }
-
-    // NB: Calling Peek on |this|, not |thisVis| (see above).
-    if (!change && PeekStyleBackground()) {
-      if (thisVis->StyleBackground()->mBackgroundColor !=
-          otherVis->StyleBackground()->mBackgroundColor) {
-        change = true;
-      }
-    }
-
-    // NB: Calling Peek on |this|, not |thisVis| (see above).
-    if (!change && PeekStyleBorder()) {
-      const nsStyleBorder *thisVisBorder = thisVis->StyleBorder();
-      const nsStyleBorder *otherVisBorder = otherVis->StyleBorder();
-      NS_FOR_CSS_SIDES(side) {
-        if (thisVisBorder->mBorderColor[side] !=
-            otherVisBorder->mBorderColor[side]) {
-          change = true;
-          break;
-        }
-      }
-    }
-
-    // NB: Calling Peek on |this|, not |thisVis| (see above).
-    if (!change && PeekStyleOutline()) {
-      const nsStyleOutline *thisVisOutline = thisVis->StyleOutline();
-      const nsStyleOutline *otherVisOutline = otherVis->StyleOutline();
-      if (thisVisOutline->mOutlineColor != otherVisOutline->mOutlineColor) {
-        change = true;
-      }
+#define STYLE_FIELD(name_) thisVisStruct->name_ != otherVisStruct->name_ ||
+#define STYLE_STRUCT(name_, fields_)                                    \
+    if (!change && PeekStyle##name_()) {                                \
+      const nsStyle##name_* thisVisStruct = thisVis->Style##name_();    \
+      const nsStyle##name_* otherVisStruct = otherVis->Style##name_();  \
+      if (MOZ_FOR_EACH(STYLE_FIELD, (), fields_) false) {               \
+        change = true;                                                  \
+      }                                                                 \
     }
-
-    // NB: Calling Peek on |this|, not |thisVis| (see above).
-    if (!change && PeekStyleColumn()) {
-      const nsStyleColumn *thisVisColumn = thisVis->StyleColumn();
-      const nsStyleColumn *otherVisColumn = otherVis->StyleColumn();
-      if (thisVisColumn->mColumnRuleColor != otherVisColumn->mColumnRuleColor) {
-        change = true;
-      }
-    }
-
-    // NB: Calling Peek on |this|, not |thisVis| (see above).
-    if (!change && PeekStyleText()) {
-      const nsStyleText* thisVisText = thisVis->StyleText();
-      const nsStyleText* otherVisText = otherVis->StyleText();
-      if (thisVisText->mTextEmphasisColor != otherVisText->mTextEmphasisColor ||
-          thisVisText->mWebkitTextFillColor != otherVisText->mWebkitTextFillColor ||
-          thisVisText->mWebkitTextStrokeColor != otherVisText->mWebkitTextStrokeColor) {
-        change = true;
-      }
-    }
-
-    // NB: Calling Peek on |this|, not |thisVis| (see above).
-    if (!change && PeekStyleTextReset()) {
-      const nsStyleTextReset *thisVisTextReset = thisVis->StyleTextReset();
-      const nsStyleTextReset *otherVisTextReset = otherVis->StyleTextReset();
-      if (thisVisTextReset->mTextDecorationColor !=
-          otherVisTextReset->mTextDecorationColor) {
-        change = true;
-      }
-    }
-
-    // NB: Calling Peek on |this|, not |thisVis| (see above).
-    if (!change && PeekStyleSVG()) {
-      const nsStyleSVG *thisVisSVG = thisVis->StyleSVG();
-      const nsStyleSVG *otherVisSVG = otherVis->StyleSVG();
-      if (thisVisSVG->mFill != otherVisSVG->mFill ||
-          thisVisSVG->mStroke != otherVisSVG->mStroke) {
-        change = true;
-      }
-    }
+#include "nsCSSVisitedDependentPropList.h"
+#undef STYLE_STRUCT
+#undef STYLE_FIELD
 
     if (change) {
       hint |= nsChangeHint_RepaintFrame;
     }
   }
 
   if (hint & nsChangeHint_UpdateContainingBlock) {
     // If a struct returned nsChangeHint_UpdateContainingBlock, that
@@ -1447,98 +1382,73 @@ NS_NewStyleContext(nsStyleContext* aPare
 }
 
 nsIPresShell*
 nsStyleContext::Arena()
 {
   return PresContext()->PresShell();
 }
 
-static inline void
-ExtractAnimationValue(nsCSSPropertyID aProperty,
-                      nsStyleContext* aStyleContext,
-                      StyleAnimationValue& aResult)
+template<typename Func>
+static nscolor
+GetVisitedDependentColorInternal(nsStyleContext* aSc, Func aColorFunc)
 {
-  DebugOnly<bool> success =
-    StyleAnimationValue::ExtractComputedValue(aProperty, aStyleContext,
-                                              aResult);
-  MOZ_ASSERT(success,
-             "aProperty must be extractable by StyleAnimationValue");
+  nscolor colors[2];
+  colors[0] = aColorFunc(aSc);
+  if (nsStyleContext* visitedStyle = aSc->GetStyleIfVisited()) {
+    colors[1] = aColorFunc(visitedStyle);
+    return nsStyleContext::
+      CombineVisitedColors(colors, aSc->RelevantLinkVisited());
+  }
+  return colors[0];
 }
 
-static Maybe<nscolor>
-ExtractColor(nsCSSPropertyID aProperty,
-             nsStyleContext *aStyleContext)
+static nscolor
+ExtractColor(nsStyleContext* aContext, const nscolor& aColor)
 {
-  StyleAnimationValue val;
-  ExtractAnimationValue(aProperty, aStyleContext, val);
-  switch (val.GetUnit()) {
-    case StyleAnimationValue::eUnit_Color:
-      return Some(val.GetCSSValueValue()->GetColorValue());
-    case StyleAnimationValue::eUnit_CurrentColor:
-      return Some(aStyleContext->StyleColor()->mColor);
-    case StyleAnimationValue::eUnit_ComplexColor:
-      return Some(aStyleContext->StyleColor()->
-                  CalcComplexColor(val.GetStyleComplexColorValue()));
-    default:
-      return Nothing();
-  }
+  return aColor;
 }
 
 static nscolor
-ExtractColorLenient(nsCSSPropertyID aProperty,
-                    nsStyleContext *aStyleContext)
+ExtractColor(nsStyleContext* aContext, const StyleComplexColor& aColor)
+{
+  return aContext->StyleColor()->CalcComplexColor(aColor);
+}
+
+static nscolor
+ExtractColor(nsStyleContext* aContext, const nsStyleSVGPaint& aPaintServer)
 {
-  return ExtractColor(aProperty, aStyleContext).valueOr(NS_RGBA(0, 0, 0, 0));
+  return aPaintServer.Type() == eStyleSVGPaintType_Color
+    ? aPaintServer.GetColor() : NS_RGBA(0, 0, 0, 0);
 }
 
+#define STYLE_FIELD(struct_, field_) aField == &struct_::field_ ||
+#define STYLE_STRUCT(name_, fields_)                                          \
+  template<> nscolor                                                          \
+  nsStyleContext::GetVisitedDependentColor(                                   \
+    decltype(nsStyle##name_::MOZ_ARG_1 fields_) nsStyle##name_::* aField)     \
+  {                                                                           \
+    MOZ_ASSERT(MOZ_FOR_EACH(STYLE_FIELD, (nsStyle##name_,), fields_) false,   \
+               "Getting visited-dependent color for a field in nsStyle"#name_ \
+               " which is not listed in nsCSSVisitedDependentPropList.h");    \
+    return GetVisitedDependentColorInternal(this,                             \
+                                            [aField](nsStyleContext* sc) {    \
+      return ExtractColor(sc, sc->Style##name_()->*aField);                   \
+    });                                                                       \
+  }
+#include "nsCSSVisitedDependentPropList.h"
+#undef STYLE_STRUCT
+#undef STYLE_FIELD
+
 struct ColorIndexSet {
   uint8_t colorIndex, alphaIndex;
 };
 
 static const ColorIndexSet gVisitedIndices[2] = { { 0, 0 }, { 1, 0 } };
 
-nscolor
-nsStyleContext::GetVisitedDependentColor(nsCSSPropertyID aProperty)
-{
-  NS_ASSERTION(aProperty == eCSSProperty_color ||
-               aProperty == eCSSProperty_background_color ||
-               aProperty == eCSSProperty_border_top_color ||
-               aProperty == eCSSProperty_border_right_color ||
-               aProperty == eCSSProperty_border_bottom_color ||
-               aProperty == eCSSProperty_border_left_color ||
-               aProperty == eCSSProperty_outline_color ||
-               aProperty == eCSSProperty_column_rule_color ||
-               aProperty == eCSSProperty_text_decoration_color ||
-               aProperty == eCSSProperty_text_emphasis_color ||
-               aProperty == eCSSProperty__webkit_text_fill_color ||
-               aProperty == eCSSProperty__webkit_text_stroke_color ||
-               aProperty == eCSSProperty_fill ||
-               aProperty == eCSSProperty_stroke,
-               "we need to add to nsStyleContext::CalcStyleDifference");
-
-  bool isPaintProperty = aProperty == eCSSProperty_fill ||
-                         aProperty == eCSSProperty_stroke;
-
-  nscolor colors[2];
-  colors[0] = isPaintProperty ? ExtractColorLenient(aProperty, this)
-                              : ExtractColor(aProperty, this).value();
-
-  nsStyleContext *visitedStyle = this->GetStyleIfVisited();
-  if (!visitedStyle) {
-    return colors[0];
-  }
-
-  colors[1] = isPaintProperty ? ExtractColorLenient(aProperty, visitedStyle)
-                              : ExtractColor(aProperty, visitedStyle).value();
-
-  return nsStyleContext::CombineVisitedColors(colors,
-                                              this->RelevantLinkVisited());
-}
-
 /* static */ nscolor
 nsStyleContext::CombineVisitedColors(nscolor *aColors, bool aLinkIsVisited)
 {
   if (NS_GET_A(aColors[1]) == 0) {
     // If the style-if-visited is transparent, then just use the
     // unvisited style rather than using the (meaningless) color
     // components of the visited style along with a potentially
     // non-transparent alpha value.
--- a/layout/style/nsStyleContext.h
+++ b/layout/style/nsStyleContext.h
@@ -6,16 +6,17 @@
 /* the interface (to internal code) for retrieving computed style data */
 
 #ifndef _nsStyleContext_h_
 #define _nsStyleContext_h_
 
 #include "mozilla/Assertions.h"
 #include "mozilla/RestyleLogging.h"
 #include "mozilla/StyleContextSource.h"
+#include "mozilla/StyleComplexColor.h"
 #include "nsCSSAnonBoxes.h"
 #include "nsStyleSet.h"
 
 class nsIAtom;
 class nsPresContext;
 
 namespace mozilla {
 enum class CSSPseudoElementType : uint8_t;
@@ -404,21 +405,22 @@ private:
                                            uint32_t* aEqualStructs,
                                            uint32_t* aSamePointerStructs);
 
 public:
   /**
    * Get a color that depends on link-visitedness using this and
    * this->GetStyleIfVisited().
    *
-   * aProperty must be a color-valued property that StyleAnimationValue
-   * knows how to extract.  It must also be a property that we know to
-   * do change handling for in nsStyleContext::CalcDifference.
+   * @param aField A pointer to a member variable in a style struct.
+   *               The member variable and its style struct must have
+   *               been listed in nsCSSVisitedDependentPropList.h.
    */
-  nscolor GetVisitedDependentColor(nsCSSPropertyID aProperty);
+  template<typename T, typename S>
+  nscolor GetVisitedDependentColor(T S::* aField);
 
   /**
    * aColors should be a two element array of nscolor in which the first
    * color is the unvisited color and the second is the visited color.
    *
    * Combine the R, G, and B components of whichever of aColors should
    * be used based on aLinkIsVisited with the A component of aColors[0].
    */
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1405,16 +1405,32 @@ public:
       mozilla::StyleComplexColor mBorderTopColor;
       mozilla::StyleComplexColor mBorderRightColor;
       mozilla::StyleComplexColor mBorderBottomColor;
       mozilla::StyleComplexColor mBorderLeftColor;
     };
     mozilla::StyleComplexColor mBorderColor[4];
   };
 
+  static mozilla::StyleComplexColor nsStyleBorder::*
+  BorderColorFieldFor(mozilla::Side aSide) {
+    switch (aSide) {
+      case mozilla::eSideTop:
+        return &nsStyleBorder::mBorderTopColor;
+      case mozilla::eSideRight:
+        return &nsStyleBorder::mBorderRightColor;
+      case mozilla::eSideBottom:
+        return &nsStyleBorder::mBorderBottomColor;
+      case mozilla::eSideLeft:
+        return &nsStyleBorder::mBorderLeftColor;
+    }
+    MOZ_ASSERT_UNREACHABLE("Unknown side");
+    return nullptr;
+  }
+
 protected:
   // mComputedBorder holds the CSS2.1 computed border-width values.
   // In particular, these widths take into account the border-style
   // for the relevant side, and the values are rounded to the nearest
   // device pixel (which is not part of the definition of computed
   // values). The presence or absence of a border-image does not
   // affect border-width values.
   nsMargin      mComputedBorder;
--- a/layout/style/test/test_transitions_per_property.html
+++ b/layout/style/test/test_transitions_per_property.html
@@ -1576,16 +1576,17 @@ function test_filter_transition(prop) {
     var actual = cs.getPropertyValue(prop);
     ok(filter_function_list_equals(actual, test.expected),
                                    "Filter property is " + actual + " expected values of " +
                                    test.expected);
   }
 }
 
 function test_shadow_transition(prop) {
+  var origTimingFunc = div.style.getPropertyValue("transition-timing-function");
   var spreadStr = (prop == "box-shadow") ? " 0px" : "";
 
   div.style.setProperty("transition-property", "none", "");
   div.style.setProperty(prop, "none", "");
   is(cs.getPropertyValue(prop), "none",
      "shadow-valued property " + prop + ": computed value before transition");
   div.style.setProperty("transition-property", prop, "");
   div.style.setProperty(prop, "4px 8px 3px red", "");
@@ -1663,17 +1664,34 @@ function test_shadow_transition(prop) {
           "shadow-valued property " + prop + " (x): clamping of negatives");
     isnot(vals[4], "0px",
           "shadow-valued property " + prop + " (y): clamping of negatives");
     is(vals[5], "0px",
        "shadow-valued property " + prop + " (radius): clamping of negatives");
     isnot(vals[6], "0px",
           "shadow-valued property " + prop + " (spread): clamping of negatives");
   }
-  div.style.setProperty("transition-timing-function", "linear", "");
+
+  // A test case that timing function produces values greater than 1.0.
+  div.style.setProperty("transition-timing-function",
+                        // This function produces 1.2989961788069297 at 25%.
+                        "cubic-bezier(0, 1.5, 0, 1.5)", "");
+  div.style.setProperty("transition-property", "none", "");
+  div.style.setProperty(prop, "none", "");
+  is(cs.getPropertyValue(prop), "none",
+     "shadow-valued property " + prop + ": computed value before transition");
+  div.style.setProperty("transition-property", prop, "");
+  div.style.setProperty(prop, "0px 0px 0px rgba(100, 100, 100, 0.5)", "");
+  // The alpha value, 0.5 * 1.2989961788069297 * 255, is clamped to 166 and then
+  // converted to 0.65.
+  is(cs.getPropertyValue(prop), "rgba(100, 100, 100, 0.65) 0px 0px 0px" + spreadStr,
+     "shadow-valued property " + prop + ": interpolation of shadows with " +
+     "timing function which produces values greater than 1.0");
+
+  div.style.setProperty("transition-timing-function", origTimingFunc, "");
 }
 
 function test_dasharray_transition(prop) {
   div.style.setProperty("transition-property", "none", "");
   div.style.setProperty(prop, "3", "");
   is(cs.getPropertyValue(prop), "3",
      "dasharray-valued property " + prop +
      ": computed value before transition");
--- a/layout/svg/nsSVGIntegrationUtils.cpp
+++ b/layout/svg/nsSVGIntegrationUtils.cpp
@@ -415,46 +415,31 @@ public:
   }
 
 private:
   nsDisplayListBuilder* mBuilder;
   LayerManager* mLayerManager;
   nsPoint mOffset;
 };
 
-/**
- * Returns true if any of the masks is an image mask (and not an SVG mask).
- */
-static bool
-HasNonSVGMask(const nsTArray<nsSVGMaskFrame*>& aMaskFrames)
-{
-  for (size_t i = 0; i < aMaskFrames.Length() ; i++) {
-    nsSVGMaskFrame *maskFrame = aMaskFrames[i];
-    if (!maskFrame) {
-      return true;
-    }
-  }
-
-  return false;
-}
-
 typedef nsSVGIntegrationUtils::PaintFramesParams PaintFramesParams;
 
 /**
  * Paint css-positioned-mask onto a given target(aMaskDT).
  */
 static DrawResult
 PaintMaskSurface(const PaintFramesParams& aParams,
                  DrawTarget* aMaskDT, float aOpacity, nsStyleContext* aSC,
                  const nsTArray<nsSVGMaskFrame*>& aMaskFrames,
                  const gfxMatrix& aMaskSurfaceMatrix,
                  const nsPoint& aOffsetToUserSpace)
 {
   MOZ_ASSERT(aMaskFrames.Length() > 0);
   MOZ_ASSERT(aMaskDT->GetFormat() == SurfaceFormat::A8);
+  MOZ_ASSERT(aOpacity == 1.0 || aMaskFrames.Length() == 1);
 
   const nsStyleSVGReset *svgReset = aSC->StyleSVGReset();
   gfxMatrix cssPxToDevPxMatrix =
     nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aParams.frame);
 
   nsPresContext* presContext = aParams.frame->PresContext();
   gfxPoint devPixelOffsetToUserSpace =
     nsLayoutUtils::PointToGfxPoint(aOffsetToUserSpace,
@@ -504,20 +489,21 @@ PaintMaskSurface(const PaintFramesParams
       nsRenderingContext rc(maskContext);
       nsCSSRendering::PaintBGParams  params =
         nsCSSRendering::PaintBGParams::ForSingleLayer(*presContext,
                                                       rc, aParams.dirtyRect,
                                                       aParams.borderArea,
                                                       aParams.frame,
                                                       aParams.builder->GetBackgroundPaintFlags() |
                                                       nsCSSRendering::PAINTBG_MASK_IMAGE,
-                                                      i, compositionOp);
+                                                      i, compositionOp,
+                                                      aOpacity);
 
       result =
-        nsCSSRendering::PaintBackgroundWithSC(params, aSC,
+        nsCSSRendering::PaintStyleImageLayerWithSC(params, aSC,
                                               *aParams.frame->StyleBorder());
       if (result != DrawResult::SUCCESS) {
         return result;
       }
     }
   }
 
   return DrawResult::SUCCESS;
@@ -575,20 +561,21 @@ CreateAndPaintMaskSurface(const PaintFra
   RefPtr<DrawTarget> maskDT =
       ctx.GetDrawTarget()->CreateSimilarDrawTarget(maskSurfaceRect.Size(),
                                                    SurfaceFormat::A8);
   if (!maskDT || !maskDT->IsValid()) {
     paintResult.result = DrawResult::TEMPORARY_ERROR;
     return paintResult;
   }
 
-  // Set aAppliedOpacity as true only if all mask layers are svg mask.
-  // In this case, we will apply opacity into the final mask surface, so the
-  // caller does not need to apply it again.
-  paintResult.opacityApplied = !HasNonSVGMask(aMaskFrames);
+  // We can paint mask along with opacity only if
+  // 1. There is only one mask, or
+  // 2. No overlap among masks.
+  // Collision detect in #2 is not that trivial, we only accept #1 here.
+  paintResult.opacityApplied = (aMaskFrames.Length() == 1);
 
   // Set context's matrix on maskContext, offset by the maskSurfaceRect's
   // position. This makes sure that we combine the masks in device space.
   gfxMatrix maskSurfaceMatrix =
     ctx.CurrentMatrix() * gfxMatrix::Translation(-aParams.maskRect.TopLeft());
 
   paintResult.result = PaintMaskSurface(aParams, maskDT,
                                         paintResult.opacityApplied
@@ -733,32 +720,47 @@ nsSVGIntegrationUtils::IsMaskResourceRea
       return false;
     }
   }
 
   // Either all mask resources are ready, or no mask resource is needed.
   return true;
 }
 
+class AutoPopGroup
+{
+public:
+  AutoPopGroup() : mContext(nullptr) { }
+
+  ~AutoPopGroup() {
+    if (mContext) {
+      mContext->PopGroupAndBlend();
+    }
+  }
+
+  void SetContext(gfxContext* aContext) {
+    mContext = aContext;
+  }
+
+private:
+  gfxContext* mContext;
+};
+
 DrawResult
 nsSVGIntegrationUtils::PaintMask(const PaintFramesParams& aParams)
 {
   nsSVGUtils::MaskUsage maskUsage;
   nsSVGUtils::DetermineMaskUsage(aParams.frame, aParams.handleOpacity,
                                  maskUsage);
 
   nsIFrame* frame = aParams.frame;
   if (!ValidateSVGFrame(frame)) {
     return DrawResult::SUCCESS;
   }
 
-  // XXX Bug 1323912.
-  MOZ_ASSERT(maskUsage.opacity == 1.0,
-             "nsSVGIntegrationUtils::PaintMask can not handle opacity now.");
-
   gfxContext& ctx = aParams.ctx;
   nsIFrame* firstFrame =
     nsLayoutUtils::FirstContinuationOrIBSplitSibling(frame);
   nsSVGEffects::EffectProperties effectProperties =
     nsSVGEffects::GetEffectProperties(firstFrame);
 
   DrawResult result = DrawResult::SUCCESS;
   RefPtr<DrawTarget> maskTarget = ctx.GetDrawTarget();
@@ -770,16 +772,25 @@ nsSVGIntegrationUtils::PaintMask(const P
     //
     // Create one extra draw target for drawing positioned mask, so that we do
     // not have to copy the content of maskTarget before painting
     // clip-path into it.
     maskTarget = maskTarget->CreateSimilarDrawTarget(maskTarget->GetSize(),
                                                      SurfaceFormat::A8);
   }
 
+  nsTArray<nsSVGMaskFrame *> maskFrames = effectProperties.GetMaskFrames();
+  AutoPopGroup autoPop;
+  bool shouldPushOpacity = (maskUsage.opacity != 1.0) &&
+                           (maskFrames.Length() != 1);
+  if (shouldPushOpacity) {
+    ctx.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, maskUsage.opacity);
+    autoPop.SetContext(&ctx);
+  }
+
   gfxContextMatrixAutoSaveRestore matSR;
   nsPoint offsetToBoundingBox;
   nsPoint offsetToUserSpace;
 
   // Paint clip-path-basic-shape onto ctx
   gfxContextAutoSaveRestore basicShapeSR;
   if (maskUsage.shouldApplyBasicShape) {
     matSR.SetContext(&ctx);
@@ -801,19 +812,18 @@ nsSVGIntegrationUtils::PaintMask(const P
 
   // Paint mask onto ctx.
   if (maskUsage.shouldGenerateMaskLayer) {
     matSR.Restore();
     matSR.SetContext(&ctx);
 
     SetupContextMatrix(frame, aParams, offsetToBoundingBox,
                        offsetToUserSpace);
-    nsTArray<nsSVGMaskFrame *> maskFrames = effectProperties.GetMaskFrames();
-
-    result = PaintMaskSurface(aParams, maskTarget, 1.0,
+    result = PaintMaskSurface(aParams, maskTarget,
+                              shouldPushOpacity ?  1.0 : maskUsage.opacity,
                               firstFrame->StyleContext(), maskFrames,
                               ctx.CurrentMatrix(), offsetToUserSpace);
     if (result != DrawResult::SUCCESS) {
       return result;
     }
   }
 
   // Paint clip-path onto ctx.
--- a/layout/tables/nsTableCellFrame.cpp
+++ b/layout/tables/nsTableCellFrame.cpp
@@ -371,17 +371,17 @@ nsTableCellFrame::PaintBackground(nsRend
                                   uint32_t             aFlags)
 {
   nsRect rect(aPt, GetSize());
   nsCSSRendering::PaintBGParams params =
     nsCSSRendering::PaintBGParams::ForAllLayers(*PresContext(),
                                                 aRenderingContext,
                                                 aDirtyRect, rect,
                                                 this, aFlags);
-  return nsCSSRendering::PaintBackground(params);
+  return nsCSSRendering::PaintStyleImageLayer(params);
 }
 
 // Called by nsTablePainter
 DrawResult
 nsTableCellFrame::PaintCellBackground(nsRenderingContext& aRenderingContext,
                                       const nsRect& aDirtyRect, nsPoint aPt,
                                       uint32_t aFlags)
 {
@@ -1199,17 +1199,16 @@ nsBCTableCellFrame::GetBorderOverflow()
   LogicalMargin halfBorder(wm,
                            BC_BORDER_START_HALF_COORD(p2t, mBStartBorder),
                            BC_BORDER_END_HALF_COORD(p2t, mIEndBorder),
                            BC_BORDER_END_HALF_COORD(p2t, mBEndBorder),
                            BC_BORDER_START_HALF_COORD(p2t, mIStartBorder));
   return halfBorder.GetPhysicalMargin(wm);
 }
 
-
 DrawResult
 nsBCTableCellFrame::PaintBackground(nsRenderingContext& aRenderingContext,
                                     const nsRect&        aDirtyRect,
                                     nsPoint              aPt,
                                     uint32_t             aFlags)
 {
   // make border-width reflect the half of the border-collapse
   // assigned border that's inside the cell
@@ -1225,10 +1224,11 @@ nsBCTableCellFrame::PaintBackground(nsRe
   // bypassing nsCSSRendering::PaintBackground is safe because this kind
   // of frame cannot be used for the root element
   nsRect rect(aPt, GetSize());
   nsCSSRendering::PaintBGParams params =
     nsCSSRendering::PaintBGParams::ForAllLayers(*PresContext(),
                                                 aRenderingContext, aDirtyRect,
                                                 rect, this,
                                                 aFlags);
-  return nsCSSRendering::PaintBackgroundWithSC(params, StyleContext(), myBorder);
+  return nsCSSRendering::PaintStyleImageLayerWithSC(params, StyleContext(),
+                                                    myBorder);
 }
--- a/layout/tables/nsTableFrame.cpp
+++ b/layout/tables/nsTableFrame.cpp
@@ -4807,18 +4807,18 @@ GetColorAndStyle(const nsIFrame* aFrame,
   const nsStyleBorder* styleData = aFrame->StyleBorder();
   mozilla::Side physicalSide = aTableWM.PhysicalSide(aSide);
   *aStyle = styleData->GetBorderStyle(physicalSide);
 
   if ((NS_STYLE_BORDER_STYLE_NONE == *aStyle) ||
       (NS_STYLE_BORDER_STYLE_HIDDEN == *aStyle)) {
     return;
   }
-  *aColor = aFrame->StyleContext()->GetVisitedDependentColor(
-    nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color)[physicalSide]);
+  *aColor = aFrame->StyleContext()->
+    GetVisitedDependentColor(nsStyleBorder::BorderColorFieldFor(physicalSide));
 
   if (aWidth) {
     nscoord width = styleData->GetComputedBorderWidth(physicalSide);
     *aWidth = nsPresContext::AppUnitsToIntCSSPixels(width);
   }
 }
 
 /** coerce the paint style as required by CSS2.1
--- a/layout/tables/nsTablePainter.cpp
+++ b/layout/tables/nsTablePainter.cpp
@@ -234,17 +234,17 @@ TableBackgroundPainter::PaintTableFrame(
       nsCSSRendering::PaintBGParams::ForAllLayers(*mPresContext,
                                                   mRenderingContext,
                                                   mDirtyRect,
                                                   tableData.mRect + mRenderPt,
                                                   tableData.mFrame,
                                                   mBGPaintFlags);
 
     result &=
-      nsCSSRendering::PaintBackgroundWithSC(params,
+      nsCSSRendering::PaintStyleImageLayerWithSC(params,
                                             tableData.mFrame->StyleContext(),
                                             tableData.StyleBorder(mZeroBorder));
   }
 
   return result;
 }
 
 void
@@ -575,60 +575,60 @@ TableBackgroundPainter::PaintCell(nsTabl
     nsCSSRendering::PaintBGParams params =
       nsCSSRendering::PaintBGParams::ForAllLayers(*mPresContext, mRenderingContext,
                                                  mDirtyRect,
                                                  mCols[colIndex].mColGroup.mRect + mRenderPt,
                                                  mCols[colIndex].mColGroup.mFrame,
                                                  mBGPaintFlags);
     params.bgClipRect = &aColBGRect;
     result &=
-      nsCSSRendering::PaintBackgroundWithSC(params,
+      nsCSSRendering::PaintStyleImageLayerWithSC(params,
                                             mCols[colIndex].mColGroup.mFrame->StyleContext(),
                                             mCols[colIndex].mColGroup.StyleBorder(mZeroBorder));
   }
 
   //Paint column background
   if (haveColumns && mCols[colIndex].mCol.IsVisible()) {
     nsCSSRendering::PaintBGParams params =
       nsCSSRendering::PaintBGParams::ForAllLayers(*mPresContext, mRenderingContext,
                                                   mDirtyRect,
                                                   mCols[colIndex].mCol.mRect + mRenderPt,
                                                   mCols[colIndex].mCol.mFrame,
                                                   mBGPaintFlags);
     params.bgClipRect = &aColBGRect;
     result &=
-      nsCSSRendering::PaintBackgroundWithSC(params,
+      nsCSSRendering::PaintStyleImageLayerWithSC(params,
                                             mCols[colIndex].mCol.mFrame->StyleContext(),
                                             mCols[colIndex].mCol.StyleBorder(mZeroBorder));
   }
 
   //Paint row group background
   if (aRowGroupBGData.IsVisible()) {
     nsCSSRendering::PaintBGParams params =
       nsCSSRendering::PaintBGParams::ForAllLayers(*mPresContext, mRenderingContext,
                                                   mDirtyRect,
                                                   aRowGroupBGData.mRect + mRenderPt,
                                                   aRowGroupBGData.mFrame, mBGPaintFlags);
     params.bgClipRect = &aRowGroupBGRect;
     result &=
-      nsCSSRendering::PaintBackgroundWithSC(params,
+      nsCSSRendering::PaintStyleImageLayerWithSC(params,
                                             aRowGroupBGData.mFrame->StyleContext(),
                                             aRowGroupBGData.StyleBorder(mZeroBorder));
   }
 
   //Paint row background
   if (aRowBGData.IsVisible()) {
     nsCSSRendering::PaintBGParams params =
       nsCSSRendering::PaintBGParams::ForAllLayers(*mPresContext, mRenderingContext,
                                                   mDirtyRect,
                                                   aRowBGData.mRect + mRenderPt,
                                                   aRowBGData.mFrame, mBGPaintFlags);
     params.bgClipRect = &aRowBGRect;
     result &=
-      nsCSSRendering::PaintBackgroundWithSC(params,
+      nsCSSRendering::PaintStyleImageLayerWithSC(params,
                                             aRowBGData.mFrame->StyleContext(),
                                             aRowBGData.StyleBorder(mZeroBorder));
   }
 
   //Paint cell background in border-collapse unless we're just passing
   if (mIsBorderCollapse && !aPassSelf) {
     result &=
       aCell->PaintCellBackground(mRenderingContext, mDirtyRect,
--- a/layout/xul/tree/nsTreeBodyFrame.cpp
+++ b/layout/xul/tree/nsTreeBodyFrame.cpp
@@ -4144,17 +4144,18 @@ nsTreeBodyFrame::PaintBackgroundLayer(ns
                                       const nsRect&        aDirtyRect)
 {
   const nsStyleBorder* myBorder = aStyleContext->StyleBorder();
   nsCSSRendering::PaintBGParams params =
     nsCSSRendering::PaintBGParams::ForAllLayers(*aPresContext, aRenderingContext,
                                                 aDirtyRect, aRect, this,
                                                 nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES);
   DrawResult result =
-    nsCSSRendering::PaintBackgroundWithSC(params, aStyleContext, *myBorder);
+    nsCSSRendering::PaintStyleImageLayerWithSC(params, aStyleContext,
+                                               *myBorder);
 
   result &=
     nsCSSRendering::PaintBorderWithStyleBorder(aPresContext, aRenderingContext,
                                                this, aDirtyRect, aRect,
                                                *myBorder, mStyleContext,
                                                PaintBorderFlags::SYNC_DECODE_IMAGES);
 
   nsCSSRendering::PaintOutline(aPresContext, aRenderingContext, this,
--- a/mobile/android/base/java/org/mozilla/gecko/AboutPages.java
+++ b/mobile/android/base/java/org/mozilla/gecko/AboutPages.java
@@ -5,16 +5,20 @@
 
 package org.mozilla.gecko;
 
 import org.mozilla.gecko.annotation.RobocopTarget;
 import org.mozilla.gecko.home.HomeConfig;
 import org.mozilla.gecko.home.HomeConfig.PanelType;
 import org.mozilla.gecko.util.StringUtils;
 
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
 public class AboutPages {
     // All of our special pages.
     public static final String ACCOUNTS        = "about:accounts";
     public static final String ADDONS          = "about:addons";
     public static final String CONFIG          = "about:config";
     public static final String DOWNLOADS       = "about:downloads";
     public static final String FIREFOX         = "about:firefox";
     public static final String HEALTHREPORT    = "about:healthreport";
@@ -67,41 +71,41 @@ public class AboutPages {
         return isAboutPage(PRIVATEBROWSING, url);
     }
 
     public static boolean isAboutPage(String page, String url) {
         return url != null && url.toLowerCase().startsWith(page);
 
     }
 
-    public static final String[] DEFAULT_ICON_PAGES = new String[] {
-        HOME,
-        ACCOUNTS,
-        ADDONS,
-        CONFIG,
-        DOWNLOADS,
-        FIREFOX,
-        HEALTHREPORT,
-        UPDATER
-    };
+    public static final List<String> DEFAULT_ICON_PAGES = Collections.unmodifiableList(Arrays.asList(
+            HOME,
+            ACCOUNTS,
+            ADDONS,
+            CONFIG,
+            DOWNLOADS,
+            FIREFOX,
+            HEALTHREPORT,
+            UPDATER
+    ));
 
     public static boolean isBuiltinIconPage(final String url) {
         if (url == null ||
             !url.startsWith("about:")) {
             return false;
         }
 
         // about:home uses a separate search built-in icon.
         if (isAboutHome(url)) {
             return true;
         }
 
         // TODO: it'd be quicker to not compare the "about:" part every time.
-        for (int i = 0; i < DEFAULT_ICON_PAGES.length; ++i) {
-            if (DEFAULT_ICON_PAGES[i].equals(url)) {
+        for (String page : DEFAULT_ICON_PAGES) {
+            if (page.equals(url)) {
                 return true;
             }
         }
         return false;
     }
 
     /**
      * Get a URL that navigates to the specified built-in Home Panel.
--- a/mobile/android/base/java/org/mozilla/gecko/icons/preparation/AboutPagesPreparer.java
+++ b/mobile/android/base/java/org/mozilla/gecko/icons/preparation/AboutPagesPreparer.java
@@ -16,19 +16,17 @@ import java.util.Set;
 
 /**
  * Preparer implementation for adding the omni.ja URL for internal about: pages.
  */
 public class AboutPagesPreparer implements Preparer {
     private Set<String> aboutUrls;
 
     public AboutPagesPreparer() {
-        aboutUrls = new HashSet<>();
-
-        Collections.addAll(aboutUrls, AboutPages.DEFAULT_ICON_PAGES);
+        aboutUrls = new HashSet<>(AboutPages.DEFAULT_ICON_PAGES);
     }
 
     @Override
     public void prepare(IconRequest request) {
         if (aboutUrls.contains(request.getPageUrl())) {
             final String iconUrl = GeckoJarReader.getJarURL(request.getContext(), "chrome/chrome/content/branding/favicon64.png");
 
             request.modify()
--- a/mobile/android/base/java/org/mozilla/gecko/util/ViewUtil.java
+++ b/mobile/android/base/java/org/mozilla/gecko/util/ViewUtil.java
@@ -36,17 +36,17 @@ public class ViewUtil {
 
     /**
      * Android framework have a bug margin start/end for RTL between 19~22. We can only use MarginLayoutParamsCompat before 17 and after 23.
      * @param layoutParams
      * @param marginStart
      * @param isLayoutRtl
      */
     public static void setMarginStart(ViewGroup.MarginLayoutParams layoutParams, int marginStart, boolean isLayoutRtl) {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+        if (AppConstants.Versions.feature17Plus && AppConstants.Versions.preN) {
             if (isLayoutRtl) {
                 layoutParams.rightMargin = marginStart;
             } else {
                 layoutParams.leftMargin = marginStart;
             }
         } else {
             MarginLayoutParamsCompat.setMarginStart(layoutParams, marginStart);
         }
new file mode 100644
index 0000000000000000000000000000000000000000..5bbc9e6fce29affb6137f09ffbe0974839064b73
GIT binary patch
literal 506
zc$@+H0R{evP)<h;3K|Lk000e1NJLTq001cf001Be1^@s6k8e>v0005ONkl<ZcmeE|
zg%R8^5Jlh06lP|oey)WZ2$zsHLUp8@Ff;mWNxNBxnK{<@&w}ZRCH$Gi-ta|UB!oY=
z*nbB(LIUtVK#q_j<OpdXOb9@6Za$%@pZVnZ*oK7Cx$e65VI0E*>=#150Gv|ZENSMp
z%#9Uu15{EXW~gRTPNX?T)IiP!Ih8JlU)zv>d<8xPIia|@(!rF)L0B%vGE?SDL1CF}
zLLZ=Wz`n@z3UN^BvWk>i;+&eKk@T_xox^XqM0Rim*9-XXfvg`bXGt>FB5cFhq_9k&
z!AK&^)M!Oq(Ar=<R?i2wS!sj&g)1AKL-tKa-y3noe0r9V9Ws9x<i(@RUC^_N$Tkad
zS^`@TL4v?=xhTY~fKz$i#VgA*-z6`719_2iA@<_lVBd&&9lu%R0K5lzWy-vH1#7ag
zQ;;(f;Lm~la7&($!1g~4^3!8d$j`<f%m!<LR@8mB>TyuoJdDl4ZDLl|bkXQOF(!ZT
z>=sHBvKd`a`e(B{yqMiWtOlzjO{C=h+{SHmeaGzn{&lh@SwT+mS*&q@>Cr8Z(GS4<
w%{$I`z!)J%$Px1CM$X{Cs3T#FLF`CI0H2Ii5&)F3`~Uy|07*qoM6N<$g6MwjNdN!<
new file mode 100644
index 0000000000000000000000000000000000000000..1c5d20ff385d324fe4b81b151080b501b7a823f3
GIT binary patch
literal 684
zc$@*O0#p5oP)<h;3K|Lk000e1NJLTq001@s001fo1^@s6_)(}m0007WNkl<ZcmeI1
z1&%B?6h+T_o@(wevmm&Fh_i`^BUS|A4FttmN*0ivj97us%+Ou-d*5YMX-c0NZRzUX
z;yN?k*Opp%dWMg(NWVjPx`tP<$7WQp$6{2l3Rb}?SOu$K6|90e2)_{6Mfbl$XpW=0
zM(iAJb9}SKK3L;yFiv57hVvUP;EyGkgYYH#?~>*Pn8gY6#+bE2BZ9hPjgVYP*6A|E
z=Uon22}?1n#jg1Syom$GZ^aSt2M*?rh-CW(-G7$p3#VCK-r+@6UIw05VQ!!b=1z2I
zLzWxSIuFFMEu^wfNs}&bNohhH{{g&%52O?^pW!ikTH^0y=kN&jMST3n5b(PP`<mu#
zV!fU4syZJ4?`3}z;W=}H3dx<V3tO@5uu<u^y==Fgw0C;&(8JsK53)mSptYP+_7sVW
zg!uw9%gT9siTL^pL=I>Dg$ejwf$jT=`QjWei}C>~e<FM?A|QYYT}0?zKsy^8WZ3RA
zV;4YsFWIlU-+0?$*auhT8?(u`a9yy&U$LC6cs&^(iOAmwJjP%Z?COu=9|&JO4Pe)n
zhw;7&Zx}oe1Rx2t?rckGI2$RR1+5Cj-HAhixb}_dqw>hIzBn4EEzKE;GbOoU7D)H6
zJg%<$884~vQmK1b40VqZJyKp+Wv}vg0<p_3B+K5<cSW|z%3fYPl;d{woYz%;JcXBe
z?=XIYy1$PTbYR{>BMI?<5A&1hp5~YOoG@Essq=Gq`wCWG_5xO3^y*d0Ltj$;f&$7(
z!l4a8FCpV4)i9wBD++e;`~Sjq5nRD4SOu$K6|90)u=>Ma1*>2ctOTB}Ay)vN=?x*=
SzUwal0000<MNUMnLSTZl=|Il_
--- a/mobile/android/chrome/content/Reader.js
+++ b/mobile/android/chrome/content/Reader.js
@@ -121,20 +121,17 @@ var Reader = {
           url: message.data.url
         }).then(data => {
           message.target.messageManager.sendAsyncMessage("Reader:FaviconReturn", JSON.parse(data));
         });
         break;
       }
 
       case "Reader:SystemUIVisibility":
-        WindowEventDispatcher.sendRequest({
-          type: "SystemUI:Visibility",
-          visible: message.data.visible
-        });
+        this._showSystemUI(message.data.visible);
         break;
 
       case "Reader:ToolbarHidden":
         if (!this._hasUsedToolbar) {
           Snackbars.show(Strings.browser.GetStringFromName("readerMode.toolbarTip"), Snackbars.LENGTH_LONG);
           Services.prefs.setBoolPref("reader.has_used_toolbar", true);
           this._hasUsedToolbar = true;
         }
@@ -184,27 +181,37 @@ var Reader = {
     if (browser.currentURI.spec.startsWith("about:reader")) {
       showPageAction("drawable://reader_active", Strings.reader.GetStringFromName("readerView.close"));
       // Only start a reader session if the viewer is in the foreground. We do
       // not track background reader viewers.
       UITelemetry.startSession("reader.1", null);
       return;
     }
 
+    // not in ReaderMode, to make sure System UI is visible, not dimmed.
+    this._showSystemUI(true);
+
     // Only stop a reader session if the foreground viewer is not visible.
     UITelemetry.stopSession("reader.1", "", null);
 
     if (browser.isArticle) {
       showPageAction("drawable://reader", Strings.reader.GetStringFromName("readerView.enter"));
       UITelemetry.addEvent("show.1", "button", null, "reader_available");
     } else {
       UITelemetry.addEvent("show.1", "button", null, "reader_unavailable");
     }
   },
 
+  _showSystemUI: function(visibility) {
+      WindowEventDispatcher.sendRequest({
+          type: "SystemUI:Visibility",
+          visible: visibility
+      });
+  },
+
   /**
    * Gets an article for a given URL. This method will download and parse a document
    * if it does not find the article in the cache.
    *
    * @param url The article URL.
    * @return {Promise}
    * @resolves JS object representing the article, or null if no article is found.
    */
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -298,17 +298,16 @@
 @BINPATH@/components/EditorUtils.js
 @BINPATH@/components/extensions.manifest
 @BINPATH@/components/utils.manifest
 @BINPATH@/components/simpleServices.js
 @BINPATH@/components/addonManager.js
 @BINPATH@/components/amContentHandler.js
 @BINPATH@/components/amInstallTrigger.js
 @BINPATH@/components/amWebAPI.js
-@BINPATH@/components/amWebInstallListener.js
 @BINPATH@/components/nsBlocklistService.js
 #ifndef RELEASE_OR_BETA
 @BINPATH@/components/TabSource.js
 #endif
 @BINPATH@/components/webvtt.xpt
 @BINPATH@/components/WebVTT.manifest
 @BINPATH@/components/WebVTTParserWrapper.js
 
--- a/security/certverifier/CertVerifier.cpp
+++ b/security/certverifier/CertVerifier.cpp
@@ -346,20 +346,20 @@ CertVerifier::VerifyCert(CERTCertificate
         /*optional out*/ OCSPStaplingStatus* ocspStaplingStatus,
         /*optional out*/ KeySizeStatus* keySizeStatus,
         /*optional out*/ SHA1ModeResult* sha1ModeResult,
         /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo,
         /*optional out*/ CertificateTransparencyInfo* ctInfo)
 {
   MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("Top of VerifyCert\n"));
 
-  PR_ASSERT(cert);
-  PR_ASSERT(usage == certificateUsageSSLServer || !(flags & FLAG_MUST_BE_EV));
-  PR_ASSERT(usage == certificateUsageSSLServer || !keySizeStatus);
-  PR_ASSERT(usage == certificateUsageSSLServer || !sha1ModeResult);
+  MOZ_ASSERT(cert);
+  MOZ_ASSERT(usage == certificateUsageSSLServer || !(flags & FLAG_MUST_BE_EV));
+  MOZ_ASSERT(usage == certificateUsageSSLServer || !keySizeStatus);
+  MOZ_ASSERT(usage == certificateUsageSSLServer || !sha1ModeResult);
 
   if (evOidPolicy) {
     *evOidPolicy = SEC_OID_UNKNOWN;
   }
   if (ocspStaplingStatus) {
     if (usage != certificateUsageSSLServer) {
       return Result::FATAL_ERROR_INVALID_ARGS;
     }
@@ -829,20 +829,20 @@ CertVerifier::VerifySSLServerCert(const 
                      /*optional*/ const NeckoOriginAttributes& originAttributes,
                  /*optional out*/ SECOidTag* evOidPolicy,
                  /*optional out*/ OCSPStaplingStatus* ocspStaplingStatus,
                  /*optional out*/ KeySizeStatus* keySizeStatus,
                  /*optional out*/ SHA1ModeResult* sha1ModeResult,
                  /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo,
                  /*optional out*/ CertificateTransparencyInfo* ctInfo)
 {
-  PR_ASSERT(peerCert);
-  // XXX: PR_ASSERT(pinarg)
-  PR_ASSERT(hostname);
-  PR_ASSERT(hostname[0]);
+  MOZ_ASSERT(peerCert);
+  // XXX: MOZ_ASSERT(pinarg);
+  MOZ_ASSERT(hostname);
+  MOZ_ASSERT(hostname[0]);
 
   if (evOidPolicy) {
     *evOidPolicy = SEC_OID_UNKNOWN;
   }
 
   if (!hostname || !hostname[0]) {
     return Result::ERROR_BAD_CERT_DOMAIN;
   }
--- a/security/certverifier/ExtendedValidation.cpp
+++ b/security/certverifier/ExtendedValidation.cpp
@@ -1207,17 +1207,17 @@ isEVPolicy(SECOidTag policyOIDTag)
 }
 
 namespace mozilla { namespace psm {
 
 bool
 CertIsAuthoritativeForEVPolicy(const UniqueCERTCertificate& cert,
                                const mozilla::pkix::CertPolicyId& policy)
 {
-  PR_ASSERT(cert);
+  MOZ_ASSERT(cert);
   if (!cert) {
     return false;
   }
 
   unsigned char fingerprint[SHA256_LENGTH];
   SECStatus srv =
     PK11_HashBuf(SEC_OID_SHA256, fingerprint, cert->derCert.data,
                  AssertedCast<int32_t>(cert->derCert.len));
--- a/security/certverifier/NSSCertDBTrustDomain.cpp
+++ b/security/certverifier/NSSCertDBTrustDomain.cpp
@@ -376,17 +376,17 @@ NSSCertDBTrustDomain::CheckRevocation(En
   // We keep track of the result of verifying the stapled response but don't
   // immediately return failure if the response has expired.
   //
   // We only set the OCSP stapling status if we're validating the end-entity
   // certificate. Non-end-entity certificates would always be
   // OCSP_STAPLING_NONE unless/until we implement multi-stapling.
   Result stapledOCSPResponseResult = Success;
   if (stapledOCSPResponse) {
-    PR_ASSERT(endEntityOrCA == EndEntityOrCA::MustBeEndEntity);
+    MOZ_ASSERT(endEntityOrCA == EndEntityOrCA::MustBeEndEntity);
     bool expired;
     stapledOCSPResponseResult =
       VerifyAndMaybeCacheEncodedOCSPResponse(certID, time,
                                              maxOCSPLifetimeInDays,
                                              *stapledOCSPResponse,
                                              ResponseWasStapled, expired);
     if (stapledOCSPResponseResult == Success) {
       // stapled OCSP response present and good
@@ -454,18 +454,18 @@ NSSCertDBTrustDomain::CheckRevocation(En
       cachedResponsePresent = false;
     }
   } else {
     MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
            ("NSSCertDBTrustDomain: no cached OCSP response"));
   }
   // At this point, if and only if cachedErrorResult is Success, there was no
   // cached response.
-  PR_ASSERT((!cachedResponsePresent && cachedResponseResult == Success) ||
-            (cachedResponsePresent && cachedResponseResult != Success));
+  MOZ_ASSERT((!cachedResponsePresent && cachedResponseResult == Success) ||
+             (cachedResponsePresent && cachedResponseResult != Success));
 
   // If we have a fresh OneCRL Blocklist we can skip OCSP for CA certs
   bool blocklistIsFresh;
   nsresult nsrv = mCertBlocklist->IsBlocklistFresh(&blocklistIsFresh);
   if (NS_FAILED(nsrv)) {
     return Result::FATAL_ERROR_LIBRARY_FAILURE;
   }
 
@@ -664,17 +664,17 @@ NSSCertDBTrustDomain::VerifyAndMaybeCach
   // allowed signature digest algorithms.
   OCSPVerificationTrustDomain trustDomain(*this);
   Result rv = VerifyEncodedOCSPResponse(trustDomain, certID, time,
                                         maxLifetimeInDays, encodedResponse,
                                         expired, &thisUpdate, &validThrough);
   // If a response was stapled and expired, we don't want to cache it. Return
   // early to simplify the logic here.
   if (responseSource == ResponseWasStapled && expired) {
-    PR_ASSERT(rv != Success);
+    MOZ_ASSERT(rv != Success);
     return rv;
   }
   // validThrough is only trustworthy if the response successfully verifies
   // or it indicates a revoked or unknown certificate.
   // If this isn't the case, store an indication of failure (to prevent
   // repeatedly requesting a response from a failing server).
   if (rv != Success && rv != Result::ERROR_REVOKED_CERTIFICATE &&
       rv != Result::ERROR_OCSP_UNKNOWN_CERT) {
@@ -1151,17 +1151,17 @@ LoadLoadableRoots(const nsCString& dir, 
   }
 
   return true;
 }
 
 void
 UnloadLoadableRoots(const char* modNameUTF8)
 {
-  PR_ASSERT(modNameUTF8);
+  MOZ_ASSERT(modNameUTF8);
   UniqueSECMODModule rootsModule(SECMOD_FindModule(modNameUTF8));
 
   if (rootsModule) {
     SECMOD_UnloadUserModule(rootsModule.get());
   }
 }
 
 nsresult
--- a/security/manager/ssl/CertBlocklist.cpp
+++ b/security/manager/ssl/CertBlocklist.cpp
@@ -1,16 +1,17 @@
 /* -*- 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/Assertions.h"
 #include "mozilla/Base64.h"
 #include "mozilla/Casting.h"
 #include "mozilla/Logging.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Unused.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsCRTGlue.h"
 #include "nsDirectoryServiceUtils.h"
@@ -507,17 +508,17 @@ CertBlocklist::SaveEntries()
         MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
                ("CertBlocklist::SaveEntries writing revocation data failed"));
         return NS_ERROR_FAILURE;
       }
     }
   }
 
   nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(outputStream);
-  NS_ASSERTION(safeStream, "expected a safe output stream!");
+  MOZ_ASSERT(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"));
     return rv;
--- a/security/manager/ssl/DataStorage.cpp
+++ b/security/manager/ssl/DataStorage.cpp
@@ -1,16 +1,17 @@
 /* -*- 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 "DataStorage.h"
 
+#include "mozilla/Assertions.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/dom/PContent.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/Unused.h"
@@ -86,17 +87,17 @@ DataStorage::GetIfExists(const nsString&
   return storage.forget();
 }
 
 nsresult
 DataStorage::Init(bool& aDataWillPersist)
 {
   // Don't access the observer service or preferences off the main thread.
   if (!NS_IsMainThread()) {
-    NS_NOTREACHED("DataStorage::Init called off main thread");
+    MOZ_ASSERT_UNREACHABLE("DataStorage::Init called off main thread");
     return NS_ERROR_NOT_SAME_THREAD;
   }
 
   MutexAutoLock lock(mMutex);
 
   // Ignore attempts to initialize several times.
   if (mInitCalled) {
     return NS_OK;
@@ -809,17 +810,17 @@ DataStorage::SetTimer()
   Unused << NS_WARN_IF(NS_FAILED(rv));
 }
 
 void
 DataStorage::NotifyObservers(const char* aTopic)
 {
   // Don't access the observer service off the main thread.
   if (!NS_IsMainThread()) {
-    NS_NOTREACHED("DataStorage::NotifyObservers called off main thread");
+    MOZ_ASSERT_UNREACHABLE("DataStorage::NotifyObservers called off main thread");
     return;
   }
 
   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
   if (os) {
     os->NotifyObservers(nullptr, aTopic, mFilename.get());
   }
 }
@@ -849,22 +850,22 @@ DataStorage::ShutdownTimer()
   mTimer = nullptr;
 }
 
 //------------------------------------------------------------
 // DataStorage::nsIObserver
 //------------------------------------------------------------
 
 NS_IMETHODIMP
-DataStorage::Observe(nsISupports* aSubject, const char* aTopic,
-                     const char16_t* aData)
+DataStorage::Observe(nsISupports* /*aSubject*/, const char* aTopic,
+                     const char16_t* /*aData*/)
 {
   // Don't access preferences off the main thread.
   if (!NS_IsMainThread()) {
-    NS_NOTREACHED("DataStorage::Observe called off main thread");
+    MOZ_ASSERT_UNREACHABLE("DataStorage::Observe called off main thread");
     return NS_ERROR_NOT_SAME_THREAD;
   }
 
   nsresult rv;
   if (strcmp(aTopic, "last-pb-context-exited") == 0) {
     MutexAutoLock lock(mMutex);
     mPrivateDataTable.Clear();
   } else if (strcmp(aTopic, "profile-before-change") == 0 ||
--- a/security/manager/ssl/RootCertificateTelemetryUtils.cpp
+++ b/security/manager/ssl/RootCertificateTelemetryUtils.cpp
@@ -21,17 +21,17 @@ mozilla::LazyLogModule gPublicKeyPinning
 // This implementation assumes everything to be of HASH_LEN, so it should not
 // be used generically.
 class BinaryHashSearchArrayComparator
 {
 public:
   explicit BinaryHashSearchArrayComparator(const uint8_t* aTarget, size_t len)
     : mTarget(aTarget)
   {
-    NS_ASSERTION(len == HASH_LEN, "Hashes should be of the same length.");
+    MOZ_ASSERT(len == HASH_LEN, "Hashes should be of the same length.");
   }
 
   int operator()(const CertAuthorityHash val) const {
     return memcmp(mTarget, val.hash, HASH_LEN);
   }
 
 private:
   const uint8_t* mTarget;
--- a/security/manager/ssl/SSLServerCertVerification.cpp
+++ b/security/manager/ssl/SSLServerCertVerification.cpp
@@ -822,42 +822,40 @@ BlockServerCertChangeForSpdy(nsNSSSocket
     // If we didn't have a status, then this is the
     // first handshake on this connection, not a
     // renegotiation.
     return SECSuccess;
   }
 
   status->GetServerCert(getter_AddRefs(cert));
   if (!cert) {
-    NS_NOTREACHED("every nsSSLStatus must have a cert"
-                  "that implements nsIX509Cert");
+    MOZ_ASSERT_UNREACHABLE("nsSSLStatus must have a cert implementing nsIX509Cert");
     PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0);
     return SECFailure;
   }
 
   // Filter out sockets that did not neogtiate SPDY via NPN
   nsAutoCString negotiatedNPN;
   nsresult rv = infoObject->GetNegotiatedNPN(negotiatedNPN);
-  NS_ASSERTION(NS_SUCCEEDED(rv),
-               "GetNegotiatedNPN() failed during renegotiation");
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "GetNegotiatedNPN() failed during renegotiation");
 
   if (NS_SUCCEEDED(rv) && !StringBeginsWith(negotiatedNPN,
                                             NS_LITERAL_CSTRING("spdy/"))) {
     return SECSuccess;
   }
   // If GetNegotiatedNPN() failed we will assume spdy for safety's safe
   if (NS_FAILED(rv)) {
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
            ("BlockServerCertChangeForSpdy failed GetNegotiatedNPN() call."
             " Assuming spdy.\n"));
   }
 
   // Check to see if the cert has actually changed
   UniqueCERTCertificate c(cert->GetCert());
-  NS_ASSERTION(c, "very bad and hopefully impossible state");
+  MOZ_ASSERT(c, "Somehow couldn't get underlying cert from nsIX509Cert");
   bool sameCert = CERT_CompareCerts(c.get(), serverCert.get());
   if (sameCert) {
     return SECSuccess;
   }
 
   // Report an error - changed cert is confirmed
   MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
          ("SPDY Refused to allow new cert during renegotiation\n"));
@@ -908,32 +906,32 @@ TryMatchingWildcardSubjectAltName(const 
 // in Mozilla's Root CA program.
 // certList consists of a validated certificate chain. The end-entity
 // certificate is first and the root (trust anchor) is last.
 void
 GatherBaselineRequirementsTelemetry(const UniqueCERTCertList& certList)
 {
   CERTCertListNode* endEntityNode = CERT_LIST_HEAD(certList);
   CERTCertListNode* rootNode = CERT_LIST_TAIL(certList);
-  PR_ASSERT(!(CERT_LIST_END(endEntityNode, certList) ||
-              CERT_LIST_END(rootNode, certList)));
+  MOZ_ASSERT(!(CERT_LIST_END(endEntityNode, certList) ||
+               CERT_LIST_END(rootNode, certList)));
   if (CERT_LIST_END(endEntityNode, certList) ||
       CERT_LIST_END(rootNode, certList)) {
     return;
   }
   CERTCertificate* cert = endEntityNode->cert;
-  PR_ASSERT(cert);
+  MOZ_ASSERT(cert);
   if (!cert) {
     return;
   }
   UniquePORTString commonName(CERT_GetCommonName(&cert->subject));
   // This only applies to certificates issued by authorities in our root
   // program.
   CERTCertificate* rootCert = rootNode->cert;
-  PR_ASSERT(rootCert);
+  MOZ_ASSERT(rootCert);
   if (!rootCert) {
     return;
   }
   bool isBuiltIn = false;
   Result result = IsCertBuiltInRoot(rootCert, isBuiltIn);
   if (result != Success || !isBuiltIn) {
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
            ("BR telemetry: root certificate for '%s' is not a built-in root "
@@ -1068,31 +1066,31 @@ GatherBaselineRequirementsTelemetry(cons
 
 // Gather telemetry on whether the end-entity cert for a server has the
 // required TLS Server Authentication EKU, or any others
 void
 GatherEKUTelemetry(const UniqueCERTCertList& certList)
 {
   CERTCertListNode* endEntityNode = CERT_LIST_HEAD(certList);
   CERTCertListNode* rootNode = CERT_LIST_TAIL(certList);
-  PR_ASSERT(!(CERT_LIST_END(endEntityNode, certList) ||
-              CERT_LIST_END(rootNode, certList)));
+  MOZ_ASSERT(!(CERT_LIST_END(endEntityNode, certList) ||
+               CERT_LIST_END(rootNode, certList)));
   if (CERT_LIST_END(endEntityNode, certList) ||
       CERT_LIST_END(rootNode, certList)) {
     return;
   }
   CERTCertificate* endEntityCert = endEntityNode->cert;
-  PR_ASSERT(endEntityCert);
+  MOZ_ASSERT(endEntityCert);
   if (!endEntityCert) {
     return;
   }
 
   // Only log telemetry if the root CA is built-in
   CERTCertificate* rootCert = rootNode->cert;
-  PR_ASSERT(rootCert);
+  MOZ_ASSERT(rootCert);
   if (!rootCert) {
     return;
   }
   bool isBuiltIn = false;
   Result rv = IsCertBuiltInRoot(rootCert, isBuiltIn);
   if (rv != Success || !isBuiltIn) {
     return;
   }
@@ -1149,61 +1147,61 @@ GatherEKUTelemetry(const UniqueCERTCertL
 
 // Gathers telemetry on which CA is the root of a given cert chain.
 // If the root is a built-in root, then the telemetry makes a count
 // by root.  Roots that are not built-in are counted in one bin.
 void
 GatherRootCATelemetry(const UniqueCERTCertList& certList)
 {
   CERTCertListNode* rootNode = CERT_LIST_TAIL(certList);
-  PR_ASSERT(rootNode);
+  MOZ_ASSERT(rootNode);
   if (!rootNode) {
     return;
   }
-  PR_ASSERT(!CERT_LIST_END(rootNode, certList));
+  MOZ_ASSERT(!CERT_LIST_END(rootNode, certList));
   if (CERT_LIST_END(rootNode, certList)) {
     return;
   }
   CERTCertificate* rootCert = rootNode->cert;
-  PR_ASSERT(rootCert);
+  MOZ_ASSERT(rootCert);
   if (!rootCert) {
     return;
   }
   AccumulateTelemetryForRootCA(Telemetry::CERT_VALIDATION_SUCCESS_BY_CA,
                                rootCert);
 }
 
 // These time are appoximate, i.e., doesn't account for leap seconds, etc
 const uint64_t ONE_WEEK_IN_SECONDS = (7 * (24 * 60 *60));
 const uint64_t ONE_YEAR_IN_WEEKS   = 52;
 
 // Gathers telemetry on the certificate lifetimes we observe in the wild
 void
 GatherEndEntityTelemetry(const UniqueCERTCertList& certList)
 {
   CERTCertListNode* endEntityNode = CERT_LIST_HEAD(certList);
-  PR_ASSERT(endEntityNode);
+  MOZ_ASSERT(endEntityNode);
   if (!endEntityNode) {
     return;
   }
 
   CERTCertificate * endEntityCert = endEntityNode->cert;
-  PR_ASSERT(endEntityCert);
+  MOZ_ASSERT(endEntityCert);
   if (!endEntityCert) {
     return;
   }
 
   PRTime notBefore;
   PRTime notAfter;
 
   if (CERT_GetCertTimes(endEntityCert, &notBefore, &notAfter) != SECSuccess) {
     return;
   }
 
-  PR_ASSERT(notAfter > notBefore);
+  MOZ_ASSERT(notAfter > notBefore);
   if (notAfter <= notBefore) {
     return;
   }
 
   uint64_t durationInWeeks = (notAfter - notBefore)
     / PR_USEC_PER_SEC
     / ONE_WEEK_IN_SECONDS;
 
@@ -1563,17 +1561,17 @@ SSLServerCertVerificationJob::Run()
 
         NS_ERROR("Failed to dispatch CertErrorRunnable");
         error = PR_INVALID_STATE_ERROR;
       }
     }
   }
 
   if (error == 0) {
-    NS_NOTREACHED("no error set during certificate validation failure");
+    MOZ_ASSERT_UNREACHABLE("No error set during certificate validation failure");
     error = PR_INVALID_STATE_ERROR;
   }
 
   RefPtr<SSLServerCertVerificationResult> failure(
     new SSLServerCertVerificationResult(mInfoObject, error));
   failure->Dispatch();
   return NS_OK;
 }
@@ -1594,21 +1592,21 @@ AuthCertificateHook(void* arg, PRFileDes
 
   // Runs on the socket transport thread
 
   MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
          ("[%p] starting AuthCertificateHook\n", fd));
 
   // Modern libssl always passes PR_TRUE for checkSig, and we have no means of
   // doing verification without checking signatures.
-  NS_ASSERTION(checkSig, "AuthCertificateHook: checkSig unexpectedly false");
+  MOZ_ASSERT(checkSig, "AuthCertificateHook: checkSig unexpectedly false");
 
   // PSM never causes libssl to call this function with PR_TRUE for isServer,
   // and many things in PSM assume that we are a client.
-  NS_ASSERTION(!isServer, "AuthCertificateHook: isServer unexpectedly true");
+  MOZ_ASSERT(!isServer, "AuthCertificateHook: isServer unexpectedly true");
 
   nsNSSSocketInfo* socketInfo = static_cast<nsNSSSocketInfo*>(arg);
 
   UniqueCERTCertificate serverCert(SSL_PeerCertificate(fd));
 
   if (!checkSig || isServer || !socketInfo || !serverCert) {
       PR_SetError(PR_INVALID_STATE_ERROR, 0);
       return SECFailure;
@@ -1771,21 +1769,21 @@ MOZ_ASSERT(telemetryID == Telemetry::His
 }
 
 void
 SSLServerCertVerificationResult::Dispatch()
 {
   nsresult rv;
   nsCOMPtr<nsIEventTarget> stsTarget
     = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
-  NS_ASSERTION(stsTarget,
-               "Failed to get socket transport service event target");
+  MOZ_ASSERT(stsTarget,
+             "Failed to get socket transport service event target");
   rv = stsTarget->Dispatch(this, NS_DISPATCH_NORMAL);
-  NS_ASSERTION(NS_SUCCEEDED(rv),
-               "Failed to dispatch SSLServerCertVerificationResult");
+  MOZ_ASSERT(NS_SUCCEEDED(rv),
+             "Failed to dispatch SSLServerCertVerificationResult");
 }
 
 NS_IMETHODIMP
 SSLServerCertVerificationResult::Run()
 {
   // TODO: Assert that we're on the socket transport thread
   if (mTelemetryID != Telemetry::HistogramCount) {
      Telemetry::Accumulate(mTelemetryID, mTelemetryValue);
--- a/security/manager/ssl/TransportSecurityInfo.cpp
+++ b/security/manager/ssl/TransportSecurityInfo.cpp
@@ -245,20 +245,20 @@ TransportSecurityInfo::formatErrorMessag
   }
 
   if (!XRE_IsParentProcess()) {
     return NS_ERROR_UNEXPECTED;
   }
 
   nsresult rv;
   NS_ConvertASCIItoUTF16 hostNameU(mHostName);
-  NS_ASSERTION(errorMessageType != OverridableCertErrorMessage || 
-                (mSSLStatus && mSSLStatus->HasServerCert() &&
-                 mSSLStatus->mHaveCertErrorBits),
-                "GetErrorLogMessage called for cert error without cert");
+  MOZ_ASSERT(errorMessageType != OverridableCertErrorMessage ||
+               (mSSLStatus && mSSLStatus->HasServerCert() &&
+                mSSLStatus->mHaveCertErrorBits),
+             "formatErrorMessage() called for cert error without cert");
   if (errorMessageType == OverridableCertErrorMessage && 
       mSSLStatus && mSSLStatus->HasServerCert()) {
     rv = formatOverridableCertErrorMessage(*mSSLStatus, errorCode,
                                            mHostName, mPort,
                                            suppressPort443,
                                            wantsHtml,
                                            result);
   } else {
@@ -990,18 +990,17 @@ RememberCertErrorsTable::RememberCertHas
   nsresult rv;
 
   nsAutoCString hostPortKey;
   rv = GetHostPortKey(infoObject, hostPortKey);
   if (NS_FAILED(rv))
     return;
 
   if (certVerificationResult != SECSuccess) {
-    NS_ASSERTION(status,
-        "Must have nsSSLStatus object when remembering flags");
+    MOZ_ASSERT(status, "Must have nsSSLStatus object when remembering flags");
 
     if (!status)
       return;
 
     CertStateBits bits;
     bits.mIsDomainMismatch = status->mIsDomainMismatch;
     bits.mIsNotValidAtThisTime = status->mIsNotValidAtThisTime;
     bits.mIsUntrusted = status->mIsUntrusted;
@@ -1070,17 +1069,17 @@ TransportSecurityInfo::SetStatusErrorBit
   RememberCertErrorsTable::GetInstance().RememberCertHasError(this,
                                                               mSSLStatus,
                                                               SECFailure);
 }
 
 NS_IMETHODIMP
 TransportSecurityInfo::GetFailedCertChain(nsIX509CertList** _result)
 {
-  NS_ASSERTION(_result, "non-NULL destination required");
+  MOZ_ASSERT(_result);
 
   *_result = mFailedCertChain;
   NS_IF_ADDREF(*_result);
 
   return NS_OK;
 }
 
 nsresult
--- a/security/manager/ssl/TransportSecurityInfo.h
+++ b/security/manager/ssl/TransportSecurityInfo.h
@@ -4,16 +4,17 @@
  * 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 TransportSecurityInfo_h
 #define TransportSecurityInfo_h
 
 #include "ScopedNSSTypes.h"
 #include "certt.h"
+#include "mozilla/Assertions.h"
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/RefPtr.h"
 #include "nsDataHashtable.h"
 #include "nsIAssociatedContentSecurity.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsISSLStatusProvider.h"
 #include "nsITransportSecurityInfo.h"
--- a/security/manager/ssl/nsCertOverrideService.cpp
+++ b/security/manager/ssl/nsCertOverrideService.cpp
@@ -4,16 +4,17 @@
  * 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"
+#include "mozilla/Assertions.h"
 #include "mozilla/Telemetry.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsCRT.h"
 #include "nsILineInputStream.h"
 #include "nsIObserver.h"
 #include "nsIObserverService.h"
 #include "nsIOutputStream.h"
 #include "nsISafeOutputStream.h"
@@ -94,17 +95,17 @@ nsCertOverrideService::nsCertOverrideSer
 nsCertOverrideService::~nsCertOverrideService()
 {
 }
 
 nsresult
 nsCertOverrideService::Init()
 {
   if (!NS_IsMainThread()) {
-    NS_NOTREACHED("nsCertOverrideService initialized off main thread");
+    MOZ_ASSERT_UNREACHABLE("nsCertOverrideService initialized off main thread");
     return NS_ERROR_NOT_SAME_THREAD;
   }
 
   // Note that the names of these variables would seem to indicate that at one
   // point another hash algorithm was used and is still supported for backwards
   // compatibility. This is not the case. It has always been SHA256.
   mOidTagForStoringNewHashes = SEC_OID_SHA256;
   mDottedOidForStoringNewHashes.Assign("OID.2.16.840.1.101.3.4.2.1");
@@ -327,17 +328,17 @@ nsCertOverrideService::Write()
     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!");
+  MOZ_ASSERT(safeStream, "Expected a safe output stream!");
   if (safeStream) {
     rv = safeStream->Finish();
     if (NS_FAILED(rv)) {
       NS_WARNING("failed to save cert warn settings file! possible dataloss");
       return rv;
     }
   }
 
--- a/security/manager/ssl/nsKeygenThread.cpp
+++ b/security/manager/ssl/nsKeygenThread.cpp
@@ -1,22 +1,23 @@
 /* -*- 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 "pk11func.h"
+#include "PSMRunnable.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/DebugOnly.h"
 #include "nsCOMPtr.h"
-#include "nsThreadUtils.h"
+#include "nsIObserver.h"
 #include "nsKeygenThread.h"
-#include "nsIObserver.h"
 #include "nsNSSShutDown.h"
-#include "PSMRunnable.h"
-#include "mozilla/DebugOnly.h"
+#include "nsThreadUtils.h"
+#include "pk11func.h"
 
 using namespace mozilla;
 using namespace mozilla::psm;
 
 NS_IMPL_ISUPPORTS(nsKeygenThread, nsIKeygenThread)
 
 
 nsKeygenThread::nsKeygenThread()
@@ -87,17 +88,17 @@ nsresult nsKeygenThread::ConsumeResult(
   }
 
   nsresult rv;
 
   MutexAutoLock lock(mutex);
   
     // GetParams must not be called until thread creator called
     // Join on this thread.
-    NS_ASSERTION(keygenReady, "logic error in nsKeygenThread::GetParams");
+    MOZ_ASSERT(keygenReady, "Logic error in nsKeygenThread::GetParams");
 
     if (keygenReady) {
       *a_privateKey = privateKey;
       *a_publicKey = publicKey;
       *a_used_slot = usedSlot;
 
       privateKey = 0;
       publicKey = 0;
@@ -141,18 +142,17 @@ nsresult nsKeygenThread::StartKeyGenerat
 
     iAmRunning = true;
 
     threadHandle = PR_CreateThread(PR_USER_THREAD, nsKeygenThreadRunner, static_cast<void*>(this), 
       PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
 
     // bool thread_started_ok = (threadHandle != nullptr);
     // we might want to return "thread started ok" to caller in the future
-    NS_ASSERTION(threadHandle, "Could not create nsKeygenThreadRunner thread\n");
-  
+    MOZ_ASSERT(threadHandle, "Could not create nsKeygenThreadRunner thread");
   return NS_OK;
 }
 
 nsresult nsKeygenThread::UserCanceled(bool *threadAlreadyClosedDialog)
 {
   if (!threadAlreadyClosedDialog)
     return NS_OK;
 
@@ -232,18 +232,18 @@ void nsKeygenThread::Run(void)
     if (!statusDialogClosed && mNotifyObserver)
       notifyObserver = mNotifyObserver;
 
     mNotifyObserver = nullptr;
   }
 
   if (notifyObserver) {
     DebugOnly<nsresult> rv = NS_DispatchToMainThread(notifyObserver);
-    NS_ASSERTION(NS_SUCCEEDED(rv),
-		 "failed to dispatch keygen thread observer to main thread");
+    MOZ_ASSERT(NS_SUCCEEDED(rv),
+               "Failed to dispatch keygen thread observer to main thread");
   }
 }
 
 void nsKeygenThread::Join()
 {
   if (!threadHandle)
     return;
   
--- a/security/manager/ssl/nsNSSCallbacks.cpp
+++ b/security/manager/ssl/nsNSSCallbacks.cpp
@@ -597,18 +597,18 @@ void
 nsHTTPListener::FreeLoadGroup(bool aCancelLoad)
 {
   nsILoadGroup *lg = nullptr;
 
   MutexAutoLock locker(mLock);
 
   if (mLoadGroup) {
     if (mLoadGroupOwnerThread != PR_GetCurrentThread()) {
-      NS_ASSERTION(false,
-                   "attempt to access nsHTTPDownloadEvent::mLoadGroup on multiple threads, leaking it!");
+      MOZ_ASSERT_UNREACHABLE(
+        "Attempt to access mLoadGroup on multiple threads, leaking it!");
     }
     else {
       lg = mLoadGroup;
       mLoadGroup = nullptr;
     }
   }
 
   if (lg) {
--- a/security/manager/ssl/nsNSSCertHelper.cpp
+++ b/security/manager/ssl/nsNSSCertHelper.cpp
@@ -2,16 +2,17 @@
  * 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 "nsNSSCertHelper.h"
 
 #include <algorithm>
 
 #include "ScopedNSSTypes.h"
+#include "mozilla/Assertions.h"
 #include "mozilla/Casting.h"
 #include "mozilla/NotNull.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/UniquePtr.h"
 #include "nsCOMPtr.h"
 #include "nsComponentManagerUtils.h"
 #include "nsDateTimeFormatCID.h"
 #include "nsIDateTimeFormat.h"
@@ -224,17 +225,17 @@ GetDefaultOIDFormat(SECItem *oid,
         break;
       }
     }
 
     if (written < 0)
       return NS_ERROR_FAILURE;
 
     len += written;
-    NS_ASSERTION(len < sizeof(buf), "OID data to big to display in 300 chars.");
+    MOZ_ASSERT(len < sizeof(buf), "OID data too big to display in 300 chars.");
     val = 0;      
     invalid = false;
     first = false;
   }
 
   CopyASCIItoUTF16(buf, outString);
   return NS_OK; 
 }
--- a/security/manager/ssl/nsNSSCertificateDB.cpp
+++ b/security/manager/ssl/nsNSSCertificateDB.cpp
@@ -5,18 +5,18 @@
 #include "nsNSSCertificateDB.h"
 
 #include "CertVerifier.h"
 #include "CryptoTask.h"
 #include "ExtendedValidation.h"
 #include "NSSCertDBTrustDomain.h"
 #include "SharedSSLState.h"
 #include "certdb.h"
+#include "mozilla/Assertions.h"
 #include "mozilla/Base64.h"
-#include "mozilla/Assertions.h"
 #include "mozilla/Casting.h"
 #include "mozilla/Unused.h"
 #include "nsArray.h"
 #include "nsArrayUtils.h"
 #include "nsCOMPtr.h"
 #include "nsCRT.h"
 #include "nsComponentManagerUtils.h"
 #include "nsICertificateDialogs.h"
@@ -244,17 +244,17 @@ nsNSSCertificateDB::handleCACertDownload
   // the root cert is the first cert and display it.  Otherwise,
   // we compare the last 2 entries, if the second to last cert was
   // signed by the last cert, then we assume the last cert is the
   // root and display it.
 
   uint32_t numCerts;
 
   x509Certs->GetLength(&numCerts);
-  NS_ASSERTION(numCerts > 0, "Didn't get any certs to import.");
+  MOZ_ASSERT(numCerts > 0, "Didn't get any certs to import.");
   if (numCerts == 0)
     return NS_OK; // Nothing to import, so nothing to do.
 
   nsCOMPtr<nsIX509Cert> certToShow;
   uint32_t selCertIndex;
   if (numCerts == 1) {
     // There's only one cert, so let's show it.
     selCertIndex = 0;
--- a/security/manager/ssl/nsNSSCertificateFakeTransport.cpp
+++ b/security/manager/ssl/nsNSSCertificateFakeTransport.cpp
@@ -26,185 +26,185 @@ nsNSSCertificateFakeTransport::nsNSSCert
 nsNSSCertificateFakeTransport::~nsNSSCertificateFakeTransport()
 {
   mCertSerialization = nullptr;
 }
 
 NS_IMETHODIMP
 nsNSSCertificateFakeTransport::GetDbKey(nsACString&)
 {
-  NS_NOTREACHED("Unimplemented on content process");
+  MOZ_ASSERT_UNREACHABLE("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsNSSCertificateFakeTransport::GetDisplayName(nsAString&)
 {
-  NS_NOTREACHED("Unimplemented on content process");
+  MOZ_ASSERT_UNREACHABLE("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsNSSCertificateFakeTransport::GetEmailAddress(nsAString&)
 {
-  NS_NOTREACHED("Unimplemented on content process");
+  MOZ_ASSERT_UNREACHABLE("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsNSSCertificateFakeTransport::GetEmailAddresses(uint32_t*, char16_t***)
 {
-  NS_NOTREACHED("Unimplemented on content process");
+  MOZ_ASSERT_UNREACHABLE("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsNSSCertificateFakeTransport::ContainsEmailAddress(const nsAString&, bool*)
 {
-  NS_NOTREACHED("Unimplemented on content process");
+  MOZ_ASSERT_UNREACHABLE("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsNSSCertificateFakeTransport::GetCommonName(nsAString&)
 {
-  NS_NOTREACHED("Unimplemented on content process");
+  MOZ_ASSERT_UNREACHABLE("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsNSSCertificateFakeTransport::GetOrganization(nsAString&)
 {
-  NS_NOTREACHED("Unimplemented on content process");
+  MOZ_ASSERT_UNREACHABLE("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsNSSCertificateFakeTransport::GetIssuerCommonName(nsAString&)
 {
-  NS_NOTREACHED("Unimplemented on content process");
+  MOZ_ASSERT_UNREACHABLE("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsNSSCertificateFakeTransport::GetIssuerOrganization(nsAString&)
 {
-  NS_NOTREACHED("Unimplemented on content process");
+  MOZ_ASSERT_UNREACHABLE("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsNSSCertificateFakeTransport::GetIssuerOrganizationUnit(nsAString&)
 {
-  NS_NOTREACHED("Unimplemented on content process");
+  MOZ_ASSERT_UNREACHABLE("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsNSSCertificateFakeTransport::GetIssuer(nsIX509Cert**)
 {
-  NS_NOTREACHED("Unimplemented on content process");
+  MOZ_ASSERT_UNREACHABLE("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsNSSCertificateFakeTransport::GetOrganizationalUnit(nsAString&)
 {
-  NS_NOTREACHED("Unimplemented on content process");
+  MOZ_ASSERT_UNREACHABLE("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsNSSCertificateFakeTransport::GetChain(nsIArray**)
 {
-  NS_NOTREACHED("Unimplemented on content process");
+  MOZ_ASSERT_UNREACHABLE("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsNSSCertificateFakeTransport::GetSubjectName(nsAString&)
 {
-  NS_NOTREACHED("Unimplemented on content process");
+  MOZ_ASSERT_UNREACHABLE("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsNSSCertificateFakeTransport::GetIssuerName(nsAString&)
 {
-  NS_NOTREACHED("Unimplemented on content process");
+  MOZ_ASSERT_UNREACHABLE("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsNSSCertificateFakeTransport::GetSerialNumber(nsAString&)
 {
-  NS_NOTREACHED("Unimplemented on content process");
+  MOZ_ASSERT_UNREACHABLE("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsNSSCertificateFakeTransport::GetSha256Fingerprint(nsAString&)
 {
-  NS_NOTREACHED("Unimplemented on content process");
+  MOZ_ASSERT_UNREACHABLE("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsNSSCertificateFakeTransport::GetSha1Fingerprint(nsAString&)
 {
-  NS_NOTREACHED("Unimplemented on content process");
+  MOZ_ASSERT_UNREACHABLE("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsNSSCertificateFakeTransport::GetTokenName(nsAString&)
 {
-  NS_NOTREACHED("Unimplemented on content process");
+  MOZ_ASSERT_UNREACHABLE("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsNSSCertificateFakeTransport::GetRawDER(uint32_t*, uint8_t**)
 {
-  NS_NOTREACHED("Unimplemented on content process");
+  MOZ_ASSERT_UNREACHABLE("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsNSSCertificateFakeTransport::GetValidity(nsIX509CertValidity**)
 {
-  NS_NOTREACHED("Unimplemented on content process");
+  MOZ_ASSERT_UNREACHABLE("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsNSSCertificateFakeTransport::GetKeyUsages(nsAString&)
 {
   MOZ_ASSERT_UNREACHABLE("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsNSSCertificateFakeTransport::GetASN1Structure(nsIASN1Object**)
 {
-  NS_NOTREACHED("Unimplemented on content process");
+  MOZ_ASSERT_UNREACHABLE("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsNSSCertificateFakeTransport::Equals(nsIX509Cert*, bool*)
 {
-  NS_NOTREACHED("Unimplemented on content process");
+  MOZ_ASSERT_UNREACHABLE("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsNSSCertificateFakeTransport::GetSha256SubjectPublicKeyInfoDigest(nsACString&)
 {
-  NS_NOTREACHED("Unimplemented on content process");
+  MOZ_ASSERT_UNREACHABLE("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 // NB: This serialization must match that of nsNSSCertificate.
 NS_IMETHODIMP
 nsNSSCertificateFakeTransport::Write(nsIObjectOutputStream* aStream)
 {
   // On a non-chrome process we don't have mCert because we lack
@@ -315,61 +315,61 @@ nsNSSCertificateFakeTransport::GetClassI
 
   *aClassIDNoAlloc = kNSSCertificateCID;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNSSCertificateFakeTransport::GetCertType(unsigned int*)
 {
-  NS_NOTREACHED("Unimplemented on content process");
+  MOZ_ASSERT_UNREACHABLE("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsNSSCertificateFakeTransport::GetIsSelfSigned(bool*)
 {
-  NS_NOTREACHED("Unimplemented on content process");
+  MOZ_ASSERT_UNREACHABLE("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
-nsNSSCertificateFakeTransport::GetIsBuiltInRoot(bool* aIsBuiltInRoot)
+nsNSSCertificateFakeTransport::GetIsBuiltInRoot(bool*)
 {
-  NS_NOTREACHED("Unimplemented on content process");
+  MOZ_ASSERT_UNREACHABLE("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsNSSCertificateFakeTransport::GetAllTokenNames(unsigned int*, char16_t***)
 {
-  NS_NOTREACHED("Unimplemented on content process");
+  MOZ_ASSERT_UNREACHABLE("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 CERTCertificate*
 nsNSSCertificateFakeTransport::GetCert()
 {
-  NS_NOTREACHED("Unimplemented on content process");
+  MOZ_ASSERT_UNREACHABLE("Unimplemented on content process");
   return nullptr;
 }
 
 NS_IMETHODIMP
 nsNSSCertificateFakeTransport::ExportAsCMS(unsigned int,
                                            unsigned int*,
                                            unsigned char**)
 {
-  NS_NOTREACHED("Unimplemented on content process");
+  MOZ_ASSERT_UNREACHABLE("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsNSSCertificateFakeTransport::MarkForPermDeletion()
 {
-  NS_NOTREACHED("Unimplemented on content process");
+  MOZ_ASSERT_UNREACHABLE("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMPL_CLASSINFO(nsNSSCertListFakeTransport,
                   nullptr,
                   // inferred from nsIX509Cert
                   nsIClassInfo::THREADSAFE,
                   NS_X509CERTLIST_CID)
@@ -384,45 +384,45 @@ nsNSSCertListFakeTransport::nsNSSCertLis
 
 nsNSSCertListFakeTransport::~nsNSSCertListFakeTransport()
 {
 }
 
 NS_IMETHODIMP
 nsNSSCertListFakeTransport::AddCert(nsIX509Cert* aCert)
 {
-  NS_NOTREACHED("Unimplemented on content process");
+  MOZ_ASSERT_UNREACHABLE("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsNSSCertListFakeTransport::DeleteCert(nsIX509Cert* aCert)
 {
-  NS_NOTREACHED("Unimplemented on content process");
+  MOZ_ASSERT_UNREACHABLE("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 CERTCertList*
 nsNSSCertListFakeTransport::GetRawCertList()
 {
-  NS_NOTREACHED("Unimplemented on content process");
+  MOZ_ASSERT_UNREACHABLE("Unimplemented on content process");
   return nullptr;
 }
 
 NS_IMETHODIMP
 nsNSSCertListFakeTransport::GetEnumerator(nsISimpleEnumerator**)
 {
-  NS_NOTREACHED("Unimplemented on content process");
+  MOZ_ASSERT_UNREACHABLE("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsNSSCertListFakeTransport::Equals(nsIX509CertList*, bool*)
 {
-  NS_NOTREACHED("Unimplemented on content process");
+  MOZ_ASSERT_UNREACHABLE("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 // NB: This serialization must match that of nsNSSCertList.
 NS_IMETHODIMP
 nsNSSCertListFakeTransport::Write(nsIObjectOutputStream* aStream)
 {
   uint32_t certListLen = mFakeCertList.length();
--- a/security/manager/ssl/nsNSSComponent.cpp
+++ b/security/manager/ssl/nsNSSComponent.cpp
@@ -8,16 +8,17 @@
 
 #include "ExtendedValidation.h"
 #include "NSSCertDBTrustDomain.h"
 #include "ScopedNSSTypes.h"
 #include "SharedSSLState.h"
 #include "cert.h"
 #include "certdb.h"
 #include "mozilla/ArrayUtils.h"
+#include "mozilla/Assertions.h"
 #include "mozilla/Casting.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/PublicSSL.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/SyncRunnable.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/Unused.h"
@@ -162,23 +163,23 @@ bool EnsureNSSInitialized(EnsureNSSOpera
     // safe to move with the flags here.
   case nssLoadingComponent:
     if (loading)
       return false; // We are reentered during nss component creation
     loading = true;
     return true;
 
   case nssInitSucceeded:
-    NS_ASSERTION(loading, "Bad call to EnsureNSSInitialized(nssInitSucceeded)");
+    MOZ_ASSERT(loading, "Bad call to EnsureNSSInitialized(nssInitSucceeded)");
     loading = false;
     PR_AtomicSet(&haveLoaded, 1);
     return true;
 
   case nssInitFailed:
-    NS_ASSERTION(loading, "Bad call to EnsureNSSInitialized(nssInitFailed)");
+    MOZ_ASSERT(loading, "Bad call to EnsureNSSInitialized(nssInitFailed)");
     loading = false;
     MOZ_FALLTHROUGH;
 
   case nssShutdown:
     PR_AtomicSet(&haveLoaded, 0);
     return false;
 
     // In this case we are called from a component to ensure nss initilization.
@@ -201,17 +202,17 @@ bool EnsureNSSInitialized(EnsureNSSOpera
       return false;
 
     bool isInitialized;
     nsresult rv = nssComponent->IsNSSInitialized(&isInitialized);
     return NS_SUCCEEDED(rv) && isInitialized;
     }
 
   default:
-    NS_ASSERTION(false, "Bad operator to EnsureNSSInitialized");
+    MOZ_ASSERT_UNREACHABLE("Bad operator to EnsureNSSInitialized");
     return false;
   }
 }
 
 static void
 GetRevocationBehaviorFromPrefs(/*out*/ CertVerifier::OcspDownloadConfig* odc,
                                /*out*/ CertVerifier::OcspStrictConfig* osc,
                                /*out*/ CertVerifier::OcspGetConfig* ogc,
@@ -258,17 +259,18 @@ nsNSSComponent::nsNSSComponent()
   , mNSSInitialized(false)
 #ifndef MOZ_NO_SMART_CARDS
   , mThreadList(nullptr)
 #endif
 {
   MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsNSSComponent::ctor\n"));
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
-  NS_ASSERTION( (0 == mInstanceCount), "nsNSSComponent is a singleton, but instantiated multiple times!");
+  MOZ_ASSERT(mInstanceCount == 0,
+             "nsNSSComponent is a singleton, but instantiated multiple times!");
   ++mInstanceCount;
 }
 
 nsNSSComponent::~nsNSSComponent()
 {
   MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsNSSComponent::dtor\n"));
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
@@ -1435,17 +1437,19 @@ NS_IMPL_ISUPPORTS(CipherSuiteChangeObser
 
 // static
 StaticRefPtr<CipherSuiteChangeObserver> CipherSuiteChangeObserver::sObserver;
 
 // static
 nsresult
 CipherSuiteChangeObserver::StartObserve()
 {
-  NS_ASSERTION(NS_IsMainThread(), "CipherSuiteChangeObserver::StartObserve() can only be accessed in main thread");
+  MOZ_ASSERT(NS_IsMainThread(),
+             "CipherSuiteChangeObserver::StartObserve() can only be accessed "
+             "on the main thread");
   if (!sObserver) {
     RefPtr<CipherSuiteChangeObserver> observer = new CipherSuiteChangeObserver();
     nsresult rv = Preferences::AddStrongObserver(observer.get(), "security.");
     if (NS_FAILED(rv)) {
       sObserver = nullptr;
       return rv;
     }
 
@@ -1455,21 +1459,23 @@ CipherSuiteChangeObserver::StartObserve(
                                  false);
 
     sObserver = observer;
   }
   return NS_OK;
 }
 
 nsresult
-CipherSuiteChangeObserver::Observe(nsISupports* aSubject,
+CipherSuiteChangeObserver::Observe(nsISupports* /*aSubject*/,
                                    const char* aTopic,
                                    const char16_t* someData)
 {
-  NS_ASSERTION(NS_IsMainThread(), "CipherSuiteChangeObserver::Observe can only be accessed in main thread");
+  MOZ_ASSERT(NS_IsMainThread(),
+             "CipherSuiteChangeObserver::Observe can only be accessed on main "
+             "thread");
   if (nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) {
     NS_ConvertUTF16toUTF8  prefName(someData);
     // Look through the cipher table and set according to pref setting
     const CipherPref* const cp = sCipherPrefs;
     for (size_t i = 0; cp[i].pref; ++i) {
       if (prefName.Equals(cp[i].pref)) {
         bool cipherEnabled = Preferences::GetBool(cp[i].pref,
                                                   cp[i].enabledByDefault);
@@ -2345,17 +2351,18 @@ setPassword(PK11SlotInfo* slot, nsIInter
 }
 
 namespace mozilla {
 namespace psm {
 
 nsresult
 InitializeCipherSuite()
 {
-  NS_ASSERTION(NS_IsMainThread(), "InitializeCipherSuite() can only be accessed in main thread");
+  MOZ_ASSERT(NS_IsMainThread(),
+             "InitializeCipherSuite() can only be accessed on the main thread");
 
   if (NSS_SetDomesticPolicy() != SECSuccess) {
     return NS_ERROR_FAILURE;
   }
 
   // Disable any ciphers that NSS might have enabled by default
   for (uint16_t i = 0; i < SSL_NumImplementedCiphers; ++i) {
     uint16_t cipher_id = SSL_ImplementedCiphers[i];
--- a/security/manager/ssl/nsNSSComponent.h
+++ b/security/manager/ssl/nsNSSComponent.h
@@ -4,16 +4,17 @@
  * 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 _nsNSSComponent_h_
 #define _nsNSSComponent_h_
 
 #include "ScopedNSSTypes.h"
 #include "SharedCertVerifier.h"
+#include "mozilla/Attributes.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/RefPtr.h"
 #include "nsCOMPtr.h"
 #include "nsIObserver.h"
 #include "nsIStringBundle.h"
 #include "nsNSSCallbacks.h"
 #include "prerror.h"
 #include "sslt.h"
--- a/security/manager/ssl/nsNSSIOLayer.cpp
+++ b/security/manager/ssl/nsNSSIOLayer.cpp
@@ -577,31 +577,31 @@ nsNSSSocketInfo::SetFileDescPtr(PRFileDe
 }
 
 void
 nsNSSSocketInfo::SetCertVerificationWaiting()
 {
   // mCertVerificationState may be before_cert_verification for the first
   // handshake on the connection, or after_cert_verification for subsequent
   // renegotiation handshakes.
-  NS_ASSERTION(mCertVerificationState != waiting_for_cert_verification,
-               "Invalid state transition to waiting_for_cert_verification");
+  MOZ_ASSERT(mCertVerificationState != waiting_for_cert_verification,
+             "Invalid state transition to waiting_for_cert_verification");
   mCertVerificationState = waiting_for_cert_verification;
 }
 
 // Be careful that SetCertVerificationResult does NOT get called while we are
 // processing a SSL callback function, because SSL_AuthCertificateComplete will
 // attempt to acquire locks that are already held by libssl when it calls
 // callbacks.
 void
 nsNSSSocketInfo::SetCertVerificationResult(PRErrorCode errorCode,
                                            SSLErrorMessageType errorMessageType)
 {
-  NS_ASSERTION(mCertVerificationState == waiting_for_cert_verification,
-               "Invalid state transition to cert_verification_finished");
+  MOZ_ASSERT(mCertVerificationState == waiting_for_cert_verification,
+             "Invalid state transition to cert_verification_finished");
 
   if (mFd) {
     SECStatus rv = SSL_AuthCertificateComplete(mFd, errorCode);
     // Only replace errorCode if there was originally no error
     if (rv != SECSuccess && errorCode == 0) {
       errorCode = PR_GetError();
       errorMessageType = PlainErrorMessage;
       if (errorCode == 0) {
@@ -894,29 +894,29 @@ nsSSLIOLayerClose(PRFileDesc* fd)
   nsNSSShutDownPreventionLock locker;
   if (!fd)
     return PR_FAILURE;
 
   MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("[%p] Shutting down socket\n",
          (void*) fd));
 
   nsNSSSocketInfo* socketInfo = (nsNSSSocketInfo*) fd->secret;
-  NS_ASSERTION(socketInfo,"nsNSSSocketInfo was null for an fd");
+  MOZ_ASSERT(socketInfo, "nsNSSSocketInfo was null for an fd");
 
   return socketInfo->CloseSocketAndDestroy(locker);
 }
 
 PRStatus
 nsNSSSocketInfo::CloseSocketAndDestroy(
     const nsNSSShutDownPreventionLock& /*proofOfLock*/)
 {
   PRFileDesc* popped = PR_PopIOLayer(mFd, PR_TOP_IO_LAYER);
-  NS_ASSERTION(popped &&
+  MOZ_ASSERT(popped &&
                popped->identity == nsSSLIOLayerHelpers::nsSSLIOLayerIdentity,
-               "SSL Layer not on top of stack");
+             "SSL Layer not on top of stack");
 
   // The plain text layer is not always present - so its not a fatal error
   // if it cannot be removed
   PRFileDesc* poppedPlaintext =
     PR_GetIdentitiesLayer(mFd, nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity);
   if (poppedPlaintext) {
     PR_PopIOLayer(mFd, nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity);
     poppedPlaintext->dtor(poppedPlaintext);
@@ -1251,18 +1251,18 @@ nsSSLIOLayerPoll(PRFileDesc* fd, int16_t
   if (!socketInfo) {
     // If we get here, it is probably because certificate validation failed
     // and this is the first I/O operation after the failure.
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
             ("[%p] polling SSL socket right after certificate verification failed "
                   "or NSS shutdown or SDR logout %d\n",
              fd, (int) in_flags));
 
-    NS_ASSERTION(in_flags & PR_POLL_EXCEPT,
-                 "caller did not poll for EXCEPT (canceled)");
+    MOZ_ASSERT(in_flags & PR_POLL_EXCEPT,
+               "Caller did not poll for EXCEPT (canceled)");
     // Since this poll method cannot return errors, we want the caller to call
     // PR_Send/PR_Recv right away to get the error, so we tell that we are
     // ready for whatever I/O they are asking for. (See getSocketInfoIfRunning).
     *out_flags = in_flags | PR_POLL_EXCEPT; // see also bug 480619
     return in_flags;
   }
 
   MOZ_LOG(gPIPNSSLog, LogLevel::Verbose,
@@ -1966,18 +1966,18 @@ nsNSS_SSLGetClientAuthData(void* arg, PR
     return SECFailure;
   }
 
   RefPtr<nsNSSSocketInfo> info(
     BitwiseCast<nsNSSSocketInfo*, PRFilePrivate*>(socket->higher->secret));
 
   UniqueCERTCertificate serverCert(SSL_PeerCertificate(socket));
   if (!serverCert) {
-    NS_NOTREACHED("Missing server certificate should have been detected during "
-                  "server cert authentication.");
+    MOZ_ASSERT_UNREACHABLE(
+      "Missing server cert should have been detected during server cert auth.");
     PR_SetError(SSL_ERROR_NO_CERTIFICATE, 0);
     return SECFailure;
   }
 
   if (info->GetJoined()) {
     // We refuse to send a client certificate when there are multiple hostnames
     // joined on this connection, because we only show the user one hostname
     // (mHostName) in the client certificate UI.
@@ -2286,17 +2286,17 @@ done:
 static PRFileDesc*
 nsSSLIOLayerImportFD(PRFileDesc* fd,
                      nsNSSSocketInfo* infoObject,
                      const char* host)
 {
   nsNSSShutDownPreventionLock locker;
   PRFileDesc* sslSock = SSL_ImportFD(nullptr, fd);
   if (!sslSock) {
-    NS_ASSERTION(false, "NSS: Error importing socket");
+    MOZ_ASSERT_UNREACHABLE("NSS: Error importing socket");
     return nullptr;
   }
   SSL_SetPKCS11PinArg(sslSock, (nsIInterfaceRequestor*) infoObject);
   SSL_HandshakeCallback(sslSock, HandshakeCallback, infoObject);
   SSL_SetCanFalseStartCallback(sslSock, CanFalseStartCallback, infoObject);
 
   // Disable this hook if we connect anonymously. See bug 466080.
   uint32_t flags = 0;
@@ -2310,22 +2310,22 @@ nsSSLIOLayerImportFD(PRFileDesc* fd,
   }
   if (flags & nsISocketProvider::MITM_OK) {
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
            ("[%p] nsSSLIOLayerImportFD: bypass authentication flag\n", fd));
     infoObject->SetBypassAuthentication(true);
   }
   if (SECSuccess != SSL_AuthCertificateHook(sslSock, AuthCertificateHook,
                                             infoObject)) {
-    NS_NOTREACHED("failed to configure AuthCertificateHook");
+    MOZ_ASSERT_UNREACHABLE("Failed to configure AuthCertificateHook");
     goto loser;
   }
 
   if (SECSuccess != SSL_SetURL(sslSock, host)) {
-    NS_NOTREACHED("SSL_SetURL failed");
+    MOZ_ASSERT_UNREACHABLE("SSL_SetURL failed");
     goto loser;
   }
 
   return sslSock;
 loser:
   if (sslSock) {
     PR_Close(sslSock);
   }
@@ -2516,17 +2516,17 @@ nsSSLIOLayerAddToSocket(int32_t family,
     if (stat == PR_FAILURE) {
       plaintextLayer->dtor(plaintextLayer);
       plaintextLayer = nullptr;
     }
   }
 
   PRFileDesc* sslSock = nsSSLIOLayerImportFD(fd, infoObject, host);
   if (!sslSock) {
-    NS_ASSERTION(false, "NSS: Error importing socket");
+    MOZ_ASSERT_UNREACHABLE("NSS: Error importing socket");
     goto loser;
   }
 
   infoObject->SetFileDescPtr(sslSock);
 
   rv = nsSSLIOLayerSetOptions(sslSock, forSTARTTLS, haveProxy, host, port,
                               infoObject);
 
--- a/security/manager/ssl/nsNSSIOLayer.h
+++ b/security/manager/ssl/nsNSSIOLayer.h
@@ -3,16 +3,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsNSSIOLayer_h
 #define nsNSSIOLayer_h
 
 #include "TransportSecurityInfo.h"
+#include "mozilla/Assertions.h"
 #include "mozilla/TimeStamp.h"
 #include "nsCOMPtr.h"
 #include "nsDataHashtable.h"
 #include "nsIClientAuthDialogs.h"
 #include "nsIProxyInfo.h"
 #include "nsISSLSocketControl.h"
 #include "nsNSSCertificate.h"
 #include "nsTHashtable.h"
--- a/security/manager/ssl/nsNSSShutDown.cpp
+++ b/security/manager/ssl/nsNSSShutDown.cpp
@@ -44,61 +44,61 @@ nsNSSShutDownList *singleton = nullptr;
 nsNSSShutDownList::nsNSSShutDownList()
   : mObjects(&gSetOps, sizeof(ObjectHashEntry))
   , mPK11LogoutCancelObjects(&gSetOps, sizeof(ObjectHashEntry))
 {
 }
 
 nsNSSShutDownList::~nsNSSShutDownList()
 {
-  PR_ASSERT(this == singleton);
+  MOZ_ASSERT(this == singleton);
   singleton = nullptr;
 }
 
 void nsNSSShutDownList::remember(nsNSSShutDownObject *o)
 {
   StaticMutexAutoLock lock(sListLock);
   if (!nsNSSShutDownList::construct(lock)) {
     return;
   }
 
-  PR_ASSERT(o);
+  MOZ_ASSERT(o);
   singleton->mObjects.Add(o, fallible);
 }
 
 void nsNSSShutDownList::forget(nsNSSShutDownObject *o)
 {
   StaticMutexAutoLock lock(sListLock);
   if (!singleton) {
     return;
   }
 
-  PR_ASSERT(o);
+  MOZ_ASSERT(o);
   singleton->mObjects.Remove(o);
 }
 
 void nsNSSShutDownList::remember(nsOnPK11LogoutCancelObject *o)
 {
   StaticMutexAutoLock lock(sListLock);
   if (!nsNSSShutDownList::construct(lock)) {
     return;
   }
 
-  PR_ASSERT(o);
+  MOZ_ASSERT(o);
   singleton->mPK11LogoutCancelObjects.Add(o, fallible);
 }
 
 void nsNSSShutDownList::forget(nsOnPK11LogoutCancelObject *o)
 {
   StaticMutexAutoLock lock(sListLock);
   if (!singleton) {
     return;
   }
 
-  PR_ASSERT(o);
+  MOZ_ASSERT(o);
   singleton->mPK11LogoutCancelObjects.Remove(o);
 }
 
 nsresult nsNSSShutDownList::doPK11Logout()
 {
   StaticMutexAutoLock lock(sListLock);
   if (!singleton) {
     return NS_OK;
--- a/security/manager/ssl/nsNTLMAuthModule.cpp
+++ b/security/manager/ssl/nsNTLMAuthModule.cpp
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsNTLMAuthModule.h"
 
 #include <time.h>
 
 #include "ScopedNSSTypes.h"
 #include "md4.h"
+#include "mozilla/Assertions.h"
 #include "mozilla/Base64.h"
 #include "mozilla/Casting.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/EndianUtils.h"
 #include "mozilla/Likely.h"
 #include "mozilla/Logging.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Sprintf.h"
@@ -988,24 +989,23 @@ nsNTLMAuthModule::InitTest()
   nsNSSShutDownPreventionLock locker;
   //
   // disable NTLM authentication when FIPS mode is enabled.
   //
   return PK11_IsFIPS() ? NS_ERROR_NOT_AVAILABLE : NS_OK;
 }
 
 NS_IMETHODIMP
-nsNTLMAuthModule::Init(const char      *serviceName,
-                       uint32_t         serviceFlags,
-                       const char16_t *domain,
-                       const char16_t *username,
-                       const char16_t *password)
+nsNTLMAuthModule::Init(const char* /*serviceName*/, uint32_t serviceFlags,
+                       const char16_t* domain, const char16_t* username,
+                       const char16_t* password)
 {
-  NS_ASSERTION((serviceFlags & ~nsIAuthModule::REQ_PROXY_AUTH) == nsIAuthModule::REQ_DEFAULT,
-      "unexpected service flags");
+  MOZ_ASSERT((serviceFlags & ~nsIAuthModule::REQ_PROXY_AUTH) ==
+               nsIAuthModule::REQ_DEFAULT,
+             "Unexpected service flags");
 
   mDomain = domain;
   mUsername = username;
   mPassword = password;
   mNTLMNegotiateSent = false;
 
   static bool sTelemetrySent = false;
   if (!sTelemetrySent) {
--- a/security/manager/ssl/nsPKCS12Blob.cpp
+++ b/security/manager/ssl/nsPKCS12Blob.cpp
@@ -1,15 +1,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/. */
 
 #include "nsPKCS12Blob.h"
 
 #include "ScopedNSSTypes.h"
+#include "mozilla/Assertions.h"
 #include "mozilla/Casting.h"
 #include "nsCRT.h"
 #include "nsCRTGlue.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsICertificateDialogs.h"
 #include "nsIDirectoryService.h"
 #include "nsIFile.h"
 #include "nsIInputStream.h"
@@ -267,17 +268,17 @@ nsPKCS12Blob::ExportToFile(nsIFile *file
   nsresult rv;
   SECStatus srv = SECSuccess;
   SEC_PKCS12ExportContext *ecx = nullptr;
   SEC_PKCS12SafeInfo *certSafe = nullptr, *keySafe = nullptr;
   SECItem unicodePw;
   nsAutoString filePath;
   int i;
   nsCOMPtr<nsIFile> localFileRef;
-  NS_ASSERTION(mToken, "Need to set the token before exporting");
+  MOZ_ASSERT(mToken, "Need to set the token before exporting");
   // init slot
 
   bool InformedUserNoSmartcardBackup = false;
   int numCertsExported = 0;
 
   rv = mToken->Login(true);
   if (NS_FAILED(rv)) goto finish;
   // get file password (unicode)
--- a/security/manager/ssl/nsProtectedAuthThread.cpp
+++ b/security/manager/ssl/nsProtectedAuthThread.cpp
@@ -1,21 +1,22 @@
 /* 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 "pk11func.h"
+#include "PSMRunnable.h"
+#include "mozilla/Assertions.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/RefPtr.h"
 #include "nsCOMPtr.h"
-#include "PSMRunnable.h"
-#include "nsString.h"
-#include "nsReadableUtils.h"
 #include "nsPKCS11Slot.h"
 #include "nsProtectedAuthThread.h"
+#include "nsReadableUtils.h"
+#include "nsString.h"
+#include "pk11func.h"
 
 using namespace mozilla;
 using namespace mozilla::psm;
 
 NS_IMPL_ISUPPORTS(nsProtectedAuthThread, nsIProtectedAuthThread)
 
 static void nsProtectedAuthThreadRunner(void *arg)
 {
@@ -56,24 +57,24 @@ NS_IMETHODIMP nsProtectedAuthThread::Log
     if (aObserver) {
       // We must AddRef aObserver here on the main thread, because it probably
       // does not implement a thread-safe AddRef.
       mNotifyObserver = new NotifyObserverRunnable(aObserver,
                                                    "operation-completed");
     }
 
     mIAmRunning = true;
-    
+
     mThreadHandle = PR_CreateThread(PR_USER_THREAD, nsProtectedAuthThreadRunner, static_cast<void*>(this), 
         PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
-    
+
     // bool thread_started_ok = (threadHandle != nullptr);
     // we might want to return "thread started ok" to caller in the future
-    NS_ASSERTION(mThreadHandle, "Could not create nsProtectedAuthThreadRunner thread\n");
-    
+    MOZ_ASSERT(mThreadHandle,
+               "Could not create nsProtectedAuthThreadRunner thread");
     return NS_OK;
 }
 
 NS_IMETHODIMP nsProtectedAuthThread::GetTokenName(nsAString &_retval)
 {
     MutexAutoLock lock(mMutex);
 
     // Get token name
@@ -126,18 +127,18 @@ void nsProtectedAuthThread::Run(void)
             mSlot = 0;
         }
 
         notifyObserver.swap(mNotifyObserver);
     }
     
     if (notifyObserver) {
         DebugOnly<nsresult> rv = NS_DispatchToMainThread(notifyObserver);
-	NS_ASSERTION(NS_SUCCEEDED(rv),
-		     "failed to dispatch protected auth observer to main thread");
+	MOZ_ASSERT(NS_SUCCEEDED(rv),
+		   "Failed to dispatch protected auth observer to main thread");
     }
 }
 
 void nsProtectedAuthThread::Join()
 {
     if (!mThreadHandle)
         return;
     
--- a/security/manager/ssl/nsSecureBrowserUIImpl.cpp
+++ b/security/manager/ssl/nsSecureBrowserUIImpl.cpp
@@ -1,16 +1,17 @@
 /* -*- 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 "nsSecureBrowserUIImpl.h"
 
 #include "imgIRequest.h"
+#include "mozilla/Assertions.h"
 #include "mozilla/Logging.h"
 #include "nsCURILoader.h"
 #include "nsIAssociatedContentSecurity.h"
 #include "nsIChannel.h"
 #include "nsIDOMWindow.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIDocument.h"
@@ -203,17 +204,18 @@ nsSecureBrowserUIImpl::MapInternalToExte
   if (!docShell)
     return NS_OK;
 
   // For content docShell's, the mixed content security state is set on the root docShell.
   if (docShell->ItemType() == nsIDocShellTreeItem::typeContent) {
     nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem(do_QueryInterface(docShell));
     nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
     docShellTreeItem->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
-    NS_ASSERTION(sameTypeRoot, "No document shell root tree item from document shell tree item!");
+    MOZ_ASSERT(sameTypeRoot,
+               "No document shell root tree item from document shell tree item!");
     docShell = do_QueryInterface(sameTypeRoot);
     if (!docShell)
       return NS_OK;
   }
 
   // Has a Mixed Content Load initiated in nsMixedContentBlocker?
   // * If not, the state should not be broken because of mixed content;
   // overriding the previous state if it is inaccurately flagged as mixed.
@@ -327,25 +329,21 @@ static uint32_t GetSecurityStateFromSecu
 
   MOZ_LOG(gSecureDocLog, LogLevel::Debug, ("SecureUI: GetSecurityState: - Returning %d\n", 
                                        securityState));
   return securityState;
 }
 
 
 //  nsIWebProgressListener
-NS_IMETHODIMP 
-nsSecureBrowserUIImpl::OnProgressChange(nsIWebProgress* aWebProgress,
-                                        nsIRequest* aRequest,
-                                        int32_t aCurSelfProgress,
-                                        int32_t aMaxSelfProgress,
-                                        int32_t aCurTotalProgress,
-                                        int32_t aMaxTotalProgress)
+NS_IMETHODIMP
+nsSecureBrowserUIImpl::OnProgressChange(nsIWebProgress*, nsIRequest*, int32_t,
+                                        int32_t, int32_t, int32_t)
 {
-  NS_NOTREACHED("notification excluded in AddProgressListener(...)");
+  MOZ_ASSERT_UNREACHABLE("Should have been excluded in AddProgressListener()");
   return NS_OK;
 }
 
 void
 nsSecureBrowserUIImpl::ResetStateTracking()
 {
   mDocumentRequestsInProgress = 0;
   mTransferringRequests.Clear();
@@ -523,17 +521,17 @@ nsSecureBrowserUIImpl::OnStateChange(nsI
     If a request is NOT for the toplevel DOM window, we will always treat it as a subdocument request,
     regardless of whether the load flags indicate a top level document.
   */
 
   nsCOMPtr<mozIDOMWindowProxy> windowForProgress;
   aWebProgress->GetDOMWindow(getter_AddRefs(windowForProgress));
 
   nsCOMPtr<mozIDOMWindowProxy> window(do_QueryReferent(mWindow));
-  NS_ASSERTION(window, "Window has gone away?!");
+  MOZ_ASSERT(window, "Window has gone away?!");
 
   if (!mIOService) {
     mIOService = do_GetService(NS_IOSERVICE_CONTRACTID);
   }
 
   bool isNoContentResponse = false;
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
   if (httpChannel) 
@@ -590,21 +588,21 @@ nsSecureBrowserUIImpl::OnStateChange(nsI
   nsCOMPtr<nsIURI> uri;
   nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
   if (channel) {
     channel->GetURI(getter_AddRefs(uri));
   }
 
   nsCOMPtr<imgIRequest> imgRequest(do_QueryInterface(aRequest));
   if (imgRequest) {
-    NS_ASSERTION(!channel, "How did that happen, exactly?");
+    MOZ_ASSERT(!channel, "Request channel somehow not available");
     // for image requests, we get the URI from here
     imgRequest->GetURI(getter_AddRefs(uri));
   }
-  
+
   if (uri) {
     bool vs;
     if (NS_SUCCEEDED(uri->SchemeIs("javascript", &vs)) && vs) {
       // We ignore the progress events for javascript URLs.
       // If a document loading gets triggered, we will see more events.
       return NS_OK;
     }
   }
@@ -1075,17 +1073,17 @@ nsSecureBrowserUIImpl::OnLocationChange(
     temp_IsViewSource = vs;
   }
 
   if (updateIsViewSource) {
     mIsViewSource = temp_IsViewSource;
   }
   mCurrentURI = aLocation;
   window = do_QueryReferent(mWindow);
-  NS_ASSERTION(window, "Window has gone away?!");
+  MOZ_ASSERT(window, "Window has gone away?!");
 
   // When |aRequest| is null, basically we don't trust that document. But if
   // docshell insists that the document has not changed at all, we will reuse
   // the previous security state, no matter what |aRequest| may be.
   if (aFlags & LOCATION_CHANGE_SAME_DOCUMENT)
     return NS_OK;
 
   // The location bar has changed, so we must update the security state.  The
@@ -1125,22 +1123,20 @@ nsSecureBrowserUIImpl::OnLocationChange(
   if (mNewToplevelSecurityStateKnown) {
     UpdateSecurityState(aRequest, true, false);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsSecureBrowserUIImpl::OnStatusChange(nsIWebProgress* aWebProgress,
-                                      nsIRequest* aRequest,
-                                      nsresult aStatus,
-                                      const char16_t* aMessage)
+nsSecureBrowserUIImpl::OnStatusChange(nsIWebProgress*, nsIRequest*, nsresult,
+                                      const char16_t*)
 {
-  NS_NOTREACHED("notification excluded in AddProgressListener(...)");
+  MOZ_ASSERT_UNREACHABLE("Should have been excluded in AddProgressListener()");
   return NS_OK;
 }
 
 nsresult
 nsSecureBrowserUIImpl::OnSecurityChange(nsIWebProgress* aWebProgress,
                                         nsIRequest* aRequest,
                                         uint32_t state)
 {
--- a/security/manager/ssl/nsSiteSecurityService.cpp
+++ b/security/manager/ssl/nsSiteSecurityService.cpp
@@ -1,38 +1,39 @@
 /* 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 "nsSiteSecurityService.h"
 
-#include "mozilla/LinkedList.h"
-#include "mozilla/Preferences.h"
+#include "CertVerifier.h"
+#include "PublicKeyPinningService.h"
+#include "ScopedNSSTypes.h"
+#include "SharedCertVerifier.h"
+#include "base64.h"
+#include "mozilla/Assertions.h"
 #include "mozilla/Base64.h"
-#include "base64.h"
-#include "CertVerifier.h"
+#include "mozilla/LinkedList.h"
+#include "mozilla/Logging.h"
+#include "mozilla/Preferences.h"
 #include "nsCRTGlue.h"
 #include "nsISSLStatus.h"
 #include "nsISocketProvider.h"
 #include "nsIURI.h"
 #include "nsIX509Cert.h"
+#include "nsNSSComponent.h"
 #include "nsNetUtil.h"
-#include "nsNSSComponent.h"
 #include "nsSecurityHeaderParser.h"
 #include "nsString.h"
 #include "nsThreadUtils.h"
 #include "nsXULAppAPI.h"
 #include "pkix/pkixtypes.h"
 #include "plstr.h"
-#include "mozilla/Logging.h"
 #include "prnetdb.h"
 #include "prprf.h"
-#include "PublicKeyPinningService.h"
-#include "ScopedNSSTypes.h"
-#include "SharedCertVerifier.h"
 
 // A note about the preload list:
 // When a site specifically disables HSTS by sending a header with
 // 'max-age: 0', we keep a "knockout" value that means "we have no information
 // regarding the HSTS state of this host" (any ancestor of "this host" can still
 // influence its HSTS status via include subdomains, however).
 // This prevents the preload list from overriding the site's current
 // desired HSTS status.
@@ -222,17 +223,17 @@ NS_IMPL_ISUPPORTS(nsSiteSecurityService,
                   nsIObserver,
                   nsISiteSecurityService)
 
 nsresult
 nsSiteSecurityService::Init()
 {
   // Don't access Preferences off the main thread.
   if (!NS_IsMainThread()) {
-    NS_NOTREACHED("nsSiteSecurityService initialized off main thread");
+    MOZ_ASSERT_UNREACHABLE("nsSiteSecurityService initialized off main thread");
     return NS_ERROR_NOT_SAME_THREAD;
   }
 
   mMaxMaxAge = mozilla::Preferences::GetInt(
     "security.cert_pinning.max_max_age_seconds", kSixtyDaysInSeconds);
   mozilla::Preferences::AddStrongObserver(this,
     "security.cert_pinning.max_max_age_seconds");
   mUsePreloadList = mozilla::Preferences::GetBool(
@@ -300,17 +301,17 @@ SetStorageKey(nsAutoCString& storageKey,
   switch (aType) {
     case nsISiteSecurityService::HEADER_HSTS:
       storageKey.AppendLiteral(":HSTS");
       break;
     case nsISiteSecurityService::HEADER_HPKP:
       storageKey.AppendLiteral(":HPKP");
       break;
     default:
-      NS_ASSERTION(false, "SSS:SetStorageKey got invalid type");
+      MOZ_ASSERT_UNREACHABLE("SSS:SetStorageKey got invalid type");
   }
 }
 
 // Expire times are in millis.  Since Headers max-age is in seconds, and
 // PR_Now() is in micros, normalize the units at milliseconds.
 static int64_t
 ExpireTimeFromMaxAge(uint64_t maxAge)
 {
@@ -1358,23 +1359,22 @@ nsSiteSecurityService::SetHPKPState(cons
   return NS_OK;
 }
 
 //------------------------------------------------------------
 // nsSiteSecurityService::nsIObserver
 //------------------------------------------------------------
 
 NS_IMETHODIMP
-nsSiteSecurityService::Observe(nsISupports *subject,
-                               const char *topic,
-                               const char16_t *data)
+nsSiteSecurityService::Observe(nsISupports* /*subject*/, const char* topic,
+                               const char16_t* /*data*/)
 {
   // Don't access Preferences off the main thread.
   if (!NS_IsMainThread()) {
-    NS_NOTREACHED("Preferences accessed off main thread");
+    MOZ_ASSERT_UNREACHABLE("Preferences accessed off main thread");
     return NS_ERROR_NOT_SAME_THREAD;
   }
 
   if (strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) {
     mUsePreloadList = mozilla::Preferences::GetBool(
       "network.stricttransportsecurity.preloadlist", true);
     mPreloadListTimeOffset =
       mozilla::Preferences::GetInt("test.currentTimeOffsetSeconds", 0);
--- a/storage/.eslintrc.js
+++ b/storage/.eslintrc.js
@@ -1,7 +1,11 @@
 "use strict";
 
 module.exports = {
   "extends": [
     "../toolkit/.eslintrc.js"
-  ]
+  ],
+
+  rules: {
+    "no-undef": "error"
+  }
 };
new file mode 100644
--- /dev/null
+++ b/storage/test/unit/.eslintrc.js
@@ -0,0 +1,7 @@
+"use strict";
+
+module.exports = {
+  "extends": [
+    "../../../testing/xpcshell/xpcshell.eslintrc.js"
+  ]
+};
--- a/storage/test/unit/test_statement_executeAsync.js
+++ b/storage/test/unit/test_statement_executeAsync.js
@@ -2,16 +2,19 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*
  * This file tests the functionality of mozIStorageBaseStatement::executeAsync
  * for both mozIStorageStatement and mozIStorageAsyncStatement.
  */
 
+// This file uses the internal _quit from testing/xpcshell/head.js */
+/* global _quit */
+
 const INTEGER = 1;
 const TEXT = "this is test text";
 const REAL = 3.23;
 const BLOB = [1, 2];
 
 /**
  * Execute the given statement asynchronously, spinning an event loop until the
  * async statement completes.
--- a/taskcluster/ci/test/test-platforms.yml
+++ b/taskcluster/ci/test/test-platforms.yml
@@ -48,16 +48,17 @@ linux64-asan/opt:
 linux64-stylo/debug:
     build-platform: linux64-stylo/debug
     test-sets:
         - stylo-tests
 linux64-stylo/opt:
     build-platform: linux64-stylo/opt
     test-sets:
         - stylo-tests
+        - talos
 
 linux64-ccov/opt:
     build-platform: linux64-ccov/opt
     test-sets:
         - ccov-code-coverage-tests
 linux64-jsdcov/opt:
     build-platform: linux64-jsdcov/opt
     test-sets:
--- a/taskcluster/ci/test/tests.yml
+++ b/taskcluster/ci/test/tests.yml
@@ -254,17 +254,17 @@ marionette:
             android.*: 3
             default: default
     chunks:
         by-test-platform:
             android.*: 10
             default: 1
     mozharness:
         by-test-platform:
-            android:
+            android.*:
                 script: android_emulator_unittest.py
                 no-read-buildbot-config: true
                 config:
                     - android/androidarm_4_3.py
                     - remove_executables.py
                     - android/androidarm_4_3-tc.py
                 extra-options:
                     - --test-suite=marionette
@@ -548,17 +548,17 @@ mochitest-devtools-chrome:
                 linux.*:
                     - unittests/linux_unittest.py
                     - remove_executables.py
         extra-options:
             by-test-platform:
                 linux64-ccov/opt:
                     - --mochitest-suite=mochitest-devtools-chrome-chunked
                     - --code-coverage
-                linux64-jsdcov:
+                linux64-jsdcov/opt:
                     - --mochitest-suite=mochitest-devtools-chrome-coverage
                 default:
                     - --mochitest-suite=mochitest-devtools-chrome-chunked
     instance-size:
         by-test-platform:
             # Bug 1281241: migrating to m3.large instances
             linux64-asan/opt: legacy
             default: default
--- a/taskcluster/docs/transforms.rst
+++ b/taskcluster/docs/transforms.rst
@@ -62,23 +62,30 @@ For example, a test description's chunks
 In the item, this looks like:
 
 .. code-block:: yaml
 
     chunks:
         by-test-platform:
             linux64/debug: 12
             linux64/opt: 8
+            android.*: 14
             default: 10
 
 This is a simple but powerful way to encode business rules in the items
 provided as input to the transforms, rather than expressing those rules in the
 transforms themselves.  If you are implementing a new business rule, prefer
 this mode where possible.  The structure is easily resolved to a single value
-using :func:`taskgraph.transform.base.get_keyed_by`.
+using :func:`taskgraph.transform.base.resolve_keyed_by`.
+
+Exact matches are used immediately.  If no exact matches are found, each
+alternative is treated as a regular expression, matched against the whole
+value.  Thus ``android.*`` would match ``android-api-15/debug``.  If nothing
+matches as a regular expression, but there is a ``default`` alternative, it is
+used.  Otherwise, an exception is raised and graph generation stops.
 
 Organization
 -------------
 
 Task creation operates broadly in a few phases, with the interfaces of those
 stages defined by schemas.  The process begins with the raw data structures
 parsed from the YAML files in the kind configuration.  This data can processed
 by kind-specific transforms resulting, for test jobs, in a "test description".
--- a/taskcluster/taskgraph/test/test_transforms_base.py
+++ b/taskcluster/taskgraph/test/test_transforms_base.py
@@ -3,17 +3,17 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 import unittest
 from mozunit import main
 from taskgraph.transforms.base import (
     validate_schema,
-    get_keyed_by,
+    resolve_keyed_by,
     TransformSequence
 )
 from voluptuous import Schema
 
 schema = Schema({
     'x': int,
     'y': basestring,
 })
@@ -54,90 +54,81 @@ class TestValidateSchema(unittest.TestCa
     def test_invalid(self):
         try:
             validate_schema(schema, {'x': 'not-int'}, "pfx")
             self.fail("no exception raised")
         except Exception, e:
             self.failUnless(str(e).startswith("pfx\n"))
 
 
-class TestKeyedBy(unittest.TestCase):
+class TestResolveKeyedBy(unittest.TestCase):
 
-    def test_simple_value(self):
-        test = {
-            'test-name': 'tname',
-            'option': 10,
-        }
-        self.assertEqual(get_keyed_by(test, 'option', 'x'), 10)
+    def test_no_by(self):
+        self.assertEqual(
+            resolve_keyed_by({'x': 10}, 'z', 'n'),
+            {'x': 10})
+
+    def test_no_by_dotted(self):
+        self.assertEqual(
+            resolve_keyed_by({'x': {'y': 10}}, 'x.z', 'n'),
+            {'x': {'y': 10}})
 
-    def test_by_value(self):
-        test = {
-            'test-name': 'tname',
-            'option': {
-                'by-other-value': {
-                    'a': 10,
-                    'b': 20,
-                },
-            },
-            'other-value': 'b',
-        }
-        self.assertEqual(get_keyed_by(test, 'option', 'x'), 20)
+    def test_no_by_not_dict(self):
+        self.assertEqual(
+            resolve_keyed_by({'x': 10}, 'x.y', 'n'),
+            {'x': 10})
+
+    def test_no_by_not_by(self):
+        self.assertEqual(
+            resolve_keyed_by({'x': {'a': 10}}, 'x', 'n'),
+            {'x': {'a': 10}})
 
-    def test_by_value_regex(self):
-        test = {
-            'test-name': 'tname',
-            'option': {
-                'by-test-platform': {
-                    'macosx64/.*': 10,
-                    'linux64/debug': 20,
-                    'default': 5,
-                },
-            },
-            'test-platform': 'macosx64/debug',
-        }
-        self.assertEqual(get_keyed_by(test, 'option', 'x'), 10)
+    def test_no_by_empty_dict(self):
+        self.assertEqual(
+            resolve_keyed_by({'x': {}}, 'x', 'n'),
+            {'x': {}})
+
+    def test_no_by_not_only_by(self):
+        self.assertEqual(
+            resolve_keyed_by({'x': {'by-y': True, 'a': 10}}, 'x', 'n'),
+            {'x': {'by-y': True, 'a': 10}})
 
-    def test_by_value_default(self):
-        test = {
-            'test-name': 'tname',
-            'option': {
-                'by-other-value': {
-                    'a': 10,
-                    'default': 30,
-                },
-            },
-            'other-value': 'xxx',
-        }
-        self.assertEqual(get_keyed_by(test, 'option', 'x'), 30)
+    def test_match_nested_exact(self):
+        self.assertEqual(
+            resolve_keyed_by(
+                {'f': 'shoes', 'x': {'y': {'by-f': {'shoes': 'feet', 'gloves': 'hands'}}}},
+                'x.y', 'n'),
+            {'f': 'shoes', 'x': {'y': 'feet'}})
+
+    def test_match_regexp(self):
+        self.assertEqual(
+            resolve_keyed_by(
+                {'f': 'shoes', 'x': {'by-f': {'s?[hH]oes?': 'feet', 'gloves': 'hands'}}},
+                'x', 'n'),
+            {'f': 'shoes', 'x': 'feet'})
 
-    def test_by_value_dict(self):
-        test = {
-            'test-name': 'tname',
-            'option': {
-                'by-something-else': {},
-                'by-other-value': {},
-            },
-        }
-        self.assertEqual(get_keyed_by(test, 'option', 'x'), test['option'])
+    def test_match_partial_regexp(self):
+        self.assertEqual(
+            resolve_keyed_by(
+                {'f': 'shoes', 'x': {'by-f': {'sh': 'feet', 'default': 'hands'}}},
+                'x', 'n'),
+            {'f': 'shoes', 'x': 'hands'})
 
-    def test_by_value_invalid_no_default(self):
-        test = {
-            'test-name': 'tname',
-            'option': {
-                'by-other-value': {
-                    'a': 10,
-                },
-            },
-            'other-value': 'b',
-        }
-        self.assertRaises(Exception, get_keyed_by, test, 'option', 'x')
+    def test_match_default(self):
+        self.assertEqual(
+            resolve_keyed_by(
+                {'f': 'shoes', 'x': {'by-f': {'hat': 'head', 'default': 'anywhere'}}},
+                'x', 'n'),
+            {'f': 'shoes', 'x': 'anywhere'})
 
-    def test_by_value_no_by(self):
-        test = {
-            'test-name': 'tname',
-            'option': {
-                'other-value': {},
-            },
-        }
-        self.assertEqual(get_keyed_by(test, 'option', 'x'), test['option'])
+    def test_no_match(self):
+        self.assertRaises(
+            Exception, resolve_keyed_by,
+            {'f': 'shoes', 'x': {'by-f': {'hat': 'head'}}}, 'x', 'n')
+
+    def test_multiple_matches(self):
+        self.assertRaises(
+            Exception, resolve_keyed_by,
+            {'f': 'hats', 'x': {'by-f': {'hat.*': 'head', 'ha.*': 'hair'}}}, 'x', 'n')
+
 
 if __name__ == '__main__':
     main()
--- a/taskcluster/taskgraph/transforms/base.py
+++ b/taskcluster/taskgraph/transforms/base.py
@@ -88,62 +88,75 @@ def optionally_keyed_by(*arguments):
     subschema = arguments[-1]
     fields = arguments[:-1]
     options = [subschema]
     for field in fields:
         options.append({'by-' + field: {basestring: subschema}})
     return voluptuous.Any(*options)
 
 
-def get_keyed_by(item, field, item_name, subfield=None):
+def resolve_keyed_by(item, field, item_name):
     """
     For values which can either accept a literal value, or be keyed by some
-    other attribute of the item, perform that lookup.  For example, this supports
+    other attribute of the item, perform that lookup and replacement in-place
+    (modifying `item` directly).  The field is specified using dotted notation
+    to traverse dictionaries.
+
+    For example, given item
 
-        chunks:
-            by-test-platform:
-                macosx-10.11/debug: 13
-                win.*: 6
-                default: 12
+        job:
+            chunks:
+                by-test-platform:
+                    macosx-10.11/debug: 13
+                    win.*: 6
+                    default: 12
+
+    a call to `resolve_keyed_by(item, 'job.chunks', item['thing-name'])
+    would mutate item in-place to
+
+        job:
+            chunks: 12
 
     The `item_name` parameter is used to generate useful error messages.
-    The `subfield` parameter, if specified, allows access to a second level
-    of the item dictionary: item[field][subfield]. For example, this supports
+    """
+    # find the field, returning the item unchanged if anything goes wrong
+    container, subfield = item, field
+    while '.' in subfield:
+        f, subfield = subfield.split('.', 1)
+        if f not in container:
+            return item
+        container = container[f]
+        if not isinstance(container, dict):
+            return item
 
-        mozharness:
-            config:
-                by-test-platform:
-                    default: ...
-    """
-    value = item[field]
-    if not isinstance(value, dict):
-        return value
-    if subfield:
-        value = item[field][subfield]
-        if not isinstance(value, dict):
-            return value
+    if subfield not in container:
+        return item
+    value = container[subfield]
+    if not isinstance(value, dict) or len(value) != 1 or not value.keys()[0].startswith('by-'):
+        return item
 
-    keyed_by = value.keys()[0]
-    if len(value) > 1 or not keyed_by.startswith('by-'):
-        return value
+    keyed_by = value.keys()[0][3:]  # strip off 'by-' prefix
+    key = item[keyed_by]
+    alternatives = value.values()[0]
 
-    values = value[keyed_by]
-    keyed_by = keyed_by[3:]  # strip 'by-' off the keyed-by field name
-    if item[keyed_by] in values:
-        return values[item[keyed_by]]
+    # exact match
+    if key in alternatives:
+        container[subfield] = alternatives[key]
+        return item
 
-    matches = [(k, v) for k, v in values.iteritems() if re.match(k, item[keyed_by])]
+    # regular expression match
+    matches = [(k, v) for k, v in alternatives.iteritems() if re.match(k + '$', key)]
     if len(matches) > 1:
         raise Exception(
             "Multiple matching values for {} {!r} found while determining item {} in {}".format(
-                keyed_by, item[keyed_by], field, item_name))
+                keyed_by, key, field, item_name))
     elif matches:
-        return matches[0][1]
+        container[subfield] = matches[0][1]
+        return item
 
-    if 'default' in values:
-        return values['default']
-    for k in item[keyed_by], 'default':
-        if k in values:
-            return values[k]
-    else:
-        raise Exception(
-            "No {} matching {!r} nor 'default' found while determining item {} in {}".format(
-                keyed_by, item[keyed_by], field, item_name))
+    # default
+    if 'default' in alternatives:
+        container[subfield] = alternatives['default']
+        return item
+
+    raise Exception(
+        "No {} matching {!r} nor 'default' found while determining item {} in {}".format(
+            keyed_by, key, field, item_name))
--- a/taskcluster/taskgraph/transforms/job/__init__.py
+++ b/taskcluster/taskgraph/transforms/job/__init__.py
@@ -10,17 +10,17 @@ run-using handlers in `taskcluster/taskg
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 import copy
 import logging
 import os
 
-from taskgraph.transforms.base import get_keyed_by, validate_schema, TransformSequence
+from taskgraph.transforms.base import resolve_keyed_by, validate_schema, TransformSequence
 from taskgraph.transforms.task import task_description_schema
 from voluptuous import (
     Any,
     Extra,
     Optional,
     Required,
     Schema,
 )
@@ -99,25 +99,25 @@ def expand_platforms(config, jobs):
             del pjob['platforms']
 
             platform, buildtype = platform.rsplit('/', 1)
             pjob['name'] = '{}-{}-{}'.format(pjob['name'], platform, buildtype)
             yield pjob
 
 
 @transforms.add
-def resolve_keyed_by(config, jobs):
+def handle_keyed_by(config, jobs):
     fields = [
         'worker-type',
         'worker',
     ]
 
     for job in jobs:
         for field in fields:
-            job[field] = get_keyed_by(item=job, field=field, item_name=job['name'])
+            resolve_keyed_by(job, field, item_name=job['name'])
         yield job
 
 
 @transforms.add
 def make_task_description(config, jobs):
     """Given a build description, create a task description"""
     # import plugin modules first, before iterating over jobs
     import_all()
--- a/taskcluster/taskgraph/transforms/tests.py
+++ b/taskcluster/taskgraph/transforms/tests.py
@@ -14,17 +14,17 @@ This is a good place to translate a test
 The test description should be fully formed by the time it reaches these
 transforms, and these transforms should not embody any specific knowledge about
 what should run where. this is the wrong place for special-casing platforms,
 for example - use `all_tests.py` instead.
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
-from taskgraph.transforms.base import TransformSequence, get_keyed_by
+from taskgraph.transforms.base import TransformSequence, resolve_keyed_by
 from taskgraph.util.treeherder import split_symbol, join_symbol
 from taskgraph.transforms.job.common import (
     docker_worker_support_vcs_checkout,
 )
 from taskgraph.transforms.base import validate_schema, optionally_keyed_by
 from voluptuous import (
     Any,
     Optional,
@@ -58,16 +58,17 @@ ARTIFACTS = [
 ]
 
 BUILDER_NAME_PREFIX = {
     'linux64-pgo': 'Ubuntu VM 12.04 x64',
     'linux64': 'Ubuntu VM 12.04 x64',
     'linux64-asan': 'Ubuntu ASAN VM 12.04 x64',
     'linux64-ccov': 'Ubuntu Code Coverage VM 12.04 x64',
     'linux64-jsdcov': 'Ubuntu Code Coverage VM 12.04 x64',
+    'linux64-stylo': 'Ubuntu VM 12.04 x64',
     'macosx64': 'Rev7 MacOSX Yosemite 10.10.5',
     'android-4.3-arm7-api-15': 'Android 4.3 armv7 API 15+',
     'android-4.2-x86': 'Android 4.2 x86 Emulator',
     'android-4.3-arm7-api-15-gradle': 'Android 4.3 armv7 API 15+',
 }
 
 logger = logging.getLogger(__name__)
 
@@ -290,22 +291,20 @@ test_description_schema = Schema({
 @transforms.add
 def validate(config, tests):
     for test in tests:
         yield validate_schema(test_description_schema, test,
                               "In test {!r}:".format(test['test-name']))
 
 
 @transforms.add
-def resolve_keyed_by_mozharness(config, tests):
+def handle_keyed_by_mozharness(config, tests):
     """Resolve a mozharness field if it is keyed by something"""
     for test in tests:
-        test['mozharness'] = get_keyed_by(
-            item=test, field='mozharness',
-            item_name=test['test-name'])
+        resolve_keyed_by(test, 'mozharness', item_name=test['test-name'])
         yield test
 
 
 @transforms.add
 def set_defaults(config, tests):
     for test in tests:
         build_platform = test['build-platform']
         if build_platform.startswith('android'):
@@ -402,17 +401,17 @@ def set_worker_implementation(config, te
 
 
 @transforms.add
 def set_tier(config, tests):
     """Set the tier based on policy for all test descriptions that do not
     specify a tier otherwise."""
     for test in tests:
         if 'tier' in test:
-            test['tier'] = get_keyed_by(item=test, field='tier', item_name=test['test-name'])
+            resolve_keyed_by(test, 'tier', item_name=test['test-name'])
 
         # only override if not set for the test
         if 'tier' not in test or test['tier'] == 'default':
             if test['test-platform'] in ['linux64/debug',
                                          'linux64-asan/opt',
                                          'android-4.3-arm7-api-15/debug',
                                          'android-x86/opt']:
                 test['tier'] = 1
@@ -448,38 +447,33 @@ def set_download_symbols(config, tests):
             if 'download-symbols' in test['mozharness']:
                 del test['mozharness']['download-symbols']
         else:
             test['mozharness']['download-symbols'] = 'ondemand'
         yield test
 
 
 @transforms.add
-def resolve_keyed_by(config, tests):
+def handle_keyed_by(config, tests):
     """Resolve fields that can be keyed by platform, etc."""
     fields = [
         'instance-size',
         'docker-image',
         'max-run-time',
         'chunks',
         'e10s',
         'suite',
         'run-on-projects',
         'os-groups',
-    ]
-    mozharness_fields = [
-        'config',
-        'extra-options',
+        'mozharness.config',
+        'mozharness.extra-options',
     ]
     for test in tests:
         for field in fields:
-            test[field] = get_keyed_by(item=test, field=field, item_name=test['test-name'])
-        for subfield in mozharness_fields:
-            test['mozharness'][subfield] = get_keyed_by(
-                item=test, field='mozharness', subfield=subfield, item_name=test['test-name'])
+            resolve_keyed_by(test, field, item_name=test['test-name'])
         yield test
 
 
 @transforms.add
 def split_e10s(config, tests):
     for test in tests:
         e10s = test['e10s']
 
@@ -493,20 +487,16 @@ def split_e10s(config, tests):
         if e10s:
             test['test-name'] += '-e10s'
             test['e10s'] = True
             test['attributes']['e10s'] = True
             group, symbol = split_symbol(test['treeherder-symbol'])
             if group != '?':
                 group += '-e10s'
             test['treeherder-symbol'] = join_symbol(group, symbol)
-            test['mozharness']['extra-options'] = get_keyed_by(item=test,
-                                                               field='mozharness',
-                                                               subfield='extra-options',
-                                                               item_name=test['test-name'])
             test['mozharness']['extra-options'].append('--e10s')
         yield test
 
 
 @transforms.add
 def split_chunks(config, tests):
     """Based on the 'chunks' key, split tests up into chunks by duplicating
     them and assigning 'this-chunk' appropriately and updating the treeherder
--- a/testing/marionette/browser.js
+++ b/testing/marionette/browser.js
@@ -1,25 +1,21 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-
-Cu.import("resource://gre/modules/Log.jsm");
+const {utils: Cu} = Components;
 
 Cu.import("chrome://marionette/content/element.js");
 Cu.import("chrome://marionette/content/frame.js");
 
 this.EXPORTED_SYMBOLS = ["browser"];
 
-const logger = Log.repository.getLogger("Marionette");
-
 this.browser = {};
 
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
 /**
  * Creates a browsing context wrapper.
  *
  * Browsing contexts handle interactions with the browser, according to
@@ -27,52 +23,72 @@ const XUL_NS = "http://www.mozilla.org/k
  *
  * @param {nsIDOMWindow} win
  *     The window whose browser needs to be accessed.
  * @param {GeckoDriver} driver
  *     Reference to the driver the browser is attached to.
  */
 browser.Context = class {
 
+  /**
+   * @param {<xul:browser>} win
+   *     Frame that is expected to contain the view of the web document.
+   * @param {GeckoDriver} driver
+   *     Reference to driver instance.
+   */
   constructor(win, driver) {
-    this.browser = undefined;
     this.window = win;
     this.driver = driver;
+
+    // In Firefox this is <xul:tabbrowser> (not <xul:browser>!)
+    // and BrowserApp in Fennec
+    this.browser = undefined;
+    this.setBrowser(win);
+
     this.knownFrames = [];
-    this.startPage = "about:blank";
-    // used in B2G to identify the homescreen content page
+
+    // Used in B2G to identify the homescreen content page
     this.mainContentId = null;
-    // used to set curFrameId upon new session
+
+    // Used to set curFrameId upon new session
     this.newSession = true;
+
     this.seenEls = new element.Store();
-    this.setBrowser(win);
 
     // A reference to the tab corresponding to the current window handle, if any.
     // Specifically, this.tab refers to the last tab that Marionette switched
     // to in this browser window. Note that this may not equal the currently
     // selected tab. For example, if Marionette switches to tab A, and then
     // clicks on a button that opens a new tab B in the same browser window,
     // this.tab will still point to tab A, despite tab B being the currently
     // selected tab.
     this.tab = null;
     this.pendingCommands = [];
 
-    // we should have one FM per BO so that we can handle modals in each Browser
+    // We should have one frame.Manager per browser.Context so that we
+    // can handle modals in each <xul:browser>.
     this.frameManager = new frame.Manager(driver);
     this.frameRegsPending = 0;
 
     // register all message listeners
     this.frameManager.addMessageManagerListeners(driver.mm);
     this.getIdForBrowser = driver.getIdForBrowser.bind(driver);
     this.updateIdForBrowser = driver.updateIdForBrowser.bind(driver);
     this._curFrameId = null;
     this._browserWasRemote = null;
     this._hasRemotenessChange = false;
   }
 
+  /**
+   * Get the <xul:browser> for the current tab in this tab browser.
+   *
+   * @return {<xul:browser>}
+   *     Browser linked to |this.tab| or the tab browser's
+   *     |selectedBrowser|.
+   */
   get browserForTab() {
     if (this.browser.getBrowserForTab) {
       return this.browser.getBrowserForTab(this.tab);
     } else {
       return this.browser.selectedBrowser;
     }
   }
 
--- a/testing/marionette/capture.js
+++ b/testing/marionette/capture.js
@@ -12,29 +12,34 @@ this.EXPORTED_SYMBOLS = ["capture"];
 const CONTEXT_2D = "2d";
 const BG_COLOUR = "rgb(255,255,255)";
 const PNG_MIME = "image/png";
 const XHTML_NS = "http://www.w3.org/1999/xhtml";
 
 /** Provides primitives to capture screenshots. */
 this.capture = {};
 
+capture.Format = {
+  Base64: 0,
+  Hash: 1,
+};
+
 /**
  * Take a screenshot of a single element.
  *
  * @param {Node} node
  *     The node to take a screenshot of.
  * @param {Array.<Node>=} highlights
  *     Optional array of nodes, around which a border will be marked to
  *     highlight them in the screenshot.
  *
  * @return {HTMLCanvasElement}
  *     The canvas element where the element has been painted on.
  */
-capture.element = function (node, highlights=[]) {
+capture.element = function (node, highlights = []) {
   let win = node.ownerDocument.defaultView;
   let rect = node.getBoundingClientRect();
 
   return capture.canvas(
       win,
       rect.left,
       rect.top,
       rect.width,
@@ -51,17 +56,17 @@ capture.element = function (node, highli
  *     and the offsets for the viewport.
  * @param {Array.<Node>=} highlights
  *     Optional array of nodes, around which a border will be marked to
  *     highlight them in the screenshot.
  *
  * @return {HTMLCanvasElement}
  *     The canvas element where the viewport has been painted on.
  */
-capture.viewport = function (win, highlights=[]) {
+capture.viewport = function (win, highlights = []) {
   let rootNode = win.document.documentElement;
 
   return capture.canvas(
       win,
       win.pageXOffset,
       win.pageYOffset,
       rootNode.clientWidth,
       rootNode.clientHeight,
@@ -85,17 +90,17 @@ capture.viewport = function (win, highli
  * @param {Array.<Node>=} highlights
  *     Optional array of nodes, around which a border will be marked to
  *     highlight them in the screenshot.
  *
  * @return {HTMLCanvasElement}
  *     The canvas on which the selection from the window's framebuffer
  *     has been painted on.
  */
-capture.canvas = function (win, left, top, width, height, highlights=[]) {
+capture.canvas = function (win, left, top, width, height, highlights = []) {
   let scale = win.devicePixelRatio;
 
   let canvas = win.document.createElementNS(XHTML_NS, "canvas");
   canvas.width = width * scale;
   canvas.height = height * scale;
 
   let ctx = canvas.getContext(CONTEXT_2D);
   let flags = ctx.DRAWWINDOW_DRAW_CARET;
@@ -107,17 +112,17 @@ capture.canvas = function (win, left, to
 
   ctx.scale(scale, scale);
   ctx.drawWindow(win, left, top, width, height, BG_COLOUR, flags);
   ctx = capture.highlight_(ctx, highlights, top, left);
 
   return canvas;
 };
 
-capture.highlight_ = function (context, highlights, top=0, left=0) {
+capture.highlight_ = function (context, highlights, top = 0, left = 0) {
   if (!highlights) {
     return;
   }
 
   context.lineWidth = "2";
   context.strokeStyle = "red";
   context.save();
 
--- a/testing/marionette/client/marionette_driver/marionette.py
+++ b/testing/marionette/client/marionette_driver/marionette.py
@@ -2022,17 +2022,17 @@ class Marionette(object):
         This is the equivalent of calling `document.cookie` and
         parsing the result.
 
         :returns: A list of cookies for the current domain.
         """
         return self._send_message("getCookies", key="value" if self.protocol == 1 else None)
 
     def screenshot(self, element=None, highlights=None, format="base64",
-                   full=True):
+                   full=True, scroll=True):
         """Takes a screenshot of a web element or the current frame.
 
         The screen capture is returned as a lossless PNG image encoded
         as a base 64 string by default. If the `element` argument is defined the
         capture area will be limited to the bounding box of that
         element.  Otherwise, the capture area will be the bounding box
         of the current frame.
 
@@ -2045,28 +2045,33 @@ class Marionette(object):
         :param format: if "base64" (the default), returns the screenshot
             as a base64-string. If "binary", the data is decoded and
             returned as raw binary. If "hash", the data is hashed using
             the SHA-256 algorithm and the result is returned as a hex digest.
 
         :param full: If True (the default), the capture area will be the
             complete frame. Else only the viewport is captured. Only applies
             when `element` is None.
+
+        :param scroll: When `element` is provided, scroll to it before
+            taking the screenshot (default).  Otherwise, avoid scrolling
+            `element` into view.
         """
 
         if element:
             element = element.id
         lights = None
         if highlights:
             lights = [highlight.id for highlight in highlights]
 
         body = {"id": element,
                 "highlights": lights,
                 "full": full,
-                "hash": False}
+                "hash": False,
+                "scroll": scroll}
         if format == "hash":
             body["hash"] = True
         data = self._send_message("takeScreenshot", body, key="value")
 
         if format == "base64" or format == "hash":
             return data
         elif format == "binary":
             return base64.b64decode(data.encode("ascii"))
--- a/testing/marionette/driver.js
+++ b/testing/marionette/driver.js
@@ -593,16 +593,18 @@ GeckoDriver.prototype.newSession = funct
     }, BROWSER_STARTUP_FINISHED, false);
   } else {
     runSessionStart.call(this);
   }
 
   yield registerBrowsers;
   yield browserListening;
 
+  this.curBrowser.browserForTab.focus();
+
   return {
     sessionId: this.sessionId,
     capabilities: this.sessionCapabilities,
   };
 };
 
 /**
  * Send the current session's capabilities to the client.
@@ -1030,16 +1032,17 @@ GeckoDriver.prototype.get = function*(cm
     cmd.parameters.command_id = id;
     cmd.parameters.pageTimeout = this.pageTimeout;
     this.mm.broadcastAsyncMessage(
         "Marionette:pollForReadyState" + this.curBrowser.curFrameId,
         cmd.parameters);
   });
 
   yield get;
+  this.curBrowser.browserForTab.focus();
 };
 
 /**
  * Get a string representing the current URL.
  *
  * On Desktop this returns a string representation of the URL of the
  * current top level browsing context.  This is equivalent to
  * document.location.href.
@@ -2339,18 +2342,17 @@ GeckoDriver.prototype.sessionTearDown = 
 
   this.switchToGlobalMessageManager();
 
   // reset frame to the top-most frame
   this.curFrame = null;
   if (this.mainFrame) {
     try {
       this.mainFrame.focus();
-    }
-    catch (e) {
+    } catch (e) {
       this.mainFrame = null;
     }
   }
 
   this.sessionId = null;
 
   if (this.observing !== null) {
     for (let topic in this.observing) {
@@ -2420,79 +2422,78 @@ GeckoDriver.prototype.clearImportedScrip
  * If called in the content context, the <code>id</code> argument is not null
  * and refers to a present and visible web element's ID, the capture area
  * will be limited to the bounding box of that element. Otherwise, the
  * capture area will be the bounding box of the current frame.
  *
  * If called in the chrome context, the screenshot will always represent the
  * entire viewport.
  *
- * @param {string} id
- *     Reference to a web element.
- * @param {string} highlights
+ * @param {string=} id
+ *     Optional web element reference to take a screenshot of.
+ *     If undefined, a screenshot will be taken of the document element.
+ * @param {Array.<string>=} highlights
  *     List of web elements to highlight.
  * @param {boolean} full
  *     True to take a screenshot of the entire document element. Is not
  *     considered if {@code id} is not defined. Defaults to true.
- * @param {boolean} hash
+ * @param {boolean=} hash
  *     True if the user requests a hash of the image data.
+ * @param {boolean=} scroll
+ *     Scroll to element if |id| is provided.  If undefined, it will
+ *     scroll to the element.
  *
  * @return {string}
  *     If {@code hash} is false, PNG image encoded as base64 encoded string. If
  *     'hash' is True, hex digest of the SHA-256 hash of the base64 encoded
  *     string.
  */
 GeckoDriver.prototype.takeScreenshot = function (cmd, resp) {
-  let {id, highlights, full, hash} = cmd.parameters;
+  let {id, highlights, full, hash, scroll} = cmd.parameters;
   highlights = highlights || [];
+  let format = hash ? capture.Format.Hash : capture.Format.Base64;
 
   switch (this.context) {
     case Context.CHROME:
-      let canvas;
-      let highlightEls = [];
-
       let container = {frame: this.getCurrentWindow().document.defaultView};
-
       if (!container.frame) {
-        throw new NoSuchWindowError('Unable to locate window');
+        throw new NoSuchWindowError("Unable to locate window");
       }
 
-      for (let h of highlights) {
-        let el = this.curBrowser.seenEls.get(h, container);
-        highlightEls.push(el);
-      }
+      let highlightEls = highlights.map(
+          ref => this.curBrowser.seenEls.get(ref, container));
 
       // viewport
+      let canvas;
       if (!id && !full) {
         canvas = capture.viewport(container.frame, highlightEls);
 
       // element or full document element
       } else {
         let node;
         if (id) {
           node = this.curBrowser.seenEls.get(id, container);
         } else {
           node = container.frame.document.documentElement;
         }
 
         canvas = capture.element(node, highlightEls);
       }
 
-      if (hash) {
-        return capture.toHash(canvas);
-      } else {
-        return capture.toBase64(canvas);
+      switch (format) {
+        case capture.Format.Hash:
+          return capture.toHash(canvas);
+
+        case capture.Format.Base64:
+          return capture.toBase64(canvas);
       }
+      break;
 
     case Context.CONTENT:
-      if (hash) {
-        return this.listener.getScreenshotHash(id, full, highlights);
-      } else {
-        return this.listener.takeScreenshot(id, full, highlights);
-      }
+      return this.listener.takeScreenshot(format, cmd.parameters);
   }
 };
 
 /**
  * Get the current browser orientation.
  *
  * Will return one of the valid primary orientation values
  * portrait-primary, landscape-primary, portrait-secondary, or
--- a/testing/marionette/harness/marionette_harness/runner/base.py
+++ b/testing/marionette/harness/marionette_harness/runner/base.py
@@ -437,23 +437,16 @@ class BaseMarionetteArguments(ArgumentPa
                 self.error('Chunk to run must be between 1 and {}.'.format(args.total_chunks))
 
         if args.jsdebugger:
             args.app_args.append('-jsdebugger')
             args.socket_timeout = None
 
         args.prefs = self._get_preferences(args.prefs_files, args.prefs_args)
 
-        if args.e10s:
-            args.prefs.update({
-                'browser.tabs.remote.autostart': True,
-                'browser.tabs.remote.force-enable': True,
-                'extensions.e10sBlocksEnabling': False
-            })
-
         for container in self.argument_containers:
             if hasattr(container, 'verify_usage_handler'):
                 container.verify_usage_handler(args)
 
         return args
 
 
 class RemoteMarionetteArguments(object):
@@ -545,16 +538,22 @@ class BaseMarionetteTestRunner(object):
         self.test_tags = test_tags
         self.startup_timeout = startup_timeout
         self.workspace = workspace
         # If no workspace is set, default location for gecko.log is .
         # and default location for profile is TMP
         self.workspace_path = workspace or os.getcwd()
         self.verbose = verbose
         self.e10s = e10s
+        if self.e10s:
+            self.prefs.update({
+                'browser.tabs.remote.autostart': True,
+                'browser.tabs.remote.force-enable': True,
+                'extensions.e10sBlocksEnabling': False
+            })
 
         def gather_debug(test, status):
             # No screenshots and page source for skipped tests
             if status == "SKIP":
                 return
 
             rv = {}
             marionette = test._marionette_weakref()
@@ -827,20 +826,23 @@ class BaseMarionetteTestRunner(object):
 
         device_info = None
         if self.marionette.instance and self.emulator:
             try:
                 device_info = self.marionette.instance.runner.device.dm.getInfo()
             except Exception:
                 self.logger.warning('Could not get device info', exc_info=True)
 
-        if self.e10s:
-            self.logger.info("e10s is enabled")
-        else:
-            self.logger.info("e10s is disabled")
+        appinfo_e10s = self.appinfo.get('browserTabsRemoteAutostart', False)
+        self.logger.info("e10s is {}".format("enabled" if appinfo_e10s else "disabled"))
+        if self.e10s != appinfo_e10s:
+            message_e10s = ("BaseMarionetteTestRunner configuration (self.e10s) does "
+                            "not match browser appinfo")
+            self.cleanup()
+            raise AssertionError(message_e10s)
 
         self.logger.suite_start(self.tests,
                                 version_info=self.version_info,
                                 device_info=device_info)
 
         self._log_skipped_tests()
 
         interrupted = None
--- a/testing/marionette/harness/marionette_harness/runtests.py
+++ b/testing/marionette/harness/marionette_harness/runtests.py
@@ -63,17 +63,20 @@ class MarionetteHarness(object):
     def process_args(self):
         if self.args.get('pydebugger'):
             self._testcase_class.pydebugger = __import__(self.args['pydebugger'])
 
     def run(self):
         self.process_args()
         tests = self.args.pop('tests')
         runner = self._runner_class(**self.args)
-        runner.run_tests(tests)
+        try:
+            runner.run_tests(tests)
+        finally:
+            runner.cleanup()
         return runner.failed + runner.crashed
 
 
 def cli(runner_class=MarionetteTestRunner, parser_class=MarionetteArguments,
         harness_class=MarionetteHarness, testcase_class=MarionetteTestCase, args=None):
     """
     Call the harness to parse args and run tests.
 
--- a/testing/marionette/harness/marionette_harness/tests/harness_unit/conftest.py
+++ b/testing/marionette/harness/marionette_harness/tests/harness_unit/conftest.py
@@ -53,21 +53,17 @@ def mach_parsed_kwargs(logger):
         'log_raw_level': None,
         'log_tbpl': None,
         'log_tbpl_buffer': None,
         'log_tbpl_compact': None,
         'log_tbpl_level': None,
         'log_unittest': None,
         'log_xunit': None,
         'logger_name': 'Marionette-based Tests',
-        'prefs': {
-            'browser.tabs.remote.autostart': True,
-            'browser.tabs.remote.force-enable': True,
-            'extensions.e10sBlocksEnabling': False,
-        },
+        'prefs': {},
         'prefs_args': None,
         'prefs_files': None,
         'profile': None,
         'pydebugger': None,
         'repeat': 0,
         'server_root': None,
         'shuffle': False,
         'shuffle_seed': 2276870381009474531,
--- a/testing/marionette/harness/marionette_harness/tests/harness_unit/test_marionette_runner.py
+++ b/testing/marionette/harness/marionette_harness/tests/harness_unit/test_marionette_runner.py
@@ -24,16 +24,18 @@ def mock_runner(runner, mock_marionette,
     MarionetteTestRunner instance with mocked-out
     self.marionette and other properties,
     to enable testing runner.run_tests().
     """
     runner.driverclass = mock_marionette
     for attr in ['run_test_set', '_capabilities']:
         setattr(runner, attr, Mock())
     runner._appName = 'fake_app'
+    # simulate that browser runs with e10s by default
+    runner._appinfo = {'browserTabsRemoteAutostart': True}
     monkeypatch.setattr('marionette_harness.runner.base.mozversion', Mock())
     return runner
 
 
 @pytest.fixture
 def build_kwargs_using(mach_parsed_kwargs):
     '''Helper function for test_build_kwargs_* functions'''
     def kwarg_builder(new_items, return_socket=False):
@@ -120,16 +122,19 @@ def test_args_passed_to_driverclass(mock
 def test_build_kwargs_basic_args(build_kwargs_using):
     '''Test the functionality of runner._build_kwargs:
     make sure that basic arguments (those which should
     always be included, irrespective of the runner's settings)
     get passed to the call to runner.driverclass'''
 
     basic_args = ['socket_timeout', 'prefs',
                   'startup_timeout', 'verbose', 'symbols_path']
+    args_dict = {a: getattr(sentinel, a) for a in basic_args}
+    # Mock an update method to work with calls to MarionetteTestRunner()
+    args_dict['prefs'].update = Mock(return_value={})
     built_kwargs = build_kwargs_using([(a, getattr(sentinel, a)) for a in basic_args])
     for arg in basic_args:
         assert built_kwargs[arg] is getattr(sentinel, arg)
 
 
 @pytest.mark.parametrize('workspace', ['path/to/workspace', None])
 def test_build_kwargs_with_workspace(build_kwargs_using, workspace):
     built_kwargs = build_kwargs_using({'workspace': workspace})
@@ -405,12 +410,31 @@ def test_catch_invalid_test_names(runner
         runner._add_tests(good_tests + bad_tests)
     msg = exc.value.message
     assert "Test file names must be of the form" in msg
     for bad_name in bad_tests:
         assert bad_name in msg
     for good_name in good_tests:
         assert good_name not in msg
 
+@pytest.mark.parametrize('e10s', (True, False))
+def test_e10s_option_sets_prefs(mach_parsed_kwargs, e10s):
+    mach_parsed_kwargs['e10s'] = e10s
+    runner = MarionetteTestRunner(**mach_parsed_kwargs)
+    e10s_prefs = {
+        'browser.tabs.remote.autostart': True,
+        'browser.tabs.remote.force-enable': True,
+        'extensions.e10sBlocksEnabling': False
+    }
+    for k,v in e10s_prefs.iteritems():
+        if k == 'extensions.e10sBlocksEnabling' and not e10s:
+            continue
+        assert runner.prefs.get(k, False) == (v and e10s)
+
+def test_e10s_option_clash_raises(mock_runner):
+    mock_runner.e10s = False
+    with pytest.raises(AssertionError) as e:
+        mock_runner.run_tests([u'test_fake_thing.py'])
+        assert "configuration (self.e10s) does not match browser appinfo" in e.value.message
 
 if __name__ == '__main__':
     import sys
     sys.exit(pytest.main(['--verbose', __file__]))
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_navigation.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_navigation.py
@@ -178,16 +178,25 @@ class TestNavigate(WindowManagerMixin, M
     def test_image_document_to_image_document(self):
         self.marionette.navigate(self.fixtures.where_is("test.html"))
 
         self.marionette.navigate(self.fixtures.where_is("white.png"))
         self.assertIn("white.png", self.marionette.title)
         self.marionette.navigate(self.fixtures.where_is("black.png"))
         self.assertIn("black.png", self.marionette.title)
 
+    def test_focus_after_navigation(self):
+        self.marionette.quit()
+        self.marionette.start_session()
+
+        self.marionette.navigate(inline("<input autofocus>"))
+        active_el = self.marionette.execute_script("return document.activeElement")
+        focus_el = self.marionette.find_element(By.CSS_SELECTOR, ":focus")
+        self.assertEqual(active_el, focus_el)
+
 
 class TestTLSNavigation(MarionetteTestCase):
     insecure_tls = {"acceptInsecureCerts": True}
     secure_tls = {"acceptInsecureCerts": False}
 
     def setUp(self):
         MarionetteTestCase.setUp(self)
         self.marionette.delete_session()
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_screenshot.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_screenshot.py
@@ -210,20 +210,16 @@ class TestScreenCaptureChrome(WindowMana
         self.assertEqual(self.scale(self.viewport_dimensions),
                          self.get_image_dimensions(screenshot))
         self.assertNotEqual(self.scale(self.window_dimensions),
                             self.get_image_dimensions(screenshot))
 
         self.marionette.close_chrome_window()
         self.marionette.switch_to_window(self.start_window)
 
-    @skip("Bug 1213875")
-    def test_capture_scroll_element_into_view(self):
-        pass
-
     def test_capture_window_already_closed(self):
         dialog = self.open_dialog()
         self.marionette.switch_to_window(dialog)
         self.marionette.close_chrome_window()
 
         self.assertRaises(NoSuchWindowException, self.marionette.screenshot)
         self.marionette.switch_to_window(self.start_window)
 
@@ -392,8 +388,35 @@ class TestScreenCaptureContent(WindowMan
         self.assertNotEqual(screenshot_element, screenshot_highlight)
 
         # Highlighting a sub element
         paragraph = self.marionette.find_element(By.ID, "green")
         screenshot_highlight_paragraph = self.marionette.screenshot(element=element,
                                                                     highlights=[paragraph])
         self.assertNotEqual(screenshot_element, screenshot_highlight_paragraph)
         self.assertNotEqual(screenshot_highlight, screenshot_highlight_paragraph)
+
+    def test_scroll_default(self):
+        self.marionette.navigate(long)
+        before = self.page_y_offset
+        el = self.marionette.find_element(By.TAG_NAME, "p")
+        self.marionette.screenshot(element=el, format="hash")
+        self.assertNotEqual(before, self.page_y_offset)
+
+    def test_scroll(self):
+        self.marionette.navigate(long)
+        before = self.page_y_offset
+        el = self.marionette.find_element(By.TAG_NAME, "p")
+        self.marionette.screenshot(element=el, format="hash", scroll=True)
+        self.assertNotEqual(before, self.page_y_offset)
+
+    def test_scroll_off(self):
+        self.marionette.navigate(long)
+        el = self.marionette.find_element(By.TAG_NAME, "p")
+        before = self.page_y_offset
+        self.marionette.screenshot(element=el, format="hash", scroll=False)
+        self.assertEqual(before, self.page_y_offset)
+
+    def test_scroll_no_element(self):
+        self.marionette.navigate(long)
+        before = self.page_y_offset
+        self.marionette.screenshot(format="hash", scroll=True)
+        self.assertEqual(before, self.page_y_offset)
--- a/testing/marionette/listener.js
+++ b/testing/marionette/listener.js
@@ -234,17 +234,16 @@ var findElementsContentFn = dispatch(fin
 var isElementSelectedFn = dispatch(isElementSelected);
 var clearElementFn = dispatch(clearElement);
 var isElementDisplayedFn = dispatch(isElementDisplayed);
 var getElementValueOfCssPropertyFn = dispatch(getElementValueOfCssProperty);
 var switchToShadowRootFn = dispatch(switchToShadowRoot);
 var getCookiesFn = dispatch(getCookies);
 var singleTapFn = dispatch(singleTap);
 var takeScreenshotFn = dispatch(takeScreenshot);
-var getScreenshotHashFn = dispatch(getScreenshotHash);
 var performActionsFn = dispatch(performActions);
 var releaseActionsFn = dispatch(releaseActions);
 var actionChainFn = dispatch(actionChain);
 var multiActionFn = dispatch(multiAction);
 var addCookieFn = dispatch(addCookie);
 var deleteCookieFn = dispatch(deleteCookie);
 var deleteAllCookiesFn = dispatch(deleteAllCookies);
 var executeFn = dispatch(execute);
@@ -292,17 +291,16 @@ function startListeners() {
   addMessageListenerId("Marionette:switchToFrame", switchToFrame);
   addMessageListenerId("Marionette:switchToParentFrame", switchToParentFrame);
   addMessageListenerId("Marionette:switchToShadowRoot", switchToShadowRootFn);
   addMessageListenerId("Marionette:deleteSession", deleteSession);
   addMessageListenerId("Marionette:sleepSession", sleepSession);
   addMessageListenerId("Marionette:getAppCacheStatus", getAppCacheStatus);
   addMessageListenerId("Marionette:setTestName", setTestName);
   addMessageListenerId("Marionette:takeScreenshot", takeScreenshotFn);
-  addMessageListenerId("Marionette:getScreenshotHash", getScreenshotHashFn);
   addMessageListenerId("Marionette:addCookie", addCookieFn);
   addMessageListenerId("Marionette:getCookies", getCookiesFn);
   addMessageListenerId("Marionette:deleteAllCookies", deleteAllCookiesFn);
   addMessageListenerId("Marionette:deleteCookie", deleteCookieFn);
 }
 
 /**
  * Used during newSession and restart, called to set up the modal dialog listener in b2g
@@ -398,17 +396,16 @@ function deleteSession(msg) {
   removeMessageListenerId("Marionette:switchToFrame", switchToFrame);
   removeMessageListenerId("Marionette:switchToParentFrame", switchToParentFrame);
   removeMessageListenerId("Marionette:switchToShadowRoot", switchToShadowRootFn);
   removeMessageListenerId("Marionette:deleteSession", deleteSession);
   removeMessageListenerId("Marionette:sleepSession", sleepSession);
   removeMessageListenerId("Marionette:getAppCacheStatus", getAppCacheStatus);
   removeMessageListenerId("Marionette:setTestName", setTestName);
   removeMessageListenerId("Marionette:takeScreenshot", takeScreenshotFn);
-  removeMessageListenerId("Marionette:getScreenshotHash", getScreenshotHashFn);
   removeMessageListenerId("Marionette:addCookie", addCookieFn);
   removeMessageListenerId("Marionette:getCookies", getCookiesFn);
   removeMessageListenerId("Marionette:deleteAllCookies", deleteAllCookiesFn);
   removeMessageListenerId("Marionette:deleteCookie", deleteCookieFn);
   if (isB2G) {
     content.removeEventListener("mozbrowsershowmodalprompt", modalHandler, false);
   }
   seenEls.clear();
@@ -1615,94 +1612,74 @@ function deleteAllCookies() {
 function getAppCacheStatus(msg) {
   sendResponse(
       curContainer.frame.applicationCache.status, msg.json.command_id);
 }
 
 /**
  * Perform a screen capture in content context.
  *
- * @param {UUID=} id
- *     Optional web element reference of an element to take a screenshot
- *     of.
- * @param {boolean=} full
- *     True to take a screenshot of the entire document element.  Is not
- *     considered if {@code id} is not defined.  Defaults to true.
- * @param {Array.<UUID>=} highlights
- *     Draw a border around the elements found by their web element
- *     references.
+ * Accepted values for |opts|:
+ *
+ *     @param {UUID=} id
+ *         Optional web element reference of an element to take a screenshot
+ *         of.
+ *     @param {boolean=} full
+ *         True to take a screenshot of the entire document element.  Is not
+ *         considered if {@code id} is not defined.  Defaults to true.
+ *     @param {Array.<UUID>=} highlights
+ *         Draw a border around the elements found by their web element
+ *         references.
+ *     @param {boolean=} scroll
+ *         When |id| is given, scroll it into view before taking the
+ *         screenshot.  Defaults to true.
+ *
+ * @param {capture.Format} format
+ *     Format to return the screenshot in.
+ * @param {Object.<string, ?>} opts
+ *     Options.
  *
  * @return {string}
- *     Base64 encoded string of an image/png type.
+ *     Base64 encoded string or a SHA-256 hash of the screenshot.
  */
-function takeScreenshot(id, full=true, highlights=[]) {
-  let canvas = screenshot(id, full, highlights);
-  return capture.toBase64(canvas);
-}
+function takeScreenshot(format, opts = {}) {
+  let id = opts.id;
+  let full = !!opts.full;
+  let highlights = opts.highlights || [];
+  let scroll = !!opts.scroll;
 
-/**
-* Perform a screen capture in content context.
-*
-* @param {UUID=} id
-*     Optional web element reference of an element to take a screenshot
-*     of.
-* @param {boolean=} full
-*     True to take a screenshot of the entire document element.  Is not
-*     considered if {@code id} is not defined.  Defaults to true.
-* @param {Array.<UUID>=} highlights
-*     Draw a border around the elements found by their web element
-*     references.
-*
-* @return {string}
-*     Hex Digest of a SHA-256 hash of the base64 encoded string of an
-*     image/png type.
-*/
-function getScreenshotHash(id, full=true, highlights=[]) {
-  let canvas = screenshot(id, full, highlights);
-  return capture.toHash(canvas);
-}
+  let highlightEls = highlights.map(ref => seenEls.get(ref, curContainer));
 
-/**
-* Perform a screen capture in content context.
-*
-* @param {UUID=} id
-*     Optional web element reference of an element to take a screenshot
-*     of.
-* @param {boolean=} full
-*     True to take a screenshot of the entire document element.  Is not
-*     considered if {@code id} is not defined.  Defaults to true.
-* @param {Array.<UUID>=} highlights
-*     Draw a border around the elements found by their web element
-*     references.
-*
-* @return {HTMLCanvasElement}
-*     The canvas element to be encoded or hashed.
-*/
-function screenshot(id, full=true, highlights=[]) {
   let canvas;
 
-  let highlightEls = [];
-  for (let h of highlights) {
-    let el = seenEls.get(h, curContainer);
-    highlightEls.push(el);
-  }
-
   // viewport
   if (!id && !full) {
     canvas = capture.viewport(curContainer.frame, highlightEls);
 
   // element or full document element
   } else {
-    let node;
+    let el;
     if (id) {
-      node = seenEls.get(id, curContainer);
+      el = seenEls.get(id, curContainer);
+      if (scroll) {
+        element.scrollIntoView(el);
+      }
     } else {
-      node = curContainer.frame.document.documentElement;
+      el = curContainer.frame.document.documentElement;
     }
 
-    canvas = capture.element(node, highlightEls);
+    canvas = capture.element(el, highlightEls);
   }
 
-  return canvas;
+  switch (format) {
+    case capture.Format.Base64:
+      return capture.toBase64(canvas);
+
+    case capture.Format.Hash:
+      return capture.toHash(canvas);
+
+    default:
+      throw new TypeError("Unknown screenshot format: " + format);
+  }
 }
 
 // Call register self when we get loaded
 registerSelf();
--- a/toolkit/.eslintrc.js
+++ b/toolkit/.eslintrc.js
@@ -55,16 +55,25 @@ module.exports = {
     "linebreak-style": ["error", "unix"],
 
     // Always require parenthesis for new calls
     // "new-parens": "error",
 
     // Use [] instead of Array()
     // "no-array-constructor": "error",
 
+    // Disallow assignment operators in conditional statements
+    "no-cond-assign": "error",
+
+    // Disallow the use of debugger
+    "no-debugger": "error",
+
+    // Disallow deleting variables
+    "no-delete-var": "error",
+
     // No duplicate arguments in function declarations
     "no-dupe-args": "error",
 
     // No duplicate keys in object declarations
     "no-dupe-keys": "error",
 
     // No duplicate cases in switch statements
     "no-duplicate-case": "error",
@@ -85,28 +94,34 @@ module.exports = {
     "no-empty-pattern": "error",
 
     // No assiging to exception variable
     "no-ex-assign": "error",
 
     // No using !! where casting to boolean is already happening
     "no-extra-boolean-cast": "error",
 
+    // Disallow unnecessary labels
+    "no-extra-label": "error",
+
     // No double semicolon
     "no-extra-semi": "error",
 
     // No overwriting defined functions
     "no-func-assign": "error",
 
     // No invalid regular expresions
     "no-invalid-regexp": "error",
 
     // No odd whitespace characters
     "no-irregular-whitespace": "error",
 
+    // Disallow the use of the __iterator__ property
+    "no-iterator": "error",
+
     // No single if block inside an else block
     "no-lonely-if": "error",
 
     // No mixing spaces and tabs in indent
     "no-mixed-spaces-and-tabs": ["error", "smart-tabs"],
 
     // No unnecessary spacing
     "no-multi-spaces": ["error", { exceptions: { "AssignmentExpression": true, "VariableDeclarator": true, "ArrayExpression": true, "ObjectExpression": true } }],
@@ -124,37 +139,55 @@ module.exports = {
     "no-obj-calls": "error",
 
     // No octal literals
     "no-octal": "error",
 
     // No redeclaring variables
     "no-redeclare": "error",
 
+    // Disallow multiple spaces in regular expressions
+    "no-regex-spaces": "error",
+
+    // Disallow assignments where both sides are exactly the same
+    "no-self-assign": "error",
+
     // No unnecessary comparisons
     "no-self-compare": "error",
 
     // No declaring variables from an outer scope
     // "no-shadow": "error",
 
     // No declaring variables that hide things like arguments
     "no-shadow-restricted-names": "error",
 
+    // Disallow sparse arrays
+    "no-sparse-arrays": "error",
+
     // No trailing whitespace
     "no-trailing-spaces": "error",
 
     // No using undeclared variables
     // "no-undef": "error",
 
     // Error on newline where a semicolon is needed
     "no-unexpected-multiline": "error",
 
     // No unreachable statements
     "no-unreachable": "error",
 
+    // Disallow control flow statements in finally blocks
+    "no-unsafe-finally": "error",
+
+    // Disallow negating the left operand of relational operators
+    "no-unsafe-negation": "error",
+
+    // Disallow unused labels
+    "no-unused-labels": "error",
+
     // No declaring variables that are never used
     "no-unused-vars": ["error", {
       "vars": "local",
       "varsIgnorePattern": "^Cc|Ci|Cu|Cr|EXPORTED_SYMBOLS",
       "args": "none",
     }],
 
     // No using variables before defined
--- a/toolkit/components/aboutperformance/content/aboutPerformance.js
+++ b/toolkit/components/aboutperformance/content/aboutPerformance.js
@@ -1011,17 +1011,17 @@ var SubprocessMonitor = {
           SubprocessMonitor.queueUpdate();
           return;
         }
         let resultTable = document.getElementById("subprocess-reports");
         let recycle = [];
         // We first iterate the table to check if summaries exist for rowPids,
         // if yes, update them and delete the pid's summary or else hide the row
         // for recycling it. Start at row 1 instead of 0 (to skip the header row).
-        for (let i = 1, row; row = resultTable.rows[i]; i++) {
+        for (let i = 1, row; (row = resultTable.rows[i]); i++) {
           let rowPid = row.dataset.pid;
           let summary = summaries[rowPid];
           if (summary) {
             // Now we update the values in the row, which is hardcoded for now,
             // but we might want to make this more adaptable in the future.
             SubprocessMonitor.updateRow(row, summaries, rowPid);
             delete summaries[rowPid];
           } else {
--- a/toolkit/components/contentprefs/tests/unit_cps2/head.js
+++ b/toolkit/components/contentprefs/tests/unit_cps2/head.js
@@ -336,17 +336,17 @@ function dbOK(expectedRows) {
 
   db.executeAsync([stmt], 1, {
     handleCompletion(reason) {
       arraysOfArraysOK(actualRows, expectedRows);
       next();
     },
     handleResult(results) {
       let row = null;
-      while (row = results.getNextRow()) {
+      while ((row = results.getNextRow())) {
         actualRows.push(cols.map(c => row.getResultByName(c)));
       }
     },
     handleError(err) {
       do_throw(err);
     }
   });
   stmt.finalize();
--- a/toolkit/components/crashes/tests/xpcshell/test_crash_manager.js
+++ b/toolkit/components/crashes/tests/xpcshell/test_crash_manager.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
-var bsp = Cu.import("resource://gre/modules/CrashManager.jsm", this);
+var {CrashStore, CrashManager} = Cu.import("resource://gre/modules/CrashManager.jsm", {});
 Cu.import("resource://gre/modules/Promise.jsm", this);
 Cu.import("resource://gre/modules/Task.jsm", this);
 Cu.import("resource://gre/modules/osfile.jsm", this);
 Cu.import("resource://gre/modules/TelemetryEnvironment.jsm", this);
 
 Cu.import("resource://testing-common/CrashManagerTest.jsm", this);
 Cu.import("resource://testing-common/TelemetryArchiveTesting.jsm", this);
 
@@ -357,17 +357,17 @@ add_task(function* test_high_water_mark(
 
   let store = yield m._getStore();
 
   for (let i = 0; i < store.HIGH_WATER_DAILY_THRESHOLD + 1; i++) {
     yield m.createEventsFile("m" + i, "crash.main.2", DUMMY_DATE, "m" + i);
   }
 
   let count = yield m.aggregateEventsFiles();
-  Assert.equal(count, bsp.CrashStore.prototype.HIGH_WATER_DAILY_THRESHOLD + 1);
+  Assert.equal(count, CrashStore.prototype.HIGH_WATER_DAILY_THRESHOLD + 1);
 
   // Need to fetch again in case the first one was garbage collected.
   store = yield m._getStore();
 
   Assert.equal(store.crashesCount, store.HIGH_WATER_DAILY_THRESHOLD + 1);
 });
 
 add_task(function* test_addCrash() {
--- a/toolkit/components/crashes/tests/xpcshell/test_crash_service.js
+++ b/toolkit/components/crashes/tests/xpcshell/test_crash_service.js
@@ -2,17 +2,17 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 Cu.import("resource://gre/modules/Services.jsm", this);
 Cu.import("resource://testing-common/AppData.jsm", this);
-var bsp = Cu.import("resource://gre/modules/CrashManager.jsm", this);
+var bsp = Cu.import("resource://gre/modules/CrashManager.jsm", {});
 
 function run_test() {
   run_next_test();
 }
 
 add_task(function* test_instantiation() {
   Assert.ok(!bsp.gCrashManager, "CrashManager global instance not initially defined.");
 
--- a/toolkit/components/crashes/tests/xpcshell/test_crash_store.js
+++ b/toolkit/components/crashes/tests/xpcshell/test_crash_store.js
@@ -4,17 +4,17 @@
 /*
  * This file tests the CrashStore type in CrashManager.jsm.
  */
 
 "use strict";
 
 var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
-var bsp = Cu.import("resource://gre/modules/CrashManager.jsm", this);
+var {CrashManager, CrashStore, dateToDays} = Cu.import("resource://gre/modules/CrashManager.jsm", {});
 Cu.import("resource://gre/modules/osfile.jsm", this);
 Cu.import("resource://gre/modules/Task.jsm", this);
 
 const DUMMY_DATE = new Date(Date.now() - 10 * 24 * 60 * 60 * 1000);
 DUMMY_DATE.setMilliseconds(0);
 
 const DUMMY_DATE_2 = new Date(Date.now() - 5 * 24 * 60 * 60 * 1000);
 DUMMY_DATE_2.setMilliseconds(0);
@@ -26,18 +26,16 @@ const {
   PROCESS_TYPE_GMPLUGIN,
   PROCESS_TYPE_GPU,
   CRASH_TYPE_CRASH,
   CRASH_TYPE_HANG,
   SUBMISSION_RESULT_OK,
   SUBMISSION_RESULT_FAILED,
 } = CrashManager.prototype;
 
-const CrashStore = bsp.CrashStore;
-
 var STORE_DIR_COUNT = 0;
 
 function getStore() {
   return Task.spawn(function* () {
     let storeDir = do_get_tempdir().path;
     storeDir = OS.Path.join(storeDir, "store-" + STORE_DIR_COUNT++);
 
     yield OS.File.makeDir(storeDir, {unixMode: OS.Constants.libc.S_IRWXU});
@@ -464,18 +462,18 @@ add_task(function* test_high_water() {
   Assert.equal(crashes.length, 2 * s.HIGH_WATER_DAILY_THRESHOLD);
 
   crashes = s.getCrashesOfType(PROCESS_TYPE_PLUGIN, CRASH_TYPE_CRASH);
   Assert.equal(crashes.length, 2 * s.HIGH_WATER_DAILY_THRESHOLD);
   crashes = s.getCrashesOfType(PROCESS_TYPE_PLUGIN, CRASH_TYPE_HANG);
   Assert.equal(crashes.length, 2 * s.HIGH_WATER_DAILY_THRESHOLD);
 
   // But raw counts should be preserved.
-  let day1 = bsp.dateToDays(d1);
-  let day2 = bsp.dateToDays(d2);
+  let day1 = dateToDays(d1);
+  let day2 = dateToDays(d2);
   Assert.ok(s._countsByDay.has(day1));
   Assert.ok(s._countsByDay.has(day2));
 
   Assert.equal(s._countsByDay.get(day1).
                  get(PROCESS_TYPE_MAIN + "-" + CRASH_TYPE_CRASH),
                s.HIGH_WATER_DAILY_THRESHOLD + 1);
   Assert.equal(s._countsByDay.get(day1).
                  get(PROCESS_TYPE_MAIN + "-" + CRASH_TYPE_HANG),
--- a/toolkit/components/places/ClusterLib.js
+++ b/toolkit/components/places/ClusterLib.js
@@ -79,18 +79,17 @@ HierarchicalClustering.prototype = {
 
         if (dist < distances[i][neighbors[i]]) {
           neighbors[i] = j;
         }
       }
     }
 
     // merge the next two closest clusters until none of them are close enough
-    let next = null, i = 0;
-    for (; next = this.closestClusters(clusters, distances, neighbors); i++) {
+    for (let next = null, i = 0; (next = this.closestClusters(clusters, distances, neighbors)); i++) {
       if (snapshotCallback && (i % snapshotGap) == 0) {
         snapshotCallback(clusters);
       }
       this.mergeClusters(clusters, distances, neighbors, clustersByKey,
                          clustersByKey[next[0]], clustersByKey[next[1]]);
     }
     return clusters;
   },
--- a/toolkit/components/places/tests/unit/test_tagging.js
+++ b/toolkit/components/places/tests/unit/test_tagging.js
@@ -114,23 +114,23 @@ function run_test() {
   do_check_true(uri4Tags.includes(tagTitle));
   do_check_true(uri4Tags.includes("tag 3"));
   do_check_true(uri4Tags.includes("456"));
 
   // Test sparse arrays.
   let curChildCount = tagRoot.childCount;
 
   try {
-    tagssvc.tagURI(uri1, [, "tagSparse"]);
+    tagssvc.tagURI(uri1, [undefined, "tagSparse"]);
     do_check_eq(tagRoot.childCount, curChildCount + 1);
   } catch (ex) {
     do_throw("Passing a sparse array should not throw");
   }
   try {
-    tagssvc.untagURI(uri1, [, "tagSparse"]);
+    tagssvc.untagURI(uri1, [undefined, "tagSparse"]);
     do_check_eq(tagRoot.childCount, curChildCount);
   } catch (ex) {
     do_throw("Passing a sparse array should not throw");
   }
 
   // Test that the API throws for bad arguments.
   try {
     tagssvc.tagURI(uri1, ["", "test"]);
--- a/toolkit/components/telemetry/TelemetryStorage.jsm
+++ b/toolkit/components/telemetry/TelemetryStorage.jsm
@@ -1516,18 +1516,18 @@ var TelemetryStorageImpl = {
           try {
             yield OS.File.remove(file.path);
           } catch (ex) {
             this._log.error("_scanPendingPings - failed to remove file " + file.path, ex);
           } finally {
             Telemetry.getHistogramById("TELEMETRY_DISCARDED_PENDING_PINGS_SIZE_MB")
                      .add(Math.floor(info.size / 1024 / 1024));
             Telemetry.getHistogramById("TELEMETRY_PING_SIZE_EXCEEDED_PENDING").add();
-            continue;
           }
+          continue;
         }
 
         let id = OS.Path.basename(file.path);
         if (!UUID_REGEX.test(id)) {
           this._log.trace("_scanPendingPings - filename is not a UUID: " + id);
           id = Utils.generateUUID();
         }
 
--- a/toolkit/content/aboutSupport.js
+++ b/toolkit/content/aboutSupport.js
@@ -452,18 +452,18 @@ var snapshotFormatters = {
         let trs = [];
         for (let entry of feature.log) {
           if (entry.type == "default" && entry.status == "available")
             continue;
 
           let contents;
           if (entry.message.length > 0 && entry.message[0] == "#") {
             // This is a failure ID. See nsIGfxInfo.idl.
-            let m;
-            if (m = /#BLOCKLIST_FEATURE_FAILURE_BUG_(\d+)/.exec(entry.message)) {
+            let m = /#BLOCKLIST_FEATURE_FAILURE_BUG_(\d+)/.exec(entry.message);
+            if (m) {
               let bugSpan = $.new("span");
               bugSpan.textContent = strings.GetStringFromName("blocklistedBug") + "; ";
 
               let bugHref = $.new("a");
               bugHref.href = "https://bugzilla.mozilla.org/show_bug.cgi?id=" + m[1];
               bugHref.textContent = strings.formatStringFromName("bugLink", [m[1]], 1);
 
               contents = [bugSpan, bugHref];
--- a/toolkit/modules/PopupNotifications.jsm
+++ b/toolkit/modules/PopupNotifications.jsm
@@ -214,16 +214,43 @@ this.PopupNotifications = function Popup
   this.window = tabbrowser.ownerDocument.defaultView;
   this.panel = panel;
   this.tabbrowser = tabbrowser;
   this.iconBox = iconBox;
   this.buttonDelay = Services.prefs.getIntPref(PREF_SECURITY_DELAY);
 
   this.panel.addEventListener("popuphidden", this, true);
 
+  // This listener will be attached to the chrome window whenever a notification
+  // is showing, to allow the user to dismiss notifications using the escape key.
+  this._handleWindowKeyPress = aEvent => {
+    if (aEvent.keyCode != aEvent.DOM_VK_ESCAPE) {
+      return;
+    }
+
+    // Esc key cancels the topmost notification, if there is one.
+    let notification = this.panel.firstChild;
+    if (!notification) {
+      return;
+    }
+
+    let doc = this.window.document;
+    let activeElement = doc.activeElement;
+
+    // If the chrome window has a focused element, let it handle the ESC key instead.
+    if (!activeElement ||
+        activeElement == doc.body ||
+        activeElement == this.tabbrowser.selectedBrowser ||
+        // Ignore focused elements inside the notification.
+        getNotificationFromElement(activeElement) == notification ||
+        notification.contains(activeElement)) {
+      this._onButtonEvent(aEvent, "secondarybuttoncommand", notification);
+    }
+  };
+
   this.window.addEventListener("activate", this, true);
   if (this.tabbrowser.tabContainer)
     this.tabbrowser.tabContainer.addEventListener("TabSelect", this, true);
 }
 
 PopupNotifications.prototype = {
 
   window: null,
@@ -997,16 +1024,20 @@ PopupNotifications.prototype = {
       }
     }
 
     if (notificationsToShow.length > 0) {
       let anchorElement = anchors.values().next().value;
       if (anchorElement) {
         this._showPanel(notificationsToShow, anchorElement);
       }
+
+      // Setup a capturing event listener on the whole window to catch the
+      // escape key while persistent notifications are visible.
+      this.window.addEventListener("keypress", this._handleWindowKeyPress, true);
     } else {
       // Notify observers that we're not showing the popup (useful for testing)
       this._notify("updateNotShowing");
 
       // Close the panel if there are no notifications to show.
       // When called from PopupNotifications.show() we should never close the
       // panel, however. It may just be adding a dismissed notification, in
       // which case we want to continue showing any existing notifications.
@@ -1018,16 +1049,19 @@ PopupNotifications.prototype = {
       if (!haveNotifications) {
         if (useIconBox) {
           this.iconBox.hidden = true;
         } else if (anchors.size) {
           for (let anchorElement of anchors)
             anchorElement.removeAttribute(ICON_ATTRIBUTE_SHOWING);
         }
       }
+
+      // Stop listening to keyboard events for notifications.
+      this.window.removeEventListener("keypress", this._handleWindowKeyPress, true);
     }
   },
 
   _updateAnchorIcons: function PopupNotifications_updateAnchorIcons(notifications,
                                                                     anchorElements) {
     for (let anchorElement of anchorElements) {
       anchorElement.setAttribute(ICON_ATTRIBUTE_SHOWING, "true");
       // Use the anchorID as a class along with the default icon class as a
@@ -1276,18 +1310,20 @@ PopupNotifications.prototype = {
         this._remove(notificationObj);
       } else {
         notificationObj.dismissed = true;
         this._fireCallback(notificationObj, NOTIFICATION_EVENT_DISMISSED);
       }
     }, this);
   },
 
-  _onButtonEvent(event, type) {
-    let notificationEl = getNotificationFromElement(event.originalTarget);
+  _onButtonEvent(event, type, notificationEl = null) {
+    if (!notificationEl) {
+      notificationEl = getNotificationFromElement(event.originalTarget);
+    }
 
     if (!notificationEl)
       throw "PopupNotifications._onButtonEvent: couldn't find notification element";
 
     if (!notificationEl.notification)
       throw "PopupNotifications._onButtonEvent: couldn't find notification";
 
     let notification = notificationEl.notification;
--- a/toolkit/modules/WindowsRegistry.jsm
+++ b/toolkit/modules/WindowsRegistry.jsm
@@ -79,12 +79,12 @@ var WindowsRegistry = {
         registry.removeValue(aKey);
         result = !registry.hasValue(aKey);
       } else {
         result = true;
       }
     } catch (ex) {
     } finally {
       registry.close();
-      return result;
     }
+    return result;
   }
 };
--- a/toolkit/modules/tests/browser/browser_Battery.js
+++ b/toolkit/modules/tests/browser/browser_Battery.js
@@ -1,35 +1,35 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
-var imported = Components.utils.import("resource://gre/modules/Battery.jsm", this);
+var {GetBattery, Debugging} = Components.utils.import("resource://gre/modules/Battery.jsm", {});
 Cu.import("resource://gre/modules/Services.jsm", this);
 
 function test() {
   waitForExplicitFinish();
 
-  is(imported.Debugging.fake, false, "Battery spoofing is initially false")
+  is(Debugging.fake, false, "Battery spoofing is initially false")
 
   GetBattery().then(function(battery) {
     for (let k of ["charging", "chargingTime", "dischargingTime", "level"]) {
       let backup = battery[k];
       try {
         battery[k] = "__magic__";
       } catch (e) {
         // We are testing that we cannot set battery to new values
         // when "use strict" is enabled, this throws a TypeError
         if (e.name != "TypeError")
           throw e;
       }
       is(battery[k], backup, "Setting battery " + k + " preference without spoofing enabled should fail");
     }
 
-    imported.Debugging.fake = true;
+    Debugging.fake = true;
 
     // reload again to get the fake one
     GetBattery().then(function(battery) {
       battery.charging = true;
       battery.chargingTime = 100;
       battery.level = 0.5;
       ok(battery.charging, "Test for charging setter");
       is(battery.chargingTime, 100, "Test for chargingTime setter");
@@ -39,13 +39,13 @@ function test() {
       battery.dischargingTime = 50;
       battery.level = 0.7;
       ok(!battery.charging, "Test for charging setter");
       is(battery.dischargingTime, 50, "Test for dischargingTime setter");
       is(battery.level, 0.7, "Test for level setter");
 
       // Resetting the value to make the test run successful
       // for multiple runs in same browser session.
-      imported.Debugging.fake = false;
+      Debugging.fake = false;
       finish();
     });
   });
 }
--- a/toolkit/mozapps/extensions/AddonManager.jsm
+++ b/toolkit/mozapps/extensions/AddonManager.jsm
@@ -84,16 +84,20 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
                                   "resource://gre/modules/Promise.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "AddonRepository",
                                   "resource://gre/modules/addons/AddonRepository.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Extension",
                                   "resource://gre/modules/Extension.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
                                   "resource://gre/modules/FileUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Preferences",
+                                  "resource://gre/modules/Preferences.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "PromptUtils",
+                                  "resource://gre/modules/SharedPromptUtils.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "CertUtils", function() {
   let certUtils = {};
   Components.utils.import("resource://gre/modules/CertUtils.jsm", certUtils);
   return certUtils;
 });
 
 const INTEGER = /^[1-9]\d*$/;
@@ -361,105 +365,316 @@ function webAPIForAddon(addon) {
 
   return result;
 }
 
 /**
  * Listens for a browser changing origin and cancels the installs that were
  * started by it.
  */
-function BrowserListener(aBrowser, aInstallingPrincipal, aInstalls) {
+function BrowserListener(aBrowser, aInstallingPrincipal, aInstall) {
   this.browser = aBrowser;
   this.principal = aInstallingPrincipal;
-  this.installs = aInstalls;
-  this.installCount = aInstalls.length;
+  this.install = aInstall;
 
   aBrowser.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_LOCATION);
   Services.obs.addObserver(this, "message-manager-close", true);
 
-  for (let install of this.installs)
-    install.addListener(this);
+  aInstall.addListener(this);
 
   this.registered = true;
 }
 
 BrowserListener.prototype = {
   browser: null,
-  installs: null,
-  installCount: null,
+  install: null,
   registered: false,
 
   unregister() {
     if (!this.registered)
       return;
     this.registered = false;
 
     Services.obs.removeObserver(this, "message-manager-close");
     // The browser may have already been detached
     if (this.browser.removeProgressListener)
       this.browser.removeProgressListener(this);
 
-    for (let install of this.installs)
-      install.removeListener(this);
-    this.installs = null;
+    this.install.removeListener(this);
+    this.install = null;
   },
 
-  cancelInstalls() {
-    for (let install of this.installs) {
-      try {
-        install.cancel();
-      } catch (e) {
-        // Some installs may have already failed or been cancelled, ignore these
-      }
+  cancelInstall() {
+    try {
+      this.install.cancel();
+    } catch (e) {
+      // install may have already failed or been cancelled, ignore these
     }
   },
 
   observe(subject, topic, data) {
     if (subject != this.browser.messageManager)
       return;
 
     // The browser's message manager has closed and so the browser is
-    // going away, cancel all installs
-    this.cancelInstalls();
+    // going away, cancel the install
+    this.cancelInstall();
   },
 
   onLocationChange(webProgress, request, location) {
     if (this.browser.contentPrincipal && this.principal.subsumes(this.browser.contentPrincipal))
       return;
 
-    // The browser has navigated to a new origin so cancel all installs
-    this.cancelInstalls();
+    // The browser has navigated to a new origin so cancel the install
+    this.cancelInstall();
   },
 
   onDownloadCancelled(install) {
-    // Don't need to hear more events from this install
-    install.removeListener(this);
-
-    // Once all installs have ended unregister everything
-    if (--this.installCount == 0)
-      this.unregister();
+    this.unregister();
   },
 
   onDownloadFailed(install) {
-    this.onDownloadCancelled(install);
+    this.unregister();
   },
 
   onInstallFailed(install) {
-    this.onDownloadCancelled(install);
+    this.unregister();
   },
 
   onInstallEnded(install) {
-    this.onDownloadCancelled(install);
+    this.unregister();
   },
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference,
                                          Ci.nsIWebProgressListener,
                                          Ci.nsIObserver])
 };
 
+function installNotifyObservers(aTopic, aBrowser, aUri, aInstall, aInstallFn) {
+  let info = {
+    wrappedJSObject: {
+      browser: aBrowser,
+      originatingURI: aUri,
+      installs: [aInstall],
+      install: aInstallFn,
+    },
+  };
+  Services.obs.notifyObservers(info, aTopic, null);
+}
+
+/**
+ * Helper to monitor a download and prompt to install when ready
+ */
+class Installer {
+  /**
+   *
+   * @param  aBrowser
+   *         The browser that started the installations
+   * @param  aUrl
+   *         The URL that started the installations
+   * @param  aInstall
+   *         An AddonInstall
+   */
+  constructor(aBrowser, aUrl, aInstall) {
+    this.browser = aBrowser;
+    this.url = aUrl;
+    this.install = aInstall;
+    this.isDownloading = true;
+
+    installNotifyObservers("addon-install-started", aBrowser, aUrl, aInstall);
+
+    this.install.addListener(this);
+
+    // Start downloading if it hasn't already begun
+    const READY_STATES = [
+      AddonManager.STATE_AVAILABLE,
+      AddonManager.STATE_DOWNLOAD_FAILED,
+      AddonManager.STATE_INSTALL_FAILED,
+      AddonManager.STATE_CANCELLED,
+    ];
+    if (READY_STATES.includes(this.install.state)) {
+      this.install.install();
+    }
+
+    this.checkDownloaded();
+  }
+
+  get URI_XPINSTALL_DIALOG() {
+    return "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul";
+  }
+
+  /**
+   * Checks if the download is complete and if so prompts to install.
+   */
+  checkDownloaded() {
+    // Prevent re-entrancy caused by the confirmation dialog cancelling
+    // unwanted installs.
+    if (!this.isDownloading)
+      return;
+
+    let failed = false;
+
+    switch (this.install.state) {
+      case AddonManager.STATE_AVAILABLE:
+      case AddonManager.STATE_DOWNLOADING:
+        // Exit early if the add-on hasn't started downloading yet or is
+        // still downloading
+        return;
+      case AddonManager.STATE_DOWNLOAD_FAILED:
+        failed = true;
+        break;
+      case AddonManager.STATE_DOWNLOADED:
+        // App disabled items are not compatible and so fail to install
+        failed = this.install.addon.appDisabled
+        break;
+      case AddonManager.STATE_CANCELLED:
+        // Just ignore cancelled downloads
+        return;
+      default:
+        logger.warn(`Download of ${this.install.sourceURI.spec} in unexpected state ${this.install.state}`);
+        return;
+    }
+
+    this.isDownloading = false;
+
+    if (failed) {
+      // Stop listening and cancel any installs that are failed because of
+      // compatibility reasons.
+      if (this.install.state == AddonManager.STATE_DOWNLOADED) {
+        this.install.removeListener(this);
+        this.install.cancel();
+      }
+      installNotifyObservers("addon-install-failed", this.browser, this.url, this.install);
+      return;
+    }
+
+    // Check for a custom installation prompt that may be provided by the
+    // applicaton
+    if ("@mozilla.org/addons/web-install-prompt;1" in Cc) {
+      try {
+        let prompt = Cc["@mozilla.org/addons/web-install-prompt;1"].
+                                  getService(Ci.amIWebInstallPrompt);
+        prompt.confirm(this.browser, this.url, [this.install]);
+        return;
+      } catch (e) {}
+    }
+
+    if (Preferences.get("xpinstall.customConfirmationUI", false)) {
+      installNotifyObservers("addon-install-confirmation", this.browser, this.url, this.install);
+      return;
+    }
+
+    let args = {};
+    args.url = this.url;
+    args.installs = [this.install];
+    args.wrappedJSObject = args;
+
+    try {
+      Cc["@mozilla.org/base/telemetry;1"].
+                   getService(Ci.nsITelemetry).
+                   getHistogramById("SECURITY_UI").
+                   add(Ci.nsISecurityUITelemetry.WARNING_CONFIRM_ADDON_INSTALL);
+      let parentWindow = null;
+      if (this.browser) {
+        parentWindow = this.browser.ownerDocument.defaultView;
+        PromptUtils.fireDialogEvent(parentWindow, "DOMWillOpenModalDialog", this.browser);
+      }
+      Services.ww.openWindow(parentWindow, this.URI_XPINSTALL_DIALOG,
+                             null, "chrome,modal,centerscreen", args);
+    } catch (e) {
+      logger.warn("Exception showing install confirmation dialog", e);
+      this.install.removeListener(this);
+      // Cancel the install, as currently there is no way to make it fail
+      // from here.
+      this.install.cancel();
+
+      installNotifyObservers("addon-install-cancelled", this.browser, this.url,
+                             this.install);
+    }
+  }
+
+  /**
+   * Checks if install is now complete and if so notifies observers.
+   */
+  checkInstalled() {
+    switch (this.install.state) {
+      case AddonManager.STATE_DOWNLOADED:
+      case AddonManager.STATE_INSTALLING:
+        // Exit early if the add-on hasn't started installing yet or is
+        // still installing
+        return;
+      case AddonManager.STATE_INSTALL_FAILED:
+        installNotifyObservers("addon-install-failed", this.browser, this.url, this.install);
+        break;
+      default:
+        installNotifyObservers("addon-install-complete", this.browser, this.url, this.install);
+    }
+  }
+
+  // InstallListener methods:
+  onDownloadCancelled(aInstall) {
+    aInstall.removeListener(this);
+    this.checkDownloaded();
+  }
+
+  onDownloadFailed(aInstall) {
+    aInstall.removeListener(this);
+    this.checkDownloaded();
+  }
+
+  onDownloadEnded(aInstall) {
+    this.checkDownloaded();
+    return false;
+  }
+
+  onInstallCancelled(aInstall) {
+    aInstall.removeListener(this);
+    this.checkInstalled();
+  }
+
+  onInstallFailed(aInstall) {
+    aInstall.removeListener(this);
+    this.checkInstalled();
+  }
+
+  onInstallEnded(aInstall) {
+    aInstall.removeListener(this);
+
+    // If installing a theme that is disabled and can be enabled then enable it
+    if (aInstall.addon.type == "theme" &&
+        aInstall.addon.userDisabled == true &&
+        aInstall.addon.appDisabled == false) {
+          aInstall.addon.userDisabled = false;
+    }
+
+    this.checkInstalled();
+  }
+}
+
+const weblistener = {
+  onWebInstallDisabled(aBrowser, aUri, aInstall) {
+    installNotifyObservers("addon-install-disabled", aBrowser, aUri, aInstall);
+  },
+
+  onWebInstallOriginBlocked(aBrowser, aUri, aInstall) {
+    installNotifyObservers("addon-install-origin-blocked", aBrowser, aUri, aInstall);
+    return false;
+  },
+
+  onWebInstallBlocked(aBrowser, aUri, aInstall) {
+    installNotifyObservers("addon-install-blocked", aBrowser, aUri, aInstall,
+                           function() { new Installer(this.browser, this.originatingURI, aInstall); });
+    return false;
+  },
+
+  onWebInstallRequested(aBrowser, aUri, aInstall) {
+    new Installer(aBrowser, aUri, aInstall);
+  },
+};
+
 /**
  * This represents an author of an add-on (e.g. creator or developer)
  *
  * @param  aName
  *         The name of the author
  * @param  aURL
  *         The URL of the author's profile page
  */
@@ -2021,119 +2236,115 @@ var AddonManagerInternal = {
       if (callProvider(provider, "supportsMimetype", false, aMimetype) &&
           callProvider(provider, "isInstallAllowed", null, aInstallingPrincipal))
         return true;
     }
     return false;
   },
 
   /**
-   * Starts installation of an array of AddonInstalls notifying the registered
+   * Starts installation of an AddonInstall notifying the registered
    * web install listener of blocked or started installs.
    *
    * @param  aMimetype
    *         The mimetype of add-ons being installed
    * @param  aBrowser
    *         The optional browser element that started the installs
    * @param  aInstallingPrincipal
    *         The nsIPrincipal that initiated the install
-   * @param  aInstalls
-   *         The array of AddonInstalls to be installed
+   * @param  aInstall
+   *         The AddonInstall to be installed
    */
-  installAddonsFromWebpage(aMimetype, aBrowser,
-                                     aInstallingPrincipal, aInstalls) {
+  installAddonFromWebpage(aMimetype, aBrowser,
+                                    aInstallingPrincipal, aInstall) {
     if (!gStarted)
       throw Components.Exception("AddonManager is not initialized",
                                  Cr.NS_ERROR_NOT_INITIALIZED);
 
     if (!aMimetype || typeof aMimetype != "string")
       throw Components.Exception("aMimetype must be a non-empty string",
                                  Cr.NS_ERROR_INVALID_ARG);
 
     if (aBrowser && !(aBrowser instanceof Ci.nsIDOMElement))
       throw Components.Exception("aSource must be a nsIDOMElement, or null",
                                  Cr.NS_ERROR_INVALID_ARG);
 
     if (!aInstallingPrincipal || !(aInstallingPrincipal instanceof Ci.nsIPrincipal))
       throw Components.Exception("aInstallingPrincipal must be a nsIPrincipal",
                                  Cr.NS_ERROR_INVALID_ARG);
 
-    if (!Array.isArray(aInstalls))
-      throw Components.Exception("aInstalls must be an array",
-                                 Cr.NS_ERROR_INVALID_ARG);
-
-    if (!("@mozilla.org/addons/web-install-listener;1" in Cc)) {
-      logger.warn("No web installer available, cancelling all installs");
-      for (let install of aInstalls)
-        install.cancel();
-      return;
-    }
-
     // When a chrome in-content UI has loaded a <browser> inside to host a
     // website we want to do our security checks on the inner-browser but
     // notify front-end that install events came from the outer-browser (the
     // main tab's browser). Check this by seeing if the browser we've been
     // passed is in a content type docshell and if so get the outer-browser.
     let topBrowser = aBrowser;
     let docShell = aBrowser.ownerDocument.defaultView
                            .QueryInterface(Ci.nsIInterfaceRequestor)
                            .getInterface(Ci.nsIDocShell)
                            .QueryInterface(Ci.nsIDocShellTreeItem);
     if (docShell.itemType == Ci.nsIDocShellTreeItem.typeContent)
       topBrowser = docShell.chromeEventHandler;
 
     try {
-      let weblistener = Cc["@mozilla.org/addons/web-install-listener;1"].
-                        getService(Ci.amIWebInstallListener);
-
       if (!this.isInstallEnabled(aMimetype)) {
-        for (let install of aInstalls)
-          install.cancel();
+        aInstall.cancel();
 
         weblistener.onWebInstallDisabled(topBrowser, aInstallingPrincipal.URI,
-                                         aInstalls, aInstalls.length);
+                                         aInstall);
         return;
       } else if (!aBrowser.contentPrincipal || !aInstallingPrincipal.subsumes(aBrowser.contentPrincipal)) {
-        for (let install of aInstalls)
-          install.cancel();
-
-        if (weblistener instanceof Ci.amIWebInstallListener2) {
-          weblistener.onWebInstallOriginBlocked(topBrowser, aInstallingPrincipal.URI,
-                                                aInstalls, aInstalls.length);
-        }
+        aInstall.cancel();
+
+        weblistener.onWebInstallOriginBlocked(topBrowser, aInstallingPrincipal.URI,
+                                              aInstall);
         return;
       }
 
-      // The installs may start now depending on the web install listener,
+      // The install may start now depending on the web install listener,
       // listen for the browser navigating to a new origin and cancel the
-      // installs in that case.
-      new BrowserListener(aBrowser, aInstallingPrincipal, aInstalls);
+      // install in that case.
+      new BrowserListener(aBrowser, aInstallingPrincipal, aInstall);
 
       if (!this.isInstallAllowed(aMimetype, aInstallingPrincipal)) {
-        if (weblistener.onWebInstallBlocked(topBrowser, aInstallingPrincipal.URI,
-                                            aInstalls, aInstalls.length)) {
-          for (let install of aInstalls)
-            install.install();
-        }
-      } else if (weblistener.onWebInstallRequested(topBrowser, aInstallingPrincipal.URI,
-                                                 aInstalls, aInstalls.length)) {
-        for (let install of aInstalls)
-          install.install();
+        weblistener.onWebInstallBlocked(topBrowser, aInstallingPrincipal.URI,
+                                        aInstall);
+      } else {
+        weblistener.onWebInstallRequested(topBrowser, aInstallingPrincipal.URI,
+                                          aInstall);
       }
     } catch (e) {
       // In the event that the weblistener throws during instantiation or when
-      // calling onWebInstallBlocked or onWebInstallRequested all of the
-      // installs sh