Merge mozilla-central to inbound. a=merge CLOSED TREE
authorshindli <shindli@mozilla.com>
Thu, 26 Jul 2018 14:21:26 +0300
changeset 483673 dcf2daeeb39042a6514416e21cb54e97f76f2e14
parent 483672 0f171ed1fd00de6c42d09e0f945bc42649e511c8 (current diff)
parent 483527 4e6486b672b32aba075b704c6b1e41e8ccf7a135 (diff)
child 483674 0fd93c0985bbdfdb1233583164c386d54fc8d498
push id9719
push userffxbld-merge
push dateFri, 24 Aug 2018 17:49:46 +0000
treeherdermozilla-beta@719ec98fba77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to inbound. a=merge CLOSED TREE
Cargo.lock
devtools/shared/security/socket.js
dom/animation/Animation.cpp
security/manager/ssl/nsISSLStatusProvider.idl
testing/web-platform/meta/MANIFEST.json
--- a/browser/base/content/browser-siteIdentity.js
+++ b/browser/base/content/browser-siteIdentity.js
@@ -342,22 +342,18 @@ var gIdentityHandler = {
    */
   updateIdentity(state, uri) {
     let shouldHidePopup = this._uri && (this._uri.spec != uri.spec);
     this._state = state;
 
     // Firstly, populate the state properties required to display the UI. See
     // the documentation of the individual properties for details.
     this.setURI(uri);
-    this._sslStatus = gBrowser.securityUI
-                              .QueryInterface(Ci.nsISSLStatusProvider)
-                              .SSLStatus;
-    if (this._sslStatus) {
-      this._sslStatus.QueryInterface(Ci.nsISSLStatus);
-    }
+    this._sslStatus = gBrowser.securityUI.secInfo &&
+                      gBrowser.securityUI.secInfo.SSLStatus;
 
     // Then, update the user interface with the available data.
     this.refreshIdentityBlock();
     // Handle a location change while the Control Center is focused
     // by closing the popup (bug 1207542)
     if (shouldHidePopup) {
       PanelMultiView.hidePopup(this._identityPopup);
     }
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -364,67 +364,63 @@ toolbarpaletteitem {
   width: 16px;
 }
 
 @media not all and (min-resolution: 1.1dppx) {
   .webextension-browser-action {
     list-style-image: var(--webextension-toolbar-image, inherit);
   }
 
-  .webextension-browser-action:-moz-lwtheme-brighttext {
+  toolbar[brighttext] .webextension-browser-action {
     list-style-image: var(--webextension-toolbar-image-light, inherit);
   }
 
-  .webextension-browser-action:-moz-lwtheme-darktext {
+  toolbar:not([brighttext]) .webextension-browser-action:-moz-lwtheme {
     list-style-image: var(--webextension-toolbar-image-dark, inherit);
   }
 
-  .webextension-browser-action[cui-areatype="menu-panel"],
-  toolbarpaletteitem[place="palette"] > .webextension-browser-action {
+  .webextension-browser-action[cui-areatype="menu-panel"] {
     list-style-image: var(--webextension-menupanel-image, inherit);
   }
 
-  toolbarpaletteitem[place="palette"] > .webextension-browser-action:-moz-lwtheme-brighttext {
+  :root[lwt-popup-brighttext] .webextension-browser-action[cui-areatype="menu-panel"] {
     list-style-image: var(--webextension-menupanel-image-light, inherit);
   }
 
-  .webextension-browser-action[cui-areatype="menu-panel"]:-moz-lwtheme-darktext,
-  toolbarpaletteitem[place="palette"] > .webextension-browser-action:-moz-lwtheme-darktext {
+  :root:not([lwt-popup-brighttext]) .webextension-browser-action[cui-areatype="menu-panel"]:-moz-lwtheme {
     list-style-image: var(--webextension-menupanel-image-dark, inherit);
   }
 
   .webextension-menuitem {
     list-style-image: var(--webextension-menuitem-image, inherit) !important;
   }
 }
 
 @media (min-resolution: 1.1dppx) {
   .webextension-browser-action {
     list-style-image: var(--webextension-toolbar-image-2x, inherit);
   }
 
-  .webextension-browser-action:-moz-lwtheme-brighttext {
+  toolbar[brighttext] .webextension-browser-action {
     list-style-image: var(--webextension-toolbar-image-2x-light, inherit);
   }
 
-  .webextension-browser-action:-moz-lwtheme-darktext {
+  toolbar:not([brighttext]) .webextension-browser-action:-moz-lwtheme {
     list-style-image: var(--webextension-toolbar-image-2x-dark, inherit);
   }
 
-  .webextension-browser-action[cui-areatype="menu-panel"],
-  toolbarpaletteitem[place="palette"] > .webextension-browser-action {
+  .webextension-browser-action[cui-areatype="menu-panel"] {
     list-style-image: var(--webextension-menupanel-image-2x, inherit);
   }
 
-  toolbarpaletteitem[place="palette"] > .webextension-browser-action:-moz-lwtheme-brighttext {
+  :root[lwt-popup-brighttext] .webextension-browser-action[cui-areatype="menu-panel"] {
     list-style-image: var(--webextension-menupanel-image-2x-light, inherit);
   }
 
-  .webextension-browser-action[cui-areatype="menu-panel"]:-moz-lwtheme-darktext,
-  toolbarpaletteitem[place="palette"] > .webextension-browser-action:-moz-lwtheme-darktext {
+  :root:not([lwt-popup-brighttext]) .webextension-browser-action[cui-areatype="menu-panel"]:-moz-lwtheme {
     list-style-image: var(--webextension-menupanel-image-2x-dark, inherit);
   }
 
   .webextension-menuitem {
     list-style-image: var(--webextension-menuitem-image-2x, inherit) !important;
   }
 }
 
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -2989,18 +2989,17 @@ var BrowserOnClick = {
 
     switch (elementId) {
       case "exceptionDialogButton":
         if (isTopFrame) {
           secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_BAD_CERT_TOP_CLICK_ADD_EXCEPTION);
         }
 
         securityInfo = getSecurityInfo(securityInfoAsString);
-        sslStatus = securityInfo.QueryInterface(Ci.nsISSLStatusProvider)
-                                .SSLStatus;
+        sslStatus = securityInfo.SSLStatus;
         let params = { exceptionAdded: false,
                        sslStatus };
 
         try {
           switch (Services.prefs.getIntPref("browser.ssl_override_behavior")) {
             case 2 : // Pre-fetch & pre-populate
               params.prefetchCert = true;
             case 1 : // Pre-populate
@@ -3031,18 +3030,17 @@ var BrowserOnClick = {
         break;
 
       case "advancedButton":
         if (isTopFrame) {
           secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_BAD_CERT_TOP_UNDERSTAND_RISKS);
         }
 
         securityInfo = getSecurityInfo(securityInfoAsString);
-        sslStatus = securityInfo.QueryInterface(Ci.nsISSLStatusProvider)
-                                .SSLStatus;
+        sslStatus = securityInfo.SSLStatus;
         let errorInfo = getDetailedCertErrorInfo(location,
                                                  securityInfo);
         let validityInfo = {
           notAfter: sslStatus.serverCert.validity.notAfter,
           notBefore: sslStatus.serverCert.validity.notBefore,
           notAfterLocalTime: sslStatus.serverCert.validity.notAfterLocalTime,
           notBeforeLocalTime: sslStatus.serverCert.validity.notBeforeLocalTime,
         };
--- a/browser/base/content/pageinfo/security.js
+++ b/browser/base/content/pageinfo/security.js
@@ -22,17 +22,16 @@ var security = {
 
   // Display the server certificate (static)
   viewCert() {
     var cert = security._cert;
     viewCertHelper(window, cert);
   },
 
   _getSecurityInfo() {
-    const nsISSLStatusProvider = Ci.nsISSLStatusProvider;
     const nsISSLStatus = Ci.nsISSLStatus;
 
     // We don't have separate info for a frame, return null until further notice
     // (see bug 138479)
     if (!this.windowInfo.isTopWindow)
       return null;
 
     var hostName = this.windowInfo.hostName;
@@ -45,21 +44,19 @@ var security = {
       (ui.state & Ci.nsIWebProgressListener.STATE_IS_BROKEN);
     var isMixed =
       (ui.state & (Ci.nsIWebProgressListener.STATE_LOADED_MIXED_ACTIVE_CONTENT |
                    Ci.nsIWebProgressListener.STATE_LOADED_MIXED_DISPLAY_CONTENT));
     var isInsecure =
       (ui.state & Ci.nsIWebProgressListener.STATE_IS_INSECURE);
     var isEV =
       (ui.state & Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL);
-    ui.QueryInterface(nsISSLStatusProvider);
-    var status = ui.SSLStatus;
+    var status = ui.secInfo && ui.secInfo.SSLStatus;
 
     if (!isInsecure && status) {
-      status.QueryInterface(nsISSLStatus);
       var cert = status.serverCert;
       var issuerName = cert.issuerOrganization || cert.issuerName;
 
       var retval = {
         hostName,
         cAName: issuerName,
         encryptionAlgorithm: undefined,
         encryptionStrength: undefined,
--- a/browser/components/extensions/parent/ext-browserAction.js
+++ b/browser/components/extensions/parent/ext-browserAction.js
@@ -67,17 +67,18 @@ this.browserAction = class extends Exten
     this.eventQueue = [];
 
     this.tabManager = extension.tabManager;
 
     this.defaults = {
       enabled: true,
       title: options.default_title || extension.name,
       badgeText: "",
-      badgeBackgroundColor: null,
+      badgeBackgroundColor: [0xd9, 0, 0, 255],
+      badgeDefaultColor: [255, 255, 255, 255],
       badgeTextColor: null,
       popup: options.default_popup || "",
       area: browserAreas[options.default_area || "navbar"],
     };
     this.globals = Object.create(this.defaults);
 
     this.browserStyle = options.browser_style;
 
@@ -437,31 +438,21 @@ this.browserAction = class extends Exten
       }
 
       if (tabData.enabled) {
         node.removeAttribute("disabled");
       } else {
         node.setAttribute("disabled", "true");
       }
 
-      let {badgeBackgroundColor, badgeTextColor} = tabData;
-      let badgeStyle = [];
-      if (badgeBackgroundColor) {
-        let [r, g, b, a] = badgeBackgroundColor;
-        badgeStyle.push(`background-color: rgba(${r}, ${g}, ${b}, ${a / 255})`);
-      }
-      if (badgeTextColor) {
-        let [r, g, b, a] = badgeTextColor;
-        badgeStyle.push(`color: rgba(${r}, ${g}, ${b}, ${a / 255})`);
-      }
-      if (badgeStyle.length) {
-        node.setAttribute("badgeStyle", badgeStyle.join("; "));
-      } else {
-        node.removeAttribute("badgeStyle");
-      }
+      let serializeColor = ([r, g, b, a]) => `rgba(${r}, ${g}, ${b}, ${a / 255})`;
+      node.setAttribute("badgeStyle", [
+        `background-color: ${serializeColor(tabData.badgeBackgroundColor)}`,
+        `color: ${serializeColor(this.getTextColor(tabData))}`,
+      ].join("; "));
 
       let style = this.iconData.get(tabData.icon);
       node.setAttribute("style", style);
     };
     if (sync) {
       callback();
     } else {
       node.ownerGlobal.requestAnimationFrame(callback);
@@ -564,30 +555,80 @@ this.browserAction = class extends Exten
   }
 
   /**
    * Set a global, window specific or tab specific property.
    *
    * @param {Object} details
    *        An object with optional `tabId` or `windowId` properties.
    * @param {string} prop
-   *        String property to set. Should should be one of "icon", "title", "badgeText"
+   *        String property to set. Should should be one of "icon", "title", "badgeText",
    *        "popup", "badgeBackgroundColor", "badgeTextColor" or "enabled".
    * @param {string} value
    *        Value for prop.
+   * @returns {Object}
+   *        The object to which the property has been set.
    */
   setProperty(details, prop, value) {
     let {target, values} = this.getContextData(details);
     if (value === null) {
       delete values[prop];
     } else {
       values[prop] = value;
     }
 
     this.updateOnChange(target);
+    return values;
+  }
+
+  /**
+   * Determines the text badge color to be used in a tab, window, or globally.
+   *
+   * @param {Object} values
+   *        The values associated with the tab or window, or global values.
+   * @returns {ColorArray}
+   */
+  getTextColor(values) {
+    // If a text color has been explicitly provided, use it.
+    let {badgeTextColor} = values;
+    if (badgeTextColor) {
+      return badgeTextColor;
+    }
+
+    // Otherwise, check if the default color to be used has been cached previously.
+    let {badgeDefaultColor} = values;
+    if (badgeDefaultColor) {
+      return badgeDefaultColor;
+    }
+
+    // Choose a color among white and black, maximizing contrast with background
+    // according to https://www.w3.org/TR/WCAG20-TECHS/G18.html#G18-procedure
+    let [r, g, b] = values.badgeBackgroundColor.slice(0, 3).map(function(channel) {
+      channel /= 255;
+      if (channel <= 0.03928) {
+        return channel / 12.92;
+      }
+      return ((channel + 0.055) / 1.055) ** 2.4;
+    });
+    let lum = 0.2126 * r + 0.7152 * g + 0.0722 * b;
+
+    // The luminance is 0 for black, 1 for white, and `lum` for the background color.
+    // Since `0 <= lum`, the contrast ratio for black is `c0 = (lum + 0.05) / 0.05`.
+    // Since `lum <= 1`, the contrast ratio for white is `c1 = 1.05 / (lum + 0.05)`.
+    // We want to maximize contrast, so black is chosen if `c1 < c0`, that is, if
+    // `1.05 * 0.05 < (L + 0.05) ** 2`. Otherwise white is chosen.
+    let channel = 1.05 * 0.05 < (lum + 0.05) ** 2 ? 0 : 255;
+    let result = [channel, channel, channel, 255];
+
+    // Cache the result as high as possible in the prototype chain
+    while (!Object.getOwnPropertyDescriptor(values, "badgeDefaultColor")) {
+      values = Object.getPrototypeOf(values);
+    }
+    values.badgeDefaultColor = result;
+    return result;
   }
 
   /**
    * Retrieve the value of a global, window specific or tab specific property.
    *
    * @param {Object} details
    *        An object with optional `tabId` or `windowId` properties.
    * @param {string} prop
@@ -687,32 +728,38 @@ this.browserAction = class extends Exten
         },
 
         getPopup: function(details) {
           return browserAction.getProperty(details, "popup");
         },
 
         setBadgeBackgroundColor: function(details) {
           let color = parseColor(details.color, "background");
-          browserAction.setProperty(details, "badgeBackgroundColor", color);
+          let values = browserAction.setProperty(details, "badgeBackgroundColor", color);
+          if (color === null) {
+            // Let the default text color inherit after removing background color
+            delete values.badgeDefaultColor;
+          } else {
+            // Invalidate a cached default color calculated with the old background
+            values.badgeDefaultColor = null;
+          }
         },
 
         getBadgeBackgroundColor: function(details, callback) {
-          let color = browserAction.getProperty(details, "badgeBackgroundColor");
-          return color || [0xd9, 0, 0, 255];
+          return browserAction.getProperty(details, "badgeBackgroundColor");
         },
 
         setBadgeTextColor: function(details) {
           let color = parseColor(details.color, "text");
           browserAction.setProperty(details, "badgeTextColor", color);
         },
 
-        getBadgeTextColor: function(details, callback) {
-          let color = browserAction.getProperty(details, "badgeTextColor");
-          return color || [255, 255, 255, 255];
+        getBadgeTextColor: function(details) {
+          let {values} = browserAction.getContextData(details);
+          return browserAction.getTextColor(values);
         },
 
         openPopup: function() {
           let window = windowTracker.topWindow;
           browserAction.triggerAction(window);
         },
       },
     };
--- a/browser/components/extensions/test/browser/browser_ext_browserAction_context.js
+++ b/browser/components/extensions/test/browser/browser_ext_browserAction_context.js
@@ -755,16 +755,99 @@ add_task(async function testMultipleWind
 
           expect(null, details[1], null, details[0]);
         },
       ];
     },
   });
 });
 
+add_task(async function testDefaultBadgeTextColor() {
+  await runTests({
+    manifest: {
+      "browser_action": {
+        "default_icon": "default.png",
+        "default_popup": "default.html",
+        "default_title": "Default Title",
+      },
+    },
+
+    "files": {
+      "default.png": imageBuffer,
+      "window1.png": imageBuffer,
+      "window2.png": imageBuffer,
+    },
+
+    getTests: function(tabs, windows) {
+      let details = [
+        {"icon": browser.runtime.getURL("default.png"),
+         "popup": browser.runtime.getURL("default.html"),
+         "title": "Default Title",
+         "badge": "",
+         "badgeBackgroundColor": [0xd9, 0x00, 0x00, 0xFF],
+         "badgeTextColor": [0xff, 0xff, 0xff, 0xff],
+         "enabled": true},
+        {"badgeBackgroundColor": [0xff, 0xff, 0x00, 0xFF],
+         "badgeTextColor": [0x00, 0x00, 0x00, 0xff]},
+        {"badgeBackgroundColor": [0x00, 0x00, 0xff, 0xFF],
+         "badgeTextColor": [0xff, 0xff, 0xff, 0xff]},
+        {"badgeBackgroundColor": [0xff, 0xff, 0xff, 0x00],
+         "badgeTextColor": [0x00, 0x00, 0x00, 0xff]},
+        {"badgeBackgroundColor": [0x00, 0x00, 0xff, 0xFF],
+         "badgeTextColor": [0xff, 0x00, 0xff, 0xff]},
+        {"badgeBackgroundColor": [0xff, 0xff, 0xff, 0x00]},
+        {"badgeBackgroundColor": [0x00, 0x00, 0x00, 0x00],
+         "badgeTextColor": [0xff, 0xff, 0xff, 0xff]},
+      ];
+
+      return [
+        async expect => {
+          browser.test.log("Initial state, expect default properties.");
+          expect(null, null, null, details[0]);
+        },
+        async expect => {
+          browser.test.log("Set a global light bgcolor, expect black text.");
+          browser.browserAction.setBadgeBackgroundColor({color: "#ff0"});
+          expect(null, null, details[1], details[0]);
+        },
+        async expect => {
+          browser.test.log("Set a window-specific dark bgcolor, expect white text.");
+          let windowId = windows[0];
+          browser.browserAction.setBadgeBackgroundColor({windowId, color: "#00f"});
+          expect(null, details[2], details[1], details[0]);
+        },
+        async expect => {
+          browser.test.log("Set a tab-specific transparent-white bgcolor, expect black text.");
+          let tabId = tabs[0];
+          browser.browserAction.setBadgeBackgroundColor({tabId, color: "#fff0"});
+          expect(details[3], details[2], details[1], details[0]);
+        },
+        async expect => {
+          browser.test.log("Set a window-specific text color, expect it in the tab.");
+          let windowId = windows[0];
+          browser.browserAction.setBadgeTextColor({windowId, color: "#f0f"});
+          expect(details[5], details[4], details[1], details[0]);
+        },
+        async expect => {
+          browser.test.log("Remove the window-specific text color, expect black again.");
+          let windowId = windows[0];
+          browser.browserAction.setBadgeTextColor({windowId, color: null});
+          expect(details[3], details[2], details[1], details[0]);
+        },
+        async expect => {
+          browser.test.log("Set a tab-specific transparent-black bgcolor, expect white text.");
+          let tabId = tabs[0];
+          browser.browserAction.setBadgeBackgroundColor({tabId, color: "#0000"});
+          expect(details[6], details[2], details[1], details[0]);
+        },
+      ];
+    },
+  });
+});
+
 add_task(async function testNavigationClearsData() {
   let url = "http://example.com/";
   let default_title = "Default title";
   let tab_title = "Tab title";
 
   let {Management: {global: {tabTracker}}} = ChromeUtils.import("resource://gre/modules/Extension.jsm", {});
   let extension, tabs = [];
   async function addTab(...args) {
--- a/browser/components/extensions/test/browser/browser_ext_browserAction_theme_icons.js
+++ b/browser/components/extensions/test/browser/browser_ext_browserAction_theme_icons.js
@@ -1,151 +1,271 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
-// PNG image data for a simple red dot.
-const BACKGROUND = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";
-
 const LIGHT_THEME_COLORS = {
   "accentcolor": "#FFF",
   "textcolor": "#000",
 };
 
 const DARK_THEME_COLORS = {
   "accentcolor": "#000",
   "textcolor": "#FFF",
 };
 
+const TOOLBAR_MAPPING = {
+  "navbar": "nav-bar",
+  "tabstrip": "TabsToolbar",
+};
+
 async function testBrowserAction(extension, expectedIcon) {
   let browserActionWidget = getBrowserActionWidget(extension);
   await promiseAnimationFrame();
   let browserActionButton = browserActionWidget.forWindow(window).node;
   ok(getListStyleImage(browserActionButton).includes(expectedIcon), `Expected browser action icon to be ${expectedIcon}`);
 }
 
 async function testStaticTheme(options) {
-  let {themeType, themeIcons, withDefaultIcon} = options;
+  let {
+    themeData,
+    themeIcons,
+    withDefaultIcon,
+    expectedIcon,
+    defaultArea = "navbar",
+  } = options;
 
   let manifest = {
     "browser_action": {
       "theme_icons": themeIcons,
+      "default_area": defaultArea,
     },
   };
 
   if (withDefaultIcon) {
     manifest.browser_action.default_icon = "default.png";
   }
 
   let extension = ExtensionTestUtils.loadExtension({manifest});
 
   await extension.startup();
 
   // Confirm that the browser action has the correct default icon before a theme is loaded.
-  let expectedDefaultIcon = withDefaultIcon ? "default.png" : "dark.png";
+  let toolbarId = TOOLBAR_MAPPING[defaultArea];
+  let expectedDefaultIcon;
+  // Some platforms have dark toolbars by default, take it in account when picking the default icon.
+  if (toolbarId && document.getElementById(toolbarId).hasAttribute("brighttext")) {
+    expectedDefaultIcon = "light.png";
+  } else {
+    expectedDefaultIcon = withDefaultIcon ? "default.png" : "dark.png";
+  }
   await testBrowserAction(extension, expectedDefaultIcon);
 
   let theme = ExtensionTestUtils.loadExtension({
     manifest: {
       "theme": {
-        "images": {
-          "headerURL": "background.png",
-        },
-        "colors": themeType === "light" ? LIGHT_THEME_COLORS : DARK_THEME_COLORS,
+        "colors": themeData,
       },
     },
-    files: {
-      "background.png": BACKGROUND,
-    },
   });
 
   await theme.startup();
 
   // Confirm that the correct icon is used when the theme is loaded.
-  if (themeType == "light") {
-    // The dark icon should be used if the theme is light.
+  if (expectedIcon == "dark") {
+    // The dark icon should be used if the area is light.
     await testBrowserAction(extension, "dark.png");
   } else {
-    // The light icon should be used if the theme is dark.
+    // The light icon should be used if the area is dark.
     await testBrowserAction(extension, "light.png");
   }
 
   await theme.unload();
 
   // Confirm that the browser action has the correct default icon when the theme is unloaded.
   await testBrowserAction(extension, expectedDefaultIcon);
 
   await extension.unload();
 }
 
 add_task(async function browseraction_theme_icons_light_theme() {
   await testStaticTheme({
-    themeType: "light",
+    themeData: LIGHT_THEME_COLORS,
+    expectedIcon: "dark",
     themeIcons: [{
       "light": "light.png",
       "dark": "dark.png",
       "size": 19,
     }],
     withDefaultIcon: true,
   });
   await testStaticTheme({
-    themeType: "light",
+    themeData: LIGHT_THEME_COLORS,
+    expectedIcon: "dark",
     themeIcons: [{
       "light": "light.png",
       "dark": "dark.png",
       "size": 16,
     }, {
       "light": "light.png",
       "dark": "dark.png",
       "size": 32,
     }],
     withDefaultIcon: false,
   });
 });
 
 add_task(async function browseraction_theme_icons_dark_theme() {
   await testStaticTheme({
-    themeType: "dark",
+    themeData: DARK_THEME_COLORS,
+    expectedIcon: "light",
     themeIcons: [{
       "light": "light.png",
       "dark": "dark.png",
       "size": 19,
     }],
     withDefaultIcon: true,
   });
   await testStaticTheme({
-    themeType: "dark",
+    themeData: DARK_THEME_COLORS,
+    expectedIcon: "light",
     themeIcons: [{
       "light": "light.png",
       "dark": "dark.png",
       "size": 16,
     }, {
       "light": "light.png",
       "dark": "dark.png",
       "size": 32,
     }],
     withDefaultIcon: false,
   });
 });
 
+add_task(async function browseraction_theme_icons_different_toolbars() {
+  let themeData = {
+    "accentcolor": "#000",
+    "textcolor": "#fff",
+    "toolbar": "#fff",
+    "toolbar_text": "#000",
+  };
+  await testStaticTheme({
+    themeData,
+    expectedIcon: "dark",
+    themeIcons: [{
+      "light": "light.png",
+      "dark": "dark.png",
+      "size": 19,
+    }],
+    withDefaultIcon: true,
+  });
+  await testStaticTheme({
+    themeData,
+    expectedIcon: "dark",
+    themeIcons: [{
+      "light": "light.png",
+      "dark": "dark.png",
+      "size": 16,
+    }, {
+      "light": "light.png",
+      "dark": "dark.png",
+      "size": 32,
+    }],
+  });
+  await testStaticTheme({
+    themeData,
+    expectedIcon: "light",
+    defaultArea: "tabstrip",
+    themeIcons: [{
+      "light": "light.png",
+      "dark": "dark.png",
+      "size": 19,
+    }],
+    withDefaultIcon: true,
+  });
+  await testStaticTheme({
+    themeData,
+    expectedIcon: "light",
+    defaultArea: "tabstrip",
+    themeIcons: [{
+      "light": "light.png",
+      "dark": "dark.png",
+      "size": 16,
+    }, {
+      "light": "light.png",
+      "dark": "dark.png",
+      "size": 32,
+    }],
+  });
+});
+
+add_task(async function browseraction_theme_icons_overflow_panel() {
+  let themeData = {
+    "popup": "#000",
+    "popup_text": "#fff",
+  };
+  await testStaticTheme({
+    themeData,
+    expectedIcon: "dark",
+    themeIcons: [{
+      "light": "light.png",
+      "dark": "dark.png",
+      "size": 19,
+    }],
+    withDefaultIcon: true,
+  });
+  await testStaticTheme({
+    themeData,
+    expectedIcon: "dark",
+    themeIcons: [{
+      "light": "light.png",
+      "dark": "dark.png",
+      "size": 16,
+    }, {
+      "light": "light.png",
+      "dark": "dark.png",
+      "size": 32,
+    }],
+  });
+  await testStaticTheme({
+    themeData,
+    expectedIcon: "light",
+    defaultArea: "menupanel",
+    themeIcons: [{
+      "light": "light.png",
+      "dark": "dark.png",
+      "size": 19,
+    }],
+    withDefaultIcon: true,
+  });
+  await testStaticTheme({
+    themeData,
+    expectedIcon: "light",
+    defaultArea: "menupanel",
+    themeIcons: [{
+      "light": "light.png",
+      "dark": "dark.png",
+      "size": 16,
+    }, {
+      "light": "light.png",
+      "dark": "dark.png",
+      "size": 32,
+    }],
+  });
+});
+
 add_task(async function browseraction_theme_icons_dynamic_theme() {
   let themeExtension = ExtensionTestUtils.loadExtension({
     manifest: {
       permissions: ["theme"],
     },
-    files: {
-      "background.png": BACKGROUND,
-    },
     background() {
       browser.test.onMessage.addListener((msg, colors) => {
         if (msg === "update-theme") {
           browser.theme.update({
-            "images": {
-              "headerURL": "background.png",
-            },
             "colors": colors,
           });
           browser.test.sendMessage("theme-updated");
         }
       });
     },
   });
 
--- a/browser/components/extensions/test/browser/browser_ext_contextMenus.js
+++ b/browser/components/extensions/test/browser/browser_ext_contextMenus.js
@@ -19,24 +19,24 @@ add_task(async function() {
         id: "clickme-image",
         title: "Click me!",
         contexts: ["image"],
       });
       browser.contextMenus.create({
         id: "clickme-page",
         title: "Click me!",
         contexts: ["page"],
+      }, () => {
+        browser.test.sendMessage("ready");
       });
-      browser.contextMenus.onClicked.addListener(() => {});
-      browser.test.notifyPass();
     },
   });
 
   await extension.startup();
-  await extension.awaitFinish();
+  await extension.awaitMessage("ready");
 
   let contentAreaContextMenu = await openContextMenu("#img1");
   let item = contentAreaContextMenu.getElementsByAttribute("label", "Click me!");
   is(item.length, 1, "contextMenu item for image was found");
   await closeContextMenu();
 
   contentAreaContextMenu = await openContextMenu("body");
   item = contentAreaContextMenu.getElementsByAttribute("label", "Click me!");
@@ -132,22 +132,22 @@ add_task(async function() {
         id: "ext-without-onclick",
       });
 
       await browser.test.assertRejects(
         browser.contextMenus.update(parent, {parentId: child2}),
         /cannot be an ancestor/,
         "Should not be able to reparent an item as descendent of itself");
 
-      browser.test.notifyPass("contextmenus");
+      browser.test.sendMessage("contextmenus");
     },
   });
 
   await extension.startup();
-  await extension.awaitFinish("contextmenus");
+  await extension.awaitMessage("contextmenus");
 
   let expectedClickInfo = {
     menuItemId: "ext-image",
     mediaType: "image",
     srcUrl: "http://mochi.test:8888/browser/browser/components/extensions/test/browser/ctxmenu-image.png",
     pageUrl: PAGE,
     editable: false,
   };
@@ -525,31 +525,32 @@ add_task(async function test_bookmark_co
     async background() {
       const url = "https://example.com/";
       const title = "Example";
       let newBookmark = await browser.bookmarks.create({
         url,
         title,
         parentId: "toolbar_____",
       });
-      await browser.contextMenus.create({
-        title: "Get bookmark",
-        contexts: ["bookmark"],
-      });
       browser.contextMenus.onClicked.addListener(async (info) => {
         browser.test.assertEq(newBookmark.id, info.bookmarkId, "Bookmark ID matches");
 
         let [bookmark] = await browser.bookmarks.get(info.bookmarkId);
         browser.test.assertEq(title, bookmark.title, "Bookmark title matches");
         browser.test.assertEq(url, bookmark.url, "Bookmark url matches");
         browser.test.assertFalse(info.hasOwnProperty("pageUrl"), "Context menu does not expose pageUrl");
         await browser.bookmarks.remove(info.bookmarkId);
         browser.test.sendMessage("test-finish");
       });
-      browser.test.sendMessage("bookmark-created");
+      browser.contextMenus.create({
+        title: "Get bookmark",
+        contexts: ["bookmark"],
+      }, () => {
+        browser.test.sendMessage("bookmark-created");
+      });
     },
   });
   await extension.startup();
   await extension.awaitMessage("bookmark-created");
   let menu = await openChromeContextMenu(
     "placesContext",
     "#PersonalToolbar .bookmark-item:last-child");
 
@@ -564,22 +565,23 @@ add_task(async function test_bookmark_co
 add_task(async function test_bookmark_context_requires_permission() {
   const bookmarksToolbar = document.getElementById("PersonalToolbar");
   setToolbarVisibility(bookmarksToolbar, true);
 
   const extension = ExtensionTestUtils.loadExtension({
     manifest: {
       permissions: ["contextMenus"],
     },
-    async background() {
-      await browser.contextMenus.create({
+    background() {
+      browser.contextMenus.create({
         title: "Get bookmark",
         contexts: ["bookmark"],
+      }, () => {
+        browser.test.sendMessage("bookmark-created");
       });
-      browser.test.sendMessage("bookmark-created");
     },
   });
   await extension.startup();
   await extension.awaitMessage("bookmark-created");
   let menu = await openChromeContextMenu(
     "placesContext",
     "#PersonalToolbar .bookmark-item:last-child");
 
--- a/browser/components/extensions/test/browser/browser_ext_contextMenus_icons.js
+++ b/browser/components/extensions/test/browser/browser_ext_contextMenus_icons.js
@@ -29,31 +29,31 @@ add_task(async function test_root_icon()
         onclick: () => {
           browser.contextMenus.remove(menuitemId);
           browser.test.sendMessage("child-deleted");
         },
       });
 
       browser.contextMenus.create({
         title: "child",
+      }, () => {
+        browser.test.sendMessage("contextmenus-icons");
       });
-
-      browser.test.notifyPass("contextmenus-icons");
     },
   });
 
   let confirmContextMenuIcon = (rootElements) => {
     let expectedURL = new RegExp(String.raw`^moz-extension://[^/]+/extension\.png$`);
     is(rootElements.length, 1, "Found exactly one menu item");
     let imageUrl = rootElements[0].getAttribute("image");
     ok(expectedURL.test(imageUrl), "The context menu should display the extension icon next to the root element");
   };
 
   await extension.startup();
-  await extension.awaitFinish("contextmenus-icons");
+  await extension.awaitMessage("contextmenus-icons");
 
   let extensionMenu = await openExtensionContextMenu();
 
   let contextMenu = document.getElementById("contentAreaContextMenu");
   let topLevelMenuItem = contextMenu.getElementsByAttribute("ext-type", "top-level-menu");
   confirmContextMenuIcon(topLevelMenuItem);
 
   let childToDelete = extensionMenu.getElementsByAttribute("label", "child-to-delete");
@@ -99,26 +99,16 @@ add_task(async function test_child_icon(
     files: {
       "black_icon.png": IMAGE_ARRAYBUFFER_BLACK,
       "red_icon.png": IMAGE_ARRAYBUFFER_RED,
       "blue_icon.png": IMAGE_ARRAYBUFFER_BLUE,
       "green_icon.png": IMAGE_ARRAYBUFFER_GREEN,
     },
 
     background: function() {
-      browser.contextMenus.create({
-        title: "child1",
-        id: "contextmenu-child1",
-        icons: {
-          18: "red_icon.png",
-        },
-      });
-
-      browser.test.sendMessage("single-contextmenu-item-added");
-
       browser.test.onMessage.addListener(msg => {
         if (msg !== "add-additional-contextmenu-items") {
           return;
         }
 
         browser.contextMenus.create({
           title: "child2",
           id: "contextmenu-child2",
@@ -128,19 +118,29 @@ add_task(async function test_child_icon(
         });
 
         browser.contextMenus.create({
           title: "child3",
           id: "contextmenu-child3",
           icons: {
             18: "green_icon.png",
           },
+        }, () => {
+          browser.test.sendMessage("extra-contextmenu-items-added");
         });
+      });
 
-        browser.test.notifyPass("extra-contextmenu-items-added");
+      browser.contextMenus.create({
+        title: "child1",
+        id: "contextmenu-child1",
+        icons: {
+          18: "red_icon.png",
+        },
+      }, () => {
+        browser.test.sendMessage("single-contextmenu-item-added");
       });
     },
   });
 
   let confirmContextMenuIcon = (element, imageName) => {
     let imageURL = element.getAttribute("image");
     ok(imageURL.endsWith(imageName), "The context menu should display the extension icon next to the child element");
   };
@@ -151,17 +151,17 @@ add_task(async function test_child_icon(
 
   let contextMenu = await openContextMenu();
   let contextMenuChild1 = contextMenu.getElementsByAttribute("label", "child1")[0];
   confirmContextMenuIcon(contextMenuChild1, "black_icon.png");
 
   await closeContextMenu();
 
   extension.sendMessage("add-additional-contextmenu-items");
-  await extension.awaitFinish("extra-contextmenu-items-added");
+  await extension.awaitMessage("extra-contextmenu-items-added");
 
   contextMenu = await openExtensionContextMenu();
 
   contextMenuChild1 = contextMenu.getElementsByAttribute("label", "child1")[0];
   confirmContextMenuIcon(contextMenuChild1, "red_icon.png");
 
   let contextMenuChild2 = contextMenu.getElementsByAttribute("label", "child2")[0];
   confirmContextMenuIcon(contextMenuChild2, "blue_icon.png");
--- a/browser/components/extensions/test/browser/browser_ext_contextMenus_onclick.js
+++ b/browser/components/extensions/test/browser/browser_ext_contextMenus_onclick.js
@@ -200,18 +200,19 @@ add_task(async function test_onclick_mod
   const manifest = {
     permissions: ["contextMenus"],
   };
 
   function background() {
     function onclick(info) {
       browser.test.sendMessage("click", info);
     }
-    browser.contextMenus.create({contexts: ["all"], title: "modify", onclick});
-    browser.test.sendMessage("ready");
+    browser.contextMenus.create({contexts: ["all"], title: "modify", onclick}, () => {
+      browser.test.sendMessage("ready");
+    });
   }
 
   const extension = ExtensionTestUtils.loadExtension({manifest, background});
   const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE);
 
   await extension.startup();
   await extension.awaitMessage("ready");
 
--- a/browser/components/extensions/test/browser/browser_ext_menus.js
+++ b/browser/components/extensions/test/browser/browser_ext_menus.js
@@ -52,21 +52,21 @@ add_task(async function test_actionConte
     browser_action: {},
     permissions: ["menus"],
   };
 
   async function background() {
     const contexts = ["page_action", "browser_action"];
 
     const parentId = browser.menus.create({contexts, title: "parent"});
-    await browser.menus.create({parentId, title: "click A"});
-    await browser.menus.create({parentId, title: "click B"});
+    browser.menus.create({parentId, title: "click A"});
+    browser.menus.create({parentId, title: "click B"});
 
     for (let i = 1; i < 9; i++) {
-      await browser.menus.create({contexts, id: `${i}`, title: `click ${i}`});
+      browser.menus.create({contexts, id: `${i}`, title: `click ${i}`});
     }
 
     browser.menus.onClicked.addListener((info, tab) => {
       browser.test.sendMessage("click", {info, tab});
     });
 
     const [tab] = await browser.tabs.query({active: true});
     await browser.pageAction.show(tab.id);
@@ -115,21 +115,21 @@ add_task(async function test_hiddenPageA
     page_action: {},
     permissions: ["menus"],
   };
 
   async function background() {
     const contexts = ["page_action"];
 
     const parentId = browser.menus.create({contexts, title: "parent"});
-    await browser.menus.create({parentId, title: "click A"});
-    await browser.menus.create({parentId, title: "click B"});
+    browser.menus.create({parentId, title: "click A"});
+    browser.menus.create({parentId, title: "click B"});
 
     for (let i = 1; i < 9; i++) {
-      await browser.menus.create({contexts, id: `${i}`, title: `click ${i}`});
+      browser.menus.create({contexts, id: `${i}`, title: `click ${i}`});
     }
 
     const [tab] = await browser.tabs.query({active: true});
     await browser.pageAction.hide(tab.id);
     browser.test.sendMessage("ready", tab.id);
   }
 
   const extension = ExtensionTestUtils.loadExtension({manifest, background});
@@ -166,22 +166,23 @@ add_task(async function test_bookmarkCon
     setToolbarVisibility(bt, visible);
     await transitionPromise;
   }
 
   const ext = ExtensionTestUtils.loadExtension({
     manifest: {
       permissions: ["menus", "bookmarks"],
     },
-    async background() {
-      await browser.menus.create({title: "blarg", contexts: ["bookmark"]});
+    background() {
       browser.menus.onShown.addListener(() => {
         browser.test.sendMessage("hello");
       });
-      browser.test.sendMessage("ready");
+      browser.menus.create({title: "blarg", contexts: ["bookmark"]}, () => {
+        browser.test.sendMessage("ready");
+      });
     },
   });
 
   await showBookmarksToolbar();
   await ext.startup();
   await ext.awaitMessage("ready");
 
   let menu = await openChromeContextMenu("placesContext",
@@ -197,41 +198,42 @@ add_task(async function test_bookmarkCon
 });
 
 add_task(async function test_tabContextMenu() {
   const first = ExtensionTestUtils.loadExtension({
     manifest: {
       permissions: ["menus"],
     },
     async background() {
-      await browser.menus.create({
+      browser.menus.create({
         id: "alpha-beta-parent", title: "alpha-beta parent", contexts: ["tab"],
       });
 
-      await browser.menus.create({parentId: "alpha-beta-parent", title: "alpha"});
-      await browser.menus.create({parentId: "alpha-beta-parent", title: "beta"});
+      browser.menus.create({parentId: "alpha-beta-parent", title: "alpha"});
+      browser.menus.create({parentId: "alpha-beta-parent", title: "beta"});
 
-      await browser.menus.create({title: "dummy", contexts: ["page"]});
+      browser.menus.create({title: "dummy", contexts: ["page"]});
 
       browser.menus.onClicked.addListener((info, tab) => {
         browser.test.sendMessage("click", {info, tab});
       });
 
       const [tab] = await browser.tabs.query({active: true});
       browser.test.sendMessage("ready", tab.id);
     },
   });
 
   const second = ExtensionTestUtils.loadExtension({
     manifest: {
       permissions: ["menus"],
     },
-    async background() {
-      await browser.menus.create({title: "gamma", contexts: ["tab"]});
-      browser.test.sendMessage("ready");
+    background() {
+      browser.menus.create({title: "gamma", contexts: ["tab"]}, () => {
+        browser.test.sendMessage("ready");
+      });
     },
   });
 
   const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
   await first.startup();
   await second.startup();
 
   const tabId = await first.awaitMessage("ready");
@@ -274,18 +276,19 @@ add_task(async function test_onclick_fra
   const manifest = {
     permissions: ["menus"],
   };
 
   function background() {
     function onclick(info) {
       browser.test.sendMessage("click", info);
     }
-    browser.menus.create({contexts: ["frame", "page"], title: "modify", onclick});
-    browser.test.sendMessage("ready");
+    browser.menus.create({contexts: ["frame", "page"], title: "modify", onclick}, () => {
+      browser.test.sendMessage("ready");
+    });
   }
 
   const extension = ExtensionTestUtils.loadExtension({manifest, background});
   const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE);
 
   await extension.startup();
   await extension.awaitMessage("ready");
 
@@ -308,30 +311,30 @@ add_task(async function test_onclick_fra
 });
 
 add_task(async function test_multiple_contexts_init() {
   const manifest = {
     permissions: ["menus"],
   };
 
   function background() {
-    browser.menus.create({id: "parent", title: "parent"});
-    browser.tabs.create({url: "tab.html", active: false});
+    browser.menus.create({id: "parent", title: "parent"}, () => {
+      browser.tabs.create({url: "tab.html", active: false});
+    });
   }
 
   const files = {
     "tab.html": "<!DOCTYPE html><meta charset=utf-8><script src=tab.js></script>",
     "tab.js": function() {
-      browser.menus.create({parentId: "parent", id: "child", title: "child"});
-
       browser.menus.onClicked.addListener(info => {
         browser.test.sendMessage("click", info);
       });
-
-      browser.test.sendMessage("ready");
+      browser.menus.create({parentId: "parent", id: "child", title: "child"}, () => {
+        browser.test.sendMessage("ready");
+      });
     },
   };
 
   const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE);
   const extension = ExtensionTestUtils.loadExtension({manifest, background, files});
 
   await extension.startup();
   await extension.awaitMessage("ready");
@@ -353,29 +356,30 @@ add_task(async function test_multiple_co
   await extension.unload();
 });
 
 add_task(async function test_tools_menu() {
   const first = ExtensionTestUtils.loadExtension({
     manifest: {
       permissions: ["menus"],
     },
-    async background() {
-      await browser.menus.create({title: "alpha", contexts: ["tools_menu"]});
-      await browser.menus.create({title: "beta", contexts: ["tools_menu"]});
-      browser.test.sendMessage("ready");
+    background() {
+      browser.menus.create({title: "alpha", contexts: ["tools_menu"]});
+      browser.menus.create({title: "beta", contexts: ["tools_menu"]}, () => {
+        browser.test.sendMessage("ready");
+      });
     },
   });
 
   const second = ExtensionTestUtils.loadExtension({
     manifest: {
       permissions: ["menus"],
     },
     async background() {
-      await browser.menus.create({title: "gamma", contexts: ["tools_menu"]});
+      browser.menus.create({title: "gamma", contexts: ["tools_menu"]});
       browser.menus.onClicked.addListener((info, tab) => {
         browser.test.sendMessage("click", {info, tab});
       });
 
       const [tab] = await browser.tabs.query({active: true});
       browser.test.sendMessage("ready", tab.id);
     },
   });
--- a/browser/components/payments/res/containers/basic-card-form.js
+++ b/browser/components/payments/res/containers/basic-card-form.js
@@ -156,17 +156,21 @@ export default class BasicCardForm exten
     this.formHandler.loadRecord(record, addresses, basicCardPage.preserveFieldValues);
 
     this.form.querySelector(".billingAddressRow").hidden = false;
 
     let billingAddressSelect = this.form.querySelector("#billingAddressGUID");
     if (basicCardPage.billingAddressGUID) {
       billingAddressSelect.value = basicCardPage.billingAddressGUID;
     } else if (!editing) {
-      billingAddressSelect.value = Object.keys(addresses)[0];
+      if (paymentRequest.getAddresses(state)[selectedShippingAddress]) {
+        billingAddressSelect.value = selectedShippingAddress;
+      } else {
+        billingAddressSelect.value = Object.keys(addresses)[0];
+      }
     }
   }
 
   handleEvent(event) {
     switch (event.type) {
       case "click": {
         this.onClick(event);
         break;
--- a/browser/components/payments/test/mochitest/test_basic_card_form.html
+++ b/browser/components/payments/test/mochitest/test_basic_card_form.html
@@ -165,32 +165,35 @@ add_task(async function test_add_selecte
 
   info("have an existing card in storage");
   let card1 = deepClone(PTU.BasicCards.JohnDoe);
   card1.guid = "9864798564";
   card1["cc-exp-year"] = 2011;
 
   let address1 = deepClone(PTU.Addresses.TimBL);
   address1.guid = "TimBLGUID";
+  let address2 = deepClone(PTU.Addresses.TimBL2);
+  address2.guid = "TimBL2GUID";
 
   await form.requestStore.setState({
     page: {
       id: "basic-card-page",
     },
     savedAddresses: {
       [address1.guid]: deepClone(address1),
+      [address2.guid]: deepClone(address2),
     },
     savedBasicCards: {
       [card1.guid]: deepClone(card1),
     },
-    selectedShippingAddress: address1.guid,
+    selectedShippingAddress: address2.guid,
   });
   await asyncElementRendered();
   checkCCForm(form, {
-    billingAddressGUID: address1.guid,
+    billingAddressGUID: address2.guid,
   });
 
   form.remove();
   await form.requestStore.reset();
 });
 
 add_task(async function test_add_noSelectedShippingAddress() {
   let form = new BasicCardForm();
--- a/devtools/client/netmonitor/test/browser_net_filter-autocomplete.js
+++ b/devtools/client/netmonitor/test/browser_net_filter-autocomplete.js
@@ -46,17 +46,17 @@ add_task(async function() {
   // Let the requests load completely before the autocomplete tests begin
   // as autocomplete values also rely on the network requests.
   const waitNetwork = waitForNetworkEvents(monitor, REQUESTS.length);
   loadFrameScriptUtils();
   await performRequestsInContent(REQUESTS);
   await waitNetwork;
 
   EventUtils.synthesizeMouseAtCenter(
-    document.querySelector(".devtools-filterinput"), {}, window);
+    document.querySelector(".devtools-filterinput"), {}, document.defaultView);
   // Empty Mouse click should keep autocomplete hidden
   ok(!document.querySelector(".devtools-autocomplete-popup"),
     "Autocomplete Popup still hidden");
 
   document.querySelector(".devtools-filterinput").focus();
   // Typing a char should invoke a autocomplete
   EventUtils.sendString("s");
   ok(document.querySelector(".devtools-autocomplete-popup"),
--- a/devtools/client/shared/components/menu/MenuItem.js
+++ b/devtools/client/shared/components/menu/MenuItem.js
@@ -2,90 +2,137 @@
  * 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-env browser */
 "use strict";
 
 // A command in a menu.
 
+const { createRef, PureComponent } = require("devtools/client/shared/vendor/react");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const { button, li, span } = dom;
 
-const MenuItem = props => {
-  const attr = {
-    className: "command"
-  };
+class MenuItem extends PureComponent {
+  static get propTypes() {
+    return {
+      // An optional keyboard shortcut to display next to the item.
+      // (This does not actually register the event listener for the key.)
+      accelerator: PropTypes.string,
+
+      // A tri-state value that may be true/false if item should be checkable,
+      // and undefined otherwise.
+      checked: PropTypes.bool,
+
+      // Any additional classes to assign to the button specified as
+      // a space-separated string.
+      className: PropTypes.string,
 
-  if (props.id) {
-    attr.id = props.id;
-  }
+      // An optional ID to be assigned to the item.
+      id: PropTypes.string,
+
+      // The item label.
+      label: PropTypes.string.isRequired,
+
+      // An optional callback to be invoked when the item is selected.
+      onClick: PropTypes.func,
 
-  if (props.className) {
-    attr.className += " " + props.className;
+      // URL of the icon to associate with the MenuItem. (Optional)
+      //
+      //   e.g. chrome://devtools/skim/image/foo.svg
+      //
+      // This may also be set in CSS using the --menuitem-icon-image variable.
+      // Note that in this case, the variable should specify the CSS <image> to
+      // use, not simply the URL (e.g.
+      // "url(chrome://devtools/skim/image/foo.svg)").
+      icon: PropTypes.string,
+    };
   }
 
-  if (props.icon) {
-    attr.className += " iconic";
-    attr.style = { "--menuitem-icon-image": "url(" + props.icon + ")" };
-  }
-
-  if (props.onClick) {
-    attr.onClick = props.onClick;
-  }
-
-  if (typeof props.checked !== "undefined") {
-    attr.role = "menuitemcheckbox";
-    if (props.checked) {
-      attr["aria-checked"] = true;
-    }
-  } else {
-    attr.role = "menuitem";
+  constructor(props) {
+    super(props);
+    this.labelRef = createRef();
   }
 
-  const textLabel = span({ className: "label" }, props.label);
-  const children = [textLabel];
+  componentDidMount() {
+    if (!this.labelRef.current) {
+      return;
+    }
+
+    // Pre-fetch any backgrounds specified for the item.
+    const win = this.labelRef.current.ownerDocument.defaultView;
+    this.preloadCallback = win.requestIdleCallback(() => {
+      this.preloadCallback = null;
+      if (!this.labelRef.current) {
+        return;
+      }
 
-  if (typeof props.accelerator !== "undefined") {
-    const acceleratorLabel = span(
-      { className: "accelerator" },
-      props.accelerator
-    );
-    children.push(acceleratorLabel);
+      const backgrounds = win
+        .getComputedStyle(this.labelRef.current, ":before")
+        .getCSSImageURLs("background-image");
+      for (const background of backgrounds) {
+        const image = new Image();
+        image.src = background;
+      }
+    });
+  }
+
+  componentWillUnmount() {
+    if (!this.labelRef.current || !this.preloadCallback) {
+      return;
+    }
+
+    const win = this.labelRef.current.ownerDocument.defaultView;
+    win.cancelIdleCallback(this.preloadCallback);
+    this.preloadCallback = null;
   }
 
-  return li({ className: "menuitem" }, button(attr, children));
-};
+  render() {
+    const attr = {
+      className: "command",
+    };
 
-MenuItem.propTypes = {
-  // An optional keyboard shortcut to display next to the item.
-  // (This does not actually register the event listener for the key.)
-  accelerator: PropTypes.string,
+    if (this.props.id) {
+      attr.id = this.props.id;
+    }
 
-  // A tri-state value that may be true/false if item should be checkable, and
-  // undefined otherwise.
-  checked: PropTypes.bool,
+    if (this.props.className) {
+      attr.className += " " + this.props.className;
+    }
 
-  // Any additional classes to assign to the button specified as
-  // a space-separated string.
-  className: PropTypes.string,
+    if (this.props.icon) {
+      attr.className += " iconic";
+      attr.style = { "--menuitem-icon-image": "url(" + this.props.icon + ")" };
+    }
+
+    if (this.props.onClick) {
+      attr.onClick = this.props.onClick;
+    }
 
-  // An optional ID to be assigned to the item.
-  id: PropTypes.string,
-
-  // The item label.
-  label: PropTypes.string.isRequired,
-
-  // An optional callback to be invoked when the item is selected.
-  onClick: PropTypes.func,
+    if (typeof this.props.checked !== "undefined") {
+      attr.role = "menuitemcheckbox";
+      if (this.props.checked) {
+        attr["aria-checked"] = true;
+      }
+    } else {
+      attr.role = "menuitem";
+    }
 
-  // URL of the icon to associate with the MenuItem. (Optional)
-  //
-  //   e.g. chrome://devtools/skim/image/foo.svg
-  //
-  // This may also be set in CSS using the --menuitem-icon-image variable.
-  // Note that in this case, the variable should specify the CSS <image> to use,
-  // not simply the URL (e.g. "url(chrome://devtools/skim/image/foo.svg)").
-  icon: PropTypes.string,
-};
+    const textLabel = span(
+      { className: "label", ref: this.labelRef },
+      this.props.label
+    );
+    const children = [textLabel];
+
+    if (typeof this.props.accelerator !== "undefined") {
+      const acceleratorLabel = span(
+        { className: "accelerator" },
+        this.props.accelerator
+      );
+      children.push(acceleratorLabel);
+    }
+
+    return li({ className: "menuitem" }, button(attr, children));
+  }
+}
 
 module.exports = MenuItem;
--- a/devtools/client/webconsole/components/JSTerm.js
+++ b/devtools/client/webconsole/components/JSTerm.js
@@ -569,23 +569,20 @@ class JSTerm extends Component {
    *        - selectedNodeActor: tells the NodeActor ID of the current selection
    *        in the Inspector, if such a selection exists. This is used by
    *        helper functions that can evaluate on the current selection.
    * @return object
    *         A promise object that is resolved when the server response is
    *         received.
    */
   requestEvaluation(str, options = {}) {
-    const toolbox = gDevTools.getToolbox(this.hud.owner.target);
-
     // Send telemetry event. If we are in the browser toolbox we send -1 as the
     // toolbox session id.
-    this._telemetry.recordEvent("devtools.main", "execute_js", "webconsole", null, {
-      "lines": str.split(/\n/).length,
-      "session_id": toolbox ? toolbox.sessionId : -1
+    this.props.serviceContainer.recordTelemetryEvent("execute_js", {
+      "lines": str.split(/\n/).length
     });
 
     let frameActor = null;
     if ("frame" in options) {
       frameActor = this.getFrameActor(options.frame);
     }
 
     const evalOptions = {
--- a/devtools/client/webconsole/utils/object-inspector.js
+++ b/devtools/client/webconsole/utils/object-inspector.js
@@ -56,16 +56,17 @@ function getObjectInspector(grip, servic
       new LongStringClient(serviceContainer.hudProxy.client, object),
     releaseActor: actor => {
       if (!actor || !serviceContainer.hudProxy.releaseActor) {
         return;
       }
       serviceContainer.hudProxy.releaseActor(actor);
     },
     onViewSourceInDebugger: serviceContainer.onViewSourceInDebugger,
+    recordTelemetryEvent: serviceContainer.recordTelemetryEvent,
     openLink: serviceContainer.openLink,
   };
 
   if (!(typeof grip === "string" || (grip && grip.type === "longString"))) {
     Object.assign(objectInspectorProps, {
       onDOMNodeMouseOver,
       onDOMNodeMouseOut,
       onInspectIconClick,
--- a/devtools/client/webconsole/webconsole-output-wrapper.js
+++ b/devtools/client/webconsole/webconsole-output-wrapper.js
@@ -104,16 +104,22 @@ WebConsoleOutputWrapper.prototype = {
         },
         requestData(id, type) {
           return hud.proxy.networkDataProvider.requestData(id, type);
         },
         onViewSource(frame) {
           if (hud && hud.owner && hud.owner.viewSource) {
             hud.owner.viewSource(frame.url, frame.line);
           }
+        },
+        recordTelemetryEvent: (eventName, extra = {}) => {
+          this.telemetry.recordEvent("devtools.main", eventName, "webconsole", null, {
+            ...extra,
+            "session_id": this.toolbox && this.toolbox.sessionId || -1
+          });
         }
       };
 
       // Set `openContextMenu` this way so, `serviceContainer` variable
       // is available in the current scope and we can pass it into
       // `createContextMenu` method.
       serviceContainer.openContextMenu = (e, message) => {
         const { screenX, screenY, target } = e;
--- a/devtools/shared/security/auth.js
+++ b/devtools/shared/security/auth.js
@@ -295,17 +295,17 @@ OOBCert.Client.prototype = {
    *         Whether the connection is valid.
    */
   // eslint-disable-next-line no-shadow
   validateConnection({ cert, socket }) {
     // Step B.7
     // Client verifies that Server's cert matches hash(ServerCert) from the
     // advertisement
     dumpv("Validate server cert hash");
-    const serverCert = socket.securityInfo.QueryInterface(Ci.nsISSLStatusProvider)
+    const serverCert = socket.securityInfo.QueryInterface(Ci.nsITransportSecurityInfo)
                            .SSLStatus.serverCert;
     const advertisedCert = cert;
     if (serverCert.sha256Fingerprint != advertisedCert.sha256) {
       dumpn("Server cert hash doesn't match advertisement");
       return false;
     }
     return true;
   },
--- a/devtools/shared/security/socket.js
+++ b/devtools/shared/security/socket.js
@@ -348,17 +348,17 @@ function _isInputAlive(input) {
 
 /**
  * To allow the connection to proceed with self-signed cert, we store a cert
  * override.  This implies that we take on the burden of authentication for
  * these connections.
  */
 function _storeCertOverride(s, host, port) {
   // eslint-disable-next-line no-shadow
-  const cert = s.securityInfo.QueryInterface(Ci.nsISSLStatusProvider)
+  const cert = s.securityInfo.QueryInterface(Ci.nsITransportSecurityInfo)
               .SSLStatus.serverCert;
   const overrideBits = Ci.nsICertOverrideService.ERROR_UNTRUSTED |
                      Ci.nsICertOverrideService.ERROR_MISMATCH;
   certOverrideService.rememberValidityOverride(host, port, cert, overrideBits,
                                                true /* temporary */);
 }
 
 /**
--- a/devtools/shared/webconsole/network-helper.js
+++ b/devtools/shared/webconsole/network-helper.js
@@ -595,17 +595,16 @@ var NetworkHelper = {
      * - request is mixed content (which makes no sense whatsoever)
      *   => .securityState has STATE_IS_BROKEN flag
      *   => .errorCode is NOT an NSS error code
      *   => .errorMessage is not available
      *      => state === "weak"
      */
 
     securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
-    securityInfo.QueryInterface(Ci.nsISSLStatusProvider);
 
     const wpl = Ci.nsIWebProgressListener;
     const NSSErrorsService = Cc["@mozilla.org/nss_errors_service;1"]
                                .getService(Ci.nsINSSErrorsService);
     const SSLStatus = securityInfo.SSLStatus;
     if (!NSSErrorsService.isNSSErrorCode(securityInfo.errorCode)) {
       const state = securityInfo.securityState;
 
--- a/devtools/shared/webconsole/test/unit/test_security-info-parser.js
+++ b/devtools/shared/webconsole/test/unit/test_security-info-parser.js
@@ -28,18 +28,17 @@ const MockCertificate = {
   sha1Fingerprint: "qwertyuiop",
   validity: {
     notBeforeLocalDay: "yesterday",
     notAfterLocalDay: "tomorrow",
   }
 };
 
 const MockSecurityInfo = {
-  QueryInterface: ChromeUtils.generateQI([Ci.nsITransportSecurityInfo,
-                                          Ci.nsISSLStatusProvider]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsITransportSecurityInfo]),
   securityState: wpl.STATE_IS_SECURE,
   errorCode: 0,
   SSLStatus: {
     cipherSuite: "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256",
     // TLS_VERSION_1_2
     protocolVersion: 3,
     serverCert: MockCertificate,
   }
--- a/devtools/shared/webconsole/test/unit/test_security-info-state.js
+++ b/devtools/shared/webconsole/test/unit/test_security-info-state.js
@@ -14,18 +14,17 @@ Object.defineProperty(this, "NetworkHelp
   },
   configurable: true,
   writeable: false,
   enumerable: true
 });
 
 const wpl = Ci.nsIWebProgressListener;
 const MockSecurityInfo = {
-  QueryInterface: ChromeUtils.generateQI([Ci.nsITransportSecurityInfo,
-                                          Ci.nsISSLStatusProvider]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsITransportSecurityInfo]),
   securityState: wpl.STATE_IS_BROKEN,
   errorCode: 0,
   SSLStatus: {
     // nsISSLStatus.TLS_VERSION_1_2
     protocolVersion: 3,
     cipherSuite: "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256",
   }
 };
--- a/devtools/shared/webconsole/test/unit/test_security-info-static-hpkp.js
+++ b/devtools/shared/webconsole/test/unit/test_security-info-static-hpkp.js
@@ -15,18 +15,17 @@ Object.defineProperty(this, "NetworkHelp
   configurable: true,
   writeable: false,
   enumerable: true
 });
 
 const wpl = Ci.nsIWebProgressListener;
 
 const MockSecurityInfo = {
-  QueryInterface: ChromeUtils.generateQI([Ci.nsITransportSecurityInfo,
-                                          Ci.nsISSLStatusProvider]),
+  QueryInterface: ChromeUtils.generateQI([Ci.nsITransportSecurityInfo]),
   securityState: wpl.STATE_IS_SECURE,
   errorCode: 0,
   SSLStatus: {
     cipherSuite: "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256",
     // TLS_VERSION_1_2
     protocolVersion: 3,
     serverCert: {
       validity: {}
--- a/dom/animation/Animation.cpp
+++ b/dom/animation/Animation.cpp
@@ -140,23 +140,16 @@ Animation::SetEffectNoUpdate(AnimationEf
   if (mEffect == aEffect) {
     return;
   }
 
   AutoMutationBatchForAnimation mb(*this);
   bool wasRelevant = mIsRelevant;
 
   if (mEffect) {
-    if (!aEffect) {
-      // If the new effect is null, call ResetPendingTasks before clearing
-      // mEffect since ResetPendingTasks needs it to get the appropriate
-      // PendingAnimationTracker.
-      ResetPendingTasks();
-    }
-
     // We need to notify observers now because once we set mEffect to null
     // we won't be able to find the target element to notify.
     if (mIsRelevant) {
       nsNodeUtils::AnimationRemoved(this);
     }
 
     // Break links with the old effect and then drop it.
     RefPtr<AnimationEffect> oldEffect = mEffect;
--- a/dom/animation/test/crashtests/1333539-1.html
+++ b/dom/animation/test/crashtests/1333539-1.html
@@ -13,16 +13,18 @@ window.onload = function(){
   target.appendChild(document.createElement("meter"));
   anim1.startTime = 88;
   anim1.timeline = null;
   anim1.pause();
   anim1.effect = effect;
   anim2.effect = effect;
   anim1.effect = effect;
 
-  Promise.all([anim1.ready, anim2.ready]).then(function() {
+  // anim1, since it doesn't have a timeline, will remain pause-pending,
+  // so just wait on anim2.
+  anim2.ready.then(() => {
     document.documentElement.classList.remove("reftest-wait");
   });
 };
 </script>
 </head>
 <body></body>
 </html>
--- a/dom/animation/test/mozilla/test_pending_animation_tracker.html
+++ b/dom/animation/test/mozilla/test_pending_animation_tracker.html
@@ -6,27 +6,33 @@
 <script src="/resources/testharnessreport.js"></script>
 <script src="../testcommon.js"></script>
 </head>
 <body>
 <div id="log"></div>
 <script>
 "use strict";
 
-test(t => {
+promise_test(async t => {
+  // See below, but we should ensure we are in a rAF callback before proceeding
+  // or else we will get inconsistent results.
+  await waitForNextFrame();
+
   const target = addDiv(t);
   const anim = target.animate(null, 100 * MS_PER_SEC);
   assert_true(SpecialPowers.DOMWindowUtils.isAnimationInPendingTracker(anim),
               'The animation should be tracked by tracker');
 
   anim.effect = null;
+  await waitForNextFrame();
 
   assert_false(SpecialPowers.DOMWindowUtils.isAnimationInPendingTracker(anim),
                'The animation should NOT be tracked by the tracker');
-}, 'Setting null effect removes the animation from the tracker');
+}, 'An animation whose effect is made null while pending is subsequently'
+   + ' removed from the tracker');
 
 test(t => {
   const target = addDiv(t);
   const anim = target.animate(null, 100 * MS_PER_SEC);
   assert_true(SpecialPowers.DOMWindowUtils.isAnimationInPendingTracker(anim),
               'The animation should be tracked by tracker');
 
   const newEffect = new KeyframeEffect(target, null);
--- a/dom/base/nsNameSpaceManager.cpp
+++ b/dom/base/nsNameSpaceManager.cpp
@@ -269,17 +269,14 @@ nsNameSpaceManager::AddDisabledNameSpace
 
   MOZ_ASSERT(aNameSpaceID == (int32_t) mURIArray.Length());
   mURIArray.AppendElement(uri.forget());
   mDisabledURIToIDTable.Put(mURIArray.LastElement(), aNameSpaceID);
 
   return NS_OK;
 }
 
-// nsISupports
-NS_IMPL_ISUPPORTS0(nsNameSpaceManager)
-
 void
 nsNameSpaceManager::PrefChanged(const char* aPref)
 {
   mMathMLDisabled = mozilla::Preferences::GetBool(kPrefMathMLDisabled);
   mSVGDisabled = mozilla::Preferences::GetBool(kPrefSVGDisabled);
 }
--- a/dom/base/nsNameSpaceManager.h
+++ b/dom/base/nsNameSpaceManager.h
@@ -25,20 +25,21 @@
  * consistent accross the app. NameSpace IDs are only consistent at runtime
  * ie: they are not guaranteed to be consistent accross app sessions.
  *
  * The nsNameSpaceManager needs to have a live reference for as long as
  * the NameSpace IDs are needed.
  *
  */
 
-class nsNameSpaceManager final : public nsISupports
+class nsNameSpaceManager final
 {
 public:
-  NS_DECL_ISUPPORTS
+  NS_INLINE_DECL_REFCOUNTING(nsNameSpaceManager)
+
   virtual nsresult RegisterNameSpace(const nsAString& aURI,
                                      int32_t& aNameSpaceID);
   nsresult RegisterNameSpace(already_AddRefed<nsAtom> aURI,
                              int32_t& aNameSpaceID);
 
   virtual nsresult GetNameSpaceURI(int32_t aNameSpaceID, nsAString& aURI);
 
   // Returns the atom for the namespace URI associated with the given ID. The
--- a/dom/html/nsTextEditorState.cpp
+++ b/dom/html/nsTextEditorState.cpp
@@ -45,16 +45,25 @@
 #include "nsNumberControlFrame.h"
 #include "nsFrameSelection.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/Telemetry.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
+inline nsresult
+SetEditorFlagsIfNecessary(EditorBase& aEditorBase, uint32_t aFlags)
+{
+  if (aEditorBase.Flags() == aFlags) {
+    return NS_OK;
+  }
+  return aEditorBase.SetFlags(aFlags);
+}
+
 class MOZ_STACK_CLASS ValueSetter
 {
 public:
   explicit ValueSetter(TextEditor* aTextEditor)
     : mTextEditor(aTextEditor)
     // To protect against a reentrant call to SetValue, we check whether
     // another SetValue is already happening for this editor.  If it is,
     // we must wait until we unwind to re-enable oninput events.
@@ -134,29 +143,34 @@ public:
                                   MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     : mTextEditor(aTextEditor)
     , mSavedFlags(mTextEditor->Flags())
     , mSavedMaxLength(mTextEditor->MaxTextLength())
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     MOZ_ASSERT(mTextEditor);
 
+    // EditorBase::SetFlags() is a virtual method.  Even though it does nothing
+    // if new flags and current flags are same, the calling cost causes
+    // appearing the method in profile.  So, this class should check if it's
+    // necessary to call.
     uint32_t flags = mSavedFlags;
     flags &= ~(nsIPlaintextEditor::eEditorDisabledMask);
     flags &= ~(nsIPlaintextEditor::eEditorReadonlyMask);
     flags |= nsIPlaintextEditor::eEditorDontEchoPassword;
-    mTextEditor->SetFlags(flags);
-
+    if (mSavedFlags != flags) {
+      mTextEditor->SetFlags(flags);
+    }
     mTextEditor->SetMaxTextLength(-1);
   }
 
   ~AutoRestoreEditorState()
   {
      mTextEditor->SetMaxTextLength(mSavedMaxLength);
-     mTextEditor->SetFlags(mSavedFlags);
+     SetEditorFlagsIfNecessary(*mTextEditor, mSavedFlags);
   }
 
 private:
   TextEditor* mTextEditor;
   uint32_t mSavedFlags;
   int32_t mSavedMaxLength;
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
@@ -1477,45 +1491,49 @@ nsTextEditorState::PrepareEditor(const n
     if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::disabled))
       editorFlags |= nsIPlaintextEditor::eEditorDisabledMask;
 
     // Disable the selection if necessary.
     if (newTextEditor->IsDisabled()) {
       mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_OFF);
     }
 
-    newTextEditor->SetFlags(editorFlags);
+    SetEditorFlagsIfNecessary(*newTextEditor, editorFlags);
   }
 
   if (shouldInitializeEditor) {
     // Hold on to the newly created editor
     preDestroyer.Swap(mTextEditor);
   }
 
   // If we have a default value, insert it under the div we created
   // above, but be sure to use the editor so that '*' characters get
   // displayed for password fields, etc. SetValue() will call the
   // editor for us.
 
   if (!defaultValue.IsEmpty()) {
-    rv = newTextEditor->SetFlags(editorFlags);
-    NS_ENSURE_SUCCESS(rv, rv);
+    rv = SetEditorFlagsIfNecessary(*newTextEditor, editorFlags);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
 
     // Now call SetValue() which will make the necessary editor calls to set
     // the default value.  Make sure to turn off undo before setting the default
     // value, and turn it back on afterwards. This will make sure we can't undo
     // past the default value.
     // So, we use eSetValue_Internal flag only that it will turn off undo.
 
     bool success = SetValue(defaultValue, eSetValue_Internal);
     NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
 
     // Now restore the original editor flags.
-    rv = newTextEditor->SetFlags(editorFlags);
-    NS_ENSURE_SUCCESS(rv, rv);
+    rv = SetEditorFlagsIfNecessary(*newTextEditor, editorFlags);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
   }
 
   if (IsPasswordTextControl()) {
     // Disable undo for <input type="password">.  Note that we want to do this
     // at the very end of InitEditor(), so the calls to EnableUndoRedo() when
     // setting the default value don't screw us up.  Since changing the
     // control type does a reframe, we don't have to worry about dynamic type
     // changes here.
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -54,16 +54,17 @@
 #include "nsIContent.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsIDOMWindow.h"
 #include "nsIDOMWindowUtils.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsILoadInfo.h"
 #include "nsIPromptFactory.h"
+#include "nsITransportSecurityInfo.h"
 #include "nsIURI.h"
 #include "nsIWindowWatcher.h"
 #include "nsIWebBrowserChrome.h"
 #include "nsIXULBrowserWindow.h"
 #include "nsIXULWindow.h"
 #include "nsIRemoteBrowser.h"
 #include "nsViewManager.h"
 #include "nsVariant.h"
@@ -889,16 +890,25 @@ TabParent::GetState(uint32_t *aState)
 {
   NS_ENSURE_ARG(aState);
   NS_WARNING("SecurityState not valid here");
   *aState = 0;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+TabParent::GetSecInfo(nsITransportSecurityInfo** _result)
+{
+  NS_ENSURE_ARG_POINTER(_result);
+  NS_WARNING("TransportSecurityInfo not valid here");
+  *_result = nullptr;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 TabParent::SetDocShell(nsIDocShell *aDocShell)
 {
   NS_ENSURE_ARG(aDocShell);
   NS_WARNING("No mDocShell member in TabParent so there is no docShell to set");
   return NS_OK;
 }
 
   a11y::PDocAccessibleParent*
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -3294,41 +3294,27 @@ nsCSSFrameConstructor::ConstructFieldSet
       contentFrame = NS_NewGridContainerFrame(mPresShell, fieldsetContentStyle);
       InitAndRestoreFrame(aState, content, parent, contentFrame);
       contentFrameTop = contentFrame;
       break;
     default: {
       MOZ_ASSERT(fieldsetContentDisplay->mDisplay == StyleDisplay::Block,
                  "bug in nsRuleNode::ComputeDisplayData?");
 
-      nsContainerFrame* columnSetFrame = nullptr;
-      RefPtr<ComputedStyle> innerSC = fieldsetContentStyle;
-      const nsStyleColumn* columns = fieldsetContentStyle->StyleColumn();
-      if (columns->mColumnCount != nsStyleColumn::kColumnCountAuto ||
-          columns->mColumnWidth.GetUnit() != eStyleUnit_Auto) {
-        columnSetFrame =
-          NS_NewColumnSetFrame(mPresShell, fieldsetContentStyle,
-                               nsFrameState(NS_FRAME_OWNS_ANON_BOXES));
-        InitAndRestoreFrame(aState, content, parent, columnSetFrame);
-        innerSC = mPresShell->StyleSet()->
-          ResolveInheritingAnonymousBoxStyle(nsCSSAnonBoxes::columnContent,
-                                             fieldsetContentStyle);
+      contentFrame = NS_NewBlockFormattingContext(mPresShell, fieldsetContentStyle);
+      contentFrameTop =
+        InitAndWrapInColumnSetFrameIfNeeded(aState, content, parent,
+                                            contentFrame, fieldsetContentStyle);
+      if (contentFrame != contentFrameTop) {
+        // contentFrame is wrapped in nsColumnSetFrame.
         if (absPosContainer) {
-          absPosContainer = columnSetFrame;
+          absPosContainer = contentFrameTop;
         }
       }
-      contentFrame = NS_NewBlockFormattingContext(mPresShell, innerSC);
-      if (columnSetFrame) {
-        InitAndRestoreFrame(aState, content, columnSetFrame, contentFrame);
-        SetInitialSingleChild(columnSetFrame, contentFrame);
-        contentFrameTop = columnSetFrame;
-      } else {
-        InitAndRestoreFrame(aState, content, parent, contentFrame);
-        contentFrameTop = contentFrame;
-      }
+
       break;
     }
   }
 
   aState.AddChild(fieldsetFrame, aFrameItems, content, aParentFrame);
 
   // Process children
   nsFrameConstructorSaveState absoluteSaveState;
@@ -3913,38 +3899,20 @@ nsCSSFrameConstructor::ConstructFrameFro
             break;
           case StyleDisplay::Grid:
           case StyleDisplay::InlineGrid:
             outerFrame = NS_NewGridContainerFrame(mPresShell, outerSC);
             InitAndRestoreFrame(aState, content, container, outerFrame);
             innerFrame = outerFrame;
             break;
           default: {
-            nsContainerFrame* columnSetFrame = nullptr;
-            RefPtr<ComputedStyle> innerSC = outerSC;
-            const nsStyleColumn* columns = outerSC->StyleColumn();
-            if (columns->mColumnCount != nsStyleColumn::kColumnCountAuto ||
-                columns->mColumnWidth.GetUnit() != eStyleUnit_Auto) {
-              columnSetFrame =
-                NS_NewColumnSetFrame(mPresShell, outerSC,
-                                     nsFrameState(NS_FRAME_OWNS_ANON_BOXES));
-              InitAndRestoreFrame(aState, content, container, columnSetFrame);
-              innerSC = mPresShell->StyleSet()->
-                ResolveInheritingAnonymousBoxStyle(nsCSSAnonBoxes::columnContent,
-                                                   outerSC);
-            }
-            innerFrame = NS_NewBlockFormattingContext(mPresShell, innerSC);
-            if (columnSetFrame) {
-              InitAndRestoreFrame(aState, content, columnSetFrame, innerFrame);
-              SetInitialSingleChild(columnSetFrame, innerFrame);
-              outerFrame = columnSetFrame;
-            } else {
-              InitAndRestoreFrame(aState, content, container, innerFrame);
-              outerFrame = innerFrame;
-            }
+            innerFrame = NS_NewBlockFormattingContext(mPresShell, outerSC);
+            outerFrame =
+              InitAndWrapInColumnSetFrameIfNeeded(aState, content, container,
+                                                  innerFrame, outerSC);
             break;
           }
         }
       } else {
         innerFrame = NS_NewBlockFormattingContext(mPresShell, outerSC);
         InitAndRestoreFrame(aState, content, container, innerFrame);
         outerFrame = innerFrame;
       }
@@ -10913,56 +10881,78 @@ nsCSSFrameConstructor::RecoverLetterFram
 
     // Insert in the letter frame(s)
     parentFrame->InsertFrames(kPrincipalList, prevFrame, letterFrames);
   }
 }
 
 //----------------------------------------------------------------------
 
+nsContainerFrame*
+nsCSSFrameConstructor::InitAndWrapInColumnSetFrameIfNeeded(
+  nsFrameConstructorState& aState,
+  nsIContent* aContent,
+  nsContainerFrame* aParentFrame,
+  nsContainerFrame* aBlockFrame,
+  ComputedStyle* aComputedStyle)
+{
+  MOZ_ASSERT((aBlockFrame->IsBlockFrame() || aBlockFrame->IsDetailsFrame()),
+             "aBlock should either be a block frame or a details frame.");
+
+  const nsStyleColumn* styleColumn = aComputedStyle->StyleColumn();
+
+  if (styleColumn->mColumnCount == nsStyleColumn::kColumnCountAuto &&
+      styleColumn->mColumnWidth.GetUnit() == eStyleUnit_Auto) {
+    aBlockFrame->SetComputedStyleWithoutNotification(aComputedStyle);
+    InitAndRestoreFrame(aState, aContent, aParentFrame, aBlockFrame);
+    return aBlockFrame;
+  }
+
+  // Wrap the block frame in a ColumnSetFrame.
+  nsContainerFrame* columnSetFrame =
+    NS_NewColumnSetFrame(mPresShell, aComputedStyle,
+                         nsFrameState(NS_FRAME_OWNS_ANON_BOXES));
+  InitAndRestoreFrame(aState, aContent, aParentFrame, columnSetFrame);
+  SetInitialSingleChild(columnSetFrame, aBlockFrame);
+
+  RefPtr<ComputedStyle> anonBlockStyle = mPresShell->StyleSet()->
+    ResolveInheritingAnonymousBoxStyle(nsCSSAnonBoxes::columnContent,
+                                       aComputedStyle);
+  aBlockFrame->SetComputedStyleWithoutNotification(anonBlockStyle);
+  InitAndRestoreFrame(aState, aContent, columnSetFrame, aBlockFrame);
+
+  return columnSetFrame;
+}
+
 void
 nsCSSFrameConstructor::ConstructBlock(nsFrameConstructorState& aState,
                                       nsIContent*              aContent,
                                       nsContainerFrame*        aParentFrame,
                                       nsContainerFrame*        aContentParentFrame,
                                       ComputedStyle*          aComputedStyle,
                                       nsContainerFrame**       aNewFrame,
                                       nsFrameItems&            aFrameItems,
                                       nsIFrame*                aPositionedFrameForAbsPosContainer,
                                       PendingBinding*          aPendingBinding)
 {
   // Create column wrapper if necessary
   nsContainerFrame* blockFrame = *aNewFrame;
   NS_ASSERTION((blockFrame->IsBlockFrame() || blockFrame->IsDetailsFrame()),
                "not a block frame nor a details frame?");
-  nsContainerFrame* parent = aParentFrame;
-  RefPtr<ComputedStyle> blockStyle = aComputedStyle;
-  const nsStyleColumn* columns = aComputedStyle->StyleColumn();
-
-  if (columns->mColumnCount != nsStyleColumn::kColumnCountAuto
-      || columns->mColumnWidth.GetUnit() != eStyleUnit_Auto) {
-    nsContainerFrame* columnSetFrame =
-      NS_NewColumnSetFrame(mPresShell, aComputedStyle,
-                           nsFrameState(NS_FRAME_OWNS_ANON_BOXES));
-
-    InitAndRestoreFrame(aState, aContent, aParentFrame, columnSetFrame);
-    blockStyle = mPresShell->StyleSet()->
-      ResolveInheritingAnonymousBoxStyle(nsCSSAnonBoxes::columnContent,
-                                         aComputedStyle);
-    parent = columnSetFrame;
-    *aNewFrame = columnSetFrame;
+
+  *aNewFrame =
+    InitAndWrapInColumnSetFrameIfNeeded(aState, aContent, aParentFrame,
+                                        blockFrame, aComputedStyle);
+
+  if (blockFrame != *aNewFrame) {
+    // blockFrame is wrapped in nsColumnSetFrame.
     if (aPositionedFrameForAbsPosContainer == blockFrame) {
-      aPositionedFrameForAbsPosContainer = columnSetFrame;
-    }
-
-    SetInitialSingleChild(columnSetFrame, blockFrame);
-  }
-
-  blockFrame->SetComputedStyleWithoutNotification(blockStyle);
-  InitAndRestoreFrame(aState, aContent, parent, blockFrame);
+      aPositionedFrameForAbsPosContainer = *aNewFrame;
+    }
+  }
 
   aState.AddChild(*aNewFrame, aFrameItems, aContent,
                   aContentParentFrame ? aContentParentFrame :
                                         aParentFrame);
   if (!mRootElementFrame) {
     // The frame we're constructing will be the root element frame.
     // Set mRootElementFrame before processing children.
     mRootElementFrame = *aNewFrame;
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -1786,16 +1786,32 @@ private:
   bool ShouldHaveFirstLineStyle(nsIContent* aContent,
                                 ComputedStyle* aComputedStyle);
 
   void ShouldHaveSpecialBlockStyle(nsIContent*    aContent,
                                    ComputedStyle* aComputedStyle,
                                    bool*          aHaveFirstLetterStyle,
                                    bool*          aHaveFirstLineStyle);
 
+
+  // Initialize aBlockFrame, and wrap it in a ColumnSetFrame if needed.
+  //
+  // If a ColumnSetFrame needs to be created, then this function will create
+  // one, and set aBlockFrame as its child (with an updated "columnContent"
+  // ComputedStyle() pointer), and initialize both frames. Otherwise, it
+  // initializes aBlockFrame.
+  //
+  // @return the new ColumnSetFrame if needed; otherwise aBlockFrame.
+  nsContainerFrame* InitAndWrapInColumnSetFrameIfNeeded(
+    nsFrameConstructorState& aState,
+    nsIContent* aContent,
+    nsContainerFrame* aParentFrame,
+    nsContainerFrame* aBlockFrame,
+    ComputedStyle* aComputedStyle);
+
   // |aContentParentFrame| should be null if it's really the same as
   // |aParentFrame|.
   // @param aFrameItems where we want to put the block in case it's in-flow.
   // @param aNewFrame an in/out parameter. On input it is the block to be
   // constructed. On output it is reset to the outermost
   // frame constructed (e.g. if we need to wrap the block in an
   // nsColumnSetFrame.
   // @param aParentFrame is the desired parent for the (possibly wrapped)
--- a/layout/tools/layout-debug/ui/content/layoutdebug.xul
+++ b/layout/tools/layout-debug/ui/content/layoutdebug.xul
@@ -3,17 +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/. -->
 
 <!DOCTYPE window SYSTEM "chrome://layoutdebug/locale/layoutdebug.dtd">
 
-<?xml-stylesheet href="chrome://communicator/skin/" type="text/css" ?>
+<?xml-stylesheet href="chrome://global/skin/global.css" type="text/css" ?>
 
 <!--
 
   NOTE:  Because this window is used for layout regression tests, the
   persist attribute should never be used on anything.  Otherwise there
   is a risk of running baseline and verify runs under different
   conditions.
 
--- a/mobile/android/base/AndroidManifest.xml.in
+++ b/mobile/android/base/AndroidManifest.xml.in
@@ -396,39 +396,35 @@
                   android:grantUriPermissions="true">
                   <meta-data android:name="android.support.FILE_PROVIDER_PATHS"
                              android:resource="@xml/provider_paths" />
         </provider>
 
         <service
             android:exported="false"
             android:name="org.mozilla.gecko.updater.UpdatesApplyService"
-            android:permission="android.permission.BIND_JOB_SERVICE"
-            android:process="@MANGLED_ANDROID_PACKAGE_NAME@.UpdateService">
+            android:permission="android.permission.BIND_JOB_SERVICE" >
         </service>
 
         <service
             android:exported="false"
             android:name="org.mozilla.gecko.updater.UpdatesCheckService"
-            android:permission="android.permission.BIND_JOB_SERVICE"
-            android:process="@MANGLED_ANDROID_PACKAGE_NAME@.UpdateService">
+            android:permission="android.permission.BIND_JOB_SERVICE" >
         </service>
 
         <service
             android:exported="false"
             android:name="org.mozilla.gecko.updater.UpdatesDownloadService"
-            android:permission="android.permission.BIND_JOB_SERVICE"
-            android:process="@MANGLED_ANDROID_PACKAGE_NAME@.UpdateService">
+            android:permission="android.permission.BIND_JOB_SERVICE" >
          </service>
 
         <service
             android:exported="false"
             android:name="org.mozilla.gecko.updater.UpdatesRegisterService"
-            android:permission="android.permission.BIND_JOB_SERVICE"
-            android:process="@MANGLED_ANDROID_PACKAGE_NAME@.UpdateService">
+            android:permission="android.permission.BIND_JOB_SERVICE" >
         </service>
 
         <receiver
             android:name="org.mozilla.gecko.updater.UpdateServiceReceiver"
             android:exported="false">
             <intent-filter>
                 <action android:name="@ANDROID_PACKAGE_NAME@.CHECK_FOR_UPDATE"></action>
                 <action android:name="@ANDROID_PACKAGE_NAME@.APPLY_UPDATE"></action>
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
@@ -2181,16 +2181,21 @@ public abstract class GeckoApp extends G
         // onConfigurationChanged is not called for 180 degree orientation changes,
         // we will miss such rotations and the screen orientation will not be
         // updated.
         if (GeckoScreenOrientation.getInstance().update(newConfig.orientation)) {
             if (mFormAssistPopup != null)
                 mFormAssistPopup.hide();
             refreshChrome();
         }
+
+        if (mPromptService != null) {
+            mPromptService.changePromptOrientation(newConfig.orientation);
+        }
+
         super.onConfigurationChanged(newConfig);
     }
 
     public String getContentProcessName() {
         return AppConstants.MOZ_CHILD_PROCESS_NAME;
     }
 
     public void addEnvToIntent(Intent intent) {
--- a/mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java
+++ b/mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.customtabs;
 
 import android.app.PendingIntent;
 import android.content.ActivityNotFoundException;
 import android.content.Intent;
 import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Bundle;
 import android.provider.Browser;
 import android.support.annotation.ColorInt;
 import android.support.annotation.NonNull;
@@ -186,16 +187,25 @@ public class CustomTabsActivity extends 
     }
 
     @Override
     public void onRequestPermissionsResult(final int requestCode, final String[] permissions,
                                            final int[] grantResults) {
         Permissions.onRequestPermissionsResult(this, permissions, grantResults);
     }
 
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+
+        if (mPromptService != null) {
+            mPromptService.changePromptOrientation(newConfig.orientation);
+        }
+    }
+
     private void sendTelemetry() {
         final SafeIntent startIntent = new SafeIntent(getIntent());
 
         Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.INTENT, "customtab");
         if (IntentUtil.hasToolbarColor(startIntent)) {
             Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.INTENT, "customtab-hasToolbarColor");
         }
         if (IntentUtil.hasActionButton(startIntent)) {
--- a/mobile/android/base/java/org/mozilla/gecko/prompts/ColorPickerInput.java
+++ b/mobile/android/base/java/org/mozilla/gecko/prompts/ColorPickerInput.java
@@ -6,32 +6,30 @@
 package org.mozilla.gecko.prompts;
 
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.util.GeckoBundle;
 import org.mozilla.gecko.widget.BasicColorPicker;
 
 import android.content.Context;
 import android.graphics.Color;
+import android.support.annotation.NonNull;
 import android.view.LayoutInflater;
 import android.view.View;
 
 public class ColorPickerInput extends PromptInput {
     public static final String INPUT_TYPE = "color";
     public static final String LOGTAG = "GeckoColorPickerInput";
 
     private static final boolean mShowAdvancedButton = true;
-    private final int mInitialColor;
+    private int mInitialColor;
 
     public ColorPickerInput(GeckoBundle obj) {
         super(obj);
-        String init = obj.getString("value");
-        mInitialColor = Color.rgb(Integer.parseInt(init.substring(1, 3), 16),
-                                  Integer.parseInt(init.substring(3, 5), 16),
-                                  Integer.parseInt(init.substring(5, 7), 16));
+        mInitialColor = getColorCode(obj.getString("value"));
     }
 
     @Override
     public View getView(Context context) throws UnsupportedOperationException {
         LayoutInflater inflater = LayoutInflater.from(context);
         mView = inflater.inflate(R.layout.basic_color_picker_dialog, null);
 
         BasicColorPicker cp = (BasicColorPicker) mView.findViewById(R.id.colorpicker);
@@ -51,9 +49,22 @@ public class ColorPickerInput extends Pr
     public boolean getScrollable() {
         return true;
     }
 
     @Override
     public boolean canApplyInputStyle() {
         return false;
     }
+
+    @Override
+    public void saveCurrentInput(@NonNull final GeckoBundle userInput) {
+        if (userInput != null && userInput.containsKey(mId)) {
+            mInitialColor = getColorCode((String) userInput.get(mId));
+        }
+    }
+
+    private int getColorCode(@NonNull final String color) {
+        return Color.rgb(Integer.parseInt(color.substring(1, 3), 16),
+                Integer.parseInt(color.substring(3, 5), 16),
+                Integer.parseInt(color.substring(5, 7), 16));
+    }
 }
--- a/mobile/android/base/java/org/mozilla/gecko/prompts/IconGridInput.java
+++ b/mobile/android/base/java/org/mozilla/gecko/prompts/IconGridInput.java
@@ -10,16 +10,17 @@ import java.util.List;
 
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.util.GeckoBundle;
 import org.mozilla.gecko.util.ResourceDrawableUtils;
 
 import android.content.Context;
 import android.graphics.drawable.Drawable;
+import android.support.annotation.NonNull;
 import android.text.TextUtils;
 import android.view.Display;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowManager;
 import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemClickListener;
@@ -140,16 +141,21 @@ public class IconGridInput extends Promp
 
             final ImageView icon = (ImageView) v.findViewById(R.id.icon);
             icon.setImageDrawable(item.icon);
             ViewGroup.LayoutParams lp = icon.getLayoutParams();
             lp.width = lp.height = mIconSize;
         }
     }
 
+    @Override
+    public void saveCurrentInput(@NonNull final GeckoBundle userInput) {
+        // No user input to save
+    }
+
     private class IconGridItem {
         final String label;
         final String description;
         final boolean selected;
         Drawable icon;
 
         public IconGridItem(final Context context, final GeckoBundle obj) {
             label = obj.getString("name", "");
--- a/mobile/android/base/java/org/mozilla/gecko/prompts/Prompt.java
+++ b/mobile/android/base/java/org/mozilla/gecko/prompts/Prompt.java
@@ -1,37 +1,38 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.prompts;
 
-import org.mozilla.gecko.R;
-import org.mozilla.gecko.util.GeckoBundle;
-import org.mozilla.gecko.util.ThreadUtils;
-import org.mozilla.gecko.Tab;
-import org.mozilla.gecko.Tabs;
-
 import android.app.AlertDialog;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.DialogInterface.OnCancelListener;
 import android.content.DialogInterface.OnClickListener;
 import android.content.res.Resources;
+import android.support.annotation.NonNull;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemClickListener;
 import android.widget.LinearLayout;
 import android.widget.ListView;
 import android.widget.ScrollView;
 
+import org.mozilla.gecko.R;
+import org.mozilla.gecko.Tab;
+import org.mozilla.gecko.Tabs;
+import org.mozilla.gecko.util.GeckoBundle;
+import org.mozilla.gecko.util.ThreadUtils;
+
 import java.util.ArrayList;
 
 public class Prompt implements OnClickListener, OnCancelListener, OnItemClickListener,
                                PromptInput.OnChangeListener, Tabs.OnTabsChangedListener {
     private static final String LOGTAG = "GeckoPromptService";
 
     private String[] mButtons;
     private PromptInput[] mInputs;
@@ -45,16 +46,21 @@ public class Prompt implements OnClickLi
     private PromptListAdapter mAdapter;
 
     private static boolean mInitialized;
     private static int mInputPaddingSize;
 
     private int mTabId = Tabs.INVALID_TAB_ID;
     private Object mPreviousInputValue = null;
 
+    private String currentTitle;
+    private String currentText;
+    private PromptListItem[] currentListItems;
+    private int currentChoiceMode;
+
     public Prompt(Context context, PromptCallback callback) {
         this(context);
         mCallback = callback;
     }
 
     private Prompt(Context context) {
         mContext = context;
         mInflater = LayoutInflater.from(mContext);
@@ -120,25 +126,67 @@ public class Prompt implements OnClickLi
                 return DialogInterface.BUTTON_NEUTRAL;
             case 2:
                 return DialogInterface.BUTTON_NEGATIVE;
             default:
                 return 0;
         }
     }
 
-    public void show(String title, String text, PromptListItem[] listItems, int choiceMode) {
+    public void show(@NonNull final String title, @NonNull final String text,
+                     @NonNull final PromptListItem[] listItems, final int choiceMode) {
+        saveInputDetails(title, text, listItems, choiceMode);
+
+        if (createPrompt(title, text, listItems, choiceMode)) {
+            tryShowingInputPrompt();
+        }
+    }
+
+    public void resetLayout() {
+        if (mDialog != null && mDialog.isShowing()) {
+            mDialog.dismiss();
+
+            final GeckoBundle currentUserInput = new GeckoBundle();
+            addInputValues(currentUserInput);
+
+            for (PromptInput input : mInputs) {
+                input.saveCurrentInput(currentUserInput);
+            }
+
+            if (createPrompt(currentTitle, currentText, currentListItems, currentChoiceMode)) {
+                // If the dialog for this Prompt was shown before it's safe to show again
+                // and avoid the superfluous tryShowingInputPrompt()
+                mDialog.show();
+            }
+        }
+    }
+
+    private void saveInputDetails(@NonNull final String title, @NonNull final String text,
+                                  @NonNull final PromptListItem[] listItems, final int choiceMode) {
+        currentTitle = title;
+        currentText = text;
+        currentListItems = listItems;
+        currentChoiceMode = choiceMode;
+    }
+
+    private boolean createPrompt(@NonNull final String title, @NonNull final String text,
+                                 @NonNull final PromptListItem[] listItems, final int choiceMode) {
         ThreadUtils.assertOnUiThread();
 
         try {
             create(title, text, listItems, choiceMode);
         } catch (IllegalStateException ex) {
             Log.i(LOGTAG, "Error building dialog", ex);
-            return;
+            return false;
         }
+        return true;
+    }
+
+    private void tryShowingInputPrompt() {
+        ThreadUtils.assertOnUiThread();
 
         if (mTabId != Tabs.INVALID_TAB_ID) {
             Tabs.registerOnTabsChangedListener(this);
 
             final Tab tab = Tabs.getInstance().getTab(mTabId);
             if (Tabs.getInstance().getSelectedTab() == tab) {
                 mDialog.show();
             }
--- a/mobile/android/base/java/org/mozilla/gecko/prompts/PromptInput.java
+++ b/mobile/android/base/java/org/mozilla/gecko/prompts/PromptInput.java
@@ -13,16 +13,17 @@ import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.util.GeckoBundle;
 import org.mozilla.gecko.widget.AllCapsTextView;
 import org.mozilla.gecko.widget.FocusableDatePicker;
 import org.mozilla.gecko.widget.DateTimePicker;
 import org.mozilla.gecko.widget.FocusableTimePicker;
 
 import android.content.Context;
 import android.content.res.Configuration;
+import android.support.annotation.NonNull;
 import android.support.design.widget.TextInputLayout;
 import android.text.Html;
 import android.text.InputType;
 import android.text.TextUtils;
 import android.text.format.DateFormat;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.View;
@@ -37,17 +38,17 @@ import android.widget.LinearLayout;
 import android.widget.Spinner;
 import android.widget.TextView;
 import android.widget.TimePicker;
 
 public abstract class PromptInput {
     protected final String mLabel;
     protected final String mType;
     protected final String mId;
-    protected final String mValue;
+    protected String mValue;
     protected final String mMinValue;
     protected final String mMaxValue;
     protected OnChangeListener mListener;
     protected View mView;
     public static final String LOGTAG = "GeckoPromptInput";
 
     public interface OnChangeListener {
         void onChange(PromptInput input);
@@ -112,17 +113,17 @@ public abstract class PromptInput {
 
         @Override
         public View getView(final Context context) throws UnsupportedOperationException {
             final TextInputLayout inputLayout = (TextInputLayout) super.getView(context);
             final EditText input = inputLayout.getEditText();
             input.setRawInputType(Configuration.KEYBOARD_12KEY);
             input.setInputType(InputType.TYPE_CLASS_NUMBER |
                                InputType.TYPE_NUMBER_FLAG_SIGNED);
-            return input;
+            return inputLayout;
         }
     }
 
     public static class PasswordInput extends EditInput {
         public static final String INPUT_TYPE = "password";
         public PasswordInput(GeckoBundle obj) {
             super(obj);
         }
@@ -134,17 +135,17 @@ public abstract class PromptInput {
                                InputType.TYPE_TEXT_VARIATION_PASSWORD |
                                InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
             return inputLayout;
         }
     }
 
     public static class CheckboxInput extends PromptInput {
         public static final String INPUT_TYPE = "checkbox";
-        private final boolean mChecked;
+        private boolean mChecked;
 
         public CheckboxInput(GeckoBundle obj) {
             super(obj);
             mChecked = obj.getBoolean("checked");
         }
 
         @Override
         public View getView(Context context) throws UnsupportedOperationException {
@@ -156,16 +157,23 @@ public abstract class PromptInput {
             return mView;
         }
 
         @Override
         public Object getValue() {
             CheckBox checkbox = (CheckBox)mView;
             return checkbox.isChecked() ? Boolean.TRUE : Boolean.FALSE;
         }
+
+        @Override
+        public void saveCurrentInput(@NonNull final GeckoBundle userInput) {
+            if (userInput.containsKey(mId)) {
+                mChecked = (Boolean) userInput.get(mId);
+            }
+        }
     }
 
     public static class DateTimeInput extends PromptInput {
         public static final String[] INPUT_TYPES = new String[] {
             "date",
             "week",
             "time",
             "datetime-local",
@@ -279,17 +287,17 @@ public abstract class PromptInput {
             }
             return super.getValue();
         }
     }
 
     public static class MenulistInput extends PromptInput {
         public static final String INPUT_TYPE = "menulist";
         private final String[] mListitems;
-        private final int mSelected;
+        private int mSelected;
 
         public Spinner spinner;
         public AllCapsTextView textView;
 
         public MenulistInput(GeckoBundle obj) {
             super(obj);
             final String[] listitems = obj.getStringArray("values");
             mListitems = listitems != null ? listitems : new String[0];
@@ -324,44 +332,62 @@ public abstract class PromptInput {
 
             return spinner;
         }
 
         @Override
         public Object getValue() {
             return spinner.getSelectedItemPosition();
         }
+
+        @Override
+        public void saveCurrentInput(@NonNull final GeckoBundle userInput) {
+            if (userInput.containsKey(mId)) {
+                mSelected = (Integer) userInput.get(mId);
+            }
+        }
     }
 
     public static class LabelInput extends PromptInput {
         public static final String INPUT_TYPE = "label";
         public LabelInput(GeckoBundle obj) {
             super(obj);
         }
 
         @Override
         public View getView(Context context) throws UnsupportedOperationException {
             // not really an input, but a way to add labels and such to the dialog
             TextView view = new TextView(context);
             view.setText(Html.fromHtml(mLabel));
             mView = view;
             return mView;
         }
+
+        @Override
+        public void saveCurrentInput(@NonNull final GeckoBundle userInput) {
+            // No user input to save
+        }
     }
 
     public PromptInput(GeckoBundle obj) {
         mLabel = obj.getString("label", "");
         mType = obj.getString("type", "");
         String id = obj.getString("id", "");
         mId = TextUtils.isEmpty(id) ? mType : id;
         mValue = obj.getString("value", "");
         mMaxValue = obj.getString("max", "");
         mMinValue = obj.getString("min", "");
     }
 
+    public void saveCurrentInput(@NonNull final GeckoBundle userInput) {
+        if (userInput.containsKey(mId)) {
+            mValue = (String) userInput.get(mId);
+        }
+    }
+
     public void putInBundle(final GeckoBundle bundle) {
         final String id = getId();
         final Object value = getValue();
 
         if (value == null) {
             bundle.putBundle(id, null);
         } else if (value instanceof Boolean) {
             bundle.putBoolean(id, (Boolean) value);
--- a/mobile/android/base/java/org/mozilla/gecko/prompts/PromptService.java
+++ b/mobile/android/base/java/org/mozilla/gecko/prompts/PromptService.java
@@ -4,47 +4,55 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.prompts;
 
 import org.mozilla.gecko.EventDispatcher;
 import org.mozilla.gecko.util.BundleEventListener;
 import org.mozilla.gecko.util.EventCallback;
 import org.mozilla.gecko.util.GeckoBundle;
-import org.mozilla.gecko.util.ThreadUtils;
 
 import android.content.Context;
-import android.util.Log;
 
 public class PromptService implements BundleEventListener {
     private static final String LOGTAG = "GeckoPromptService";
 
-    private final Context mContext;
-    private final EventDispatcher mDispatcher;
+    private final Context context;
+    private final EventDispatcher dispatcher;
+    private Prompt currentPrompt;
+    private int currentOrientation;
 
     public PromptService(final Context context, final EventDispatcher dispatcher) {
-        mContext = context;
-        mDispatcher = dispatcher;
-        mDispatcher.registerUiThreadListener(this,
+        this.context = context;
+        this.currentOrientation = context.getResources().getConfiguration().orientation;
+        this.dispatcher = dispatcher;
+        this.dispatcher.registerUiThreadListener(this,
             "Prompt:Show",
             "Prompt:ShowTop");
     }
 
     public void destroy() {
-        mDispatcher.unregisterUiThreadListener(this,
+        dispatcher.unregisterUiThreadListener(this,
             "Prompt:Show",
             "Prompt:ShowTop");
     }
 
     // BundleEventListener implementation
     @Override
     public void handleMessage(final String event, final GeckoBundle message,
                               final EventCallback callback) {
-        Prompt p;
-        p = new Prompt(mContext, new Prompt.PromptCallback() {
+        currentPrompt = new Prompt(context, new Prompt.PromptCallback() {
             @Override
             public void onPromptFinished(final GeckoBundle result) {
                 callback.sendSuccess(result);
+                currentPrompt = null;
             }
         });
-        p.show(message);
+        currentPrompt.show(message);
+    }
+
+    public void changePromptOrientation(int newOrientation) {
+        if (currentPrompt != null && currentOrientation != newOrientation) {
+            currentPrompt.resetLayout();
+        }
+        currentOrientation = newOrientation;
     }
 }
--- a/mobile/android/base/java/org/mozilla/gecko/prompts/TabInput.java
+++ b/mobile/android/base/java/org/mozilla/gecko/prompts/TabInput.java
@@ -8,16 +8,17 @@ package org.mozilla.gecko.prompts;
 import java.util.LinkedHashMap;
 
 import org.mozilla.gecko.AppConstants.Versions;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.util.GeckoBundle;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.content.Context;
+import android.support.annotation.NonNull;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.AdapterView;
 import android.widget.ListView;
 import android.widget.TabHost;
 import android.widget.TextView;
 
@@ -90,9 +91,13 @@ public class TabInput extends PromptInpu
 
     @Override
     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
         ThreadUtils.assertOnUiThread();
         mPosition = position;
         notifyListeners(Integer.toString(position));
     }
 
+    @Override
+    public void saveCurrentInput(@NonNull final GeckoBundle userInput) {
+        // No user input to save
+    }
 }
--- a/mobile/android/base/java/org/mozilla/gecko/tabs/TabHistoryFragment.java
+++ b/mobile/android/base/java/org/mozilla/gecko/tabs/TabHistoryFragment.java
@@ -139,17 +139,17 @@ public class TabHistoryFragment extends 
     public void dismiss() {
         if (dismissed) {
             return;
         }
 
         dismissed = true;
 
         if (backStackId >= 0) {
-            getFragmentManager().popBackStackImmediate(backStackId, FragmentManager.POP_BACK_STACK_INCLUSIVE);
+            getChildFragmentManager().popBackStackImmediate(backStackId, FragmentManager.POP_BACK_STACK_INCLUSIVE);
             backStackId = -1;
         }
 
         if (parent != null) {
             parent.setVisibility(View.GONE);
         }
     }
 
--- a/mobile/android/base/java/org/mozilla/gecko/webapps/WebAppActivity.java
+++ b/mobile/android/base/java/org/mozilla/gecko/webapps/WebAppActivity.java
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.webapps;
 
 import android.annotation.TargetApi;
 import android.app.ActivityManager;
 import android.content.ActivityNotFoundException;
 import android.content.Intent;
+import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
 import android.support.customtabs.CustomTabsIntent;
 import android.support.v7.app.AppCompatActivity;
 import android.support.v7.view.ActionMode;
 import android.util.Log;
@@ -167,16 +168,25 @@ public class WebAppActivity extends AppC
             return;
         }
 
         updateFromManifest();
 
         mGeckoSession.loadUri(mManifest.getStartUri().toString());
     }
 
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+
+        if (mPromptService != null) {
+            mPromptService.changePromptOrientation(newConfig.orientation);
+        }
+    }
+
     private void fallbackToFennec(String message) {
         if (message != null) {
             Toast.makeText(this, message, Toast.LENGTH_LONG).show();
         }
 
         try {
             Intent intent = new Intent(this, BrowserApp.class);
             intent.setAction(Intent.ACTION_VIEW);
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -5682,19 +5682,18 @@ var IdentityHandler = {
     Telemetry.addData("TRACKING_PROTECTION_SHIELD", value);
   },
 
   /**
    * Determine the identity of the page being displayed by examining its SSL cert
    * (if available). Return the data needed to update the UI.
    */
   checkIdentity: function checkIdentity(aState, aBrowser) {
-    this._lastStatus = aBrowser.securityUI
-                               .QueryInterface(Ci.nsISSLStatusProvider)
-                               .SSLStatus;
+    this._lastStatus = aBrowser.securityUI.secInfo &&
+                       aBrowser.securityUI.secInfo.SSLStatus;
 
     // Don't pass in the actual location object, since it can cause us to
     // hold on to the window object too long.  Just pass in the fields we
     // care about. (bug 424829)
     let locationObj = {};
     try {
       let location = aBrowser.contentWindow.location;
       locationObj.host = location.host;
--- a/mobile/android/chrome/content/content.js
+++ b/mobile/android/chrome/content/content.js
@@ -355,18 +355,17 @@ var AboutCertErrorListener = {
     if (aEvent.type != "AboutCertErrorLoad") {
       return;
     }
 
     let ownerDoc = aEvent.originalTarget.ownerGlobal;
     let securityInfo = docShell.failedChannel && docShell.failedChannel.securityInfo;
     securityInfo.QueryInterface(Ci.nsITransportSecurityInfo)
                 .QueryInterface(Ci.nsISerializable);
-    let sslStatus = securityInfo.QueryInterface(Ci.nsISSLStatusProvider)
-                                                .SSLStatus;
+    let sslStatus = securityInfo.SSLStatus;
     this._setTechDetails(sslStatus, securityInfo, ownerDoc.location.href);
   },
 };
 AboutCertErrorListener.init();
 
 // This is copied from desktop's tab-content.js. See bug 1153485 about sharing this code somehow.
 var AboutReaderListener = {
 
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/GeckoProcessManager.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/GeckoProcessManager.java
@@ -199,58 +199,49 @@ public final class GeckoProcessManager e
                       final int crashAnnotationFd, final boolean retry) {
         final ChildConnection connection = getConnection(type);
         final IChildProcess child = connection.bind();
         if (child == null) {
             return 0;
         }
 
         final Bundle extras = GeckoThread.getActiveExtras();
-        final ParcelFileDescriptor prefsPfd;
-        final ParcelFileDescriptor prefMapPfd;
-        final ParcelFileDescriptor ipcPfd;
-        final ParcelFileDescriptor crashPfd;
-        final ParcelFileDescriptor crashAnnotationPfd;
-        try {
-            prefsPfd = ParcelFileDescriptor.fromFd(prefsFd);
-            prefMapPfd = ParcelFileDescriptor.fromFd(prefMapFd);
-            ipcPfd = ParcelFileDescriptor.fromFd(ipcFd);
-            crashPfd = (crashFd >= 0) ? ParcelFileDescriptor.fromFd(crashFd) : null;
-            crashAnnotationPfd = (crashAnnotationFd >= 0) ? ParcelFileDescriptor.fromFd(crashAnnotationFd) : null;
-        } catch (final IOException e) {
-            Log.e(LOGTAG, "Cannot create fd for " + type, e);
-            return 0;
-        }
+        final ParcelFileDescriptor prefsPfd = ParcelFileDescriptor.adoptFd(prefsFd);
+        final ParcelFileDescriptor prefMapPfd = ParcelFileDescriptor.adoptFd(prefMapFd);
+        final ParcelFileDescriptor ipcPfd = ParcelFileDescriptor.adoptFd(ipcFd);
+        final ParcelFileDescriptor crashPfd =
+                (crashFd >= 0) ? ParcelFileDescriptor.adoptFd(crashFd) : null;
+        final ParcelFileDescriptor crashAnnotationPfd =
+                (crashAnnotationFd >= 0) ? ParcelFileDescriptor.adoptFd(crashAnnotationFd) : null;
 
         final int flags = filterFlagsForChild(GeckoThread.getActiveFlags());
 
         boolean started = false;
         try {
             started = child.start(this, args, extras, flags, prefsPfd, prefMapPfd,
                                   ipcPfd, crashPfd, crashAnnotationPfd);
         } catch (final RemoteException e) {
         }
 
+        if (crashAnnotationPfd != null) {
+            crashAnnotationPfd.detachFd();
+        }
+        if (crashPfd != null) {
+            crashPfd.detachFd();
+        }
+        ipcPfd.detachFd();
+        prefMapPfd.detachFd();
+        prefsPfd.detachFd();
+
         if (!started) {
             if (retry) {
                 Log.e(LOGTAG, "Cannot restart child " + type);
                 return 0;
             }
             Log.w(LOGTAG, "Attempting to kill running child " + type);
             connection.unbind();
             return start(type, args, prefsFd, prefMapFd, ipcFd, crashFd, crashAnnotationFd, /* retry */ true);
         }
 
-        try {
-            if (crashAnnotationPfd != null) {
-                crashAnnotationPfd.close();
-            }
-            if (crashPfd != null) {
-                crashPfd.close();
-            }
-            ipcPfd.close();
-        } catch (final IOException e) {
-        }
-
         return connection.getPid();
     }
 
 } // GeckoProcessManager
--- a/mobile/android/modules/geckoview/GeckoViewProgress.jsm
+++ b/mobile/android/modules/geckoview/GeckoViewProgress.jsm
@@ -154,18 +154,17 @@ var IdentityHandler = {
     } catch (e) {}
 
     try {
       result.host = IDNService.convertToDisplayIDN(uri.host, {});
     } catch (e) {
       result.host = uri.host;
     }
 
-    let status = aBrowser.securityUI.QueryInterface(Ci.nsISSLStatusProvider)
-                         .SSLStatus.QueryInterface(Ci.nsISSLStatus);
+    let status = aBrowser.securityUI.secInfo.SSLStatus;
     let cert = status.serverCert;
 
     result.organization = cert.organization;
     result.subjectName = cert.subjectName;
     result.issuerOrganization = cert.issuerOrganization;
     result.issuerCommonName = cert.issuerCommonName;
 
     try {
--- a/netwerk/base/nsISecureBrowserUI.idl
+++ b/netwerk/base/nsISecureBrowserUI.idl
@@ -3,21 +3,23 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
 interface mozIDOMWindowProxy;
 interface nsIDocShell;
+interface nsITransportSecurityInfo;
 
 [scriptable, uuid(718c662a-f810-4a80-a6c9-0b1810ecade2)]
 interface nsISecureBrowserUI : nsISupports
 {
     void init(in mozIDOMWindowProxy window);
     void setDocShell(in nsIDocShell docShell);
 
     readonly attribute unsigned long state;
+    readonly attribute nsITransportSecurityInfo secInfo;
 };
 
 %{C++
 #define NS_SECURE_BROWSER_UI_CONTRACTID "@mozilla.org/secure_browser_ui;1"
 %}
--- a/netwerk/protocol/http/AlternateServices.cpp
+++ b/netwerk/protocol/http/AlternateServices.cpp
@@ -10,18 +10,16 @@
 #include "LoadInfo.h"
 #include "nsEscape.h"
 #include "nsHttpConnectionInfo.h"
 #include "nsHttpChannel.h"
 #include "nsHttpHandler.h"
 #include "nsThreadUtils.h"
 #include "nsHttpTransaction.h"
 #include "NullHttpTransaction.h"
-#include "nsISSLStatusProvider.h"
-#include "nsISSLStatus.h"
 #include "nsISSLSocketControl.h"
 #include "nsIWellKnownOpportunisticUtils.h"
 
 /* RFC 7838 Alternative Services
    http://httpwg.org/http-extensions/opsec.html
     note that connections currently do not do mixed-scheme (the I attribute
     in the ConnectionInfo prevents it) but could, do not honor tls-commit and should
     not, and always require authentication
--- a/netwerk/protocol/http/Http2Session.cpp
+++ b/netwerk/protocol/http/Http2Session.cpp
@@ -22,18 +22,16 @@
 #include "mozilla/EndianUtils.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/Preferences.h"
 #include "nsHttp.h"
 #include "nsHttpHandler.h"
 #include "nsHttpConnection.h"
 #include "nsIRequestContext.h"
 #include "nsISSLSocketControl.h"
-#include "nsISSLStatus.h"
-#include "nsISSLStatusProvider.h"
 #include "nsISupportsPriority.h"
 #include "nsStandardURL.h"
 #include "nsURLHelper.h"
 #include "prnetdb.h"
 #include "sslt.h"
 #include "mozilla/Sprintf.h"
 #include "nsSocketTransportService2.h"
 #include "nsNetUtil.h"
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -64,17 +64,16 @@
 #include "nsContentUtils.h"
 #include "nsContentSecurityManager.h"
 #include "nsIClassOfService.h"
 #include "nsIPermissionManager.h"
 #include "nsIPrincipal.h"
 #include "nsIScriptError.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsISSLStatus.h"
-#include "nsISSLStatusProvider.h"
 #include "nsITransportSecurityInfo.h"
 #include "nsIWebProgressListener.h"
 #include "LoadContextInfo.h"
 #include "netCore.h"
 #include "nsHttpTransaction.h"
 #include "nsICacheEntryDescriptor.h"
 #include "nsICancelable.h"
 #include "nsIHttpChannelAuthProvider.h"
@@ -1891,21 +1890,21 @@ nsHttpChannel::ProcessSecurityHeaders()
     // mSecurityInfo may not always be present, and if it's not then it is okay
     // to just disregard any security headers since we know nothing about the
     // security of the connection.
     NS_ENSURE_TRUE(mSecurityInfo, NS_OK);
 
     uint32_t flags =
       NS_UsePrivateBrowsing(this) ? nsISocketProvider::NO_PERMANENT_STORAGE : 0;
 
-    // Get the SSLStatus
-    nsCOMPtr<nsISSLStatusProvider> sslprov = do_QueryInterface(mSecurityInfo);
-    NS_ENSURE_TRUE(sslprov, NS_ERROR_FAILURE);
+    // Get the TransportSecurityInfo
+    nsCOMPtr<nsITransportSecurityInfo> transSecInfo = do_QueryInterface(mSecurityInfo);
+    NS_ENSURE_TRUE(transSecInfo, NS_ERROR_FAILURE);
     nsCOMPtr<nsISSLStatus> sslStatus;
-    rv = sslprov->GetSSLStatus(getter_AddRefs(sslStatus));
+    rv = transSecInfo->GetSSLStatus(getter_AddRefs(sslStatus));
     NS_ENSURE_SUCCESS(rv, rv);
     NS_ENSURE_TRUE(sslStatus, NS_ERROR_FAILURE);
 
     rv = ProcessSingleSecurityHeader(nsISiteSecurityService::HEADER_HSTS,
                                      sslStatus, flags);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = ProcessSingleSecurityHeader(nsISiteSecurityService::HEADER_HPKP,
@@ -2026,27 +2025,25 @@ nsHttpChannel::ProcessSSLInformation()
     // If this is HTTPS, record any use of RSA so that Key Exchange Algorithm
     // can be whitelisted for TLS False Start in future sessions. We could
     // do the same for DH but its rarity doesn't justify the lookup.
 
     if (mCanceled || NS_FAILED(mStatus) || !mSecurityInfo ||
         !IsHTTPS() || mPrivateBrowsing)
         return;
 
-    nsCOMPtr<nsISSLStatusProvider> statusProvider =
+    nsCOMPtr<nsITransportSecurityInfo> securityInfo =
         do_QueryInterface(mSecurityInfo);
-    if (!statusProvider)
+    if (!securityInfo)
         return;
     nsCOMPtr<nsISSLStatus> sslstat;
-    statusProvider->GetSSLStatus(getter_AddRefs(sslstat));
+    securityInfo->GetSSLStatus(getter_AddRefs(sslstat));
     if (!sslstat)
         return;
 
-    nsCOMPtr<nsITransportSecurityInfo> securityInfo =
-        do_QueryInterface(mSecurityInfo);
     uint32_t state;
     if (securityInfo &&
         NS_SUCCEEDED(securityInfo->GetSecurityState(&state)) &&
         (state & nsIWebProgressListener::STATE_IS_BROKEN)) {
         // Send weak crypto warnings to the web console
         if (state & nsIWebProgressListener::STATE_USES_WEAK_CRYPTO) {
             nsString consoleErrorTag = NS_LITERAL_STRING("WeakCipherSuiteWarning");
             nsString consoleErrorCategory = NS_LITERAL_STRING("SSL");
--- a/netwerk/protocol/http/nsHttpNTLMAuth.cpp
+++ b/netwerk/protocol/http/nsHttpNTLMAuth.cpp
@@ -19,17 +19,17 @@
 #include "nsIPrefBranch.h"
 #include "nsIPrefService.h"
 #include "nsIHttpAuthenticableChannel.h"
 #include "nsIURI.h"
 #ifdef XP_WIN
 #include "nsIChannel.h"
 #include "nsIX509Cert.h"
 #include "nsISSLStatus.h"
-#include "nsISSLStatusProvider.h"
+#include "nsITransportSecurityInfo.h"
 #endif
 #include "mozilla/Attributes.h"
 #include "mozilla/Base64.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/Tokenizer.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/Unused.h"
 #include "nsNetUtil.h"
@@ -330,22 +330,22 @@ nsHttpNTLMAuth::GenerateCredentials(nsIH
         if (NS_FAILED(rv))
             return rv;
 
         nsCOMPtr<nsISupports> security;
         rv = channel->GetSecurityInfo(getter_AddRefs(security));
         if (NS_FAILED(rv))
             return rv;
 
-        nsCOMPtr<nsISSLStatusProvider> statusProvider =
+        nsCOMPtr<nsITransportSecurityInfo> secInfo =
             do_QueryInterface(security);
 
-        if (mUseNative && statusProvider) {
+        if (mUseNative && secInfo) {
             nsCOMPtr<nsISSLStatus> status;
-            rv = statusProvider->GetSSLStatus(getter_AddRefs(status));
+            rv = secInfo->GetSSLStatus(getter_AddRefs(status));
             if (NS_FAILED(rv))
                 return rv;
 
             nsCOMPtr<nsIX509Cert> cert;
             rv = status->GetServerCert(getter_AddRefs(cert));
             if (NS_FAILED(rv))
                 return rv;
 
--- a/netwerk/socket/nsITransportSecurityInfo.idl
+++ b/netwerk/socket/nsITransportSecurityInfo.idl
@@ -1,25 +1,28 @@
 /* -*- 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 "nsISupports.idl"
 
+interface nsISSLStatus;
 interface nsIX509CertList;
 
 [builtinclass, scriptable, uuid(216112d3-28bc-4671-b057-f98cc09ba1ea)]
 interface nsITransportSecurityInfo : nsISupports {
     readonly attribute unsigned long securityState;
     [infallible] readonly attribute long errorCode; // PRErrorCode
     // errorCode as string (e.g. "SEC_ERROR_UNKNOWN_ISSUER")
     readonly attribute AString errorCodeString;
 
     /**
      * If certificate verification failed, this will be the peer certificate
      * chain provided in the handshake, so it can be used for error reporting.
      * If verification succeeded, this will be null.
      */
     readonly attribute nsIX509CertList failedCertChain;
+
+    readonly attribute nsISSLStatus SSLStatus;
 };
 
--- a/security/manager/pki/resources/content/exceptionDialog.js
+++ b/security/manager/pki/resources/content/exceptionDialog.js
@@ -21,17 +21,19 @@ function initExceptionDialog() {
   gNeedReset = false;
   gDialog = document.documentElement;
   gBundleBrand = document.getElementById("brand_bundle");
   gPKIBundle = document.getElementById("pippki_bundle");
   gSecHistogram = Services.telemetry.getHistogramById("SECURITY_UI");
   gNsISecTel = Ci.nsISecurityUITelemetry;
 
   var brandName = gBundleBrand.getString("brandShortName");
-  setText("warningText", gPKIBundle.getFormattedString("addExceptionBrandedWarning2", [brandName]));
+  setText("warningText",
+          gPKIBundle.getFormattedString("addExceptionBrandedWarning2",
+                                        [brandName]));
   gDialog.getButton("extra1").disabled = true;
 
   var args = window.arguments;
   if (args && args[0]) {
     if (args[0].location) {
       // We were pre-seeded with a location.
       document.getElementById("locationTextBox").value = args[0].location;
       document.getElementById("checkCertButton").disabled = false;
@@ -70,17 +72,17 @@ function initExceptionDialog() {
  * @param {XMLHttpRequest} req
  *        The XMLHttpRequest created and sent by checkCert.
  * @param {Event} evt
  *        The load or error event.
  */
 function grabCert(req, evt) {
   if (req.channel && req.channel.securityInfo) {
     gSSLStatus = req.channel.securityInfo
-                    .QueryInterface(Ci.nsISSLStatusProvider).SSLStatus;
+                    .QueryInterface(Ci.nsITransportSecurityInfo).SSLStatus;
     gCert = gSSLStatus ? gSSLStatus.QueryInterface(Ci.nsISSLStatus).serverCert
                        : null;
   }
   gBroken = evt.type == "error";
   gChecking = false;
   updateCertStatus();
 }
 
@@ -154,17 +156,18 @@ function resetDialog() {
   window.sizeToContent();
 }
 
 /**
  * Called by input textboxes to manage UI state
  */
 function handleTextChange() {
   var checkCertButton = document.getElementById("checkCertButton");
-  checkCertButton.disabled = !(document.getElementById("locationTextBox").value);
+  checkCertButton.disabled =
+                    !(document.getElementById("locationTextBox").value);
   if (gNeedReset) {
     gNeedReset = false;
     resetDialog();
   }
 }
 
 function updateCertStatus() {
   var shortDesc, longDesc;
@@ -196,17 +199,18 @@ function updateCertStatus() {
           longDesc  = exl;
         } else {
           use2 = true;
           shortDesc2 = exs;
           longDesc2  = exl;
         }
       }
       if (gSSLStatus.isUntrusted) {
-        bucketId += gNsISecTel.WARNING_BAD_CERT_TOP_ADD_EXCEPTION_FLAG_UNTRUSTED;
+        bucketId +=
+          gNsISecTel.WARNING_BAD_CERT_TOP_ADD_EXCEPTION_FLAG_UNTRUSTED;
         if (!use1) {
           use1 = true;
           shortDesc = uts;
           longDesc  = utl;
         } else if (!use2) {
           use2 = true;
           shortDesc2 = uts;
           longDesc2  = utl;
@@ -224,17 +228,18 @@ function updateCertStatus() {
       // If the Private Browsing service is available and the mode is active,
       // don't store permanent exceptions, since they would persist after
       // private browsing mode was disabled.
       var inPrivateBrowsing = inPrivateBrowsingMode();
       var pe = document.getElementById("permanent");
       pe.disabled = inPrivateBrowsing;
       pe.checked = !inPrivateBrowsing;
 
-      setText("headerDescription", gPKIBundle.getString("addExceptionInvalidHeader"));
+      setText("headerDescription",
+              gPKIBundle.getString("addExceptionInvalidHeader"));
     } else {
       shortDesc = "addExceptionValidShort";
       longDesc  = "addExceptionValidLong";
       gDialog.getButton("extra1").disabled = true;
       document.getElementById("permanent").disabled = true;
     }
 
     // We're done checking the certificate, so allow the user to check it again.
@@ -296,32 +301,37 @@ function viewCertButtonClick() {
 function addException() {
   if (!gCert || !gSSLStatus) {
     return;
   }
 
   var overrideService = Cc["@mozilla.org/security/certoverride;1"]
                           .getService(Ci.nsICertOverrideService);
   var flags = 0;
-  let confirmBucketId = gNsISecTel.WARNING_BAD_CERT_TOP_CONFIRM_ADD_EXCEPTION_BASE;
+  let confirmBucketId =
+        gNsISecTel.WARNING_BAD_CERT_TOP_CONFIRM_ADD_EXCEPTION_BASE;
   if (gSSLStatus.isUntrusted) {
     flags |= overrideService.ERROR_UNTRUSTED;
-    confirmBucketId += gNsISecTel.WARNING_BAD_CERT_TOP_CONFIRM_ADD_EXCEPTION_FLAG_UNTRUSTED;
+    confirmBucketId +=
+        gNsISecTel.WARNING_BAD_CERT_TOP_CONFIRM_ADD_EXCEPTION_FLAG_UNTRUSTED;
   }
   if (gSSLStatus.isDomainMismatch) {
     flags |= overrideService.ERROR_MISMATCH;
-    confirmBucketId += gNsISecTel.WARNING_BAD_CERT_TOP_CONFIRM_ADD_EXCEPTION_FLAG_DOMAIN;
+    confirmBucketId +=
+           gNsISecTel.WARNING_BAD_CERT_TOP_CONFIRM_ADD_EXCEPTION_FLAG_DOMAIN;
   }
   if (gSSLStatus.isNotValidAtThisTime) {
     flags |= overrideService.ERROR_TIME;
-    confirmBucketId += gNsISecTel.WARNING_BAD_CERT_TOP_CONFIRM_ADD_EXCEPTION_FLAG_TIME;
+    confirmBucketId +=
+           gNsISecTel.WARNING_BAD_CERT_TOP_CONFIRM_ADD_EXCEPTION_FLAG_TIME;
   }
 
   var permanentCheckbox = document.getElementById("permanent");
-  var shouldStorePermanently = permanentCheckbox.checked && !inPrivateBrowsingMode();
+  var shouldStorePermanently = permanentCheckbox.checked &&
+                               !inPrivateBrowsingMode();
   if (!permanentCheckbox.checked) {
     gSecHistogram.add(gNsISecTel.WARNING_BAD_CERT_TOP_DONT_REMEMBER_EXCEPTION);
   }
 
   gSecHistogram.add(confirmBucketId);
   var uri = getURI();
   overrideService.rememberValidityOverride(
     uri.asciiHost, uri.port,
--- a/security/manager/ssl/TransportSecurityInfo.cpp
+++ b/security/manager/ssl/TransportSecurityInfo.cpp
@@ -46,17 +46,16 @@ TransportSecurityInfo::TransportSecurity
   , mErrorCode(0)
   , mPort(0)
 {
 }
 
 NS_IMPL_ISUPPORTS(TransportSecurityInfo,
                   nsITransportSecurityInfo,
                   nsIInterfaceRequestor,
-                  nsISSLStatusProvider,
                   nsIAssociatedContentSecurity,
                   nsISerializable,
                   nsIClassInfo)
 
 void
 TransportSecurityInfo::SetHostName(const char* host)
 {
   mHostName.Assign(host);
@@ -360,17 +359,17 @@ static NS_DEFINE_CID(kNSSSocketInfoCID, 
 
 NS_IMETHODIMP
 TransportSecurityInfo::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
 {
   *aClassIDNoAlloc = kNSSSocketInfoCID;
   return NS_OK;
 }
 
-nsresult
+NS_IMETHODIMP
 TransportSecurityInfo::GetSSLStatus(nsISSLStatus** _result)
 {
   NS_ENSURE_ARG_POINTER(_result);
 
   *_result = mSSLStatus;
   NS_IF_ADDREF(*_result);
 
   return NS_OK;
--- a/security/manager/ssl/TransportSecurityInfo.h
+++ b/security/manager/ssl/TransportSecurityInfo.h
@@ -11,40 +11,37 @@
 #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"
 #include "nsSSLStatus.h"
 #include "nsString.h"
 #include "pkix/pkixtypes.h"
 
 namespace mozilla { namespace psm {
 
 class TransportSecurityInfo : public nsITransportSecurityInfo
                             , public nsIInterfaceRequestor
-                            , public nsISSLStatusProvider
                             , public nsIAssociatedContentSecurity
                             , public nsISerializable
                             , public nsIClassInfo
 {
 protected:
   virtual ~TransportSecurityInfo() {}
 public:
   TransportSecurityInfo();
 
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSITRANSPORTSECURITYINFO
   NS_DECL_NSIINTERFACEREQUESTOR
-  NS_DECL_NSISSLSTATUSPROVIDER
   NS_DECL_NSIASSOCIATEDCONTENTSECURITY
   NS_DECL_NSISERIALIZABLE
   NS_DECL_NSICLASSINFO
 
   void SetSecurityState(uint32_t aState);
 
   const nsACString & GetHostName() const { return mHostName; }
 
--- a/security/manager/ssl/moz.build
+++ b/security/manager/ssl/moz.build
@@ -31,17 +31,16 @@ XPIDL_SOURCES += [
     'nsIPKCS11Module.idl',
     'nsIPKCS11ModuleDB.idl',
     'nsIPKCS11Slot.idl',
     'nsIProtectedAuthThread.idl',
     'nsISecretDecoderRing.idl',
     'nsISecurityUITelemetry.idl',
     'nsISiteSecurityService.idl',
     'nsISSLStatus.idl',
-    'nsISSLStatusProvider.idl',
     'nsITokenDialogs.idl',
     'nsITokenPasswordDialogs.idl',
     'nsIX509Cert.idl',
     'nsIX509CertDB.idl',
     'nsIX509CertList.idl',
     'nsIX509CertValidity.idl',
 ]
 
deleted file mode 100644
--- a/security/manager/ssl/nsISSLStatusProvider.idl
+++ /dev/null
@@ -1,13 +0,0 @@
-/* -*- 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 "nsISupports.idl"
-
-interface nsISSLStatus;
-
-[scriptable, uuid(179b1ab1-0950-4427-9556-6f496dc4a27f)]
-interface nsISSLStatusProvider : nsISupports {
-  readonly attribute nsISSLStatus SSLStatus;
-};
--- a/security/manager/ssl/nsSecureBrowserUIImpl.cpp
+++ b/security/manager/ssl/nsSecureBrowserUIImpl.cpp
@@ -84,18 +84,17 @@ nsSecureBrowserUIImpl::nsSecureBrowserUI
   MOZ_ASSERT(NS_IsMainThread());
 
   ResetStateTracking();
 }
 
 NS_IMPL_ISUPPORTS(nsSecureBrowserUIImpl,
                   nsISecureBrowserUI,
                   nsIWebProgressListener,
-                  nsISupportsWeakReference,
-                  nsISSLStatusProvider)
+                  nsISupportsWeakReference)
 
 NS_IMETHODIMP
 nsSecureBrowserUIImpl::Init(mozIDOMWindowProxy* aWindow)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (MOZ_LOG_TEST(gSecureDocLog, LogLevel::Debug)) {
     nsCOMPtr<nsIDOMWindow> window(do_QueryReferent(mWindow));
@@ -369,33 +368,31 @@ nsSecureBrowserUIImpl::EvaluateAndUpdate
 
   mNewToplevelSecurityState =
     GetSecurityStateFromSecurityInfoAndRequest(info, aRequest);
 
   MOZ_LOG(gSecureDocLog, LogLevel::Debug,
           ("SecureUI:%p: OnStateChange: remember mNewToplevelSecurityState => %x\n",
            this, mNewToplevelSecurityState));
 
-  nsCOMPtr<nsISSLStatusProvider> sp(do_QueryInterface(info));
-  if (sp) {
+  nsCOMPtr<nsITransportSecurityInfo> psmInfo(do_QueryInterface(info));
+  if (psmInfo) {
     // Ignore result
     updateStatus = true;
-    (void) sp->GetSSLStatus(getter_AddRefs(temp_SSLStatus));
+    (void) psmInfo->GetSSLStatus(getter_AddRefs(temp_SSLStatus));
     if (temp_SSLStatus) {
       bool aTemp;
       if (NS_SUCCEEDED(temp_SSLStatus->GetIsExtendedValidation(&aTemp))) {
         mNewToplevelIsEV = aTemp;
       }
     }
+    mSecInfo = psmInfo;
   }
 
   mNewToplevelSecurityStateKnown = true;
-  if (updateStatus) {
-    mSSLStatus = temp_SSLStatus;
-  }
   MOZ_LOG(gSecureDocLog, LogLevel::Debug,
          ("SecureUI:%p: remember securityInfo %p\n", this,
           info));
   nsCOMPtr<nsIAssociatedContentSecurity> associatedContentSecurityFromRequest(
     do_QueryInterface(aRequest));
   if (associatedContentSecurityFromRequest) {
     mCurrentToplevelSecurityInfo = aRequest;
   } else {
@@ -1009,17 +1006,17 @@ nsSecureBrowserUIImpl::UpdateSecuritySta
   bool flagsChanged = false;
   if (mNotifiedSecurityState != newSecurityState) {
     // Something changed since the last time.
     flagsChanged = true;
     mNotifiedSecurityState = newSecurityState;
 
     // If we have no security, we also shouldn't have any SSL status.
     if (newSecurityState == lis_no_security) {
-      mSSLStatus = nullptr;
+      mSecInfo = nullptr;
     }
   }
 
   if (mNotifiedToplevelIsEV != mNewToplevelIsEV) {
     flagsChanged = true;
     mNotifiedToplevelIsEV = mNewToplevelIsEV;
   }
 
@@ -1161,19 +1158,18 @@ nsSecureBrowserUIImpl::OnSecurityChange(
            ("SecureUI:%p: OnSecurityChange: (%x) %s\n", this,
             state, aURI->GetSpecOrDefault().get()));
   }
 #endif
 
   return NS_OK;
 }
 
-// nsISSLStatusProvider methods
 NS_IMETHODIMP
-nsSecureBrowserUIImpl::GetSSLStatus(nsISSLStatus** _result)
+nsSecureBrowserUIImpl::GetSecInfo(nsITransportSecurityInfo** _result)
 {
   NS_ENSURE_ARG_POINTER(_result);
   MOZ_ASSERT(NS_IsMainThread());
 
   switch (mNotifiedSecurityState)
   {
     case lis_broken_security:
     case lis_mixed_security:
@@ -1182,13 +1178,13 @@ nsSecureBrowserUIImpl::GetSSLStatus(nsIS
 
     default:
       MOZ_FALLTHROUGH_ASSERT("if this is reached you must add more entries to the switch");
     case lis_no_security:
       *_result = nullptr;
       return NS_OK;
   }
 
-  *_result = mSSLStatus;
+  *_result = mSecInfo;
   NS_IF_ADDREF(*_result);
 
   return NS_OK;
 }
--- a/security/manager/ssl/nsSecureBrowserUIImpl.h
+++ b/security/manager/ssl/nsSecureBrowserUIImpl.h
@@ -5,44 +5,41 @@
 
 #ifndef nsSecureBrowserUIImpl_h
 #define nsSecureBrowserUIImpl_h
 
 #include "PLDHashTable.h"
 #include "mozilla/ReentrancyGuard.h"
 #include "nsCOMPtr.h"
 #include "nsINetUtil.h"
-#include "nsISSLStatusProvider.h"
 #include "nsISecureBrowserUI.h"
 #include "nsISecurityEventSink.h"
 #include "nsIURI.h"
 #include "nsIWebProgressListener.h"
 #include "nsWeakReference.h"
 
-class nsISSLStatus;
+class nsITransportSecurityInfo;
 class nsIChannel;
 
 #define NS_SECURE_BROWSER_UI_CID \
 { 0xcc75499a, 0x1dd1, 0x11b2, {0x8a, 0x82, 0xca, 0x41, 0x0a, 0xc9, 0x07, 0xb8}}
 
 
 class nsSecureBrowserUIImpl : public nsISecureBrowserUI,
                               public nsIWebProgressListener,
-                              public nsSupportsWeakReference,
-                              public nsISSLStatusProvider
+                              public nsSupportsWeakReference
 {
   friend class mozilla::ReentrancyGuard;
 
 public:
   nsSecureBrowserUIImpl();
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIWEBPROGRESSLISTENER
   NS_DECL_NSISECUREBROWSERUI
-  NS_DECL_NSISSLSTATUSPROVIDER
 
 protected:
   virtual ~nsSecureBrowserUIImpl() {};
 
   nsWeakPtr mWindow;
   nsWeakPtr mDocShell;
   nsCOMPtr<nsINetUtil> mIOService;
   nsCOMPtr<nsIURI> mCurrentURI;
@@ -82,15 +79,15 @@ protected:
 
   void EvaluateAndUpdateSecurityState(nsIRequest* aRequest, nsISupports *info,
                                       bool withNewLocation, bool withNewSink);
   void UpdateSubrequestMembers(nsISupports* securityInfo, nsIRequest* request);
 
   void ObtainEventSink(nsIChannel *channel,
                        nsCOMPtr<nsISecurityEventSink> &sink);
 
-  nsCOMPtr<nsISSLStatus> mSSLStatus;
+  nsCOMPtr<nsITransportSecurityInfo> mSecInfo;
   nsCOMPtr<nsISupports> mCurrentToplevelSecurityInfo;
 
   PLDHashTable mTransferringRequests;
 };
 
 #endif // nsSecureBrowserUIImpl_h
--- a/security/manager/ssl/tests/unit/head_psm.js
+++ b/security/manager/ssl/tests/unit/head_psm.js
@@ -715,18 +715,17 @@ FakeSSLStatus.prototype = {
   QueryInterface: ChromeUtils.generateQI(["nsISSLStatus"]),
 };
 
 // Utility functions for adding tests relating to certificate error overrides
 
 // Helper function for add_cert_override_test. Probably doesn't need to be
 // called directly.
 function add_cert_override(aHost, aExpectedBits, aSecurityInfo) {
-  let sslstatus = aSecurityInfo.QueryInterface(Ci.nsISSLStatusProvider)
-                               .SSLStatus;
+  let sslstatus = aSecurityInfo.SSLStatus;
   let bits =
     (sslstatus.isUntrusted ? Ci.nsICertOverrideService.ERROR_UNTRUSTED : 0) |
     (sslstatus.isDomainMismatch ? Ci.nsICertOverrideService.ERROR_MISMATCH : 0) |
     (sslstatus.isNotValidAtThisTime ? Ci.nsICertOverrideService.ERROR_TIME : 0);
 
   Assert.equal(bits, aExpectedBits,
                "Actual and expected override bits should match");
   let cert = sslstatus.serverCert;
@@ -744,32 +743,30 @@ function add_cert_override_test(aHost, a
                                 aExpectedSSLStatus = undefined) {
   add_connection_test(aHost, aExpectedError, null,
                       add_cert_override.bind(this, aHost, aExpectedBits));
   add_connection_test(aHost, PRErrorCodeSuccess, null, aSecurityInfo => {
     Assert.ok(aSecurityInfo.securityState &
               Ci.nsIWebProgressListener.STATE_CERT_USER_OVERRIDDEN,
               "Cert override flag should be set on the security state");
     if (aExpectedSSLStatus) {
-      let sslstatus = aSecurityInfo.QueryInterface(Ci.nsISSLStatusProvider)
-                                  .SSLStatus;
+      let sslstatus = aSecurityInfo.SSLStatus;
       if (aExpectedSSLStatus.failedCertChain) {
         ok(aExpectedSSLStatus.failedCertChain.equals(sslstatus.failedCertChain));
       }
     }
   });
 }
 
 // Helper function for add_prevented_cert_override_test. This is much like
 // add_cert_override except it may not be the case that the connection has an
 // SSLStatus set on it. In this case, the error was not overridable anyway, so
 // we consider it a success.
 function attempt_adding_cert_override(aHost, aExpectedBits, aSecurityInfo) {
-  let sslstatus = aSecurityInfo.QueryInterface(Ci.nsISSLStatusProvider)
-                               .SSLStatus;
+  let sslstatus = aSecurityInfo.SSLStatus;
   if (sslstatus) {
     let bits =
       (sslstatus.isUntrusted ? Ci.nsICertOverrideService.ERROR_UNTRUSTED : 0) |
       (sslstatus.isDomainMismatch ? Ci.nsICertOverrideService.ERROR_MISMATCH : 0) |
       (sslstatus.isNotValidAtThisTime ? Ci.nsICertOverrideService.ERROR_TIME : 0);
     Assert.equal(bits, aExpectedBits,
                  "Actual and expected override bits should match");
     let cert = sslstatus.serverCert;
--- a/security/manager/ssl/tests/unit/test_cert_overrides_read_only.js
+++ b/security/manager/ssl/tests/unit/test_cert_overrides_read_only.js
@@ -5,18 +5,17 @@
 "use strict";
 
 // Tests that permanent certificate error overrides can be added even if the
 // certificate/key databases are in read-only mode.
 
 // Helper function for add_read_only_cert_override_test. Probably doesn't need
 // to be called directly.
 function add_read_only_cert_override(aHost, aExpectedBits, aSecurityInfo) {
-  let sslstatus = aSecurityInfo.QueryInterface(Ci.nsISSLStatusProvider)
-                               .SSLStatus;
+  let sslstatus = aSecurityInfo.SSLStatus;
   let bits =
     (sslstatus.isUntrusted ? Ci.nsICertOverrideService.ERROR_UNTRUSTED : 0) |
     (sslstatus.isDomainMismatch ? Ci.nsICertOverrideService.ERROR_MISMATCH : 0) |
     (sslstatus.isNotValidAtThisTime ? Ci.nsICertOverrideService.ERROR_TIME : 0);
 
   Assert.equal(bits, aExpectedBits,
                "Actual and expected override bits should match");
   let cert = sslstatus.serverCert;
--- a/security/manager/ssl/tests/unit/test_ct.js
+++ b/security/manager/ssl/tests/unit/test_ct.js
@@ -6,18 +6,17 @@
 "use strict";
 
 do_get_profile(); // must be called before getting nsIX509CertDB
 const certdb  = Cc["@mozilla.org/security/x509certdb;1"]
                   .getService(Ci.nsIX509CertDB);
 
 function expectCT(value) {
   return (securityInfo) => {
-    let sslStatus = securityInfo.QueryInterface(Ci.nsISSLStatusProvider)
-                                .SSLStatus;
+    let sslStatus = securityInfo.SSLStatus;
     Assert.equal(sslStatus.certificateTransparencyStatus, value,
                  "actual and expected CT status should match");
   };
 }
 
 registerCleanupFunction(() => {
   Services.prefs.clearUserPref("security.pki.certificate_transparency.mode");
 });
--- a/security/manager/ssl/tests/unit/test_session_resumption.js
+++ b/security/manager/ssl/tests/unit/test_session_resumption.js
@@ -36,19 +36,17 @@ function add_resume_non_ev_with_override
   // This connects again, using session resumption. Note that we don't clear
   // the TLS session cache between these operations (that would defeat the
   // purpose).
   add_connection_test("expired.example.com", PRErrorCodeSuccess, null,
     (transportSecurityInfo) => {
       ok(transportSecurityInfo.securityState &
          Ci.nsIWebProgressListener.STATE_CERT_USER_OVERRIDDEN,
          "expired.example.com should have STATE_CERT_USER_OVERRIDDEN flag");
-      let sslStatus = transportSecurityInfo
-                        .QueryInterface(Ci.nsISSLStatusProvider)
-                        .SSLStatus;
+      let sslStatus = transportSecurityInfo.SSLStatus;
       ok(!sslStatus.succeededCertChain,
          "ev-test.example.com should not have succeededCertChain set");
       ok(!sslStatus.isDomainMismatch,
          "expired.example.com should not have isDomainMismatch set");
       ok(sslStatus.isNotValidAtThisTime,
          "expired.example.com should have isNotValidAtThisTime set");
       ok(!sslStatus.isUntrusted,
          "expired.example.com should not have isUntrusted set");
@@ -63,19 +61,17 @@ function add_resume_non_ev_with_override
 // build). This assumes that an appropriate OCSP responder is running or that
 // good responses are cached.
 function add_one_ev_test() {
   add_connection_test("ev-test.example.com", PRErrorCodeSuccess, null,
     (transportSecurityInfo) => {
       ok(!(transportSecurityInfo.securityState &
            Ci.nsIWebProgressListener.STATE_CERT_USER_OVERRIDDEN),
          "ev-test.example.com should not have STATE_CERT_USER_OVERRIDDEN flag");
-      let sslStatus = transportSecurityInfo
-                        .QueryInterface(Ci.nsISSLStatusProvider)
-                        .SSLStatus;
+      let sslStatus = transportSecurityInfo.SSLStatus;
       ok(sslStatus.succeededCertChain,
          "ev-test.example.com should have succeededCertChain set");
       ok(!sslStatus.isDomainMismatch,
          "ev-test.example.com should not have isDomainMismatch set");
       ok(!sslStatus.isNotValidAtThisTime,
          "ev-test.example.com should not have isNotValidAtThisTime set");
       ok(!sslStatus.isUntrusted,
          "ev-test.example.com should not have isUntrusted set");
@@ -125,19 +121,17 @@ const GOOD_DOMAIN = "good.include-subdom
 // succeed (but isn't EV) and verifies that its succeededCertChain gets set
 // appropriately.
 function add_one_non_ev_test() {
   add_connection_test(GOOD_DOMAIN, PRErrorCodeSuccess, null,
     (transportSecurityInfo) => {
       ok(!(transportSecurityInfo.securityState &
            Ci.nsIWebProgressListener.STATE_CERT_USER_OVERRIDDEN),
          `${GOOD_DOMAIN} should not have STATE_CERT_USER_OVERRIDDEN flag`);
-      let sslStatus = transportSecurityInfo
-                        .QueryInterface(Ci.nsISSLStatusProvider)
-                        .SSLStatus;
+      let sslStatus = transportSecurityInfo.SSLStatus;
       ok(sslStatus.succeededCertChain,
          `${GOOD_DOMAIN} should have succeededCertChain set`);
       ok(!sslStatus.isDomainMismatch,
          `${GOOD_DOMAIN} should not have isDomainMismatch set`);
       ok(!sslStatus.isNotValidAtThisTime,
          `${GOOD_DOMAIN} should not have isNotValidAtThisTime set`);
       ok(!sslStatus.isUntrusted,
          `${GOOD_DOMAIN} should not have isUntrusted set`);
--- a/security/manager/ssl/tests/unit/test_ssl_status.js
+++ b/security/manager/ssl/tests/unit/test_ssl_status.js
@@ -15,31 +15,31 @@ function run_test() {
     response.setStatusLine(request.httpVersion, 500, "Internal Server Error");
   });
   fakeOCSPResponder.start(8888);
 
   // Test successful connection (failedCertChain should be null,
   // succeededCertChain should be set as expected)
   add_connection_test(
     "good.include-subdomains.pinning.example.com", PRErrorCodeSuccess, null,
-    function withSecurityInfo(aSSLStatus) {
-      let sslstatus = aSSLStatus.QueryInterface(Ci.nsISSLStatusProvider).SSLStatus;
+    function withSecurityInfo(aSecInfo) {
+      let sslstatus = aSecInfo.SSLStatus;
       equal(sslstatus.failedCertChain, null,
             "failedCertChain for a successful connection should be null");
       ok(sslstatus.succeededCertChain.equals(build_cert_chain(["default-ee", "test-ca"])),
             "succeededCertChain for a successful connection should be as expected");
     }
   );
 
   // Test failed connection (failedCertChain should be set as expected,
   // succeededCertChain should be null)
   add_connection_test(
     "expired.example.com", SEC_ERROR_EXPIRED_CERTIFICATE, null,
-    function withSecurityInfo(aSSLStatus) {
-      let sslstatus = aSSLStatus.QueryInterface(Ci.nsISSLStatusProvider).SSLStatus;
+    function withSecurityInfo(aSecInfo) {
+      let sslstatus = aSecInfo.SSLStatus;
       equal(sslstatus.succeededCertChain, null,
             "succeededCertChain for a failed connection should be null");
       ok(sslstatus.failedCertChain.equals(build_cert_chain(["expired-ee", "test-ca"])),
             "failedCertChain for a failed connection should be as expected");
     }
   );
 
   // Ensure the correct failed cert chain is set on cert override
--- a/security/manager/tools/getHSTSPreloadList.js
+++ b/security/manager/tools/getHSTSPreloadList.js
@@ -106,18 +106,18 @@ function getHosts(rawdata) {
 
 function processStsHeader(host, header, status, securityInfo) {
   let maxAge = { value: 0 };
   let includeSubdomains = { value: false };
   let error = ERROR_NONE;
   if (header != null && securityInfo != null) {
     try {
       let uri = Services.io.newURI("https://" + host.name);
-      let sslStatus = securityInfo.QueryInterface(Ci.nsISSLStatusProvider)
-                                  .SSLStatus;
+      let sslStatus = securityInfo.
+                       QueryInterface(Ci.nsITransportSecurityInfo).SSLStatus;
       gSSService.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS,
                                uri, header, sslStatus, 0,
                                Ci.nsISiteSecurityService.SOURCE_PRELOAD_LIST,
                                {}, maxAge, includeSubdomains);
     } catch (e) {
       dump("ERROR: could not process header '" + header + "' from " +
            host.name + ": " + e + "\n");
       error = e;
--- a/services/sync/modules/engines/passwords.js
+++ b/services/sync/modules/engines/passwords.js
@@ -280,21 +280,17 @@ PasswordStore.prototype = {
     let login = this._nsLoginInfoFromRecord(record);
     if (!login) {
       return;
     }
 
     this._log.trace("Adding login for " + record.hostname);
     this._log.trace("httpRealm: " + JSON.stringify(login.httpRealm) + "; " +
                     "formSubmitURL: " + JSON.stringify(login.formSubmitURL));
-    try {
-      Services.logins.addLogin(login);
-    } catch (ex) {
-      this._log.error(`Adding record ${record.id} resulted in exception`, ex);
-    }
+    Services.logins.addLogin(login);
   },
 
   async remove(record) {
     this._log.trace("Removing login " + record.id);
 
     let loginItem = await this._getLoginFromGUID(record.id);
     if (!loginItem) {
       this._log.trace("Asked to remove record that doesn't exist, ignoring");
@@ -312,21 +308,17 @@ PasswordStore.prototype = {
     }
 
     this._log.trace("Updating " + record.hostname);
     let newinfo = this._nsLoginInfoFromRecord(record);
     if (!newinfo) {
       return;
     }
 
-    try {
-      Services.logins.modifyLogin(loginItem, newinfo);
-    } catch (ex) {
-      this._log.debug(`Modifying record ${record.id} resulted in exception; not modifying`, ex);
-    }
+    Services.logins.modifyLogin(loginItem, newinfo);
   },
 
   async wipe() {
     Services.logins.removeAllLogins();
   },
 };
 
 function PasswordTracker(name, engine) {
--- a/taskcluster/taskgraph/actions/backfill.py
+++ b/taskcluster/taskgraph/actions/backfill.py
@@ -7,17 +7,17 @@
 from __future__ import absolute_import, print_function, unicode_literals
 
 import logging
 
 import requests
 from requests.exceptions import HTTPError
 
 from .registry import register_callback_action
-from .util import find_decision_task, create_tasks
+from .util import find_decision_task, create_tasks, combine_task_graph_files
 from taskgraph.util.taskcluster import get_artifact_from_index
 from taskgraph.taskgraph import TaskGraph
 
 PUSHLOG_TMPL = '{}/json-pushes?version=2&startID={}&endID={}'
 INDEX_TMPL = 'gecko.v2.{}.pushlog-id.{}.decision'
 
 logger = logging.getLogger(__name__)
 
@@ -84,16 +84,17 @@ def backfill_action(parameters, graph_co
             break
 
         end_id = start_id - 1
         start_id -= depth
         if start_id < 0:
             break
 
     pushes = sorted(pushes)[-depth:]
+    backfill_pushes = []
 
     for push in pushes:
         try:
             full_task_graph = get_artifact_from_index(
                     INDEX_TMPL.format(parameters['project'], push),
                     'public/full-task-graph.json')
             _, full_task_graph = TaskGraph.from_json(full_task_graph)
             label_to_taskid = get_artifact_from_index(
@@ -176,18 +177,20 @@ def backfill_action(parameters, graph_co
                     task.task['extra']['suite']['flavor'] = 'test-verify'
 
                     task.task['extra']['treeherder']['symbol'] = symbol
                     del task.task['extra']['treeherder']['groupSymbol']
                 return task
 
             create_tasks([label], full_task_graph, label_to_taskid,
                          push_params, push_decision_task_id, push, modifier=modifier)
+            backfill_pushes.append(push)
         else:
             logging.info('Could not find {} on {}. Skipping.'.format(label, push))
+    combine_task_graph_files(backfill_pushes)
 
 
 def remove_args_from_command(cmd_parts, preamble_length=0, args_to_ignore=[]):
     """
        We need to remove all extra instances of command line arguments
        that are suite/job specific, like suite=jsreftest, subsuite=devtools
        and other ones like --total-chunk=X.
        args:
--- a/taskcluster/taskgraph/actions/retrigger.py
+++ b/taskcluster/taskgraph/actions/retrigger.py
@@ -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/.
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 import logging
 
 from .util import (
+    combine_task_graph_files,
     create_tasks,
     fetch_graph_and_labels
 )
 from .registry import register_callback_action
 
 logger = logging.getLogger(__name__)
 
 
@@ -63,8 +64,9 @@ def retrigger_action(parameters, graph_c
         to_run = to_run & set(label_to_taskid.keys())
         with_downstream = ' (with downstream) '
 
     times = input.get('times', 1)
     for i in xrange(times):
         create_tasks(to_run, full_task_graph, label_to_taskid, parameters, decision_task_id, i)
 
         logger.info('Scheduled {}{}(time {}/{})'.format(label, with_downstream, i+1, times))
+    combine_task_graph_files(list(range(times)))
--- a/taskcluster/taskgraph/actions/util.py
+++ b/taskcluster/taskgraph/actions/util.py
@@ -9,17 +9,17 @@ from __future__ import absolute_import, 
 import copy
 import logging
 import requests
 import os
 
 from requests.exceptions import HTTPError
 
 from taskgraph import create
-from taskgraph.decision import write_artifact
+from taskgraph.decision import read_artifact, write_artifact
 from taskgraph.taskgraph import TaskGraph
 from taskgraph.optimize import optimize_task_graph
 from taskgraph.util.taskcluster import get_session, find_task_id, get_artifact, list_tasks
 
 logger = logging.getLogger(__name__)
 
 PUSHLOG_TMPL = '{}/json-pushes?version=2&changeset={}&tipsonly=1&full=1'
 
@@ -147,8 +147,20 @@ def create_tasks(to_run, full_task_graph
                                                                 params,
                                                                 to_run,
                                                                 label_to_taskid)
     write_artifact('task-graph{}.json'.format(suffix), optimized_task_graph.to_json())
     write_artifact('label-to-taskid{}.json'.format(suffix), label_to_taskid)
     write_artifact('to-run{}.json'.format(suffix), list(to_run))
     create.create_tasks(optimized_task_graph, label_to_taskid, params, decision_task_id)
     return label_to_taskid
+
+
+def combine_task_graph_files(suffixes):
+    """Combine task-graph-{suffix}.json files into a single task-graph.json file.
+
+    Since Chain of Trust verification requires a task-graph.json file that
+    contains all children tasks, we can combine the various task-graph-0.json
+    type files into a master task-graph.json file at the end."""
+    all = {}
+    for suffix in suffixes:
+        all.update(read_artifact('task-graph-{}.json'.format(suffix)))
+    write_artifact('task-graph.json', all)
--- a/taskcluster/taskgraph/decision.py
+++ b/taskcluster/taskgraph/decision.py
@@ -310,8 +310,24 @@ def write_artifact(filename, data):
         with open(path, 'w') as f:
             json.dump(data, f, sort_keys=True, indent=2, separators=(',', ': '))
     elif filename.endswith('.gz'):
         import gzip
         with gzip.open(path, 'wb') as f:
             f.write(json.dumps(data))
     else:
         raise TypeError("Don't know how to write to {}".format(filename))
+
+
+def read_artifact(filename):
+    path = os.path.join(ARTIFACTS_DIR, filename)
+    if filename.endswith('.yml'):
+        with open(path, 'r') as f:
+            return yaml.load(f)
+    elif filename.endswith('.json'):
+        with open(path, 'r') as f:
+            return json.load(f)
+    elif filename.endswith('.gz'):
+        import gzip
+        with gzip.open(path, 'rb') as f:
+            return json.load(f)
+    else:
+        raise TypeError("Don't know how to read {}".format(filename))
--- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/api/security.py
+++ b/testing/marionette/puppeteer/firefox/firefox_puppeteer/api/security.py
@@ -35,18 +35,17 @@ class Security(BaseLib):
         """The SSL certificate assiciated with the loaded web page in the given tab.
 
         :param tab_element: The inner tab DOM element.
 
         :returns: Certificate details as JSON object.
         """
         cert = self.marionette.execute_script("""
           var securityUI = arguments[0].linkedBrowser.securityUI;
-          var status = securityUI.QueryInterface(Components.interfaces.nsISSLStatusProvider)
-                                 .SSLStatus;
+          var status = securityUI.secInfo && securityUI.secInfo.SSLStatus;
 
           return status ? status.serverCert : null;
         """, script_args=[tab_element])
 
         uri = self.marionette.execute_script("""
           return arguments[0].linkedBrowser.currentURI.spec;
         """, script_args=[tab_element])
 
--- a/testing/mozbase/moztest/moztest/selftest/fixtures.py
+++ b/testing/mozbase/moztest/moztest/selftest/fixtures.py
@@ -54,16 +54,19 @@ def setup_test_harness(request):
         if harness_root:
             sys.path.insert(0, harness_root)
 
             # Link the test files to the test package so updates are automatically
             # picked up. Fallback to copy on Windows.
             if files_dir:
                 test_root = os.path.join(harness_root, 'tests', 'selftests')
                 if not os.path.exists(test_root):
+                    if os.path.lexists(test_root):
+                        os.remove(test_root)
+
                     if hasattr(os, 'symlink'):
                         os.symlink(files_dir, test_root)
                     else:
                         shutil.copytree(files_dir, test_root)
 
         elif 'TEST_HARNESS_ROOT' in os.environ:
             # The mochitest tests will run regardless of whether a build exists or not.
             # In a local environment, they should simply be skipped if setup fails. But
--- a/testing/raptor/webext/raptor/manifest.json
+++ b/testing/raptor/webext/raptor/manifest.json
@@ -18,17 +18,18 @@
                   "*://*.google.com/*",
                   "*://*.youtube.com/*"],
       "js": ["measure.js"]
     },
     {
       "matches": ["*://*/Speedometer/index.html*",
                   "*://*/StyleBench/*",
                   "*://*/MotionMark/*",
-                  "*://*/SunSpider/*"],
+                  "*://*/SunSpider/*",
+                  "*://*/webaudio/*"],
       "js": ["benchmark-relay.js"]
     }
   ],
   "permissions": [
     "<all_urls>",
     "tabs",
     "storage",
     "alarms"
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -622724,17 +622724,17 @@
    "b2698d9a829a1eadb3ef3b6d8e0050e7a6315305",
    "testharness"
   ],
   "web-animations/timing-model/animations/setting-the-start-time-of-an-animation.html": [
    "943be76a622af2e611672864cd5db3526e81df45",
    "testharness"
   ],
   "web-animations/timing-model/animations/setting-the-target-effect-of-an-animation.html": [
-   "f4e50b805229e0170bff67530b694ee5d6dd1f1a",
+   "2bad1481d70f0a8925ffdbf2e5e3d228aa641d0a",
    "testharness"
   ],
   "web-animations/timing-model/animations/setting-the-timeline-of-an-animation.html": [
    "e4e134b566327c9d7316aee4f3e7fe4eeb2116ba",
    "testharness"
   ],
   "web-animations/timing-model/animations/the-current-time-of-an-animation.html": [
    "90ba3d81ee9e32b1f13845301c4ad1c8ad47f2f7",
--- a/testing/web-platform/tests/web-animations/timing-model/animations/setting-the-target-effect-of-an-animation.html
+++ b/testing/web-platform/tests/web-animations/timing-model/animations/setting-the-target-effect-of-an-animation.html
@@ -10,31 +10,27 @@
 <script>
 'use strict';
 
 promise_test(t => {
   const anim = createDiv(t).animate({ marginLeft: [ '0px', '100px' ] },
                                     100 * MS_PER_SEC);
   assert_true(anim.pending);
 
-  const retPromise = anim.ready.then(() => {
-    assert_unreached('ready promise is fulfilled');
-  }).catch(err => {
-    assert_equals(err.name, 'AbortError',
-                  'ready promise is rejected with AbortError');
+  const originalReadyPromise = anim.ready.catch(err => {
+    assert_unreached('Original ready promise should not be rejected');
   });
 
   anim.effect = null;
-  // This is a bit odd, see: https://github.com/w3c/web-animations/issues/207
-  assert_equals(anim.playState, 'paused');
-  assert_false(anim.pending);
+  assert_equals(anim.playState, 'finished');
+  assert_true(anim.pending);
 
-  return retPromise;
-}, 'If new effect is null and old effect is not null, we reset the pending ' +
-   'tasks and ready promise is rejected');
+  return originalReadyPromise;
+}, 'If new effect is null and old effect is not null the animation becomes'
+   + ' finish-pending');
 
 promise_test(async t => {
   const anim = new Animation();
   anim.pause();
   assert_true(anim.pending);
 
   anim.effect = new KeyframeEffect(createDiv(t),
                                    { marginLeft: [ '0px', '100px' ] },
@@ -59,16 +55,40 @@ promise_test(async t => {
   await anim.ready;
 
   assert_false(anim.pending);
   assert_equals(anim.playState, 'running');
 }, 'If animation has a pending play task, reschedule that task to run ' +
    'as soon as animation is ready to play new effect.');
 
 promise_test(async t => {
+  const anim = createDiv(t).animate({ marginLeft: [ '0px', '100px' ] },
+                                    100 * MS_PER_SEC);
+  assert_equals(anim.playState, 'running');
+  assert_true(anim.pending);
+
+  const originalEffect = anim.effect;
+  const originalReadyPromise = anim.ready;
+
+  anim.effect = null;
+  assert_equals(anim.playState, 'finished');
+  assert_true(anim.pending);
+
+  anim.effect = originalEffect;
+  assert_equals(anim.playState, 'running');
+  assert_true(anim.pending);
+
+  await originalReadyPromise;
+
+  assert_equals(anim.playState, 'running');
+  assert_false(anim.pending);
+}, 'The pending play task should be rescheduled even after temporarily setting'
+   + ' the effect to null');
+
+promise_test(async t => {
   const animA = createDiv(t).animate({ marginLeft: [ '0px', '100px' ] },
                                      100 * MS_PER_SEC);
   const animB = new Animation();
 
   await animA.ready;
 
   animB.effect = animA.effect;
   assert_equals(animA.effect, null);
@@ -88,20 +108,22 @@ test(t => {
   animB.effect = effect;
   assert_equals(effect.getComputedTiming().progress, 0.2,
                 'After setting the effect on a different animation, ' +
                 'it uses the new animation\'s timing');
 }, 'After setting the target effect of animation to the target effect of an ' +
    'existing animation, the target effect\'s timing is updated to reflect ' +
    'the current time of the new animation.');
 
-test(t => {
+test(async t => {
   const anim = createDiv(t).animate(null, 100 * MS_PER_SEC);
   anim.updatePlaybackRate(2);
   assert_equals(anim.playbackRate, 1);
 
   anim.effect = null;
+  await anim.ready;
+
   assert_equals(anim.playbackRate, 2);
 }, 'Setting the target effect to null causes a pending playback rate to be'
    + ' applied');
 
 </script>
 </body>
--- a/toolkit/components/extensions/test/xpcshell/test_ext_storage_idb_data_migration.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_storage_idb_data_migration.js
@@ -399,16 +399,87 @@ add_task(async function test_storage_loc
       },
     },
   ]);
 
   assertMigrationHistogramCount("success", 0);
   assertMigrationHistogramCount("failure", 1);
 });
 
+// Test that if the data migration fails to store the old data into the IndexedDB backend
+// then the expected telemetry histogram is being updated.
+add_task(async function test_storage_local_data_migration_failure() {
+  const EXTENSION_ID = "extension-data-migration-failure@mozilla.org";
+
+  // Reset the low disk mode, we don't want the quota manager to raise a QuotaExceededError
+  // during this test case.
+  setLowDiskMode(false);
+
+  // Create the file under the expected directory tree.
+  const {
+    jsonFile,
+    oldStorageFilename,
+  } = await createExtensionJSONFileWithData(EXTENSION_ID, {});
+
+  // Store a fake invalid value which is going to fail to be saved into IndexedDB
+  // (because it can't be cloned and it is going to raise a DataCloneError), which
+  // will trigger a data migration failure that we expect to increment the related
+  // telemetry histogram.
+  jsonFile.data.set("fake_invalid_key", new Error());
+
+  async function background() {
+    await browser.storage.local.set({"test_key_string_on_JSONFileBackend": "expected-value"});
+    browser.test.sendMessage("storage-local-data-migrated-and-set");
+  }
+
+  clearMigrationHistogram();
+
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      permissions: ["storage"],
+      applications: {
+        gecko: {
+          id: EXTENSION_ID,
+        },
+      },
+    },
+    background,
+  });
+
+  await extension.startup();
+
+  await extension.awaitMessage("storage-local-data-migrated-and-set");
+
+  const storagePrincipal = ExtensionStorageIDB.getStoragePrincipal(extension.extension);
+
+  const idbConn = await ExtensionStorageIDB.open(storagePrincipal);
+  equal(await idbConn.isEmpty(extension.extension), true,
+        "No data stored in the ExtensionStorageIDB backend as expected");
+  equal(await OS.File.exists(oldStorageFilename), true,
+        "The old json storage should still be available if failed to be read");
+
+  await extension.unload();
+
+  assertTelemetryEvents(EXTENSION_ID, [
+    {
+      method: "migrateResult",
+      extra: {
+        backend: "JSONFile",
+        data_migrated: "n",
+        error_name: "DataCloneError",
+        has_jsonfile: "y",
+        has_olddata: "y",
+      },
+    },
+  ]);
+
+  assertMigrationHistogramCount("success", 0);
+  assertMigrationHistogramCount("failure", 1);
+});
+
 add_task(async function test_storage_local_data_migration_clear_pref() {
   Services.prefs.clearUserPref(LEAVE_STORAGE_PREF);
   Services.prefs.clearUserPref(LEAVE_UUID_PREF);
   Services.prefs.clearUserPref(ExtensionStorageIDB.BACKEND_ENABLED_PREF);
   setLowDiskMode(false);
   await promiseShutdownManager();
   await TelemetryController.testShutdown();
 });
--- a/toolkit/content/browser-child.js
+++ b/toolkit/content/browser-child.js
@@ -210,17 +210,17 @@ var WebProgressListener = {
     this._send("Content:StatusChange", json, objects);
   },
 
   onSecurityChange: function onSecurityChange(aWebProgress, aRequest, aState) {
     let json = this._setupJSON(aWebProgress, aRequest);
     let objects = this._setupObjects(aWebProgress, aRequest);
 
     json.state = aState;
-    json.status = SecurityUI.getSSLStatusAsString();
+    json.secInfo = SecurityUI.getSecInfoAsString();
 
     json.matchedList = null;
     if (aRequest && aRequest instanceof Ci.nsIClassifiedChannel) {
       json.matchedList = aRequest.matchedList;
     }
 
     this._send("Content:SecurityChange", json, objects);
   },
@@ -369,25 +369,27 @@ var WebNavigation =  {
   stop(flags) {
     this.webNavigation.stop(flags);
   }
 };
 
 WebNavigation.init();
 
 var SecurityUI = {
-  getSSLStatusAsString() {
-    let status = docShell.securityUI.QueryInterface(Ci.nsISSLStatusProvider).SSLStatus;
+  getSecInfoAsString() {
+    let secInfo = docShell.securityUI.secInfo;
 
-    if (status) {
-      let helper = Cc["@mozilla.org/network/serialization-helper;1"]
-                      .getService(Ci.nsISerializationHelper);
+    if (secInfo) {
+      if (secInfo) {
+        let helper = Cc["@mozilla.org/network/serialization-helper;1"]
+                        .getService(Ci.nsISerializationHelper);
 
-      status.QueryInterface(Ci.nsISerializable);
-      return helper.serializeToString(status);
+        secInfo.QueryInterface(Ci.nsISerializable);
+        return helper.serializeToString(secInfo);
+      }
     }
 
     return null;
   }
 };
 
 var ControllerCommands = {
   init() {
--- a/toolkit/modules/CertUtils.jsm
+++ b/toolkit/modules/CertUtils.jsm
@@ -138,17 +138,17 @@ function checkCert(aChannel, aAllowNonBu
     // Require https if there are certificate values to verify
     if (aCerts) {
       throw new Ce("SSL is required and URI scheme is not https.",
                    Cr.NS_ERROR_UNEXPECTED);
     }
     return;
   }
 
-  let sslStatus = aChannel.securityInfo.QueryInterface(Ci.nsISSLStatusProvider)
+  let sslStatus = aChannel.securityInfo.QueryInterface(Ci.nsITransportSecurityInfo)
                           .SSLStatus;
   let cert = sslStatus.serverCert;
 
   validateCert(cert, aCerts);
 
   if (aAllowNonBuiltInCerts === true) {
     return;
   }
--- a/toolkit/modules/RemoteSecurityUI.jsm
+++ b/toolkit/modules/RemoteSecurityUI.jsm
@@ -3,27 +3,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/.
 
 var EXPORTED_SYMBOLS = ["RemoteSecurityUI"];
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 function RemoteSecurityUI() {
-    this._SSLStatus = null;
+    this._secInfo = null;
     this._state = 0;
 }
 
 RemoteSecurityUI.prototype = {
-  QueryInterface: ChromeUtils.generateQI([Ci.nsISSLStatusProvider, Ci.nsISecureBrowserUI]),
-
-  // nsISSLStatusProvider
-  get SSLStatus() { return this._SSLStatus; },
+  QueryInterface: ChromeUtils.generateQI([Ci.nsISecureBrowserUI]),
 
   // nsISecureBrowserUI
   get state() { return this._state; },
   get tooltipText() { return ""; },
+  get secInfo() { return this._secInfo; },
 
-  _update(aStatus, aState) {
-    this._SSLStatus = aStatus;
+  _update(aSecInfo, aState) {
+    this._secInfo = aSecInfo;
     this._state = aState;
   }
 };
--- a/toolkit/modules/RemoteWebProgress.jsm
+++ b/toolkit/modules/RemoteWebProgress.jsm
@@ -105,24 +105,24 @@ RemoteWebProgressManager.prototype = {
     });
   },
 
   removeProgressListener(aListener) {
     this._progressListeners =
       this._progressListeners.filter(l => l.listener != aListener);
   },
 
-  _fixSSLStatusAndState(aStatus, aState) {
+  _fixSecInfoAndState(aSecInfo, aState) {
     let deserialized = null;
-    if (aStatus) {
+    if (aSecInfo) {
       let helper = Cc["@mozilla.org/network/serialization-helper;1"]
                     .getService(Ci.nsISerializationHelper);
 
-      deserialized = helper.deserializeObject(aStatus);
-      deserialized.QueryInterface(Ci.nsISSLStatus);
+      deserialized = helper.deserializeObject(aSecInfo);
+      deserialized.QueryInterface(Ci.nsITransportSecurityInfo);
     }
 
     return [deserialized, aState];
   },
 
   setCurrentURI(aURI) {
     // This function is simpler than nsDocShell::SetCurrentURI since
     // it doesn't have to deal with child docshells.
@@ -236,24 +236,24 @@ RemoteWebProgressManager.prototype = {
 
       this._callProgressListeners(
         Ci.nsIWebProgress.NOTIFY_LOCATION, "onLocationChange", webProgress,
         request, location, flags
       );
       break;
 
     case "Content:SecurityChange":
-      let [status, state] = this._fixSSLStatusAndState(json.status, json.state);
+      let [secInfo, state] = this._fixSecInfoAndState(json.secInfo, json.state);
 
       if (isTopLevel) {
         // Invoking this getter triggers the generation of the underlying object,
         // which we need to access with ._securityUI, because .securityUI returns
         // a wrapper that makes _update inaccessible.
         void this._browser.securityUI;
-        this._browser._securityUI._update(status, state);
+        this._browser._securityUI._update(secInfo, state);
       }
 
       this._callProgressListeners(
         Ci.nsIWebProgress.NOTIFY_SECURITY, "onSecurityChange", webProgress,
         request, state
       );
       break;
 
--- a/toolkit/modules/addons/SecurityInfo.jsm
+++ b/toolkit/modules/addons/SecurityInfo.jsm
@@ -89,17 +89,16 @@ const SecurityInfo = {
      */
 
     let securityInfo = channel.securityInfo;
     if (!securityInfo) {
       return info;
     }
 
     securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
-    securityInfo.QueryInterface(Ci.nsISSLStatusProvider);
 
     const SSLStatus = securityInfo.SSLStatus;
     if (NSSErrorsService.isNSSErrorCode(securityInfo.errorCode)) {
       // The connection failed.
       info.state = "broken";
       info.errorMessage = securityInfo.errorMessage;
       if (options.certificateChain && SSLStatus.failedCertChain) {
         info.certificates = this.getCertificateChain(SSLStatus.failedCertChain, options);
--- a/toolkit/modules/tests/chrome/test_bug544442_checkCert.xul
+++ b/toolkit/modules/tests/chrome/test_bug544442_checkCert.xul
@@ -82,17 +82,17 @@ function testXHRLoad(aEvent) {
      "attribute that does not exist on the certificate");
 
   certs = [ { issuerName: "Incorrect issuerName" } ];
   is(getCheckCertResult(channel, false, certs), Cr.NS_ERROR_ILLEGAL_VALUE,
      "checkCert should throw NS_ERROR_ILLEGAL_VALUE when the certificate " +
      "attributes array passed to checkCert has an element that has an " +
      "issuerName that is not the same as the certificate's");
 
-  var cert = channel.securityInfo.QueryInterface(Ci.nsISSLStatusProvider).
+  var cert = channel.securityInfo.QueryInterface(Ci.nsITransportSecurityInfo).
              SSLStatus.QueryInterface(Ci.nsISSLStatus).serverCert;
 
   certs = [ { issuerName: cert.issuerName,
               commonName: cert.commonName } ];
   is(getCheckCertResult(channel, false, certs), Cr.NS_ERROR_ABORT,
      "checkCert should throw NS_ERROR_ABORT when the certificate attributes " +
      "array passed to checkCert has a single element that has the same " +
      "issuerName and commonName as the certificate's and the certificate is " +
--- a/toolkit/mozapps/update/nsUpdateService.js
+++ b/toolkit/mozapps/update/nsUpdateService.js
@@ -3111,17 +3111,17 @@ Checker.prototype = {
   onError: function UC_onError(event) {
     var request = event.target;
     var status = this._getChannelStatus(request);
     LOG("Checker:onError - request.status: " + status);
 
     // Set MitM pref.
     try {
       var sslStatus = request.channel.QueryInterface(Ci.nsIRequest)
-                        .securityInfo.QueryInterface(Ci.nsISSLStatusProvider)
+                        .securityInfo.QueryInterface(Ci.nsITransportSecurityInfo)
                         .SSLStatus.QueryInterface(Ci.nsISSLStatus);
       if (sslStatus && sslStatus.serverCert && sslStatus.serverCert.issuerName) {
         Services.prefs.setStringPref("security.pki.mitm_canary_issuer",
                                      sslStatus.serverCert.issuerName);
       }
     } catch (e) {
       LOG("Checker:onError - Getting sslStatus failed.");
     }
--- a/widget/android/GeckoEditableSupport.cpp
+++ b/widget/android/GeckoEditableSupport.cpp
@@ -1006,17 +1006,18 @@ GeckoEditableSupport::DoReplaceText(int3
 
     RefPtr<TextComposition> composition(GetComposition());
     MOZ_ASSERT(!composition || !composition->IsEditorHandlingEvent());
 
     nsString string(aText->ToString());
     const bool composing = !mIMERanges->IsEmpty();
     nsEventStatus status = nsEventStatus_eIgnore;
 
-    if (!mIMEKeyEvents.IsEmpty() || !mDispatcher->IsComposing() ||
+    if (!mIMEKeyEvents.IsEmpty() ||
+        !composition || !mDispatcher->IsComposing() ||
         uint32_t(aStart) != composition->NativeOffsetOfStartComposition() ||
         uint32_t(aEnd) != composition->NativeOffsetOfStartComposition() +
                           composition->String().Length()) {
         // Only start a new composition if we have key events,
         // if we don't have an existing composition, or
         // the replaced text does not match our composition.
         RemoveComposition();
 
@@ -1168,17 +1169,17 @@ GeckoEditableSupport::OnImeUpdateComposi
      * text content.  Only the offsets are specified and not the text content
      * to eliminate the possibility of this event altering the text content
      * unintentionally.
      */
     nsString string;
     RefPtr<TextComposition> composition(GetComposition());
     MOZ_ASSERT(!composition || !composition->IsEditorHandlingEvent());
 
-    if (!mDispatcher->IsComposing() ||
+    if (!composition || !mDispatcher->IsComposing() ||
         uint32_t(aStart) != composition->NativeOffsetOfStartComposition() ||
         uint32_t(aEnd) != composition->NativeOffsetOfStartComposition() +
                           composition->String().Length()) {
         if (keepCurrent) {
             // Don't start a new composition if we want to keep the current one.
             mIMERanges->Clear();
             return;
         }