Merge m-c to b2ginbound, a=merge CLOSED TREE
authorWes Kocher <wkocher@mozilla.com>
Thu, 12 Nov 2015 16:27:09 -0800
changeset 308637 def46d05f6f53d3f8d0ebff0a89938154fed4a53
parent 308636 59698625bd322a1ed3bf13ad7bb1c00e64fbd253 (current diff)
parent 308606 0c648a1efbe06b5ec866ba058d18256b80808b46 (diff)
child 308638 b3206d7a9689e39a2e2e01ed69b1ce005ade43b3
push id7513
push useratolfsen@mozilla.com
push dateFri, 13 Nov 2015 14:03:43 +0000
reviewersmerge
milestone45.0a1
Merge m-c to b2ginbound, a=merge CLOSED TREE
devtools/client/themes/images/commandline.png
devtools/client/themes/images/newtab-inverted.png
devtools/client/themes/images/newtab-inverted@2x.png
devtools/client/themes/images/newtab.png
devtools/client/themes/images/newtab@2x.png
devtools/client/themes/images/profiler-stopwatch-checked.svg
--- a/browser/base/content/browser-customization.js
+++ b/browser/base/content/browser-customization.js
@@ -55,19 +55,17 @@ var CustomizationHandler = {
     PlacesToolbarHelper.customizeChange();
   },
 
   _customizationEnding: function(aDetails) {
     // Update global UI elements that may have been added or removed
     if (aDetails.changed) {
       gURLBar = document.getElementById("urlbar");
 
-      gProxyFavIcon = document.getElementById("page-proxy-favicon");
       gHomeButton.updateTooltip();
-      gIdentityHandler._cacheElements();
       XULBrowserWindow.init();
 
 #ifndef XP_MACOSX
       updateEditUIVisibility();
 #endif
 
       // Hacky: update the PopupNotifications' object's reference to the iconBox,
       // if it already exists, since it may have changed if the URL bar was
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -365,27 +365,27 @@ var PlacesCommandHook = {
       gURLBar.handleRevert();
 
     // If it was not requested to open directly in "edit" mode, we are done.
     if (!aShowEditUI)
       return;
 
     // Try to dock the panel to:
     // 1. the bookmarks menu button
-    // 2. the page-proxy-favicon
+    // 2. the identity icon
     // 3. the content area
     if (BookmarkingUI.anchor) {
       StarUI.showEditBookmarkPopup(itemId, BookmarkingUI.anchor,
                                    "bottomcenter topright");
       return;
     }
 
-    let pageProxyFavicon = document.getElementById("page-proxy-favicon");
-    if (isElementVisible(pageProxyFavicon)) {
-      StarUI.showEditBookmarkPopup(itemId, pageProxyFavicon,
+    let identityIcon = document.getElementById("identity-icon");
+    if (isElementVisible(identityIcon)) {
+      StarUI.showEditBookmarkPopup(itemId, identityIcon,
                                    "bottomcenter topright");
     } else {
       StarUI.showEditBookmarkPopup(itemId, aBrowser, "overlap");
     }
   }),
 
   // TODO: Replace bookmarkPage code with this function once legacy
   // transactions are removed.
@@ -441,27 +441,27 @@ var PlacesCommandHook = {
     // If it was not requested to open directly in "edit" mode, we are done.
     if (!aShowEditUI)
       return;
 
     let node = yield PlacesUIUtils.promiseNodeLikeFromFetchInfo(info);
 
     // Try to dock the panel to:
     // 1. the bookmarks menu button
-    // 2. the page-proxy-favicon
+    // 2. the identity icon
     // 3. the content area
     if (BookmarkingUI.anchor) {
       StarUI.showEditBookmarkPopup(node, BookmarkingUI.anchor,
                                    "bottomcenter topright");
       return;
     }
 
-    let pageProxyFavicon = document.getElementById("page-proxy-favicon");
-    if (isElementVisible(pageProxyFavicon)) {
-      StarUI.showEditBookmarkPopup(node, pageProxyFavicon,
+    let identityIcon = document.getElementById("identity-icon");
+    if (isElementVisible(identityIcon)) {
+      StarUI.showEditBookmarkPopup(node, identityIcon,
                                    "bottomcenter topright");
     } else {
       StarUI.showEditBookmarkPopup(node, aBrowser, "overlap");
     }
   }),
 
   _getPageDetails(browser) {
     return new Promise(resolve => {
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -90,17 +90,16 @@ Object.defineProperty(window, "pktUIMess
 
 XPCOMUtils.defineLazyGetter(this, "gBrowserBundle", function() {
   return Services.strings.createBundle('chrome://browser/locale/browser.properties');
 });
 
 const nsIWebNavigation = Ci.nsIWebNavigation;
 
 var gLastBrowserCharset = null;
-var gProxyFavIcon = null;
 var gLastValidURLStr = "";
 var gInPrintPreviewMode = false;
 var gContextMenu = null; // nsContextMenu instance
 var gMultiProcessBrowser =
   window.QueryInterface(Ci.nsIInterfaceRequestor)
         .getInterface(Ci.nsIWebNavigation)
         .QueryInterface(Ci.nsILoadContext)
         .useRemoteTabs;
@@ -2557,21 +2556,17 @@ function UpdatePageProxyState()
 }
 
 function SetPageProxyState(aState)
 {
   BookmarkingUI.onPageProxyStateChanged(aState);
   if (!gURLBar)
     return;
 
-  if (!gProxyFavIcon)
-    gProxyFavIcon = document.getElementById("page-proxy-favicon");
-
   gURLBar.setAttribute("pageproxystate", aState);
-  gProxyFavIcon.setAttribute("pageproxystate", aState);
 
   // the page proxy state is set to valid via OnLocationChange, which
   // gets called when we switch tabs.
   if (aState == "valid") {
     gLastValidURLStr = gURLBar.value;
     gURLBar.addEventListener("input", UpdatePageProxyState, false);
   } else if (aState == "invalid") {
     gURLBar.removeEventListener("input", UpdatePageProxyState, false);
@@ -6996,49 +6991,26 @@ var gIdentityHandler = {
     delete this._overrideService;
     return this._overrideService = Cc["@mozilla.org/security/certoverride;1"]
                                      .getService(Ci.nsICertOverrideService);
   },
   get _identityIconCountryLabel () {
     delete this._identityIconCountryLabel;
     return this._identityIconCountryLabel = document.getElementById("identity-icon-country-label");
   },
-  get _identityIcons () {
-    delete this._identityIcons;
-    return this._identityIcons = document.getElementById("identity-icons");
-  },
   get _identityIcon () {
     delete this._identityIcon;
-    return this._identityIcon = document.getElementById("page-proxy-favicon");
+    return this._identityIcon = document.getElementById("identity-icon");
   },
   get _permissionList () {
     delete this._permissionList;
     return this._permissionList = document.getElementById("identity-popup-permission-list");
   },
 
   /**
-   * Rebuild cache of the elements that may or may not exist depending
-   * on whether there's a location bar.
-   */
-  _cacheElements : function() {
-    delete this._identityBox;
-    delete this._identityIcons;
-    delete this._identityIconLabel;
-    delete this._identityIconCountryLabel;
-    delete this._identityIcon;
-    delete this._permissionList;
-    this._identityBox = document.getElementById("identity-box");
-    this._identityIcons = document.getElementById("identity-icons");
-    this._identityIconLabel = document.getElementById("identity-icon-label");
-    this._identityIconCountryLabel = document.getElementById("identity-icon-country-label");
-    this._identityIcon = document.getElementById("page-proxy-favicon");
-    this._permissionList = document.getElementById("identity-popup-permission-list");
-  },
-
-  /**
    * Handler for mouseclicks on the "More Information" button in the
    * "identity-popup" panel.
    */
   handleMoreInfoClick : function(event) {
     displaySecurityInfo();
     event.stopPropagation();
     this._identityPopup.hidePopup();
   },
@@ -7527,17 +7499,17 @@ var gIdentityHandler = {
 
     // Update the popup strings
     this.refreshIdentityPopup();
 
     // Add the "open" attribute to the identity box for styling
     this._identityBox.setAttribute("open", "true");
 
     // Now open the popup, anchored off the primary chrome element
-    this._identityPopup.openPopup(this._identityIcons, "bottomcenter topleft");
+    this._identityPopup.openPopup(this._identityIcon, "bottomcenter topleft");
   },
 
   onPopupShown(event) {
     if (event.target == this._identityPopup) {
       window.addEventListener("focus", this, true);
     }
   },
 
@@ -7570,17 +7542,17 @@ var gIdentityHandler = {
     let urlString = value + "\n" + gBrowser.contentTitle;
     let htmlString = "<a href=\"" + value + "\">" + value + "</a>";
 
     let dt = event.dataTransfer;
     dt.setData("text/x-moz-url", urlString);
     dt.setData("text/uri-list", value);
     dt.setData("text/plain", value);
     dt.setData("text/html", htmlString);
-    dt.setDragImage(gProxyFavIcon, 16, 16);
+    dt.setDragImage(this._identityIcon, 16, 16);
   },
 
   updateSitePermissions: function () {
     while (this._permissionList.hasChildNodes())
       this._permissionList.removeChild(this._permissionList.lastChild);
 
     let uri = gBrowser.currentURI;
 
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -699,17 +699,17 @@
                        aria-label="&urlbar.loginFillNotificationAnchor.label;"/>
                 <image id="password-notification-icon" class="notification-anchor-icon" role="button"
                        aria-label="&urlbar.passwordNotificationAnchor.label;"/>
                 <image id="webapps-notification-icon" class="notification-anchor-icon" role="button"
                        aria-label="&urlbar.webappsNotificationAnchor.label;"/>
                 <image id="plugins-notification-icon" class="notification-anchor-icon" role="button"
                        aria-label="&urlbar.pluginsNotificationAnchor.label;"/>
                 <image id="web-notifications-notification-icon" class="notification-anchor-icon" role="button"
-                       aria-label="&urlbar.webNotsNotificationAnchor2.label;"/>
+                       aria-label="&urlbar.webNotsNotificationAnchor3.label;"/>
                 <image id="webRTC-shareDevices-notification-icon" class="notification-anchor-icon" role="button"
                        aria-label="&urlbar.webRTCShareDevicesNotificationAnchor.label;"/>
                 <image id="webRTC-sharingDevices-notification-icon" class="notification-anchor-icon" role="button"
                        aria-label="&urlbar.webRTCSharingDevicesNotificationAnchor.label;"/>
                 <image id="webRTC-shareMicrophone-notification-icon" class="notification-anchor-icon" role="button"
                        aria-label="&urlbar.webRTCShareMicrophoneNotificationAnchor.label;"/>
                 <image id="webRTC-sharingMicrophone-notification-icon" class="notification-anchor-icon" role="button"
                        aria-label="&urlbar.webRTCSharingMicrophoneNotificationAnchor.label;"/>
@@ -733,23 +733,21 @@
                    We only add the identity-box button to the tab order when the location bar
                    has focus, otherwise pressing F6 focuses it instead of the location bar -->
               <box id="identity-box" role="button"
                    align="center"
                    aria-label="&urlbar.viewSiteInfo.label;"
                    onclick="gIdentityHandler.handleIdentityButtonEvent(event);"
                    onkeypress="gIdentityHandler.handleIdentityButtonEvent(event);"
                    ondragstart="gIdentityHandler.onDragStart(event);">
-                <hbox id="identity-icons"
-                      consumeanchor="identity-box">
-                  <image id="tracking-protection-icon"/>
-                  <image id="page-proxy-favicon"
-                         onclick="PageProxyClickHandler(event);"
-                         pageproxystate="invalid"/>
-                </hbox>
+                <image id="identity-icon"
+                       consumeanchor="identity-box"
+                       onclick="PageProxyClickHandler(event);"/>
+                <image id="tracking-protection-icon"/>
+                <image id="connection-icon"/>
                 <hbox id="identity-icon-labels">
                   <label id="identity-icon-label" class="plain" flex="1"/>
                   <label id="identity-icon-country-label" class="plain"/>
                 </hbox>
               </box>
               <box id="urlbar-display-box" align="center">
                 <label class="urlbar-display urlbar-display-switchtab" value="&urlbar.switchToTab.label;"/>
               </box>
--- a/browser/base/content/test/general/browser_insecureLoginForms.js
+++ b/browser/base/content/test/general/browser_insecureLoginForms.js
@@ -36,26 +36,27 @@ add_task(function* test_simple() {
       waitForInsecureLoginFormsStateChange(browser, 2),
     ]);
 
     let { gIdentityHandler } = gBrowser.ownerGlobal;
     gIdentityHandler._identityBox.click();
     document.getElementById("identity-popup-security-expander").click();
 
     if (expectWarning) {
-      let identityBoxImage = gBrowser.ownerGlobal
-            .getComputedStyle(document.getElementById("page-proxy-favicon"), "")
+      is_element_visible(document.getElementById("connection-icon"));
+      let connectionIconImage = gBrowser.ownerGlobal
+            .getComputedStyle(document.getElementById("connection-icon"), "")
             .getPropertyValue("list-style-image");
       let securityViewBG = gBrowser.ownerGlobal
             .getComputedStyle(document.getElementById("identity-popup-securityView"), "")
             .getPropertyValue("background-image");
       let securityContentBG = gBrowser.ownerGlobal
             .getComputedStyle(document.getElementById("identity-popup-security-content"), "")
             .getPropertyValue("background-image");
-      is(identityBoxImage,
+      is(connectionIconImage,
          "url(\"chrome://browser/skin/identity-mixed-active-loaded.svg\")",
          "Using expected icon image in the identity block");
       is(securityViewBG,
          "url(\"chrome://browser/skin/controlcenter/mcb-disabled.svg\")",
          "Using expected icon image in the Control Center main view");
       is(securityContentBG,
          "url(\"chrome://browser/skin/controlcenter/mcb-disabled.svg\")",
          "Using expected icon image in the Control Center subview");
--- a/browser/base/content/test/general/head.js
+++ b/browser/base/content/test/general/head.js
@@ -776,17 +776,18 @@ function assertMixedContentBlockingState
     throw new Error("assertMixedContentBlockingState requires a browser and a states object");
   }
 
   let {passiveLoaded,activeLoaded,activeBlocked} = states;
   let {gIdentityHandler} = tabbrowser.ownerGlobal;
   let doc = tabbrowser.ownerDocument;
   let identityBox = gIdentityHandler._identityBox;
   let classList = identityBox.classList;
-  let identityBoxImage = tabbrowser.ownerGlobal.getComputedStyle(doc.getElementById("page-proxy-favicon"), "").
+  let connectionIcon = doc.getElementById("connection-icon");
+  let connectionIconImage = tabbrowser.ownerGlobal.getComputedStyle(connectionIcon, "").
                          getPropertyValue("list-style-image");
 
   let stateSecure = gIdentityHandler._state & Ci.nsIWebProgressListener.STATE_IS_SECURE;
   let stateBroken = gIdentityHandler._state & Ci.nsIWebProgressListener.STATE_IS_BROKEN;
   let stateInsecure = gIdentityHandler._state & Ci.nsIWebProgressListener.STATE_IS_INSECURE;
   let stateActiveBlocked = gIdentityHandler._state & Ci.nsIWebProgressListener.STATE_BLOCKED_MIXED_ACTIVE_CONTENT;
   let stateActiveLoaded = gIdentityHandler._state & Ci.nsIWebProgressListener.STATE_LOADED_MIXED_ACTIVE_CONTENT;
   let statePassiveLoaded = gIdentityHandler._state & Ci.nsIWebProgressListener.STATE_LOADED_MIXED_DISPLAY_CONTENT;
@@ -794,47 +795,48 @@ function assertMixedContentBlockingState
   is(activeBlocked, !!stateActiveBlocked, "Expected state for activeBlocked matches UI state");
   is(activeLoaded, !!stateActiveLoaded, "Expected state for activeLoaded matches UI state");
   is(passiveLoaded, !!statePassiveLoaded, "Expected state for passiveLoaded matches UI state");
 
   if (stateInsecure) {
     // HTTP request, there should be no MCB classes for the identity box and the non secure icon
     // should always be visible regardless of MCB state.
     ok(classList.contains("unknownIdentity"), "unknownIdentity on HTTP page");
-    is(identityBoxImage, "url(\"chrome://browser/skin/identity-not-secure.svg\")", "Using 'non-secure' icon");
+    is_element_hidden(connectionIcon);
 
     ok(!classList.contains("mixedActiveContent"), "No MCB icon on HTTP page");
     ok(!classList.contains("mixedActiveBlocked"), "No MCB icon on HTTP page");
     ok(!classList.contains("mixedDisplayContent"), "No MCB icon on HTTP page");
     ok(!classList.contains("mixedDisplayContentLoadedActiveBlocked"), "No MCB icon on HTTP page");
   } else {
     // Make sure the identity box UI has the correct mixedcontent states and icons
     is(classList.contains("mixedActiveContent"), activeLoaded,
         "identityBox has expected class for activeLoaded");
     is(classList.contains("mixedActiveBlocked"), activeBlocked && !passiveLoaded,
         "identityBox has expected class for activeBlocked && !passiveLoaded");
     is(classList.contains("mixedDisplayContent"), passiveLoaded && !(activeLoaded || activeBlocked),
        "identityBox has expected class for passiveLoaded && !(activeLoaded || activeBlocked)");
     is(classList.contains("mixedDisplayContentLoadedActiveBlocked"), passiveLoaded && activeBlocked,
        "identityBox has expected class for passiveLoaded && activeBlocked");
 
+    is_element_visible(connectionIcon);
     if (activeLoaded) {
-      is(identityBoxImage, "url(\"chrome://browser/skin/identity-mixed-active-loaded.svg\")",
+      is(connectionIconImage, "url(\"chrome://browser/skin/identity-mixed-active-loaded.svg\")",
         "Using active loaded icon");
     }
     if (activeBlocked && !passiveLoaded) {
-      is(identityBoxImage, "url(\"chrome://browser/skin/identity-mixed-active-blocked.svg\")",
+      is(connectionIconImage, "url(\"chrome://browser/skin/identity-mixed-active-blocked.svg\")",
         "Using active blocked icon");
     }
     if (passiveLoaded && !(activeLoaded || activeBlocked)) {
-      is(identityBoxImage, "url(\"chrome://browser/skin/identity-mixed-passive-loaded.svg\")",
+      is(connectionIconImage, "url(\"chrome://browser/skin/identity-mixed-passive-loaded.svg\")",
         "Using passive loaded icon");
     }
     if (passiveLoaded && activeBlocked) {
-      is(identityBoxImage, "url(\"chrome://browser/skin/identity-mixed-passive-loaded.svg\")",
+      is(connectionIconImage, "url(\"chrome://browser/skin/identity-mixed-passive-loaded.svg\")",
         "Using active blocked and passive loaded icon");
     }
   }
 
   // Make sure the identity popup has the correct mixedcontent states
   gIdentityHandler._identityBox.click();
   let popupAttr = doc.getElementById("identity-popup").getAttribute("mixedcontent");
   let bodyAttr = doc.getElementById("identity-popup-securityView-body").getAttribute("mixedcontent");
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -196,17 +196,17 @@ These should match what Safari and other
 <!ENTITY urlbar.defaultNotificationAnchor.label         "View a notification">
 <!ENTITY urlbar.geolocationNotificationAnchor.label     "View the location request">
 <!ENTITY urlbar.addonsNotificationAnchor.label          "View the add-on install message">
 <!ENTITY urlbar.indexedDBNotificationAnchor.label       "View the app-offline storage message">
 <!ENTITY urlbar.loginFillNotificationAnchor.label       "Manage your login information">
 <!ENTITY urlbar.passwordNotificationAnchor.label        "Check if you want to save your password">
 <!ENTITY urlbar.webappsNotificationAnchor.label         "View the app install message">
 <!ENTITY urlbar.pluginsNotificationAnchor.label         "Manage plugin usage on this page">
-<!ENTITY urlbar.webNotsNotificationAnchor2.label        "Change whether the site can receive notifications">
+<!ENTITY urlbar.webNotsNotificationAnchor3.label        "Change whether you can receive notifications from the site">
 
 <!ENTITY urlbar.webRTCShareDevicesNotificationAnchor.label      "Manage sharing your camera and/or microphone with the site">
 <!ENTITY urlbar.webRTCSharingDevicesNotificationAnchor.label    "You are sharing your camera and/or microphone with the site">
 <!ENTITY urlbar.webRTCShareMicrophoneNotificationAnchor.label   "Manage sharing your microphone with the site">
 <!ENTITY urlbar.webRTCSharingMicrophoneNotificationAnchor.label "You are sharing your microphone with the site">
 <!ENTITY urlbar.webRTCShareScreenNotificationAnchor.label       "Manage sharing your windows or screen with the site">
 <!ENTITY urlbar.webRTCSharingScreenNotificationAnchor.label     "You are sharing a window or your screen with the site">
 
--- a/browser/themes/shared/identity-block/identity-block.inc.css
+++ b/browser/themes/shared/identity-block/identity-block.inc.css
@@ -70,82 +70,100 @@
   transition-delay: 0s, 100s, 100s;
 }
 
 @conditionalForwardWithUrlbar@:not(:hover) > #forward-button[disabled] + #urlbar > #notification-popup-box[hidden] + #identity-box {
   /* when not hovered anymore, trigger a new non-delayed transition to react to the forward button hiding */
   padding-inline-start: calc(var(--backbutton-urlbar-overlap) + 4.01px);
 }
 
+/* MAIN IDENTITY ICON */
+
+#identity-icon {
+  width: 16px;
+  height: 16px;
+  list-style-image: url(chrome://browser/skin/identity-icon.svg#normal);
+}
+
+#identity-box:hover > #identity-icon,
+#identity-box[open=true] > #identity-icon {
+  list-style-image: url(chrome://browser/skin/identity-icon.svg#hover);
+}
+
+#urlbar[pageproxystate="valid"] > #identity-box.chromeUI > #identity-icon {
+  list-style-image: url(chrome://branding/content/identity-icons-brand.svg);
+}
+
+#urlbar[pageproxystate="invalid"] > #identity-box > #identity-icon {
+  opacity: 0.3;
+}
+
+#urlbar[actiontype="searchengine"] > #identity-box > #identity-icon {
+  -moz-image-region: inherit;
+  list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon);
+  width: 16px;
+  height: 16px;
+  opacity: 1;
+}
+
 /* TRACKING PROTECTION ICON */
 
 #tracking-protection-icon {
   width: 16px;
   height: 16px;
-  margin-inline-start: 0;
-  margin-inline-end: 2px;
+  margin-inline-start: 2px;
+  margin-inline-end: 0;
   list-style-image: url(chrome://browser/skin/tracking-protection-16.svg);
   opacity: 1;
 }
 
 #tracking-protection-icon[state="loaded-tracking-content"] {
   list-style-image: url(chrome://browser/skin/tracking-protection-disabled-16.svg);
 }
 
 #tracking-protection-icon[animate] {
   transition: margin-left 200ms ease-out, margin-right 200ms ease-out;
 }
 
 #tracking-protection-icon:not([state]) {
-  margin-inline-start: -18px;
+  margin-inline-end: -18px;
   pointer-events: none;
   opacity: 0;
   /* Only animate the shield in, when it disappears hide it immediately. */
   transition: none;
 }
 
-#urlbar[pageproxystate="invalid"] > #identity-box > #identity-icons > #tracking-protection-icon {
+#urlbar[pageproxystate="invalid"] > #identity-box > #tracking-protection-icon {
+  visibility: collapse;
+}
+
+/* CONNECTION ICON */
+
+#connection-icon {
+  width: 16px;
+  height: 16px;
+  margin-inline-start: 2px;
   visibility: collapse;
 }
 
-/* MAIN IDENTITY ICON */
-
-#page-proxy-favicon {
-  width: 16px;
-  height: 16px;
-  list-style-image: url(chrome://browser/skin/identity-not-secure.svg);
+#urlbar[pageproxystate="valid"] > #identity-box.verifiedDomain > #connection-icon,
+#urlbar[pageproxystate="valid"] > #identity-box.verifiedIdentity > #connection-icon {
+  list-style-image: url(chrome://browser/skin/identity-secure.svg);
+  visibility: visible;
 }
 
-.chromeUI > #identity-icons > #page-proxy-favicon[pageproxystate="valid"] {
-  list-style-image: url(chrome://branding/content/identity-icons-brand.svg);
-}
-
-.verifiedDomain > #identity-icons > #page-proxy-favicon[pageproxystate="valid"],
-.verifiedIdentity > #identity-icons > #page-proxy-favicon[pageproxystate="valid"] {
-  list-style-image: url(chrome://browser/skin/identity-secure.svg);
-}
-
-.insecureLoginForms > #identity-icons > #page-proxy-favicon[pageproxystate="valid"],
-.mixedActiveContent > #identity-icons > #page-proxy-favicon[pageproxystate="valid"] {
+#urlbar[pageproxystate="valid"] > #identity-box.insecureLoginForms > #connection-icon,
+#urlbar[pageproxystate="valid"] > #identity-box.mixedActiveContent > #connection-icon {
   list-style-image: url(chrome://browser/skin/identity-mixed-active-loaded.svg);
+  visibility: visible;
 }
 
-.weakCipher > #identity-icons > #page-proxy-favicon[pageproxystate="valid"],
-.mixedDisplayContent > #identity-icons > #page-proxy-favicon[pageproxystate="valid"],
-.mixedDisplayContentLoadedActiveBlocked > #identity-icons > #page-proxy-favicon[pageproxystate="valid"] {
+#urlbar[pageproxystate="valid"] > #identity-box.weakCipher > #connection-icon,
+#urlbar[pageproxystate="valid"] > #identity-box.mixedDisplayContent > #connection-icon,
+#urlbar[pageproxystate="valid"] > #identity-box.mixedDisplayContentLoadedActiveBlocked > #connection-icon {
   list-style-image: url(chrome://browser/skin/identity-mixed-passive-loaded.svg);
-}
-
-.mixedActiveBlocked > #identity-icons > #page-proxy-favicon[pageproxystate="valid"] {
-  list-style-image: url(chrome://browser/skin/identity-mixed-active-blocked.svg);
+  visibility: visible;
 }
 
-#page-proxy-favicon[pageproxystate="invalid"] {
-  opacity: 0.3;
+#urlbar[pageproxystate="valid"] > #identity-box.mixedActiveBlocked > #connection-icon {
+  list-style-image: url(chrome://browser/skin/identity-mixed-active-blocked.svg);
+  visibility: visible;
 }
-
-#urlbar[actiontype="searchengine"] > #identity-box > #identity-icons > #page-proxy-favicon {
-  -moz-image-region: inherit;
-  list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon);
-  width: 16px;
-  height: 16px;
-  opacity: 1;
-}
new file mode 100755
--- /dev/null
+++ b/browser/themes/shared/identity-block/identity-icon.svg
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
+     width="48" height="16" viewBox="0 0 32 16">
+  <defs>
+    <circle id="shape-circle-base" cx="8" cy="8" r="7" />
+    <g id="shape-i">
+      <circle cx="8" cy="5" r="1" />
+      <rect x="7" y="7" width="2" height="5" rx="1" ry="1" />
+    </g>
+    <mask id="mask-ring-cutout">
+      <rect width="16" height="16" fill="#000" />
+      <use xlink:href="#shape-circle-base" fill="#fff" />
+      <circle cx="8" cy="8" r="6" fill="#000" />
+    </mask>
+  </defs>
+
+  <view id="normal" viewBox="0 0 16 16"/>
+  <g>
+    <use xlink:href="#shape-circle-base" mask="url(#mask-ring-cutout)" fill="#999" />
+    <use xlink:href="#shape-i" fill="#999" />
+  </g>
+
+  <view id="hover" viewBox="16 0 16 16"/>
+  <g transform="translate(16)">
+    <use xlink:href="#shape-circle-base" fill="#4c9ed9" />
+    <use xlink:href="#shape-i" fill="#fff" />
+  </g>
+</svg>
--- a/browser/themes/shared/jar.inc.mn
+++ b/browser/themes/shared/jar.inc.mn
@@ -54,16 +54,17 @@
   skin/classic/browser/customizableui/whimsy@2x.png            (../shared/customizableui/whimsy@2x.png)
   skin/classic/browser/downloads/contentAreaDownloadsView.css  (../shared/downloads/contentAreaDownloadsView.css)
   skin/classic/browser/drm-icon.svg                            (../shared/drm-icon.svg)
   skin/classic/browser/fullscreen/insecure.svg                 (../shared/fullscreen/insecure.svg)
   skin/classic/browser/fullscreen/secure.svg                   (../shared/fullscreen/secure.svg)
   skin/classic/browser/heartbeat-icon.svg                      (../shared/heartbeat-icon.svg)
   skin/classic/browser/heartbeat-star-lit.svg                  (../shared/heartbeat-star-lit.svg)
   skin/classic/browser/heartbeat-star-off.svg                  (../shared/heartbeat-star-off.svg)
+  skin/classic/browser/identity-icon.svg                       (../shared/identity-block/identity-icon.svg)
   skin/classic/browser/identity-not-secure.svg                 (../shared/identity-block/identity-not-secure.svg)
   skin/classic/browser/identity-secure.svg                     (../shared/identity-block/identity-secure.svg)
   skin/classic/browser/identity-mixed-active-blocked.svg       (../shared/identity-block/identity-mixed-active-blocked.svg)
   skin/classic/browser/identity-mixed-passive-loaded.svg       (../shared/identity-block/identity-mixed-passive-loaded.svg)
   skin/classic/browser/identity-mixed-active-loaded.svg        (../shared/identity-block/identity-mixed-active-loaded.svg)
   skin/classic/browser/info.svg                                (../shared/info.svg)
   skin/classic/browser/tracking-protection-16.svg              (../shared/identity-block/tracking-protection-16.svg)
   skin/classic/browser/tracking-protection-disabled-16.svg     (../shared/identity-block/tracking-protection-disabled-16.svg)
--- a/devtools/client/framework/gDevTools.jsm
+++ b/devtools/client/framework/gDevTools.jsm
@@ -77,18 +77,17 @@ DevTools.prototype = {
    * - visibilityswitch: Property name to allow us to hide this tool from the
    *                     DevTools Toolbox.
    *                     A falsy value indicates that it cannot be hidden.
    * - icon: URL pointing to a graphic which will be used as the src for an
    *         16x16 img tag (string|required)
    * - invertIconForLightTheme: The icon can automatically have an inversion
    *         filter applied (default is false).  All builtin tools are true, but
    *         addons may omit this to prevent unwanted changes to the `icon`
-   *         image. See devtools/client/themes/filters.svg#invert for
-   *         the filter being applied to the images (boolean|optional)
+   *         image. filter: invert(1) is applied to the image (boolean|optional)
    * - url: URL pointing to a XUL/XHTML document containing the user interface
    *        (string|required)
    * - label: Localized name for the tool to be displayed to the user
    *          (string|required)
    * - hideInOptions: Boolean indicating whether or not this tool should be
                       shown in toolbox options or not. Defaults to false.
    *                  (boolean)
    * - build: Function that takes an iframe, which has been populated with the
--- a/devtools/client/jar.mn
+++ b/devtools/client/jar.mn
@@ -157,20 +157,16 @@ devtools.jar:
     skin/images/add.svg (themes/images/add.svg)
     skin/images/filters.svg (themes/images/filters.svg)
     skin/images/filter-swatch.svg (themes/images/filter-swatch.svg)
     skin/images/pseudo-class.svg (themes/images/pseudo-class.svg)
     skin/images/controls.png (themes/images/controls.png)
     skin/images/controls@2x.png (themes/images/controls@2x.png)
     skin/images/animation-fast-track.svg (themes/images/animation-fast-track.svg)
     skin/images/performance-icons.svg (themes/images/performance-icons.svg)
-    skin/images/newtab.png (themes/images/newtab.png)
-    skin/images/newtab@2x.png (themes/images/newtab@2x.png)
-    skin/images/newtab-inverted.png (themes/images/newtab-inverted.png)
-    skin/images/newtab-inverted@2x.png (themes/images/newtab-inverted@2x.png)
 *   skin/widgets.css (themes/widgets.css)
     skin/images/power.svg (themes/images/power.svg)
     skin/images/filetypes/dir-close.svg (themes/images/filetypes/dir-close.svg)
     skin/images/filetypes/dir-open.svg (themes/images/filetypes/dir-open.svg)
     skin/images/filetypes/globe.svg (themes/images/filetypes/globe.svg)
     skin/images/filetypes/store.svg (themes/images/filetypes/store.svg)
     skin/images/commandline-icon.png (themes/images/commandline-icon.png)
     skin/images/commandline-icon@2x.png (themes/images/commandline-icon@2x.png)
@@ -283,17 +279,16 @@ devtools.jar:
     skin/images/dock-bottom@2x.png (themes/images/dock-bottom@2x.png)
     skin/images/dock-bottom-minimize@2x.png (themes/images/dock-bottom-minimize@2x.png)
     skin/images/dock-bottom-maximize@2x.png (themes/images/dock-bottom-maximize@2x.png)
     skin/images/dock-side@2x.png (themes/images/dock-side@2x.png)
 *   skin/floating-scrollbars.css (themes/floating-scrollbars.css)
     skin/floating-scrollbars-light.css (themes/floating-scrollbars-light.css)
 *   skin/inspector.css (themes/inspector.css)
     skin/images/profiler-stopwatch.svg (themes/images/profiler-stopwatch.svg)
-    skin/images/profiler-stopwatch-checked.svg (themes/images/profiler-stopwatch-checked.svg)
     skin/images/tool-options.svg (themes/images/tool-options.svg)
     skin/images/tool-webconsole.svg (themes/images/tool-webconsole.svg)
     skin/images/tool-canvas.svg (themes/images/tool-canvas.svg)
     skin/images/tool-debugger.svg (themes/images/tool-debugger.svg)
     skin/images/tool-debugger-paused.svg (themes/images/tool-debugger-paused.svg)
     skin/images/tool-inspector.svg (themes/images/tool-inspector.svg)
     skin/images/tool-shadereditor.svg (themes/images/tool-shadereditor.svg)
     skin/images/tool-styleeditor.svg (themes/images/tool-styleeditor.svg)
--- a/devtools/client/shared/widgets/spectrum-frame.xhtml
+++ b/devtools/client/shared/widgets/spectrum-frame.xhtml
@@ -14,11 +14,11 @@
     body {
       margin: 0;
       padding: 0;
     }
   </style>
 </head>
 <body role="application">
   <div id="spectrum"></div>
-  <div id="eyedropper-button"></div>
+  <button id="eyedropper-button" class="devtools-button"></button>
 </body>
-</html>
\ No newline at end of file
+</html>
--- a/devtools/client/shared/widgets/spectrum.css
+++ b/devtools/client/shared/widgets/spectrum.css
@@ -1,45 +1,24 @@
 /* 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/. */
 
 #eyedropper-button {
-  background-image: url("chrome://devtools/skin/images/command-eyedropper.png");
-  width: 16px;
-  height: 16px;
-  background-size: 64px 16px;
-  background-position: 0 center;
-  background-repeat: no-repeat;
-  -moz-margin-start: 5px;
-  border-radius: 2px;
-  cursor: pointer;
+  margin-inline-start: 5px;
 }
 
-.theme-light #eyedropper-button {
-  filter: url(chrome://devtools/skin/images/filters.svg#invert);
-  border: 1px solid #AAA;
-}
-
-.theme-dark #eyedropper-button {
-  border: 1px solid #444;
-}
-
-#eyedropper-button:hover {
-  background-position: -16px center;
-}
-#eyedropper-button:hover:active {
-  background-position: -32px center;
-}
-#eyedropper-button[checked=true] {
-  background-position: -48px center;
+#eyedropper-button::before {
+  background-image: url("chrome://devtools/skin/images/command-eyedropper.png");
+  background-size: 64px 16px;
+  background-position: 0 center;
 }
 
 @media (min-resolution: 1.1dppx) {
-  #eyedropper-button {
+  #eyedropper-button::before {
     background-image: url("chrome://devtools/skin/images/command-eyedropper@2x.png");
   }
 }
 
 /* Mix-in classes */
 
 .spectrum-checker {
   background-color: #eee;
--- a/devtools/client/themes/animationinspector.css
+++ b/devtools/client/themes/animationinspector.css
@@ -128,21 +128,16 @@ body {
 .pause-button::before {
   background-image: url("images/debugger-pause.png");
 }
 
 #rewind-timeline::before {
   background-image: url("images/rewind.png");
 }
 
-#element-picker[checked]::before {
-  background-position: -48px 0;
-  filter: none; /* Icon is blue when checked, don't invert for light theme */
-}
-
 .pause-button.paused::before {
   background-image: url("images/debugger-play.png");
 }
 
 @media (min-resolution: 1.1dppx) {
   #element-picker::before {
     background-image: url("chrome://devtools/skin/images/command-pick@2x.png");
     background-size: 64px;
@@ -399,22 +394,22 @@ body {
 .animation-target .node-highlighter {
   background: url("chrome://devtools/skin/images/vview-open-inspector.png") no-repeat 0 0;
   padding-left: 16px;
   margin-right: 5px;
   cursor: pointer;
 }
 
 .animation-target .node-highlighter:hover {
-  background-position: -32px 0;
+  filter: url(images/filters.svg#checked-icon-state);
 }
 
 .animation-target .node-highlighter:active,
 .animation-target .node-highlighter.selected {
-  background-position: -16px 0;
+  filter: url(images/filters.svg#checked-icon-state) brightness(0.9);
 }
 
 /* Animation title gutter, contains the name, duration, iteration */
 
 .animation-title {
   background-color: var(--theme-toolbar-background);
   border-bottom: 1px solid var(--theme-splitter-color);
   padding: 1px 4px;
--- a/devtools/client/themes/canvasdebugger.css
+++ b/devtools/client/themes/canvasdebugger.css
@@ -37,20 +37,16 @@
 #snapshots-pane .devtools-toolbar {
   -moz-border-end: 1px solid var(--theme-splitter-color);
 }
 
 #record-snapshot {
   list-style-image: url("chrome://devtools/skin/images/profiler-stopwatch.svg");
 }
 
-#record-snapshot[checked] {
-  list-style-image: url("chrome://devtools/skin/images/profiler-stopwatch-checked.svg");
-}
-
 /* Snapshots items */
 
 .snapshot-item-thumbnail {
   image-rendering: -moz-crisp-edges;
   background-image: var(--checkerboard-pattern);
   background-size: 12px 12px, 12px 12px;
   background-position: 0px 0px, 6px 6px;
   background-repeat: repeat, repeat;
@@ -98,25 +94,18 @@
 }
 
 #snapshots-list .selected label {
   /* Text inside a selected item should not be custom colored. */
   color: inherit !important;
 }
 
 /* Debugging pane controls */
-
-#debugging-controls .devtools-toolbarbutton > .toolbarbutton-icon {
-  width: 16px;
-  height: 16px;
-}
-
 #resume {
   list-style-image: url(images/debugger-play.png);
-  -moz-image-region: rect(0px,32px,16px,16px);
 }
 
 #step-over {
   list-style-image: url(images/debugger-step-over.png);
 }
 
 #step-in {
   list-style-image: url(images/debugger-step-in.png);
@@ -124,40 +113,31 @@
 
 #step-out {
   list-style-image: url(images/debugger-step-out.png);
 }
 
 @media (min-resolution: 1.1dppx) {
   #resume {
     list-style-image: url(images/debugger-play@2x.png);
-    -moz-image-region: rect(0px,64px,32px,32px);
   }
 
   #step-over {
     list-style-image: url(images/debugger-step-over@2x.png);
   }
 
   #step-in {
     list-style-image: url(images/debugger-step-in@2x.png);
   }
 
   #step-out {
     list-style-image: url(images/debugger-step-out@2x.png);
   }
 }
 
-#debugging-controls > toolbarbutton {
-  transition: opacity 0.15s ease-in-out;
-}
-
-#debugging-controls > toolbarbutton[disabled=true] {
-  opacity: 0.5;
-}
-
 #calls-slider {
   -moz-padding-end: 24px;
 }
 
 #calls-slider .scale-slider {
   margin: 0;
 }
 
--- a/devtools/client/themes/commandline.inc.css
+++ b/devtools/client/themes/commandline.inc.css
@@ -47,17 +47,17 @@
 }
 
 .developer-toolbar-button > image {
   margin: auto 10px;
 }
 
 :root[devtoolstheme="light"] #developer-toolbar > toolbarbutton:not([checked=true]) > image,
 :root[devtoolstheme="light"] .gclitoolbar-input-node:not([focused=true])::before  {
-  filter: url("chrome://devtools/skin/images/filters.svg#invert");
+  filter: invert(1);
 }
 
 .developer-toolbar-button > .toolbarbutton-icon,
 #developer-toolbar-closebutton > .toolbarbutton-icon {
   width: 16px;
   height: 16px;
 }
 
--- a/devtools/client/themes/debugger.css
+++ b/devtools/client/themes/debugger.css
@@ -57,16 +57,20 @@
 
 /* Sources toolbar */
 
 #sources-toolbar > .devtools-toolbarbutton,
 #sources-controls > .devtools-toolbarbutton {
   min-width: 32px;
 }
 
+#sources-toolbar .devtools-toolbarbutton:not([label]) {
+  -moz-image-region: rect(0,16px,16px,0);
+}
+
 #black-box {
   list-style-image: url(images/debugger-blackbox.png);
 }
 
 @media (min-resolution: 1.1dppx) {
   #black-box {
     list-style-image: url(images/debugger-blackbox@2x.png);
   }
@@ -81,44 +85,43 @@
     list-style-image: url(images/debugger-prettyprint@2x.png);
   }
 }
 
 #toggle-breakpoints {
   list-style-image: url(images/debugger-toggleBreakpoints.png);
 }
 
+#toggle-breakpoints[checked] {
+  -moz-image-region: rect(0,32px,16px,16px) !important;
+}
+
+#toggle-breakpoints[checked] > image {
+  /* This button has a special checked image, don't make it blue */
+  filter: none;
+}
+
 @media (min-resolution: 1.1dppx) {
+  #sources-toolbar .devtools-toolbarbutton:not([label]) {
+    -moz-image-region: rect(0,32px,32px,0);
+  }
+
   #toggle-breakpoints {
     list-style-image: url(images/debugger-toggleBreakpoints@2x.png);
   }
+
+  #toggle-breakpoints[checked] {
+    -moz-image-region: rect(0,64px,32px,32px) !important;
+  }
 }
 
 #toggle-promise-debugger {
   /* TODO Bug 1186119: Add a toggle promise debugger image */
 }
 
-#sources-toolbar .devtools-toolbarbutton:not([label]) {
-  -moz-image-region: rect(0px,16px,16px,0px);
-}
-
-#sources-toolbar .devtools-toolbarbutton:not([label])[checked] {
-  -moz-image-region: rect(0px,32px,16px,16px);
-}
-
-@media (min-resolution: 1.1dppx) {
-  #sources-toolbar .devtools-toolbarbutton:not([label]) {
-    -moz-image-region: rect(0px,32px,32px,0px);
-  }
-
-  #sources-toolbar .devtools-toolbarbutton:not([label])[checked] {
-    -moz-image-region: rect(0px,64px,32px,32px);
-  }
-}
-
 #sources .black-boxed {
   color: rgba(128,128,128,0.4);
 }
 
 #sources .selected .black-boxed {
   color: rgba(255,255,255,0.4);
 }
 
@@ -214,31 +217,21 @@
   /* Text inside a selected item should not be custom colored. */
   color: inherit !important;
 }
 
 /* Tracer */
 
 #trace {
   list-style-image: url(images/tracer-icon.png);
-  -moz-image-region: rect(0px,16px,16px,0px);
-}
-
-#trace[checked] {
-  -moz-image-region: rect(0px,32px,16px,16px);
 }
 
 @media (min-resolution: 1.1dppx) {
   #trace {
     list-style-image: url(images/tracer-icon@2x.png);
-    -moz-image-region: rect(0px,32px,32px,0px);
-  }
-
-  #trace[checked] {
-    -moz-image-region: rect(0px,64px,32px,32px);
   }
 }
 
 #clear-tracer {
   /* Make this button as narrow as the text inside it. */
   min-width: 1px;
 }
 
@@ -544,52 +537,34 @@
 }
 
 .theme-light .dbg-results-line-contents-string[match=true] {
   color: var(--theme-body-color);
 }
 
 /* Toolbar controls */
 
-#debugger-toolbar .devtools-toolbarbutton:not([label]) > .toolbarbutton-icon,
-#sources-toolbar .devtools-toolbarbutton:not([label]) > .toolbarbutton-icon {
-  width: 16px;
-  height: 16px;
-}
-
 #resume {
   list-style-image: url(images/debugger-pause.png);
-  -moz-image-region: rect(0px,16px,16px,0px);
 }
 
 #resume[checked] {
   list-style-image: url(images/debugger-play.png);
 }
 
 @media (min-resolution: 1.1dppx) {
   #resume {
     list-style-image: url(images/debugger-pause@2x.png);
-    -moz-image-region: rect(0px,32px,32px,0px);
   }
 
   #resume[checked] {
     list-style-image: url(images/debugger-play@2x.png);
-    -moz-image-region: rect(0px,64px,32px,32px);
   }
 }
 
-#debugger-controls toolbarbutton {
-  transition: opacity 0.15s ease-in-out;
-}
-
-#debugger-controls toolbarbutton[disabled] {
-  opacity: .5;
-  transition: none;
-}
-
 #resume[break-on-next] {
   background: var(--theme-highlight-lightorange);
 }
 
 #step-over {
   list-style-image: url(images/debugger-step-over.png);
 }
 
@@ -611,44 +586,31 @@
   }
 
   #step-out {
     list-style-image: url(images/debugger-step-out@2x.png);
   }
 }
 
 #instruments-pane-toggle {
-  background: none;
-  box-shadow: none;
-  border: none;
   list-style-image: url(images/debugger-collapse.png);
-  -moz-image-region: rect(0px,16px,16px,0px);
 }
 
 #instruments-pane-toggle[pane-collapsed] {
   list-style-image: url(images/debugger-expand.png);
 }
 
-#instruments-pane-toggle:hover {
-  -moz-image-region: rect(0px,32px,16px,16px);
-}
-
 @media (min-resolution: 1.1dppx) {
   #instruments-pane-toggle {
     list-style-image: url(images/debugger-collapse@2x.png);
-    -moz-image-region: rect(0px,32px,32px,0px);
   }
 
   #instruments-pane-toggle[pane-collapsed] {
     list-style-image: url(images/debugger-expand@2x.png);
   }
-
-  #instruments-pane-toggle:hover {
-    -moz-image-region: rect(0px,64px,32px,32px);
-  }
 }
 
 /* Horizontal vs. vertical layout */
 
 #vertical-layout-panes-container {
   min-height: 35vh;
   max-height: 80vh;
 }
deleted file mode 100644
index 6726f282090658b241cf9dde5a566554cd4b3549..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
index 9573fc72d9430189cd685a56d121cbdadb06ed7a..75c9260436248d87df8a630a193a2e33f0542aa4
GIT binary patch
literal 192
zc$@*i06+hUP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0001oNkl<Zc-muN
z7(U?Z_g|;KeE)Tm82t6euQM11fF$0&eanZ<@Yk<j^P(Gq)c_d#`_JFC|NsBzLYF7n
z0FZaSe*eAw$B!RsWE%kW>&4ICeuh$Fz@=~BenyaMz^>1qe`=EHg`bVUK;$Ml5MRCe
u&x0)*K`}zm0HA-*5}lB~{W?F4@(=*;6FEZStfp%K0000<MNUMnLSTZCcvqeP
index 12f7223bcba6404f5fceeeae090537d9dabf11c3..1545db409483cb1c0f43d74fa8805eca7ebdfc1b
GIT binary patch
literal 216
zc%17D@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJ`JOJ0Ar-gYPTR=apdjEHdB8L0
zN~O$k&KRe;2L*N3&S|_h`OZxa*3OMV%0FAbPm&jsiYqE;4-|QN>eZ6ZkwQ!ND{okG
zCYMoZ<r!y>C4YPl>`|G?uygOmYiSOQGxr=hamLa|;mz;TIN9q4jS{z(@TxX)962)S
z0LwYmMs5dhhP^Et(m8Tn_GcXZktpK!`tf1cE`F8|@{{al9yrzvwB!)u^tAcspG=>r
Q2Xrcfr>mdKI;Vst09h?r2LJ#7
index f317997264a8b8b3172ed8d96bebcf4bbe372075..a1957a8df30a62c931a82bd0ac45d80e851b9ac7
GIT binary patch
literal 201
zc$@*r05<=LP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0001xNkl<Zc-muN
z7(U?Z_g|;KeE)Tm82t6euQM11fF$0&eanZ<@Yk<j^P(Gq)c|Dv|NsBFzWw;wjVw>J
z0Y85HQ2YA*_jY74q7C@`?Pn;^j~CGu5^cb@Z$Be|23#WB0I(}Q|I`HXcad!XG!VJJ
z{rKHSk^!$?{pZ0Jji4AIXaLaPXNgWo-+rARMtKMTvMf1~q|hF100000NkvXXu0mjf
DDpy#q
index 091601f5c65085589f02f859b3d10251011b010e..b6892f6c08a32fbef7c89060cd936b39b439b4e0
GIT binary patch
literal 223
zc%17D@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJWu7jMAr-fhC0G|Hh%9K<zhAG*
z{%E$zA?=RB1HMs7i)251eXXy|CUo$6{C=L5&luQ@81Kq_`1LjW-unCX+j%;e>;C;&
z$-AZI`}Ozx{Vtzn<oG$?Fn<64T|7a~`s@l#e{Qe$G^n()Vi0^{V7beZ!S;lE1M>?3
z))K##FX!97=y$mJ1p53x_2TvV_%@a3f3K%ADzfu*vYq6bW%=Ot`KdfWNA)n7Ffa(`
W%|CzMw^b16Vg^rFKbLh*2~7ZduvrHH
index 8288bdfc37719ae15951977b499ee8c35625d93e..5863380b0d373db4f5982ef4fc9a1754d46b9ede
GIT binary patch
literal 108
zc%17D@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`#-1*YAr-fh6C~;$oZdb^Hh%x#
zJAd9Df3H`QWWng6$n#iO+BBg;n!!tFg3IU4((`W^Ffn)(Uo$_-xy=r!ox#)9&t;uc
GLK6UioFbI~
index 53ed141a2cb726197443d51c669bbde60067ae5e..3d38eccd2af098728d1a18ba3755d2ddd5312245
GIT binary patch
literal 151
zc%17D@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJI8PVHkcwMxr)}gsV8Fw&Ihc(v
zrFD(i%^On#zt2qxk$mT%aO88$N^S<FGtd5R{}?4bzyE!m_w!X5E2k|l4&4%<KHHgr
ykww4(Nc4$x$oJaKKO6H`^>WburNs;a8yFZ)N6fmzc(wv)6N9I#pUXO@geCxVjWyK(
index 9189a0a206b1989eb9c6998beead15c3b1ddbf1f..2bde10cb475dbab35236b4a2003a0dcec5e5118b
GIT binary patch
literal 184
zc%17D@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`^`0({Ar-ftUfRvs;vm5GU^m~p
zN6p%Y<XD)QXB%#0Jm_@9gN3`edG7T-gQP7Rg<tX4|2k+BXU@}EHX-OC8$+sX_nKUe
zhp*Yrxp@VrtUdGQEu&G3k-)|K>`(L*svomQq%*9GWtjA(<J)c<rXZ=ATT65emA22m
i`TUop=O<-bW%f9iT@i*d<p+RHVeoYIb6Mw<&;$TPH$$WV
index 5fe68e1d1dc71e9c53cc70391649edf409bebafa..b6b06502e830a8de7e3003d7e6497799784f9180
GIT binary patch
literal 309
zc$@(>0m}Y~P)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F80002|Nkl<Zc-rli
zJxT;Y5QRqs0}(bgF%~1efC?glm~3F?3B*V|fsqH$KnxWJ12I_7GM6xzz^SgkUMO+d
zrNWK2E9nnj{?Yw-)l@2#Wf0|)B-3%%bsHf9K+-0Z??n0npa%g0`tdKY2q52?*(gB3
zgQP8RJ~Okm5CIpF2SnV75b$vR0+K5)X;}de>ob7v#|ZdqOpx`q@B2}JfCorgU_Ol!
z@FW=r3-CpV0Fu59l#e66V3JsGe$2K4N~ll6AbSx*^ob~!X0~2_h)C`MV7vGf1Ip_!
z<-@Yq&`Wl0W}8hZko0|CvPWj#ZAvo`9WhVOXC<ptDl6a*3s0)E%~?-e00000NkvXX
Hu0mjfhERG`
index f2c6c52b9e69ef842671be8b56b5b22d8b25b10f..5cbc7274f071b80e6660f23f3ddd66f1aca7af86
GIT binary patch
literal 195
zc%17D@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`U7jwEAr-goPTI}M<S5X#mARL-
zdjtEy0|(OdHZmUM%W>#&*|6bu!t<@gALONDi)7xkd_OFH_QOFNGwtpf_g7B29K_!c
zp1RE==Reo#a?Pc+5dkNI>>~d%B}fbUC~aHbA(PzHDSxw|oBPUsmnvS)h&Km&oIi8u
tJYD<h`2`{4?CZ?CzZrL#+FZ&qV#*I?lK)zHBOT}}22WQ%mvv4FO#q?2OhNzv
index 4c3cf88fb9a5e78a2562ee91a65e7fb44c3f1d54..345d07cea08f86cd1095c1e5ceb953dc75efddd5
GIT binary patch
literal 323
zc$@)40lfZ+P)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F80003BNkl<Zc-rlj
zJxT;Y5QSG33`E$}#8`~<0;?b*h{*<Ko<NMm6Bu~_H&_f62Lllq&oY-Vn82y7zg}oZ
zajC>)5xbIp@WOt4bU&(kP*ha(n~35;kolx(n$^GLq?BG4Ug{u_Q~~8Jklq34t^<HR
zeE}>C$X9*H$N|8Nq!n;H>cduC01QNK5OLE3z|8RlNY1RJc>&C<GXUN30kGSsk@d1|
z+mQo+8AvK%KJ)@G2bnkn@QDiml0I}4_dTAV4y+gNDXlw{P^Vod+a81H8BtFA;9>qj
zL~;!P8`(!Opxl2@9OvDJR<iRxWG&k|M$*ThlHD@%R`w4`N3_R0KmIORQBl!9>l4A4
Vd`Ig0UFiS-002ovPDHLkV1n=2hMNEY
--- a/devtools/client/themes/images/filters.svg
+++ b/devtools/client/themes/images/filters.svg
@@ -1,20 +1,19 @@
 <!-- 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/. -->
 
 <svg height="0" xmlns="http://www.w3.org/2000/svg">
-  <filter id="invert" x="0%" y="0%" width="100%" height="100%"
-          primitiveUnits="objectBoundingBox">
-    <feComponentTransfer>
-      <feFuncR type="table" tableValues=".1 0"/>
-      <feFuncG type="table" tableValues=".1 0"/>
-      <feFuncB type="table" tableValues=".1 0"/>
-    </feComponentTransfer>
+  <filter id="checked-icon-state">
+    <feColorMatrix in="SourceGraphic" type="matrix"
+      values="0 0 0 0 0.043
+              0 0 0 0 0.415
+              0 0 0 0 0.79
+              0 0 0 1 0"/>
   </filter>
 
   <!-- Web Audio Gradients -->
   <linearGradient id="bypass-light" x1="6%" y1="8%" x2="12%" y2="12%" spreadMethod="repeat">
     <stop offset="0%" stop-color="#f0f1f2"/> <!-- theme-toolbar-background -->
     <stop offset="50%" stop-color="#fff"/>
   </linearGradient>
 
deleted file mode 100644
index 2d29c2cbea688fdf44ba7d1617bd9edd8a04a4c4..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 6feba0e83eaad385ae512fc611187c10dec6e9d4..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 32e42b04cce15cb654b44bc882b264bec5efa963..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index ffde5f05086c366c5873e0d5fa383f45982ae002..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
--- a/devtools/client/themes/images/profiler-stopwatch-checked.svg
+++ /dev/null
@@ -1,17 +0,0 @@
-<!-- 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/. -->
-<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
-  <g fill="#3bace5" fill-rule="evenodd">
-    <path d="m8,1c-3.9,0-7,3.1-7,7s3.1,7 7,7c3.9,0 7-3.1 7-7s-3.1-7-7-7zm-.1,12c-2.8,0-5-2.2-5-5 0-2.8 2.2-5 5-5s5,2.2 5,5c0,2.8-2.2,5-5,5z"/>
-    <path d="m8,6.9c.6,0 1.1,.5 1.1,1.1 0,.6-.5,1.1-1.1,1.1-.6,0-1.1-.5-1.1-1.1 0-.6 .5-1.1 1.1-1.1z"/>
-    <path d="m11.3,4.6l-3.9,2.5 1.5,1.4 2.4-3.9z"/>
-    <path opacity=".4" d="m4.6,10c.7,1.2 2,2 3.4,2 1.5,0 2.7-.8 3.4-2h-6.8z"/>
-    <g opacity=".3">
-      <path d="m7.1,5.1l-.6-1.3-.9,.4 .7,1.3c.2-.1 .5-.3 .8-.4z"/>
-      <path d="m9.8,5.6l.7-1.4-.9-.4-.7,1.3c.3,.2 .6,.3 .9,.5z"/>
-      <path d="m10.8,7c.1,.3 .2,.7 .2,1h2v-1h-2.2z"/>
-      <path d="m5,8c0-.3 .1-.7 .2-1h-2.2l-.1,1h2.1z"/>
-    </g>
-  </g>
-</svg>
--- a/devtools/client/themes/images/pseudo-class.svg
+++ b/devtools/client/themes/images/pseudo-class.svg
@@ -1,29 +1,20 @@
 <!-- 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/. -->
-<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<svg width="16" height="16" viewBox="0 0 16 16" color="#babec3" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
   <defs>
-    <style>
-      use[id^="pseudo-class"]:not(:target) {
-        display: none;
-      }
-    </style>
     <rect id="class-block-maskBG" width="8" height="8" fill="#fff"/>
     <rect id="class-block" width="8" height="8" rx="1" ry="1"/>
     <mask id="mask-block-solid">
       <use xlink:href="#class-block-maskBG"/>
       <use xlink:href="#class-block" transform="translate(3 3)" fill="#000"/>
     </mask>
-    <g id="pseudo-class-shape">
-      <rect x=".5" y=".5" width="7" height="7" rx="1" ry="1" mask="url(#mask-block-solid)" fill="none" stroke="currentColor" stroke-width="1"/>
-      <use xlink:href="#class-block" mask="url(#mask-block-solid)" fill="currentColor" fill-opacity=".4"/>
-      <use xlink:href="#class-block" mask="url(#mask-block-solid)" fill="currentColor" transform="translate(4 4)"/>
-      <g transform="translate(8 8)" fill="currentColor">
-        <path d="M2.5,0C2.2,0,2,0.2,2,0.5C2,0.8,2.2,1,2.5,1C2.8,1,3,0.8,3,0.5 C3,0.2,2.8,0,2.5,0z M4.5,0C4.2,0,4,0.2,4,0.5C4,0.8,4.2,1,4.5,1C4.8,1,5,0.8,5,0.5C5,0.2,4.8,0,4.5,0z M0.5,6C0.8,6,1,5.8,1,5.5 C1,5.2,0.8,5,0.5,5C0.2,5,0,5.2,0,5.5C0,5.8,0.2,6,0.5,6z M0.5,4C0.8,4,1,3.8,1,3.5C1,3.2,0.8,3,0.5,3C0.2,3,0,3.2,0,3.5 C0,3.8,0.2,4,0.5,4z M7.5,2C7.2,2,7,2.2,7,2.5C7,2.8,7.2,3,7.5,3C7.8,3,8,2.8,8,2.5C8,2.2,7.8,2,7.5,2z M7.5,4C7.2,4,7,4.2,7,4.5 C7,4.8,7.2,5,7.5,5C7.8,5,8,4.8,8,4.5C8,4.2,7.8,4,7.5,4z M5.5,7C5.2,7,5,7.2,5,7.5C5,7.8,5.2,8,5.5,8C5.8,8,6,7.8,6,7.5 C6,7.2,5.8,7,5.5,7z M3.5,7C3.2,7,3,7.2,3,7.5C3,7.8,3.2,8,3.5,8C3.8,8,4,7.8,4,7.5C4,7.2,3.8,7,3.5,7z M0.5,2C0.8,2,1,1.8,1,1.5v-1 C1,0.2,0.8,0,0.5,0C0.2,0,0,0.2,0,0.5v1C0,1.8,0.2,2,0.5,2z M8,0.5C8,0.2,7.8,0,7.5,0h-1C6.2,0,6,0.2,6,0.5C6,0.8,6.2,1,6.5,1h1 C7.8,1,8,0.8,8,0.5z M7.5,6C7.2,6,7,6.2,7,6.5v1C7,7.8,7.2,8,7.5,8C7.8,8,8,7.8,8,7.5v-1C8,6.2,7.8,6,7.5,6z M1.5,7h-1 C0.2,7,0,7.2,0,7.5C0,7.8,0.2,8,0.5,8h1C1.8,8,2,7.8,2,7.5C2,7.2,1.8,7,1.5,7z"/>
-        <use xlink:href="#class-block" fill-opacity=".2"/>
-      </g>
-    </g>
   </defs>
-  <use xlink:href="#pseudo-class-shape" id="pseudo-class" color="#babec3"/>
-  <use xlink:href="#pseudo-class-shape" id="pseudo-class-checked" color="#3089C9"/>
+  <rect x=".5" y=".5" width="7" height="7" rx="1" ry="1" mask="url(#mask-block-solid)" fill="none" stroke="currentColor" stroke-width="1"/>
+  <use xlink:href="#class-block" mask="url(#mask-block-solid)" fill="currentColor" fill-opacity=".4"/>
+  <use xlink:href="#class-block" mask="url(#mask-block-solid)" fill="currentColor" transform="translate(4 4)"/>
+  <g transform="translate(8 8)" fill="currentColor">
+    <path d="M2.5,0C2.2,0,2,0.2,2,0.5C2,0.8,2.2,1,2.5,1C2.8,1,3,0.8,3,0.5 C3,0.2,2.8,0,2.5,0z M4.5,0C4.2,0,4,0.2,4,0.5C4,0.8,4.2,1,4.5,1C4.8,1,5,0.8,5,0.5C5,0.2,4.8,0,4.5,0z M0.5,6C0.8,6,1,5.8,1,5.5 C1,5.2,0.8,5,0.5,5C0.2,5,0,5.2,0,5.5C0,5.8,0.2,6,0.5,6z M0.5,4C0.8,4,1,3.8,1,3.5C1,3.2,0.8,3,0.5,3C0.2,3,0,3.2,0,3.5 C0,3.8,0.2,4,0.5,4z M7.5,2C7.2,2,7,2.2,7,2.5C7,2.8,7.2,3,7.5,3C7.8,3,8,2.8,8,2.5C8,2.2,7.8,2,7.5,2z M7.5,4C7.2,4,7,4.2,7,4.5 C7,4.8,7.2,5,7.5,5C7.8,5,8,4.8,8,4.5C8,4.2,7.8,4,7.5,4z M5.5,7C5.2,7,5,7.2,5,7.5C5,7.8,5.2,8,5.5,8C5.8,8,6,7.8,6,7.5 C6,7.2,5.8,7,5.5,7z M3.5,7C3.2,7,3,7.2,3,7.5C3,7.8,3.2,8,3.5,8C3.8,8,4,7.8,4,7.5C4,7.2,3.8,7,3.5,7z M0.5,2C0.8,2,1,1.8,1,1.5v-1 C1,0.2,0.8,0,0.5,0C0.2,0,0,0.2,0,0.5v1C0,1.8,0.2,2,0.5,2z M8,0.5C8,0.2,7.8,0,7.5,0h-1C6.2,0,6,0.2,6,0.5C6,0.8,6.2,1,6.5,1h1 C7.8,1,8,0.8,8,0.5z M7.5,6C7.2,6,7,6.2,7,6.5v1C7,7.8,7.2,8,7.5,8C7.8,8,8,7.8,8,7.5v-1C8,6.2,7.8,6,7.5,6z M1.5,7h-1 C0.2,7,0,7.2,0,7.5C0,7.8,0.2,8,0.5,8h1C1.8,8,2,7.8,2,7.5C2,7.2,1.8,7,1.5,7z"/>
+    <use xlink:href="#class-block" fill-opacity=".2"/>
+  </g>
 </svg>
index 098e256ab8e330887a8765634e056b303ccbd02d..3e8b4db747311a8ac511e3db2cb2dab6a5541ad7
GIT binary patch
literal 203
zc%17D@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`lRRA<Ln?0Fon*_!<jB){T{=Uz
zg5$kHL_~$;?7a;Sc=t3!bUrvZC+U-U<cUdIclC^KziqzW@_nC@=XRx;H!s^xZ4Byj
zxD?L(FYR_u4fB-w)p7!xc{k6SaO!k-D1*yw<&(Buq6`fC7+xPgoXa4&uHjQI+m4PG
z69VOW8zPd@Y}2*gvpHxi-Qs-B;_)PRrHlKbr@QrVG09iPO+F8F9fPN<pUXO@geCw>
CZcoer
index eaac45d9435c7113dd84a12063bcce603d5c1de5..c6fee45f3da3d5d4bdda03779dac7cbcd73337c9
GIT binary patch
literal 330
zc$@)B0k!^#P)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F80003INkl<Zc-rlf
zJxT;Y5QSF+0}(bgF%~1efC?glm~3F?3B*V|fsqH$KnxWJ0};`)%q0vaaO%GoS{7+K
z7!wz^I_VE8eD%KIjVhJOqKPpsi%gGu=Dq(0-_y-oE+U&qc8jE+#P;kVkg}^Nc8;VU
zv+~&i+l!09H-}vyeN4q}0syd%tk<c~K_HKeNKaA1YbtXS=zHBlvb$8`Ch!&G8j4+{
zk+}-=9QKLrDLr--K$Mg8u#kXG3JGLD<Pcb!0s@(?>&7H@i4r_VI0V49QS82eK&EZm
z6<|F>32y}iGDNgNtT$x@a%Kr691&aND=^HCf#k|xU^vnpWPA7((8gIHj}&z}VGakQ
cN~Q7#Ke>N=N2o~Fn*aa+07*qoM6N<$f}fd!TL1t6
--- a/devtools/client/themes/images/timeline-filter.svg
+++ b/devtools/client/themes/images/timeline-filter.svg
@@ -1,26 +1,6 @@
-<?xml version="1.0"?>
 <!-- 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/. -->
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16">
-  <style>
-    use:not(:target) {
-      display: none;
-    }
-    use {
-      fill: #babec3;
-    }
-    use[id$="-disabled"] {
-      fill-opacity: 0.5;
-    }
-    use[id$="-open"] {
-      fill: #3bace5;
-    }
-  </style>
-  <defs>
-    <path id="filter-shape" d="M 2,2 v 3 l 5,4 v 6 h 2 v -6 l 5,-4 v -3 L 14,2 z"/>
-  </defs>
-  <use id="filter" xlink:href="#filter-shape"/>
-  <use id="filter-disabled" xlink:href="#filter-shape"/>
-  <use id="filter-open" xlink:href="#filter-shape"/>
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
+  <path fill="#babec3" d="M2,2v3l5,4v6h2v-6l5,-4v-3L14,2z"/>
 </svg>
index 8229dd7c78f37edcf897d773a9b84bc5be707322..2b5aaf0aa40d8c0d4bd710662dd565dc3420487c
GIT binary patch
literal 290
zc$@(u0p0$IP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0002#Nkl<Zc-qyG
zy$%6E5XS+9MDPZ@h+c#!6-o&~qtIw|bP_Ls7r<j(;o@#*X7?m(wm4SU;uI2-{G0js
z%~$Gw>?+hCMA`ouAV`e`$TLth*IMUaf<9uM6GkS<DNtJjFbaXzp68Wp`oshhiEhmY
zfJZ6|K`nN6Tna(1;$k2*<C@&ZT@iJigK5VNP65cC`%r?~oOHZsU3rViTQ|U{Vf*YA
zGD4_fJfiRW)nqS1xv&oesP?R#OCiY>H!VB?K$|2VV90?1MSF!}fN~ipTo7w!4VUch
oHY5w6rq&I<GmN5!fu(+Bcd8|CN+!v5R{#J207*qoM6N<$f*XW&LI3~&
index de0ded4c8d12508c0cd4846cd794517ebcf20cbf..33c4c7d7f4b5a6836bd35afeb2da842255264ec2
GIT binary patch
literal 469
zc$@*%0V@89P)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F80004;Nkl<Zc-rlj
zPfJxn7{-rPR73<7R9FzLiXb3r6}4{@5cCbA0^GP11Q7uR0Tn^qhzKHzh^imRZ*bA$
zynpAyBLiN}<{r5oJ@<gz2cF%``}}5xcV@DI2D)zLIi3dbO=kPm2*9!GTVgv*X7v#O
zx0?azSmG3bhs^;bt1pFs=RFl*#&9Z9)w#rPMR?)Bww{~4D-BL(MG_~|;2OO{?w`8?
z1hbE6Q0*39E!ld0MQjIk1F+b1Rwj}7oi)w{@s+A3@wKw>pgw;BJn4HEB#XuIqa)mJ
zrVYja0Jv4Dpa|ZO`d<KdD*)`Pwtr+*MdV+IZ95f9AeRAP@hx)yR6ShVKhp|2t4W1>
znFrt=ssCff>q^1ttVsQTC%6Ql9szhj5+_n%-vRPGe@5(!7u;aWtZ|{bZ8qolwG<$*
z&((enYuqEk%Mt+F>c?AfdTQ^x0-QgiyLE;KiDLkUvND<BLlE2OHaP)a<(~lHMUNj6
z9wznW2HLO?fJdxxrWt?&;ts2iHUm&V46(I;v-n1<)q0!_bQ5(3#`4kEp*yYM00000
LNkvXXu0mjfo7Bz>
index db4b0620c4118ace6695af18f663d594170f3b20..6c0ef603c4ff108f36aeba83802311156180bc16
GIT binary patch
literal 136
zc%17D@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6{5@S9Lp07OCoGWPak@TQ<j^kN
zs_F9GU%m=o*KeG1M{IB1%U_&4stFJOU)PVl+H}F-vcc3pM-_RQ!nYo{RKGqeajIGZ
jlcTx77uJIX5&;Yhv&4e!l^f3kSqu!Gu6{1-oD!M<$Q3X`
index ae6fbb21c31c46e94fa7739d3be5a5b64f1ae6b7..d0718a1d657a025137a050d1785b0ef535a907cf
GIT binary patch
literal 168
zc%17D@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4U`JOJ0Ar-gYPTtLXz<|TWc=qvz
zofmx{otvZ_t{Ac)cvgqDf1Uro!qPe^=?OO+bDn(cO#Ssrg!{6a*!>&s4?kXBDl%d7
z4Q-$KY?E#_SG8t}h=~g7Ycs`#xXcm%;Ql~=Vx8MB<%{t%Wz@>%SkHZ5#B8vNvnBjX
R`W&Fu44$rjF6*2UngCykKpp@9
index f575032008eb5a312879954586423c41ecc7a412..b3009981f9b1649d93f1b10de03d6fa4e0a9d74e
GIT binary patch
literal 160
zc%17D@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6(mh=qLp07Gy{O35;2_{|(Q)?O
z&lMc32fHI|W}j5bh?=o~1G_;q&$N#$n-gxu$Ny1M-85_R*9;lIweJcark2iV_;{rC
za{A$t?N*0duKY_~eZxq{X}$6G(@KR4F3&uhD5$3<yH=~{8S|=?knjy(+p>XHGI+ZB
KxvX<aXaWFuF+W=X
index a34c8ede6f8bb66511270fb55caa379d7d5e7944..d0923fd0a477d6155036c1a9aa2197fa5e2e8ee2
GIT binary patch
literal 302
zc$@()0nz@6P)<h;3K|Lk000e1NJLTq001BW001Be1ONa4*>kdg0002>Nkl<Zc-rmG
zu}eZx6vy$q76cJE<>=6mYtj@#OGs1ZSm5N+)KvdQd$a@*tv0l@HbjB&?)N-ji!_Mf
z@<1N);BiF-<&MWe5Z?F9AAZN=t+DTIJ{W{YD{|S9IvI|b6QeIG(BYM9QXENmrsYMt
zIGnWQk}-}%#e)((9P|6+oDug5PMJQA<$Z<wn)M1pk7v?UaH$e|Je7um+Zu_-6I;`)
zH%L7mOI5?0WF8*|6$KCW?7PSNl#M2y?G<ke-U~`9{=3Tschcf%#=B9`=#u*MVQ#qP
zflgE~+IX~o_RyY6o8$j2qmLhXkydzM^71yS36~(=q-pU73IG5A07*qoM6N<$g3myT
ASpWb4
index b68cc5e7dc7b54060db5f48dc879bc16ff3928c4..e9b257e3a06563759153fcb287cf8b81ff909a48
GIT binary patch
literal 177
zc%17D@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6Dm+~rLo|Yqp4!NJC_u#d;&h(_
z93?GBFR9H|h+uf;`B_!q|GqN~5)Eo@8gsRG-RM=F-%-DK8J~*$Ccdmcy&X2n%WMo4
z_5K~Zv#}spdf_zf(|05nxc5Ka^||2%?-jv~@#QxcT1lN~s!01e)%sKtL!~)`K65_v
b>}TA9cPFNOwb&yAbOVE@tDnm{r-UW|Kk!4G
index 5d7640bbe2f04c932eec767ab8d08255af88a9d2..11d86275fee4f21f6041edfb90611f4a53f2b043
GIT binary patch
literal 272
zc$@(c0q_2aP)<h;3K|Lk000e1NJLTq001BW001Be1ONa4*>kdg0002jNkl<ZNXKJf
z7_Q(y^YzH<8?V2<{{Q-m>uatD|7Rpg`(@c1C$IlUf%`9t5v~2E^!3MR+ClL4bz$Nh
z&v@eySi|=lEmyV93*6AV-go^sSY$I%23+-rXuoQU5WD7m{SR2wo(KbOtO6;$R)Z>f
zy%%i2EFuiJ{tTq_ssviFYJv^8PK*J6um8XP5hG-nz~S(b7z6%a|9|5>7O%Yr$<W?_
zYlUFH{=7aP1O5cdq~ec49xMq675>I+z<B|J2K*m2VAKGD2K;BjC=1cyPKHr19034z
W84pFPrznL00000<MNUMnLSTZ1^LUT|
index ee1d7a59e4b2abe40bda3a5ac25071c0d860f9b1..877d195c92cab5ab76c24e36071da46e0acf5d34
GIT binary patch
literal 98
zc%17D@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6G(BA$Lp07OCn)g7{P$ni^!oor
vMN6)Nj~v<uOhmd(r$jo+<_F1L5n*7kxL?|sHMep;P!ofvtDnm{r-UW|y*wO|
index 2b09f01a566a624abcf0aa6b904c61fe0d5f9ce8..207e5376ffa9414425de2ebaec11703e54c25fd0
GIT binary patch
literal 116
zc%17D@N?(olHy`uVBq!ia0vp^3Lwk@BpAX3RW*PVQ%R6tFatx`<g*q)p01~hV@SoV
z<b(xAJL+m5h{POcU|!51-Y>^xFt=6BDd1qjhocFdXAe$T#V*3YAba9EW8f#obs*zB
MUHx3vIVCg!0PXZ35&!@I
--- a/devtools/client/themes/inspector.css
+++ b/devtools/client/themes/inspector.css
@@ -39,49 +39,31 @@
 #inspector-searchbox[focused],
 #inspector-searchbox[filled] {
   max-width: 200px !important;
 }
 
 /* Expand/collapse panel toolbar button */
 
 #inspector-pane-toggle {
-  background: none;
-  box-shadow: none;
-  border: none;
   list-style-image: url(images/debugger-collapse.png);
-  -moz-image-region: rect(0px,16px,16px,0px);
-}
-
-#inspector-pane-toggle > .toolbarbutton-icon {
-  width: 16px;
-  height: 16px;
 }
 
 #inspector-pane-toggle[pane-collapsed] {
   list-style-image: url(images/debugger-expand.png);
 }
 
-#inspector-pane-toggle:active {
-  -moz-image-region: rect(0px,32px,16px,16px);
-}
-
 @media (min-resolution: 1.1dppx) {
   #inspector-pane-toggle {
     list-style-image: url(images/debugger-collapse@2x.png);
-    -moz-image-region: rect(0px,32px,32px,0px);
   }
 
   #inspector-pane-toggle[pane-collapsed] {
     list-style-image: url(images/debugger-expand@2x.png);
   }
-
-  #inspector-pane-toggle:active {
-    -moz-image-region: rect(0px,64px,32px,32px);
-  }
 }
 
 /* Tooltip: Events */
 
 #devtools-tooltip-events-container {
   margin: -4px; /* Compensate for the .panel-arrowcontent padding. */
   max-width: 590px;
   overflow-y: auto;
--- a/devtools/client/themes/memory.css
+++ b/devtools/client/themes/memory.css
@@ -110,28 +110,16 @@ html, body, #app, #memory-tool {
 }
 
 #filter {
   align-self: stretch;
   margin: 2px;
 }
 
 /**
- * TODO bug 1213100
- * Once we figure out how to store invertable buttons (pseudo element like in
- * this case?) we should add a .invertable class to handle this generally,
- * rather than the definitions in toolbars.css.
- *
- * @see bug 1173397 for another inverted related bug
- */
-.theme-light .devtools-toolbar > .devtools-toolbarbutton.take-snapshot::before {
-  filter: url(images/filters.svg#invert);
-}
-
-/**
  * Container (sidebar + main panel)
  */
 
 #memory-tool-container {
   /**
    * Flex: contains two children: .list (sidebar) and #heap-view (main panel),
    * which need to be laid out horizontally. The sidebar has a fixed width and
    * the main panel needs to flex to fill out all remaining horizontal space.
--- a/devtools/client/themes/netmonitor.css
+++ b/devtools/client/themes/netmonitor.css
@@ -199,17 +199,17 @@ label.requests-menu-status-code {
 
 box.requests-menu-status:not([code]) {
   background-color: var(--theme-highlight-red);
   border-radius: 0; /* squares */
 }
 
 box.requests-menu-status[code="cached"] {
   border: 2px solid var(--theme-content-color2);
-  background-color: transparent;  
+  background-color: transparent;
 }
 
 box.requests-menu-status[code^="1"] {
   background-color: var(---theme-highlight-blue);
 }
 
 box.requests-menu-status[code^="2"] {
   background-color: var(--theme-highlight-green);
@@ -394,49 +394,31 @@ box.requests-menu-status[code^="5"] {
 
 .side-menu-widget-item:not(.selected):hover {
   background-color: var(--theme-selection-background-semitransparent);
 }
 
 /* Network request details */
 
 #details-pane-toggle {
-  background: none;
-  box-shadow: none;
-  border-color: transparent;
   list-style-image: url("chrome://devtools/skin/images/debugger-collapse.png");
-  -moz-image-region: rect(0px,16px,16px,0px);
-}
-
-#details-pane-toggle > .toolbarbutton-icon {
-  width: 16px;
-  height: 16px;
 }
 
 #details-pane-toggle[pane-collapsed] {
   list-style-image: url("chrome://devtools/skin/images/debugger-expand.png");
 }
 
-#details-pane-toggle:active {
-  -moz-image-region: rect(0px,32px,16px,16px);
-}
-
 @media (min-resolution: 1.1dppx) {
   #details-pane-toggle {
     list-style-image: url("chrome://devtools/skin/images/debugger-collapse@2x.png");
-    -moz-image-region: rect(0px,32px,32px,0px);
   }
 
   #details-pane-toggle[pane-collapsed] {
     list-style-image: url("chrome://devtools/skin/images/debugger-expand@2x.png");
   }
-
-  #details-pane-toggle:active {
-    -moz-image-region: rect(0px,64px,32px,32px);
-  }
 }
 
 /* Network request details tabpanels */
 
 .tabpanel-content {
   background-color: var(--theme-sidebar-background);
 }
 
--- a/devtools/client/themes/performance.css
+++ b/devtools/client/themes/performance.css
@@ -37,26 +37,17 @@
 }
 
 #performance-toolbar-controls-detail-views .toolbarbutton-text {
   -moz-padding-start: 4px;
   -moz-padding-end: 8px;
 }
 
 #filter-button {
-  list-style-image: url(images/timeline-filter.svg#filter);
-  min-width: 24px;
-}
-
-#filter-button[disabled] {
-  list-style-image: url(images/timeline-filter.svg#filter-disabled);
-}
-
-#filter-button[open] {
-  list-style-image: url(images/timeline-filter.svg#filter-open);
+  list-style-image: url(images/timeline-filter.svg);
 }
 
 #performance-filter-menupopup > menuitem:before {
   content: "";
   display: block;
   width: 8px;
   height: 8px;
   margin: 0 8px;
@@ -84,20 +75,16 @@
 }
 
 /* Recording buttons */
 
 #main-record-button {
   list-style-image: url(images/profiler-stopwatch.svg);
 }
 
-#main-record-button[checked] {
-  list-style-image: url(images/profiler-stopwatch-checked.svg);
-}
-
 #main-record-button .button-icon {
   margin: 0;
 }
 
 #main-record-button .button-text {
   display: none;
 }
 
--- a/devtools/client/themes/ruleview.css
+++ b/devtools/client/themes/ruleview.css
@@ -279,53 +279,32 @@
 .ruleview-selectorhighlighter {
   background: url("chrome://devtools/skin/images/vview-open-inspector.png") no-repeat 0 0;
   padding-left: 16px;
   margin-left: 5px;
   cursor: pointer;
 }
 
 .ruleview-selectorhighlighter:hover {
-  background-position: -32px 0;
+  filter: url(images/filters.svg#checked-icon-state);
 }
 
 .ruleview-selectorhighlighter:active,
 .ruleview-selectorhighlighter.highlighted {
-  background-position: -16px 0;
+  filter: url(images/filters.svg#checked-icon-state) brightness(0.9);
 }
 
 #ruleview-add-rule-button::before {
   background-image: url("chrome://devtools/skin/images/add.svg");
   background-size: cover;
 }
 
 #pseudo-class-panel-toggle::before {
-  background-image: url("chrome://devtools/skin/images/pseudo-class.svg#pseudo-class");
+  background-image: url("chrome://devtools/skin/images/pseudo-class.svg");
   background-size: cover;
 }
-#pseudo-class-panel-toggle[checked]::before {
-  background-image: url("chrome://devtools/skin/images/pseudo-class.svg#pseudo-class-checked");
-  filter: none !important;
-}
 
-/**
- * These buttons are using opacity instead of background color to indicate
- * the state
- */
-#ruleview-add-rule-button,
-#pseudo-class-panel-toggle,
 .ruleview-overridden-rule-filter {
   opacity: 0.8;
 }
-
-#ruleview-add-rule-button:not([disabled]):hover,
-#pseudo-class-panel-toggle:hover,
-#pseudo-class-panel-toggle[checked],
 .ruleview-overridden-rule-filter:hover {
   opacity: 1;
 }
-
-#ruleview-add-rule-button,
-#pseudo-class-panel-toggle,
-#pseudo-class-panel-toggle:hover,
-#pseudo-class-panel-toggle[checked]::before {
-  background-color: transparent !important;
-}
--- a/devtools/client/themes/shadereditor.css
+++ b/devtools/client/themes/shadereditor.css
@@ -67,17 +67,17 @@
 }
 
 .side-menu-widget-item-checkbox[checked] .checkbox-check {
   background-position: 0 0;
 }
 
 /* Invert all toggle icons but the one in the active row for light theme */
 .theme-light .side-menu-widget-item:not(.selected) .checkbox-check {
-  filter: url(images/filters.svg#invert);
+  filter: invert(1);
 }
 
 /* Shader source editors */
 
 .editor-label {
   padding: 1px 12px;
   border-top: 1px solid;
 }
--- a/devtools/client/themes/styleeditor.css
+++ b/devtools/client/themes/styleeditor.css
@@ -115,17 +115,17 @@
 }
 
 .disabled > .stylesheet-enabled {
   background-position: -24px 8px;
 }
 
 /* Invert all toggle icons but the one in the active row for light theme */
 .theme-light .splitview-nav > li:not(.splitview-active) .stylesheet-enabled {
-  filter: url(images/filters.svg#invert);
+  filter: invert(1);
 }
 
 .splitview-nav > li > .stylesheet-enabled:focus,
 .splitview-nav > li:hover > .stylesheet-enabled {
   outline: 0;
 }
 
 .stylesheet-linked-file:not(:empty){
--- a/devtools/client/themes/toolbars.css
+++ b/devtools/client/themes/toolbars.css
@@ -49,81 +49,128 @@
   border: none !important; /* overrides .checkbox-label-box from checkbox.css */
 }
 .devtools-toolbar checkbox .checkbox-label-box .checkbox-label {
   margin: 0 6px !important; /* overrides .checkbox-label from checkbox.css */
   padding: 0;
 }
 
 /* Toolbar buttons */
+
 .devtools-menulist,
-.devtools-toolbarbutton {
+.devtools-toolbarbutton,
+.devtools-button {
   -moz-appearance: none;
-  -moz-box-align: center;
   background: transparent;
-  min-width: 78px;
   min-height: 18px;
-  padding: 1px;
   text-shadow: none;
   border: none;
   border-radius: 0;
-  margin: 2px 3px;
-  color: inherit;
+  color: var(--theme-body-color);
   transition: background 0.05s ease-in-out;
-  color: var(--theme-content-color1);
-  background-color: var(--theme-toolbar-background);
+}
+
+.devtools-menulist,
+.devtools-toolbarbutton {
+  -moz-box-align: center;
+  min-width: 78px;
+  padding: 1px;
+  margin: 2px 3px;
 }
 
 .devtools-menulist:-moz-focusring,
 .devtools-toolbarbutton:-moz-focusring {
   outline: 1px dotted hsla(210,30%,85%,0.7);
   outline-offset: -4px;
 }
 
+.devtools-toolbarbutton:not([label]) > .toolbarbutton-icon,
+.devtools-button::before {
+  width: 16px;
+  height: 16px;
+  transition: opacity 0.05s ease-in-out;
+}
+
+/* HTML buttons */
+.devtools-button {
+  margin: 0;
+  padding: 0;
+  min-width: 32px;
+  /* The icon is absolutely positioned in the button using ::before */
+  position: relative;
+}
+
+.devtools-button::before {
+  content: "";
+  display: block;
+  position: absolute;
+  left: 50%;
+  top: 50%;
+  margin: -8px 0 0 -8px;
+  background-size: cover;
+  background-repeat: no-repeat;
+  transition: opacity 0.05s ease-in-out;
+}
+
+/* Standalone buttons */
+.devtools-button[standalone],
+.devtools-button[data-standalone],
+.devtools-toolbarbutton[standalone],
+.devtools-toolbarbutton[data-standalone] {
+  border-width: 1px;
+  border-style: solid;
+  min-height: 32px;
+  background-color: var(--theme-toolbar-background);
+}
+
 .devtools-toolbarbutton[standalone], .devtools-toolbarbutton[data-standalone] {
   -moz-margin-end: 5px;
-  border-width: 1px;
-  border-style: solid;
 }
+
 .devtools-toolbarbutton[label][standalone] {
   min-height: 2em;
 }
 
+.theme-dark .devtools-menulist,
+.theme-dark .devtools-toolbarbutton,
+.theme-dark .devtools-button {
+  border-color: rgba(0, 0, 0, .4); /* Splitters */
+}
+
+.theme-light .devtools-menulist,
+.theme-light .devtools-toolbarbutton,
+.theme-light .devtools-button {
+  border-color: rgba(170, 170, 170, .5); /* Splitters */
+}
+
+/* Icon button styles */
 .devtools-toolbarbutton:not([label]),
 .devtools-toolbarbutton[text-as-image] {
   min-width: 32px;
 }
 
 #toolbox-buttons .devtools-toolbarbutton[text-as-image] {
   -moz-padding-start: 5px;
   -moz-padding-end: 5px;
   min-width: inherit;
 }
 
-/* Command buttons with menupopups should be styled slightly differently -
-   no background color and a bit more narrow */
-#toolbox-buttons .devtools-toolbarbutton:not([text-as-image]):not(:hover):not([open=true]) {
-  background: transparent;
-}
 #toolbox-buttons .devtools-toolbarbutton[type=menu] > .toolbarbutton-menu-dropmarker {
   padding: 0 2px;
 }
 
 .devtools-toolbarbutton:not([label]) > .toolbarbutton-text {
   display: none;
 }
 
-.devtools-toolbar .devtools-toolbarbutton {
-  border-width: 0;
-}
-
 .devtools-toolbarbutton > .toolbarbutton-icon {
   margin: 0;
 }
 
+/* Menu button styles (eg. web console filters) */
 .devtools-toolbarbutton[type=menu-button] > .toolbarbutton-menubutton-button {
   -moz-appearance: none;
   color: inherit;
   border-width: 0;
   -moz-box-orient: horizontal;
   padding: 0;
 }
 
@@ -147,99 +194,95 @@
 .devtools-toolbarbutton[type=menu] > .toolbarbutton-menu-dropmarker,
 .devtools-toolbarbutton[type=menu-button] > .toolbarbutton-menubutton-dropmarker {
   -moz-appearance: none !important;
   list-style-image: url("chrome://devtools/skin/images/dropmarker.svg");
   -moz-box-align: center;
   padding: 0 3px;
 }
 
-.theme-dark .devtools-menulist,
-.theme-dark .devtools-toolbarbutton {
-  border-color: rgba(0, 0, 0, .4); /* Splitters */
+/* Icon-only buttons */
+.devtools-button:empty::before,
+.devtools-toolbarbutton:not([label]):not([disabled]) > image {
+  opacity: 0.8;
 }
-.theme-light .devtools-menulist,
-.theme-light .devtools-toolbarbutton {
-  border-color: rgba(170, 170, 170, .5); /* Splitters */
+
+.devtools-button:hover:empty::before,
+.devtools-button[checked]:empty::before,
+.devtools-button[open]:empty::before,
+.devtools-toolbarbutton:not([label]):hover > image,
+.devtools-toolbarbutton:not([label])[checked=true] > image,
+.devtools-toolbarbutton:not([label])[open=true] > image {
+  opacity: 1;
+}
+
+.devtools-button:disabled,
+.devtools-button[disabled],
+.devtools-toolbarbutton[disabled] {
+  opacity: 0.5 !important;
+}
+
+.devtools-button[checked]:empty::before,
+.devtools-button[open]:empty::before,
+.devtools-toolbarbutton:not([label])[checked=true] > image,
+.devtools-toolbarbutton:not([label])[open=true] > image {
+  filter: url(images/filters.svg#checked-icon-state);
 }
 
 /* Text-only buttons */
 .theme-light .devtools-toolbarbutton[label]:not([text-as-image]):not([type=menu-button]),
 .theme-light .devtools-toolbarbutton[data-text-only],
 .theme-light #toolbox-buttons .devtools-toolbarbutton[text-as-image] {
   background-color: rgba(170, 170, 170, .2); /* Splitter */
 }
 .theme-dark .devtools-toolbarbutton[label]:not([text-as-image]):not([type=menu-button]),
 .theme-dark .devtools-toolbarbutton[data-text-only],
 .theme-dark #toolbox-buttons .devtools-toolbarbutton[text-as-image] {
   background-color: rgba(0, 0, 0, .2); /* Splitter */
 }
 
-/* Button States */
-.theme-dark .devtools-toolbarbutton:not([disabled]):hover,
+/* Text-only button states */
+.theme-dark .devtools-button:not(:empty):not([disabled]):hover,
 .theme-dark #toolbox-buttons .devtools-toolbarbutton:not([disabled])[text-as-image]:hover,
-.theme-dark .devtools-toolbarbutton:not([disabled])[label]:not([text-as-image]):not([type=menu-button]):hover {
+.theme-dark .devtools-toolbarbutton:not(:-moz-any([checked=true],[disabled],[text-as-image]))[label]:hover {
   background: rgba(0, 0, 0, .3); /* Splitters */
 }
-.theme-light .devtools-toolbarbutton:not([disabled]):hover,
+.theme-light .devtools-button:not(:empty):not([disabled]):hover,
 .theme-light #toolbox-buttons .devtools-toolbarbutton:not([disabled])[text-as-image]:hover,
-.theme-light .devtools-toolbarbutton:not([disabled])[label]:not([text-as-image]):not([type=menu-button]):hover {
+.theme-light .devtools-toolbarbutton:not(:-moz-any([checked=true],[disabled],[text-as-image]))[label]:hover {
   background: rgba(170, 170, 170, .3); /* Splitters */
 }
 
-.theme-dark .devtools-toolbarbutton:not([disabled]):hover:active,
+.theme-dark .devtools-button:not(:empty):not([disabled]):hover:active,
 .theme-dark #toolbox-buttons .devtools-toolbarbutton:not([disabled])[text-as-image]:hover:active,
-.theme-dark .devtools-toolbarbutton:not([disabled])[label]:not([text-as-image]):not([type=menu-button]):hover:active {
+.theme-dark .devtools-toolbarbutton:not(:-moz-any([checked=true],[disabled],[text-as-image]))[label]:hover:active {
   background: rgba(0, 0, 0, .4); /* Splitters */
 }
-.theme-light .devtools-toolbarbutton:not([disabled]):hover:active,
+.theme-light .devtools-button:not(:empty):not([disabled]):hover:active,
 .theme-light #toolbox-buttons .devtools-toolbarbutton:not([disabled])[text-as-image]:hover:active,
-.theme-light .devtools-toolbarbutton:not([disabled])[label]:not([text-as-image]):not([type=menu-button]):hover:active {
+.theme-light .devtools-toolbarbutton:not(:-moz-any([checked=true],[disabled],[text-as-image]))[label]:hover:active {
   background: rgba(170, 170, 170, .4); /* Splitters */
 }
 
-/* Menu type buttons and checked states */
-.theme-dark .devtools-toolbarbutton[checked=true],
-.theme-dark #toolbox-buttons .devtools-toolbarbutton[text-as-image][checked] {
+.theme-dark .devtools-toolbarbutton:not([disabled])[label][checked=true],
+.theme-dark .devtools-toolbarbutton:not([disabled])[label][open],
+.theme-dark .devtools-button:not(:empty)[checked=true],
+.theme-dark #toolbox-buttons .devtools-toolbarbutton[text-as-image][checked=true] {
   background: rgba(29, 79, 115, .7); /* Select highlight blue */
   color: var(--theme-selection-color);
 }
-
-.theme-light .devtools-toolbarbutton[checked=true],
-.theme-light #toolbox-buttons .devtools-toolbarbutton[text-as-image][checked] {
-  background: rgba(76, 158, 217, .2); /* Select highlight blue */
-}
-
-.theme-dark .devtools-menulist[open=true],
-.theme-dark .devtools-toolbarbutton[open=true],
-.theme-dark .devtools-toolbarbutton[open=true]:hover,
-.theme-dark .devtools-toolbarbutton[open=true]:hover:active,
-.theme-dark .devtools-toolbarbutton[checked=true]:hover {
-  background: rgba(29, 79, 115, .8); /* Select highlight blue */
-  color: var(--theme-selection-color);
-}
-
-.theme-light .devtools-menulist[open=true],
-.theme-light .devtools-toolbarbutton[open=true],
-.theme-light .devtools-toolbarbutton[open=true]:hover,
-.theme-light .devtools-toolbarbutton[open=true]:hover:active,
-.theme-light .devtools-toolbarbutton[checked=true]:hover {
-  background: rgba(76, 158, 217, .4); /* Select highlight blue */
+.theme-light .devtools-toolbarbutton:not([disabled])[label][checked=true],
+.theme-light .devtools-toolbarbutton:not([disabled])[label][open],
+.theme-light .devtools-button:not(:empty)[checked=true],
+.theme-light #toolbox-buttons .devtools-toolbarbutton[text-as-image][checked=true] {
+  background: rgba(76, 158, 217, .3); /* Select highlight blue */
 }
 
 .devtools-option-toolbarbutton {
-  -moz-appearance: none;
   list-style-image: url("chrome://devtools/skin/images/tool-options.svg");
-  background: none;
-  opacity: .8;
-  border: none;
-}
-
-.devtools-option-toolbarbutton[open=true] {
-  opacity: 1;
 }
 
 /* Toolbar button groups */
 .devtools-toolbarbutton-group > .devtools-toolbarbutton {
   margin-left: 1px;
   margin-right: 1px;
   outline-offset: -3px;
   box-shadow: none;
@@ -252,83 +295,16 @@
 .devtools-toolbarbutton-group + .devtools-toolbarbutton {
   -moz-margin-start: 3px;
 }
 
 .devtools-separator + .devtools-toolbarbutton {
   -moz-margin-start: 1px;
 }
 
-/* HTML buttons, similar to toolbar buttons, but work in HTML documents */
-
-.devtools-button {
-  border: 0 solid var(--theme-splitter-color);
-  background: var(--theme-toolbar-background);
-  color: var(--theme-body-color);
-  margin: 0;
-  padding: 0;
-  min-width: 32px;
-  min-height: 18px;
-  /* The icon is absolutely positioned in the button using ::before */
-  position: relative;
-}
-
-.devtools-button[standalone], .devtools-button[data-standalone] {
-  min-height: 32px;
-  border-width: 1px;
-}
-
-/* Button States */
-.theme-dark .devtools-button:not([disabled]):hover {
-  background: rgba(0, 0, 0, .3); /* Splitters */
-}
-.theme-light .devtools-button:not([disabled]):hover {
-  background: rgba(170, 170, 170, .3); /* Splitters */
-}
-
-.theme-dark .devtools-button:not([disabled]):hover:active {
-  background: rgba(0, 0, 0, .4); /* Splitters */
-}
-.theme-light .devtools-button:not([disabled]):hover:active {
-  background: rgba(170, 170, 170, .4); /* Splitters */
-}
-
-/* Menu type buttons and checked states */
-.theme-dark .devtools-button[checked] {
-  background: rgba(29, 79, 115, .7) !important; /* Select highlight blue */
-  color: var(--theme-selection-color);
-}
-
-.theme-light .devtools-button[checked] {
-  background: rgba(76, 158, 217, .2) !important; /* Select highlight blue */
-}
-
-.devtools-button::before {
-  content: "";
-  display: block;
-  width: 16px;
-  height: 16px;
-  position: absolute;
-  left: 50%;
-  top: 50%;
-  margin: -8px 0 0 -8px;
-  background-repeat: no-repeat;
-}
-
-.devtools-button[disabled]::before,
-.devtools-button:disabled::before {
-  opacity: 0.5;
-}
-
-@media (min-resolution: 1.1dppx) {
-  .devtools-button::before {
-    background-size: 32px;
-  }
-}
-
 /* Text input */
 
 .devtools-textinput,
 .devtools-searchinput {
   -moz-appearance: none;
   margin: 1px 3px;
   border: 1px solid;
   border-radius: 2px;
@@ -995,37 +971,33 @@
  * inside of the light theme.
  */
 .theme-light .devtools-tab[icon-invertable] > image,
 .theme-light #toolbox-dock-buttons > toolbarbutton > image,
 .theme-light .command-button-invertable > image,
 .theme-light .command-button-invertable:active > image,
 .theme-light .devtools-closebutton > image,
 .theme-light .devtools-toolbarbutton > image,
-.theme-light .devtools-option-toolbarbutton > image,
+.theme-light .devtools-button::before,
 .theme-light #breadcrumb-separator-normal,
 .theme-light .scrollbutton-up > .toolbarbutton-icon,
 .theme-light .scrollbutton-down > .toolbarbutton-icon,
 .theme-light #black-boxed-message-button .button-icon,
-.theme-light .notice-container button .button-icon,
 .theme-light #requests-menu-perf-notice-button .button-icon,
 .theme-light #requests-menu-network-summary-button .button-icon,
-.theme-light .event-tooltip-debugger-icon,
-.theme-light .devtools-button::before {
-  filter: url(images/filters.svg#invert);
+.theme-light #toggle-breakpoints[checked] > image,
+.theme-light .event-tooltip-debugger-icon {
+  filter: invert(1);
 }
 
 /* Since selected backgrounds are blue, we want to use the normal
  * (light) icons. */
 .theme-light .command-button-invertable[checked=true]:not(:active) > image,
 .theme-light .devtools-tab[icon-invertable][selected] > image,
-.theme-light .devtools-tab[icon-invertable][highlighted] > image,
-.theme-light #record-snapshot[checked] > image,
-.theme-light #profiler-start[checked] > image,
-.theme-light .notice-container button[checked] .button-icon {
+.theme-light .devtools-tab[icon-invertable][highlighted] > image {
   filter: none !important;
 }
 
 .theme-light .command-button:hover {
   background-color: inherit;
 }
 
 .theme-light .command-button:hover:active,
--- a/devtools/client/themes/webaudioeditor.css
+++ b/devtools/client/themes/webaudioeditor.css
@@ -203,31 +203,16 @@ text {
 /**
  * Inspector toolbar
  */
 
 #audio-node-toolbar .bypass {
   list-style-image: url(images/power.svg);
 }
 
-#audio-node-toolbar toolbarbutton[disabled] {
-  opacity: 0.5;
-  background-color: transparent;
-}
-
-#audio-node-toolbar toolbarbutton[checked] {
-  background-color: var(--theme-selection-background);
-}
-
-/* don't invert checked buttons so we can have white icons on light theme */
-#audio-node-toolbar toolbarbutton[checked] > .toolbarbutton-icon {
-  filter: none;
-}
-
-
 /**
  * Responsive Styles
  * `.devtools-responsive-container` takes care of most of
  * the changing of host types.
  */
 @media (max-width: 700px) {
   /**
    * Override the inspector toggle so it's always open
--- a/devtools/client/themes/webconsole.css
+++ b/devtools/client/themes/webconsole.css
@@ -522,21 +522,22 @@ a {
   background: url("chrome://devtools/skin/images/vview-open-inspector.png") no-repeat 0 0;
   padding-left: 16px;
   margin-left: 5px;
   cursor: pointer;
 }
 
 .elementNode:hover .open-inspector,
 .open-inspector:hover {
-  background-position: -32px 0;
+  filter: url(images/filters.svg#checked-icon-state);
 }
 
+.elementNode:hover .open-inspector:active,
 .open-inspector:active {
-  background-position: -16px 0;
+  filter: url(images/filters.svg#checked-icon-state) brightness(0.9);
 }
 
 @media (max-width: 500px) {
   .message > .timestamp {
     display: none;
   }
   .hud-console-filter-toolbar .webconsole-filter-button .toolbarbutton-text {
     display: none;
--- a/devtools/client/themes/widgets.css
+++ b/devtools/client/themes/widgets.css
@@ -734,93 +734,73 @@
 
 .variable-or-property[safe-getter] > tooltip > label.WebIDL {
   -moz-padding-start: 4px;
   -moz-border-start: 1px dotted #000;
   color: #080;
 }
 
 /* Variables and properties editing */
+.variables-view-delete,
+.variables-view-edit,
+.variables-view-open-inspector {
+  width: 16px;
+  height: 16px;
+  background-size: cover;
+  cursor: pointer;
+}
+
+.variables-view-delete:hover,
+.variables-view-edit:hover,
+.variables-view-open-inspector:hover {
+  filter: url(images/filters.svg#checked-icon-state);
+}
+
+.variables-view-delete:active,
+.variables-view-edit:active,
+.variables-view-open-inspector:active {
+  filter: url(images/filters.svg#checked-icon-state) brightness(0.9);
+}
+
+.variable-or-property:focus > .title > .variables-view-delete,
+.variable-or-property:focus > .title > .variables-view-edit,
+.variable-or-property:focus > .title > .variables-view-open-inspector {
+  filter: none;
+}
 
 .variables-view-delete {
-  background: url("chrome://devtools/skin/images/vview-delete.png");
-  background-size: cover;
-  width: 16px;
-  height: 16px;
+  background-image: url("chrome://devtools/skin/images/vview-delete.png");
 }
 
 @media (min-resolution: 1.1dppx) {
   .variables-view-delete {
     background-image: url("chrome://devtools/skin/images/vview-delete@2x.png");
   }
 }
 
-.variables-view-delete:hover {
-  background-position: 16px;
-}
-
-.variables-view-delete:active {
-  background-position: 32px;
-}
-
-.variable-or-property:focus > .title > .variables-view-delete {
-  background-position: 0px;
-}
-
 .variables-view-edit {
-  background: url("chrome://devtools/skin/images/vview-edit.png");
-  background-size: cover;
-  width: 16px;
-  height: 16px;
-  cursor: pointer;
+  background-image: url("chrome://devtools/skin/images/vview-edit.png");
 }
 
 @media (min-resolution: 1.1dppx) {
   .variables-view-edit {
     background-image: url("chrome://devtools/skin/images/vview-edit@2x.png");
   }
 }
 
-.variables-view-edit:hover {
-  background-position: 16px;
-}
-
-.variables-view-edit:active {
-  background-position: 32px;
-}
-
-.variable-or-property:focus > .title > .variables-view-edit {
-  background-position: 0px;
-}
-
 .variables-view-open-inspector {
-  background: url("chrome://devtools/skin/images/vview-open-inspector.png");
-  background-size: cover;
-  width: 16px;
-  height: 16px;
-  cursor: pointer;
+  background-image: url("chrome://devtools/skin/images/vview-open-inspector.png");
 }
 
 @media (min-resolution: 1.1dppx) {
   .variables-view-open-inspector {
     background-image: url("chrome://devtools/skin/images/vview-open-inspector@2x.png");
   }
 }
 
-.variables-view-open-inspector:hover {
-  background-position: 16px;
-}
-
-.variables-view-open-inspector:active {
-  background-position: 32px;
-}
-
-.variable-or-property:focus > .title > .variables-view-open-inspector {
-  background-position: 0px;
-}
 
 /* Variables and properties input boxes */
 
 .variable-or-property > .title > .separator + .element-value-input {
   -moz-margin-start: -2px !important;
   -moz-margin-end: 2px !important;
 }
 
--- a/dom/html/HTMLImageElement.cpp
+++ b/dom/html/HTMLImageElement.cpp
@@ -75,42 +75,45 @@ namespace mozilla {
 namespace dom {
 
 // Calls LoadSelectedImage on host element unless it has been superseded or
 // canceled -- this is the synchronous section of "update the image data".
 // https://html.spec.whatwg.org/multipage/embedded-content.html#update-the-image-data
 class ImageLoadTask : public nsRunnable
 {
 public:
-  explicit ImageLoadTask(HTMLImageElement *aElement) :
-    mElement(aElement)
+  ImageLoadTask(HTMLImageElement *aElement, bool aAlwaysLoad)
+    : mElement(aElement)
+    , mAlwaysLoad(aAlwaysLoad)
   {
     mDocument = aElement->OwnerDoc();
     mDocument->BlockOnload();
   }
 
   NS_IMETHOD Run()
   {
     if (mElement->mPendingImageLoadTask == this) {
       mElement->mPendingImageLoadTask = nullptr;
-      mElement->LoadSelectedImage(true, true);
+      mElement->LoadSelectedImage(true, true, mAlwaysLoad);
     }
     mDocument->UnblockOnload(false);
     return NS_OK;
   }
 
 private:
   ~ImageLoadTask() {}
   RefPtr<HTMLImageElement> mElement;
   nsCOMPtr<nsIDocument> mDocument;
+  bool mAlwaysLoad;
 };
 
 HTMLImageElement::HTMLImageElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
   : nsGenericHTMLElement(aNodeInfo)
   , mForm(nullptr)
+  , mInDocResponsiveContent(false)
 {
   // We start out broken
   AddStatesSilently(NS_EVENT_STATE_BROKEN);
 }
 
 HTMLImageElement::~HTMLImageElement()
 {
   DestroyImageLoadingContent();
@@ -422,17 +425,17 @@ HTMLImageElement::AfterSetAttr(int32_t a
       !aValue) {
     // SetAttr handles setting src since it needs to catch img.src =
     // img.src, so we only need to handle the unset case
     if (InResponsiveMode()) {
       if (mResponsiveSelector &&
           mResponsiveSelector->Content() == this) {
         mResponsiveSelector->SetDefaultSource(NullString());
       }
-      QueueImageLoadTask();
+      QueueImageLoadTask(true);
     } else {
       // Bug 1076583 - We still behave synchronously in the non-responsive case
       CancelImageRequests(aNotify);
     }
   } else if (aName == nsGkAtoms::srcset &&
              aNameSpaceID == kNameSpaceID_None &&
              IsSrcsetEnabled()) {
     PictureSourceSrcsetChanged(this, attrVal.String(), aNotify);
@@ -442,17 +445,17 @@ HTMLImageElement::AfterSetAttr(int32_t a
     PictureSourceSizesChanged(this, attrVal.String(), aNotify);
   } else if (aName == nsGkAtoms::crossorigin &&
              aNameSpaceID == kNameSpaceID_None &&
              aNotify) {
     // Force a new load of the image with the new cross origin policy.
     if (InResponsiveMode()) {
       // per spec, full selection runs when this changes, even though
       // it doesn't directly affect the source selection
-      QueueImageLoadTask();
+      QueueImageLoadTask(true);
     } else {
       // Bug 1076583 - We still use the older synchronous algorithm in
       // non-responsive mode.  Force a new load of the image with the
       // new cross origin policy.
       ForceReload(aNotify);
     }
   }
 
@@ -538,17 +541,17 @@ HTMLImageElement::SetAttr(int32_t aNameS
       return NS_OK;
     }
 
     if (InResponsiveMode()) {
       if (mResponsiveSelector &&
           mResponsiveSelector->Content() == this) {
         mResponsiveSelector->SetDefaultSource(aValue);
       }
-      QueueImageLoadTask();
+      QueueImageLoadTask(true);
     } else if (aNotify) {
       // If aNotify is false, we are coming from the parser or some such place;
       // we'll get bound after all the attributes have been set, so we'll do the
       // sync image load from BindToTree. Skip the LoadImage call in that case.
 
       // Note that this sync behavior is partially removed from the spec, bug 1076583
 
       // A hack to get animations to reset. See bug 594771.
@@ -581,23 +584,25 @@ HTMLImageElement::BindToTree(nsIDocument
 
   nsImageLoadingContent::BindToTree(aDocument, aParent, aBindingParent,
                                     aCompileEventHandlers);
 
   if (aParent) {
     UpdateFormOwner();
   }
 
-  bool addedToPicture = aParent && aParent->IsHTMLElement(nsGkAtoms::picture) &&
-                        HTMLPictureElement::IsPictureEnabled();
-  if (addedToPicture) {
-    if (aDocument) {
+  if (HaveSrcsetOrInPicture()) {
+    if (aDocument && !mInDocResponsiveContent) {
       aDocument->AddResponsiveContent(this);
+      mInDocResponsiveContent = true;
     }
-    QueueImageLoadTask();
+
+    bool forceLoadEvent = HTMLPictureElement::IsPictureEnabled() &&
+      aParent && aParent->IsHTMLElement(nsGkAtoms::picture);
+    QueueImageLoadTask(forceLoadEvent);
   } else if (!InResponsiveMode() &&
              HasAttr(kNameSpaceID_None, nsGkAtoms::src)) {
     // We skip loading when our attributes were set from parser land,
     // so trigger a aForce=false load now to check if things changed.
     // This isn't necessary for responsive mode, since creating the
     // image load task is asynchronous we don't need to take special
     // care to avoid doing so when being filled by the parser.
 
@@ -628,27 +633,32 @@ HTMLImageElement::UnbindFromTree(bool aD
   if (mForm) {
     if (aNullParent || !FindAncestorForm(mForm)) {
       ClearForm(true);
     } else {
       UnsetFlags(MAYBE_ORPHAN_FORM_ELEMENT);
     }
   }
 
+  if (mInDocResponsiveContent) {
+    nsIDocument* doc = GetOurOwnerDoc();
+    MOZ_ASSERT(doc);
+    if (doc) {
+      doc->RemoveResponsiveContent(this);
+      mInDocResponsiveContent = false;
+    }
+  }
+
   if (GetParent() &&
       GetParent()->IsHTMLElement(nsGkAtoms::picture) &&
       HTMLPictureElement::IsPictureEnabled()) {
-    nsIDocument* doc = GetOurOwnerDoc();
-    if (doc) {
-      doc->RemoveResponsiveContent(this);
-    }
     // Being removed from picture re-triggers selection, even if we
     // weren't using a <source> peer
     if (aNullParent) {
-      QueueImageLoadTask();
+      QueueImageLoadTask(true);
     }
   }
 
   nsImageLoadingContent::UnbindFromTree(aDeep, aNullParent);
   nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
 }
 
 void
@@ -682,17 +692,17 @@ void
 HTMLImageElement::MaybeLoadImage()
 {
   // Our base URI may have changed, or we may have had responsive parameters
   // change while not bound to the tree. Re-parse src/srcset and call LoadImage,
   // which is a no-op if it resolves to the same effective URI without aForce.
 
   // Note, check LoadingEnabled() after LoadImage call.
 
-  LoadSelectedImage(false, true);
+  LoadSelectedImage(false, true, false);
 
   if (!LoadingEnabled()) {
     CancelImageRequests(true);
   }
 }
 
 EventStates
 HTMLImageElement::IntrinsicState() const
@@ -882,25 +892,25 @@ HTMLImageElement::ClearForm(bool aRemove
     }
   }
 
   UnsetFlags(ADDED_TO_FORM);
   mForm = nullptr;
 }
 
 void
-HTMLImageElement::QueueImageLoadTask()
+HTMLImageElement::QueueImageLoadTask(bool aAlwaysLoad)
 {
   // If loading is temporarily disabled, we don't want to queue tasks
   // that may then run when loading is re-enabled.
   if (!LoadingEnabled() || !this->OwnerDoc()->IsCurrentActiveDocument()) {
     return;
   }
 
-  nsCOMPtr<nsIRunnable> task = new ImageLoadTask(this);
+  nsCOMPtr<nsIRunnable> task = new ImageLoadTask(this, aAlwaysLoad);
   // The task checks this to determine if it was the last
   // queued event, and so earlier tasks are implicitly canceled.
   mPendingImageLoadTask = task;
   nsContentUtils::RunInStableState(task.forget());
 }
 
 bool
 HTMLImageElement::HaveSrcsetOrInPicture()
@@ -924,25 +934,27 @@ HTMLImageElement::InResponsiveMode()
   // will happen from the microtask, and we should behave responsively in the
   // interim
   return mResponsiveSelector ||
          mPendingImageLoadTask ||
          HaveSrcsetOrInPicture();
 }
 
 nsresult
-HTMLImageElement::LoadSelectedImage(bool aForce, bool aNotify)
+HTMLImageElement::LoadSelectedImage(bool aForce, bool aNotify, bool aAlwaysLoad)
 {
   nsresult rv = NS_ERROR_FAILURE;
 
   if (aForce) {
     // In responsive mode we generally want to re-run the full
     // selection algorithm whenever starting a new load, per
     // spec. This also causes us to re-resolve the URI as appropriate.
-    UpdateResponsiveSource();
+    if (!UpdateResponsiveSource() && !aAlwaysLoad) {
+      return NS_OK;
+    }
   }
 
   if (mResponsiveSelector) {
     nsCOMPtr<nsIURI> url = mResponsiveSelector->GetSelectedImageURL();
     if (url) {
       rv = LoadImage(url, aForce, aNotify, eImageLoadType_Imageset);
     }
   } else {
@@ -985,19 +997,27 @@ HTMLImageElement::PictureSourceSrcsetCha
     mResponsiveSelector ? mResponsiveSelector->Content() : nullptr;
 
   if (aSourceNode == currentSrc) {
     // We're currently using this node as our responsive selector
     // source.
     mResponsiveSelector->SetCandidatesFromSourceSet(aNewValue);
   }
 
+  if (!mInDocResponsiveContent) {
+    nsIDocument* doc = GetOurOwnerDoc();
+    if (doc) {
+      doc->AddResponsiveContent(this);
+      mInDocResponsiveContent = true;
+    }
+  }
+
   // This always triggers the image update steps per the spec, even if
   // we are not using this source.
-  QueueImageLoadTask();
+  QueueImageLoadTask(true);
 }
 
 void
 HTMLImageElement::PictureSourceSizesChanged(nsIContent *aSourceNode,
                                             const nsAString& aNewValue,
                                             bool aNotify)
 {
   if (!HTMLPictureElement::IsPictureEnabled()) {
@@ -1014,61 +1034,63 @@ HTMLImageElement::PictureSourceSizesChan
   if (aSourceNode == currentSrc) {
     // We're currently using this node as our responsive selector
     // source.
     mResponsiveSelector->SetSizesFromDescriptor(aNewValue);
   }
 
   // This always triggers the image update steps per the spec, even if
   // we are not using this source.
-  QueueImageLoadTask();
+  QueueImageLoadTask(true);
 }
 
 void
 HTMLImageElement::PictureSourceMediaOrTypeChanged(nsIContent *aSourceNode,
                                                   bool aNotify)
 {
   if (!HTMLPictureElement::IsPictureEnabled()) {
     return;
   }
 
   MOZ_ASSERT(IsPreviousSibling(aSourceNode, this),
              "Should not be getting notifications for non-previous-siblings");
 
   // This always triggers the image update steps per the spec, even if
   // we are not switching to/from this source
-  QueueImageLoadTask();
+  QueueImageLoadTask(true);
 }
 
 void
 HTMLImageElement::PictureSourceAdded(nsIContent *aSourceNode)
 {
   if (!HTMLPictureElement::IsPictureEnabled()) {
     return;
   }
 
-  QueueImageLoadTask();
+  QueueImageLoadTask(true);
 }
 
 void
 HTMLImageElement::PictureSourceRemoved(nsIContent *aSourceNode)
 {
   if (!HTMLPictureElement::IsPictureEnabled()) {
     return;
   }
 
-  QueueImageLoadTask();
+  QueueImageLoadTask(true);
 }
 
-void
+bool
 HTMLImageElement::UpdateResponsiveSource()
 {
+  bool hadSelector = !!mResponsiveSelector;
+
   if (!IsSrcsetEnabled()) {
     mResponsiveSelector = nullptr;
-    return;
+    return hadSelector;
   }
 
   nsIContent *currentSource =
     mResponsiveSelector ? mResponsiveSelector->Content() : nullptr;
   bool pictureEnabled = HTMLPictureElement::IsPictureEnabled();
   Element *parent = pictureEnabled ? nsINode::GetParentElement() : nullptr;
 
   nsINode *candidateSource = nullptr;
@@ -1078,29 +1100,29 @@ HTMLImageElement::UpdateResponsiveSource
   } else {
     candidateSource = this;
   }
 
   while (candidateSource) {
     if (candidateSource == currentSource) {
       // found no better source before current, re-run selection on
       // that and keep it if it's still usable.
-      mResponsiveSelector->SelectImage(true);
+      bool changed = mResponsiveSelector->SelectImage(true);
       if (mResponsiveSelector->NumCandidates()) {
         bool isUsableCandidate = true;
 
         // an otherwise-usable source element may still have a media query that may not
         // match any more.
         if (candidateSource->IsHTMLElement(nsGkAtoms::source) &&
             !SourceElementMatches(candidateSource->AsContent())) {
           isUsableCandidate = false;
         }
 
         if (isUsableCandidate) {
-          break;
+          return changed;
         }
       }
 
       // no longer valid
       mResponsiveSelector = nullptr;
       if (candidateSource == this) {
         // No further possibilities
         break;
@@ -1119,16 +1141,18 @@ HTMLImageElement::UpdateResponsiveSource
     }
     candidateSource = candidateSource->GetNextSibling();
   }
 
   if (!candidateSource) {
     // Ran out of siblings without finding ourself, e.g. XBL magic.
     mResponsiveSelector = nullptr;
   }
+
+  return !hadSelector || mResponsiveSelector;
 }
 
 /*static */ bool
 HTMLImageElement::SupportedPictureSourceType(const nsAString& aType)
 {
   return
     imgLoader::SupportImageWithMimeType(NS_ConvertUTF16toUTF8(aType).get(),
                                         AcceptedMimeTypes::IMAGES_AND_DOCUMENTS);
@@ -1291,17 +1315,17 @@ void
 HTMLImageElement::DestroyContent()
 {
   mResponsiveSelector = nullptr;
 }
 
 void
 HTMLImageElement::MediaFeatureValuesChanged()
 {
-  QueueImageLoadTask();
+  QueueImageLoadTask(false);
 }
 
 void
 HTMLImageElement::FlushUseCounters()
 {
   nsCOMPtr<imgIRequest> request;
   GetRequest(CURRENT_REQUEST, getter_AddRefs(request));
 
--- a/dom/html/HTMLImageElement.h
+++ b/dom/html/HTMLImageElement.h
@@ -275,29 +275,29 @@ protected:
   virtual ~HTMLImageElement();
 
   // Queues a task to run LoadSelectedImage pending stable state.
   //
   // Pending Bug 1076583 this is only used by the responsive image
   // algorithm (InResponsiveMode()) -- synchronous actions when just
   // using img.src will bypass this, and update source and kick off
   // image load synchronously.
-  void QueueImageLoadTask();
+  void QueueImageLoadTask(bool aAlwaysLoad);
 
   // True if we have a srcset attribute or a <picture> parent, regardless of if
   // any valid responsive sources were parsed from either.
   bool HaveSrcsetOrInPicture();
 
   // True if we are using the newer image loading algorithm. This will be the
   // only mode after Bug 1076583
   bool InResponsiveMode();
 
   // Resolve and load the current mResponsiveSelector (responsive mode) or src
   // attr image.
-  nsresult LoadSelectedImage(bool aForce, bool aNotify);
+  nsresult LoadSelectedImage(bool aForce, bool aNotify, bool aAlwaysLoad);
 
   // True if this string represents a type we would support on <source type>
   static bool SupportedPictureSourceType(const nsAString& aType);
 
   // Update/create/destroy mResponsiveSelector
   void PictureSourceSrcsetChanged(nsIContent *aSourceNode,
                                   const nsAString& aNewValue, bool aNotify);
   void PictureSourceSizesChanged(nsIContent *aSourceNode,
@@ -316,17 +316,19 @@ protected:
   // the current ResponsiveSelector is not changed, runs
   // SelectImage(true) to re-evaluate its candidates.
   //
   // Because keeping the existing selector is the common case (and we
   // often do no-op reselections), this does not re-parse values for
   // the existing mResponsiveSelector, meaning you need to update its
   // parameters as appropriate before calling (or null it out to force
   // recreation)
-  void UpdateResponsiveSource();
+  //
+  // Returns true if the source has changed, and false otherwise.
+  bool UpdateResponsiveSource();
 
   // Given a <source> node that is a previous sibling *or* ourselves, try to
   // create a ResponsiveSelector.
 
   // If the node's srcset/sizes make for an invalid selector, returns
   // false. This does not guarantee the resulting selector matches an image,
   // only that it is valid.
   bool TryCreateResponsiveSelector(nsIContent *aSourceNode,
@@ -354,15 +356,16 @@ protected:
   RefPtr<ResponsiveImageSelector> mResponsiveSelector;
 
 private:
   bool SourceElementMatches(nsIContent* aSourceNode);
 
   static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
                                     nsRuleData* aData);
 
+  bool mInDocResponsiveContent;
   nsCOMPtr<nsIRunnable> mPendingImageLoadTask;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_HTMLImageElement_h */
--- a/dom/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -3238,16 +3238,22 @@ nsHTMLDocument::ExecCommand(const nsAStr
   if (doShowUI) {
     return false;
   }
 
   // special case for cut & copy
   // cut & copy are allowed in non editable documents
   if (isCutCopy) {
     if (!nsContentUtils::IsCutCopyAllowed()) {
+      // We have rejected the event due to it not being performed in an
+      // input-driven context therefore, we report the error to the console.
+      nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
+                                      NS_LITERAL_CSTRING("DOM"), this,
+                                      nsContentUtils::eDOM_PROPERTIES,
+                                      "ExecCommandCutCopyDeniedNotInputDriven");
       return false;
     }
 
     // For cut & copy commands, we need the behaviour from nsWindowRoot::GetControllers
     // which is to look at the focused element, and defer to a focused textbox's controller
     // The code past taken by other commands in ExecCommand always uses the window directly,
     // rather than deferring to the textbox, which is desireable for most editor commands,
     // but not 'cut' and 'copy' (as those should allow copying out of embedded editors).
new file mode 100644
index 0000000000000000000000000000000000000000..df421453c25631353cfe86bde5973fdca15b1ea9
GIT binary patch
literal 91
zc%17D@N?(olHy`uVBq!ia0vp^DIm<q3?#jD*u{YqbAV5XtC5k>QX|f*K(4T-i(`m{
lWU>V7;slYNra*^q1_ss&2F5$COwvGU22WQ%mvv4FO#rqO5n=!U
new file mode 100644
index 0000000000000000000000000000000000000000..6f76d4438724111983a11860f13568361b52d9bc
GIT binary patch
literal 100
zc%17D@N?(olHy`uVBq!ia0vp^CqS5y8Awi_W^)%vF$egBxTd6}EPfSw1jv=~ba4!k
rkbHZv5y)XUyx@1R|3ikWAU1;^qeuV)<1IPC3=q%L)z4*}Q$iB}r??p{
new file mode 100644
index 0000000000000000000000000000000000000000..144a2f0b93c5913872f30056999da000ae6049cd
GIT binary patch
literal 85
zc%17D@N?(olHy`uVBq!ia0vp^Mj*_{3?x-PN__%S%mF?ju9A|H-p`i&1*>y$43Usb
dmSAlLYGa(hz<BW6Dm$PAgQu&X%Q~loCIF<=5=;O9
--- a/dom/html/test/mochitest.ini
+++ b/dom/html/test/mochitest.ini
@@ -192,16 +192,19 @@ support-files =
   image-allow-credentials.png^headers^
   nnc_lockup.gif
   reflect.js
   wakelock.ogg
   wakelock.ogv
   file_ignoreuserfocus.html
   simpleFileOpener.js
   file_mozaudiochannel.html
+  file_bug1166138_1x.png
+  file_bug1166138_2x.png
+  file_bug1166138_def.png
 
 [test_a_text.html]
 [test_anchor_href_cache_invalidation.html]
 [test_applet_attributes_reflection.html]
 [test_audio_wakelock.html]
 skip-if = buildapp == 'mulet' # TC: Bug 1144079 - Re-enable Mulet mochitests and reftests taskcluster-specific disables.
 [test_base_attributes_reflection.html]
 [test_bug100533.html]
@@ -590,8 +593,9 @@ support-files = file_bug871161-1.html fi
 [test_hash_encoded.html]
 [test_bug1081037.html]
 [test_window_open_close.html]
 skip-if = buildapp == 'b2g' # bug 1129014
 [test_img_complete.html]
 [test_viewport_resize.html]
 [test_extapp.html]
 [test_image_clone_load.html]
+[test_bug1166138.html]
new file mode 100644
--- /dev/null
+++ b/dom/html/test/test_bug1166138.html
@@ -0,0 +1,130 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1166138
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1166138</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1166138">Mozilla Bug 1166138</a>
+  <p id="display"></p>
+  <div id="content" style="display: none">
+  </div>
+
+  <img srcset="file_bug1166138_1x.png 1x, file_bug1166138_2x.png 2x"
+       src="file_bug1166138_def.png"
+       onload="onLoad()">
+
+  <script type="application/javascript">
+    var img1x = "http://mochi.test:8888/tests/dom/html/test/file_bug1166138_1x.png";
+    var img2x = "http://mochi.test:8888/tests/dom/html/test/file_bug1166138_2x.png";
+    var imgdef = "http://mochi.test:8888/tests/dom/html/test/file_bug1166138_def.png";
+    var onLoadCallback = null;
+    var done = false;
+
+    var startPromise = new Promise((a) => {
+      onLoadCallback = () => {
+        var image = document.querySelector('img');
+        // If we aren't starting at 2x scale, resize to 2x scale, and wait for a load
+        if (image.currentSrc != img2x) {
+          onLoadCallback = a;
+          SpecialPowers.pushPrefEnv({'set': [['layout.css.devPixelsPerPx', 2]]});
+        } else {
+          a();
+        }
+      };
+    });
+
+    // if aLoad is true, waits for a load event. Otherwise, spins the event loop twice to
+    // ensure that no events were queued to be fired.
+    function spin(aLoad) {
+      if (aLoad) {
+        return new Promise((a) => {
+          ok(!onLoadCallback, "Shouldn't be an existing callback");
+          onLoadCallback = a;
+        });
+      } else {
+        return new Promise((a) => SimpleTest.executeSoon(() => SimpleTest.executeSoon(a)));
+      }
+    }
+
+    function onLoad() {
+      if (done) return;
+      ok(onLoadCallback, "Expected a load event");
+      if (onLoadCallback) {
+        var cb = onLoadCallback;
+        onLoadCallback = null;
+        cb();
+      }
+    }
+
+    add_task(function* () {
+      yield startPromise;
+      var image = document.querySelector('img');
+      is(image.currentSrc, img2x, "initial scale must be 2x");
+
+      SpecialPowers.pushPrefEnv({'set': [['layout.css.devPixelsPerPx', 1]]});
+      yield spin(true);
+      is(image.currentSrc, img1x, "pre-existing img tag to 1x");
+
+      SpecialPowers.pushPrefEnv({'set': [['layout.css.devPixelsPerPx', 2]]});
+      yield spin(true);
+      is(image.currentSrc, img2x, "pre-existing img tag to 2x");
+
+      // Try removing & re-adding the image
+      document.body.removeChild(image);
+
+      SpecialPowers.pushPrefEnv({'set': [['layout.css.devPixelsPerPx', 1]]});
+      yield spin(false); // No load should occur because the element is unbound
+
+      document.body.appendChild(image);
+      yield spin(true);
+      is(image.currentSrc, img1x, "remove and re-add tag after changing to 1x");
+
+      document.body.removeChild(image);
+      SpecialPowers.pushPrefEnv({'set': [['layout.css.devPixelsPerPx', 2]]});
+      yield spin(false); // No load should occur because the element is unbound
+
+      document.body.appendChild(image);
+      yield spin(true);
+      is(image.currentSrc, img2x, "remove and re-add tag after changing to 2x");
+
+      // get rid of the srcset attribute! It should become the default
+      image.removeAttribute('srcset');
+      yield spin(true);
+      is(image.currentSrc, imgdef, "remove srcset attribute");
+
+      // Setting srcset again should return it to the correct value
+      image.setAttribute('srcset', "file_bug1166138_1x.png 1x, file_bug1166138_2x.png 2x");
+      yield spin(true);
+      is(image.currentSrc, img2x, "restore srcset attribute");
+
+      // Create a new image
+      var newImage = document.createElement('img');
+      // Switch load listening over to newImage
+      newImage.addEventListener('load', onLoad);
+      image.removeEventListener('load', onLoad);
+
+      document.body.appendChild(newImage);
+      yield spin(false); // no load event should fire - as the image has no attributes
+      is(newImage.currentSrc, "", "New element with no attributes");
+      newImage.setAttribute('srcset', "file_bug1166138_1x.png 1x, file_bug1166138_2x.png 2x");
+      yield spin(true);
+      is(newImage.currentSrc, img2x, "Adding srcset attribute");
+
+      SpecialPowers.pushPrefEnv({'set': [['layout.css.devPixelsPerPx', 1]]});
+      yield spin(true);
+      is(newImage.currentSrc, img1x, "new image after switching to 1x");
+      is(image.currentSrc, img1x, "old image after switching to 1x");
+
+      // Clear the listener
+      done = true;
+    });
+  </script>
+</body>
+</html>
--- a/dom/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -183,8 +183,9 @@ InterceptedUsedResponseWithURL=Failed to
 # LOCALIZATION NOTE: Do not translate "ServiceWorker", "opaqueredirect", "Response", "FetchEvent.respondWith()", or "FetchEvent". %s is a URL.
 BadOpaqueRedirectInterceptionWithURL=Failed to load '%S'. A ServiceWorker passed an opaqueredirect Response to FetchEvent.respondWith() while handling a non-navigation FetchEvent.
 # LOCALIZATION NOTE: Do not translate "ServiceWorker" or "FetchEvent.preventDefault()". %S is a URL.
 InterceptionCanceledWithURL=Failed to load '%S'. A ServiceWorker canceled the load by calling FetchEvent.preventDefault().
 # LOCALIZATION NOTE: Do not translate "ServiceWorker", "promise", or "FetchEvent.respondWith()". %1$S is a URL. %2$S is an error string.
 InterceptionRejectedResponseWithURL=Failed to load '%1$S'. A ServiceWorker passed a promise to FetchEvent.respondWith() that rejected with '%2$S'.
 # LOCALIZATION NOTE: Do not translate "ServiceWorker", "promise", "FetchEvent.respondWith()", or "Response". %1$S is a URL. %2$S is an error string.
 InterceptedNonResponseWithURL=Failed to load '%1$S'. A ServiceWorker passed a promise to FetchEvent.respondWith() that resolved with non-Response value '%2$S'.
+ExecCommandCutCopyDeniedNotInputDriven=document.execCommand('cut'/'copy') was denied because it was not called from inside a short running user-generated event handler.
--- a/dom/media/AbstractMediaDecoder.h
+++ b/dom/media/AbstractMediaDecoder.h
@@ -106,17 +106,17 @@ public:
   virtual void OnReadMetadataCompleted() = 0;
 
   // Returns the owner of this media decoder. The owner should only be used
   // on the main thread.
   virtual MediaDecoderOwner* GetOwner() = 0;
 
   // Called by the reader's MediaResource as data arrives over the network.
   // Must be called on the main thread.
-  virtual void NotifyDataArrived(bool aThrottleUpdates) = 0;
+  virtual void NotifyDataArrived() = 0;
 
   // Set by Reader if the current audio track can be offloaded
   virtual void SetPlatformCanOffloadAudio(bool aCanOffloadAudio) {}
 
   // Called from HTMLMediaElement when owner document activity changes
   virtual void SetElementVisibility(bool aIsVisible) {}
 
   // Stack based class to assist in notifying the frame statistics of
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -1336,21 +1336,21 @@ MediaDecoder::SizeOfAudioQueue() {
 void MediaDecoder::AddSizeOfResources(ResourceSizes* aSizes) {
   MOZ_ASSERT(NS_IsMainThread());
   if (GetResource()) {
     aSizes->mByteSize += GetResource()->SizeOfIncludingThis(aSizes->mMallocSizeOf);
   }
 }
 
 void
-MediaDecoder::NotifyDataArrived(bool aThrottleUpdates) {
+MediaDecoder::NotifyDataArrived() {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (mDecoderStateMachine) {
-    mDecoderStateMachine->DispatchNotifyDataArrived(aThrottleUpdates);
+    mDecoderStateMachine->DispatchNotifyDataArrived();
   }
 
   // ReadyState computation depends on MediaDecoder::CanPlayThrough, which
   // depends on the download rate.
   UpdateReadyState();
 }
 
 // Provide access to the state machine object
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -428,17 +428,17 @@ public:
 
   // Called by nsChannelToPipeListener or MediaResource when the
   // download has ended. Called on the main thread only. aStatus is
   // the result from OnStopRequest.
   virtual void NotifyDownloadEnded(nsresult aStatus);
 
   // Called as data arrives on the stream and is read into the cache.  Called
   // on the main thread only.
-  virtual void NotifyDataArrived(bool aThrottleUpdates) override;
+  virtual void NotifyDataArrived() override;
 
   // Called by MediaResource when the principal of the resource has
   // changed. Called on main thread only.
   virtual void NotifyPrincipalChanged() override;
 
   // Called by the MediaResource to keep track of the number of bytes read
   // from the resource. Called on the main by an event runner dispatched
   // by the MediaResource read functions.
--- a/dom/media/MediaDecoderReader.cpp
+++ b/dom/media/MediaDecoderReader.cpp
@@ -63,21 +63,18 @@ public:
 };
 
 MediaDecoderReader::MediaDecoderReader(AbstractMediaDecoder* aDecoder)
   : mAudioCompactor(mAudioQueue)
   , mDecoder(aDecoder)
   , mTaskQueue(new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK),
                              /* aSupportsTailDispatch = */ true))
   , mWatchManager(this, mTaskQueue)
-  , mTimer(new MediaTimer())
   , mBuffered(mTaskQueue, TimeIntervals(), "MediaDecoderReader::mBuffered (Canonical)")
   , mDuration(mTaskQueue, NullableTimeUnit(), "MediaDecoderReader::mDuration (Mirror)")
-  , mThrottleDuration(TimeDuration::FromMilliseconds(500))
-  , mLastThrottledNotify(TimeStamp::Now() - mThrottleDuration)
   , mIgnoreAudioOutputFormat(false)
   , mHitAudioDecodeError(false)
   , mShutdown(false)
   , mAudioDiscontinuity(false)
   , mVideoDiscontinuity(false)
 {
   MOZ_COUNT_CTOR(MediaDecoderReader);
   MOZ_ASSERT(NS_IsMainThread());
@@ -177,53 +174,16 @@ MediaDecoderReader::DecodeToFirstVideoDa
 void
 MediaDecoderReader::UpdateBuffered()
 {
   MOZ_ASSERT(OnTaskQueue());
   NS_ENSURE_TRUE_VOID(!mShutdown);
   mBuffered = GetBuffered();
 }
 
-void
-MediaDecoderReader::ThrottledNotifyDataArrived()
-{
-  MOZ_ASSERT(OnTaskQueue());
-  NS_ENSURE_TRUE_VOID(!mShutdown);
-
-  // If it's been long enough since our last update, do it.
-  if (TimeStamp::Now() - mLastThrottledNotify > mThrottleDuration) {
-    DoThrottledNotify();
-  } else if (!mThrottledNotify.Exists()) {
-    // Otherwise, schedule an update if one isn't scheduled already.
-    RefPtr<MediaDecoderReader> self = this;
-    mThrottledNotify.Begin(
-      mTimer->WaitUntil(mLastThrottledNotify + mThrottleDuration, __func__)
-      ->Then(OwnerThread(), __func__,
-             [self] () -> void {
-               self->mThrottledNotify.Complete();
-               NS_ENSURE_TRUE_VOID(!self->mShutdown);
-               self->DoThrottledNotify();
-             },
-             [self] () -> void {
-               self->mThrottledNotify.Complete();
-               NS_WARNING("throttle callback rejected");
-             })
-    );
-  }
-}
-
-void
-MediaDecoderReader::DoThrottledNotify()
-{
-  MOZ_ASSERT(OnTaskQueue());
-  mLastThrottledNotify = TimeStamp::Now();
-  mThrottledNotify.DisconnectIfExists();
-  NotifyDataArrived();
-}
-
 media::TimeIntervals
 MediaDecoderReader::GetBuffered()
 {
   MOZ_ASSERT(OnTaskQueue());
   if (!HaveStartTime()) {
     return media::TimeIntervals();
   }
   AutoPinned<MediaResource> stream(mDecoder->GetResource());
@@ -395,28 +355,25 @@ RefPtr<ShutdownPromise>
 MediaDecoderReader::Shutdown()
 {
   MOZ_ASSERT(OnTaskQueue());
   mShutdown = true;
 
   mBaseAudioPromise.RejectIfExists(END_OF_STREAM, __func__);
   mBaseVideoPromise.RejectIfExists(END_OF_STREAM, __func__);
 
-  mThrottledNotify.DisconnectIfExists();
-
   ReleaseMediaResources();
   mDuration.DisconnectIfConnected();
   mBuffered.DisconnectAll();
 
   // Shut down the watch manager before shutting down our task queue.
   mWatchManager.Shutdown();
 
   RefPtr<ShutdownPromise> p;
 
-  mTimer = nullptr;
   mDecoder = nullptr;
 
   return mTaskQueue->BeginShutdown();
 }
 
 } // namespace mozilla
 
 #undef DECODER_LOG
--- a/dom/media/MediaDecoderReader.h
+++ b/dom/media/MediaDecoderReader.h
@@ -218,28 +218,20 @@ public:
 
   // Returns the number of bytes of memory allocated by structures/frames in
   // the audio queue.
   size_t SizeOfAudioQueueInBytes() const;
 
   virtual size_t SizeOfVideoQueueInFrames();
   virtual size_t SizeOfAudioQueueInFrames();
 
-  // In situations where these notifications come from stochastic network
-  // activity, we can save significant recomputation by throttling the delivery
-  // of these updates to the reader implementation. We don't want to do this
-  // throttling when the update comes from MSE code, since that code needs the
-  // updates to be observable immediately, and is generally less
-  // trigger-happy with notifications anyway.
-  void DispatchNotifyDataArrived(bool aThrottleUpdates)
+  void DispatchNotifyDataArrived()
   {
     RefPtr<nsRunnable> r = NS_NewRunnableMethod(
-      this,
-      aThrottleUpdates ? &MediaDecoderReader::ThrottledNotifyDataArrived :
-                         &MediaDecoderReader::NotifyDataArrived);
+      this, &MediaDecoderReader::NotifyDataArrived);
 
     OwnerThread()->Dispatch(
       r.forget(), AbstractThread::DontAssertDispatchSuccess);
   }
 
   void NotifyDataArrived()
   {
     MOZ_ASSERT(OnTaskQueue());
@@ -345,33 +337,25 @@ protected:
   AbstractMediaDecoder* mDecoder;
 
   // Decode task queue.
   RefPtr<TaskQueue> mTaskQueue;
 
   // State-watching manager.
   WatchManager<MediaDecoderReader> mWatchManager;
 
-  // MediaTimer.
-  RefPtr<MediaTimer> mTimer;
-
   // Buffered range.
   Canonical<media::TimeIntervals> mBuffered;
 
   // Stores presentation info required for playback.
   MediaInfo mInfo;
 
   // Duration, mirrored from the state machine task queue.
   Mirror<media::NullableTimeUnit> mDuration;
 
-  // State for ThrottledNotifyDataArrived.
-  MozPromiseRequestHolder<MediaTimerPromise> mThrottledNotify;
-  const TimeDuration mThrottleDuration;
-  TimeStamp mLastThrottledNotify;
-
   // Whether we should accept media that we know we can't play
   // directly, because they have a number of channel higher than
   // what we support.
   bool mIgnoreAudioOutputFormat;
 
   // The start time of the media, in microseconds. This is the presentation
   // time of the first frame decoded from the media. This is initialized to -1,
   // and then set to a value >= by MediaDecoderStateMachine::SetStartTime(),
@@ -410,21 +394,16 @@ private:
     MOZ_CRASH();
   }
 
   // Recomputes mBuffered.
   virtual void UpdateBuffered();
 
   virtual void NotifyDataArrivedInternal() {}
 
-  // Invokes NotifyDataArrived while throttling the calls to occur
-  // at most every mThrottleDuration ms.
-  void ThrottledNotifyDataArrived();
-  void DoThrottledNotify();
-
   // Overrides of this function should decodes an unspecified amount of
   // audio data, enqueuing the audio data in mAudioQueue. Returns true
   // when there's more audio to decode, false if the audio is finished,
   // end of file has been reached, or an un-recoverable read error has
   // occured. This function blocks until the decode is complete.
   virtual bool DecodeAudioData()
   {
     return false;
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -163,19 +163,19 @@ public:
 
   void DispatchStartBuffering()
   {
     nsCOMPtr<nsIRunnable> runnable =
       NS_NewRunnableMethod(this, &MediaDecoderStateMachine::StartBuffering);
     OwnerThread()->Dispatch(runnable.forget());
   }
 
-  void DispatchNotifyDataArrived(bool aThrottleUpdates)
+  void DispatchNotifyDataArrived()
   {
-    mReader->DispatchNotifyDataArrived(aThrottleUpdates);
+    mReader->DispatchNotifyDataArrived();
   }
 
   // Notifies the state machine that should minimize the number of samples
   // decoded we preroll, until playback starts. The first time playback starts
   // the state machine is free to return to prerolling normally. Note
   // "prerolling" in this context refers to when we decode and buffer decoded
   // samples in advance of when they're needed for playback.
   void DispatchMinimizePrerollUntilPlaybackStarts()
--- a/dom/media/MediaResource.cpp
+++ b/dom/media/MediaResource.cpp
@@ -450,17 +450,17 @@ ChannelMediaResource::CopySegmentToCache
                                          void *aClosure,
                                          const char *aFromSegment,
                                          uint32_t aToOffset,
                                          uint32_t aCount,
                                          uint32_t *aWriteCount)
 {
   CopySegmentClosure* closure = static_cast<CopySegmentClosure*>(aClosure);
 
-  closure->mResource->mCallback->NotifyDataArrived(/* aThrottleUpdates = */ true);
+  closure->mResource->mCallback->NotifyDataArrived();
 
   // Keep track of where we're up to.
   RESOURCE_LOG("%p [ChannelMediaResource]: CopySegmentToCache at mOffset [%lld] add "
                "[%d] bytes for decoder[%p]",
                closure->mResource, closure->mResource->mOffset, aCount,
                closure->mResource->mCallback);
   closure->mResource->mOffset += aCount;
 
--- a/dom/media/MediaResourceCallback.h
+++ b/dom/media/MediaResourceCallback.h
@@ -44,17 +44,17 @@ public:
 
   // Notify that a network error is encountered.
   virtual void NotifyNetworkError() {}
 
   // Notify that decoding has failed.
   virtual void NotifyDecodeError() {}
 
   // Notify that data arrives on the stream and is read into the cache.
-  virtual void NotifyDataArrived(bool aThrottleUpdates) {}
+  virtual void NotifyDataArrived() {}
 
   // Notify that MediaResource has received some data.
   virtual void NotifyBytesDownloaded() {}
 
   // Notify download is ended.
   // NOTE: this can be called with the media cache lock held, so don't
   // block or do anything which might try to acquire a lock!
   virtual void NotifyDataEnded(nsresult aStatus) {}
--- a/dom/media/mediasource/SourceBuffer.cpp
+++ b/dom/media/mediasource/SourceBuffer.cpp
@@ -296,17 +296,17 @@ void
 SourceBuffer::Ended()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(IsAttached());
   MSE_DEBUG("Ended");
   mContentManager->Ended();
   // We want the MediaSourceReader to refresh its buffered range as it may
   // have been modified (end lined up).
-  mMediaSource->GetDecoder()->NotifyDataArrived(/* aThrottleUpdates = */ false);
+  mMediaSource->GetDecoder()->NotifyDataArrived();
 }
 
 SourceBuffer::SourceBuffer(MediaSource* aMediaSource, const nsACString& aType)
   : DOMEventTargetHelper(aMediaSource->GetParentObject())
   , mMediaSource(aMediaSource)
   , mUpdating(false)
   , mActive(false)
   , mUpdateID(0)
@@ -476,19 +476,17 @@ SourceBuffer::AppendDataCompletedWithSuc
   if (aHasActiveTracks) {
     if (!mActive) {
       mActive = true;
       mMediaSource->SourceBufferIsActive(this);
     }
   }
   if (mActive) {
     // Tell our parent decoder that we have received new data.
-    // The information provided do not matter much so long as it is monotonically
-    // increasing.
-    mMediaSource->GetDecoder()->NotifyDataArrived(/* aThrottleUpdates = */ false);
+    mMediaSource->GetDecoder()->NotifyDataArrived();
     // Send progress event.
     mMediaSource->GetDecoder()->NotifyBytesDownloaded();
   }
 
   CheckEndTime();
 
   StopUpdating();
 }
--- a/dom/media/webaudio/BufferDecoder.h
+++ b/dom/media/webaudio/BufferDecoder.h
@@ -52,17 +52,17 @@ public:
                               MediaDecoderEventVisibility aEventVisibility) final override;
   virtual void FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo,
                                 MediaDecoderEventVisibility aEventVisibility) final override;
 
   virtual void OnReadMetadataCompleted() final override;
 
   virtual MediaDecoderOwner* GetOwner() final override;
 
-  virtual void NotifyDataArrived(bool) final override {};
+  virtual void NotifyDataArrived() final override {};
 
 private:
   virtual ~BufferDecoder();
   RefPtr<TaskQueue> mTaskQueueIdentity;
   RefPtr<MediaResource> mResource;
 };
 
 } // namespace mozilla
--- a/dom/media/webspeech/synth/cocoa/OSXSpeechSynthesizerService.h
+++ b/dom/media/webspeech/synth/cocoa/OSXSpeechSynthesizerService.h
@@ -14,17 +14,17 @@
 
 namespace mozilla {
 namespace dom {
 
 class OSXSpeechSynthesizerService final : public nsISpeechService
                                         , public nsIObserver
 {
 public:
-  NS_DECL_ISUPPORTS
+  NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSISPEECHSERVICE
   NS_DECL_NSIOBSERVER
 
   bool Init();
 
   static OSXSpeechSynthesizerService* GetInstance();
   static already_AddRefed<OSXSpeechSynthesizerService> GetInstanceForService();
   static void Shutdown();
--- a/dom/media/webspeech/synth/cocoa/OSXSpeechSynthesizerService.mm
+++ b/dom/media/webspeech/synth/cocoa/OSXSpeechSynthesizerService.mm
@@ -174,77 +174,129 @@ SpeechTaskCallback::OnDidFinishSpeaking(
 {
   mCallback->OnError(aCharacterIndex);
 }
 @end
 
 namespace mozilla {
 namespace dom {
 
+struct OSXVoice
+{
+  OSXVoice() : mIsDefault(false)
+  {
+  }
+
+  nsString mUri;
+  nsString mName;
+  nsString mLocale;
+  bool mIsDefault;
+};
+
 class RegisterVoicesRunnable final : public nsRunnable
 {
 public:
-  explicit RegisterVoicesRunnable(OSXSpeechSynthesizerService* aSpeechService)
+  RegisterVoicesRunnable(OSXSpeechSynthesizerService* aSpeechService,
+                         nsTArray<OSXVoice>& aList)
     : mSpeechService(aSpeechService)
+    , mVoices(aList)
   {
   }
 
-  NS_IMETHOD Run();
+  NS_IMETHOD Run() override;
 
 private:
   ~RegisterVoicesRunnable()
   {
   }
 
-  RefPtr<OSXSpeechSynthesizerService> mSpeechService;
+  // This runnable always use sync mode.  It is unnecesarry to reference object
+  OSXSpeechSynthesizerService* mSpeechService;
+  nsTArray<OSXVoice>& mVoices;
 };
 
 NS_IMETHODIMP
 RegisterVoicesRunnable::Run()
 {
-  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
-
   nsresult rv;
   nsCOMPtr<nsISynthVoiceRegistry> registry =
     do_GetService(NS_SYNTHVOICEREGISTRY_CONTRACTID, &rv);
   if (!registry) {
     return rv;
   }
 
+  for (OSXVoice voice : mVoices) {
+    rv = registry->AddVoice(mSpeechService, voice.mUri, voice.mName, voice.mLocale, true, false);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      continue;
+    }
+
+    if (voice.mIsDefault) {
+      registry->SetDefaultVoice(voice.mUri, true);
+    }
+  }
+  return NS_OK;
+}
+
+class EnumVoicesRunnable final : public nsRunnable
+{
+public:
+  explicit EnumVoicesRunnable(OSXSpeechSynthesizerService* aSpeechService)
+    : mSpeechService(aSpeechService)
+  {
+  }
+
+  NS_IMETHOD Run() override;
+
+private:
+  ~EnumVoicesRunnable()
+  {
+  }
+
+  RefPtr<OSXSpeechSynthesizerService> mSpeechService;
+};
+
+NS_IMETHODIMP
+EnumVoicesRunnable::Run()
+{
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+  nsAutoTArray<OSXVoice, 64> list;
+
   NSArray* voices = [NSSpeechSynthesizer availableVoices];
   NSString* defaultVoice = [NSSpeechSynthesizer defaultVoice];
 
   for (NSString* voice in voices) {
+    OSXVoice item;
+
     NSDictionary* attr = [NSSpeechSynthesizer attributesForVoice:voice];
 
     nsAutoString identifier;
     nsCocoaUtils::GetStringForNSString([attr objectForKey:NSVoiceIdentifier],
                                        identifier);
 
-    nsAutoString name;
-    nsCocoaUtils::GetStringForNSString([attr objectForKey:NSVoiceName], name);
+    nsCocoaUtils::GetStringForNSString([attr objectForKey:NSVoiceName], item.mName);
 
-    nsAutoString locale;
     nsCocoaUtils::GetStringForNSString(
-      [attr objectForKey:NSVoiceLocaleIdentifier], locale);
-    locale.ReplaceChar('_', '-');
+      [attr objectForKey:NSVoiceLocaleIdentifier], item.mLocale);
+    item.mLocale.ReplaceChar('_', '-');
 
-    nsAutoString uri;
-    uri.AssignLiteral("urn:moz-tts:osx:");
-    uri.Append(identifier);
-    rv = registry->AddVoice(mSpeechService, uri, name, locale, true, false);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      continue;
+    item.mUri.AssignLiteral("urn:moz-tts:osx:");
+    item.mUri.Append(identifier);
+
+    if ([voice isEqualToString:defaultVoice]) {
+      item.mIsDefault = true;
     }
 
-    if ([voice isEqualToString:defaultVoice]) {
-      registry->SetDefaultVoice(uri, true);
-    }
+    list.AppendElement(item);
   }
 
+  RefPtr<RegisterVoicesRunnable> runnable = new RegisterVoicesRunnable(mSpeechService, list);
+  NS_DispatchToMainThread(runnable, NS_DISPATCH_SYNC);
+
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
 StaticRefPtr<OSXSpeechSynthesizerService> OSXSpeechSynthesizerService::sSingleton;
 
 NS_INTERFACE_MAP_BEGIN(OSXSpeechSynthesizerService)
@@ -269,19 +321,24 @@ bool
 OSXSpeechSynthesizerService::Init()
 {
   if (Preferences::GetBool("media.webspeech.synth.test") ||
       !Preferences::GetBool("media.webspeech.synth.enabled")) {
     // When test is enabled, we shouldn't add OS backend (Bug 1160844)
     return false;
   }
 
+  nsCOMPtr<nsIThread> thread;
+  if (NS_FAILED(NS_NewNamedThread("SpeechWorker", getter_AddRefs(thread)))) {
+  	return false;
+  }
+
   // Get all the voices and register in the SynthVoiceRegistry
-  nsCOMPtr<nsIRunnable> runnable = new RegisterVoicesRunnable(this);
-  NS_DispatchToMainThread(runnable);
+  nsCOMPtr<nsIRunnable> runnable = new EnumVoicesRunnable(this);
+  thread->Dispatch(runnable, NS_DISPATCH_NORMAL);
 
   mInitialized = true;
   return true;
 }
 
 NS_IMETHODIMP
 OSXSpeechSynthesizerService::Speak(const nsAString& aText,
                                    const nsAString& aUri,
--- a/dom/tests/mochitest/general/test_img_mutations.html
+++ b/dom/tests/mochitest/general/test_img_mutations.html
@@ -28,27 +28,29 @@
     var expectingLoads = 0;
     var afterExpectCallback;
 
     function onImgLoad() {
       ok(expectingLoads > 0, "expected load");
       if (expectingLoads > 0) {
         expectingLoads--;
       }
-      if (!expectingLoads && !expectingErrors) {
+      if (!expectingLoads && !expectingErrors && afterExpectCallback) {
         setTimeout(afterExpectCallback, 0);
+        afterExpectCallback = null;
       }
     }
     function onImgError() {
       ok(expectingErrors > 0, "expected error");
       if (expectingErrors > 0) {
         expectingErrors--;
       }
-      if (!expectingLoads && !expectingErrors) {
+      if (!expectingLoads && !expectingErrors && afterExpectCallback) {
         setTimeout(afterExpectCallback, 0);
+        afterExpectCallback = null;
       }
     }
     function expectEvents(loads, errors, callback) {
       if (!loads && !errors) {
         setTimeout(callback, 0);
       } else {
         expectingLoads += loads;
         expectingErrors += errors;
@@ -145,46 +147,47 @@
     tests.push(function () {
       info("test 7");
       img.srcset = img.srcset;
       is(img.currentSrc, testPNG100, "Should still have testPNG100 as current request");
 
       expectEvents(0, 0, nextTest);
     });
 
-    // Re-binding image to document should be a no-op
+    // re-binding the image to the document should be a no-op
     tests.push(function () {
       info("test 8");
       document.body.appendChild(img);
       is(img.currentSrc, testPNG100, "Should still have testPNG100 as current request");
 
       expectEvents(0, 0, nextTest);
     });
 
     // We should re-run our selection algorithm when any load event occurs
     tests.push(function () {
       info("test 9");
       img.srcset = testPNG50 + " 1x, " + testPNG200 + " 2x";
       is(img.currentSrc, testPNG100, "Should still have testPNG100 as current request");
 
       expectEvents(1, 0, function() {
         is(img.currentSrc, testPNG50, "Should now have testPNG50 as current request");
-        SpecialPowers.pushPrefEnv({'set': [ ["layout.css.devPixelsPerPx", "2.0"] ] },
-                                  function() {
-          // We don't currently dynamically switch, but doing so is
-          // up to the UA so we may in the future. In which case
-          // this test needs to be changed.
-          is(img.currentSrc, testPNG50, "Should now have testPNG50 as current request");
+
+        // The preference change will trigger a load, as the image will change
+        SpecialPowers.pushPrefEnv({'set': [ ["layout.css.devPixelsPerPx", "2.0"] ] });
+        expectEvents(1, 0, function() {
+          is(img.currentSrc, testPNG200, "Should now have testPNG200 as current request");
           img.src = img.src;
-          is(img.currentSrc, testPNG50, "Should still have testPNG50 as current request");
+          is(img.currentSrc, testPNG200, "Should still have testPNG200 as current request");
+          // img.src = img.src is special-cased by the spec. It should always
+          // trigger an load event
           expectEvents(1, 0, function() {
-            is(img.currentSrc, testPNG200, "Should now have testPNG200 as current request");
-            nextTest();
+            is(img.currentSrc, testPNG200, "Should still have testPNG200 as current request");
+            expectEvents(0, 0, nextTest);
           });
-        });
+        })
       });
     });
 
     // Removing srcset attr should async switch back to src
     tests.push(function () {
       info("test 10");
       is(img.currentSrc, testPNG200, "Should have testPNG200 as current request");
 
@@ -201,16 +204,20 @@
     function nextTest() {
       if (tests.length) {
         // Spin event loop to make sure no unexpected image events are
         // pending (unexpected events will assert in the handlers)
         setTimeout(function() {
           (tests.shift())();
         }, 0);
       } else {
+        // Remove the event listeners to prevent the prefenv being popped from
+        // causing test failures.
+        img.removeEventListener("load", onImgLoad);
+        img.removeEventListener("error", onImgError);
         SimpleTest.finish();
       }
     }
 
     addEventListener("load", function() {
       SpecialPowers.pushPrefEnv({'set': [ ["layout.css.devPixelsPerPx", "1.0"],
                                           [ "dom.image.srcset.enabled", true ]] },
                                 function() {
--- a/gfx/2d/convolverLS3.cpp
+++ b/gfx/2d/convolverLS3.cpp
@@ -32,51 +32,52 @@
 
 #if defined(_MIPS_ARCH_LOONGSON3A)
 
 #include "MMIHelpers.h"
 
 namespace skia {
 
 // Convolves horizontally along a single row. The row data is given in
-// |src_data| and continues for the [begin, end) of the filter.
+// |src_data| and continues for the num_values() of the filter.
 void ConvolveHorizontally_LS3(const unsigned char* src_data,
-                               int begin, int end,
                                const ConvolutionFilter1D& filter,
                                unsigned char* out_row) {
+  int num_values = filter.num_values();
   int tmp, filter_offset, filter_length;
-  double zero, mask[3], shuf_50, shuf_fa;
+  double zero, mask[4], shuf_50, shuf_fa;
 
   asm volatile (
     ".set push \n\t"
     ".set arch=loongson3a \n\t"
     "xor %[zero], %[zero], %[zero] \n\t"
     // |mask| will be used to decimate all extra filter coefficients that are
     // loaded by SIMD when |filter_length| is not divisible by 4.
+    // mask[0] is not used in following algorithm.
     "li %[tmp], 1 \n\t"
     "dsll32 %[tmp], 0x10 \n\t"
     "daddiu %[tmp], -1 \n\t"
-    "dmtc1 %[tmp], %[mask2] \n\t"
+    "dmtc1 %[tmp], %[mask3] \n\t"
+    "dsrl %[tmp], 0x10 \n\t"
+    "mtc1 %[tmp], %[mask2] \n\t"
     "dsrl %[tmp], 0x10 \n\t"
     "mtc1 %[tmp], %[mask1] \n\t"
-    "dsrl %[tmp], 0x10 \n\t"
-    "mtc1 %[tmp], %[mask0] \n\t"
     "ori %[tmp], $0, 0x50 \n\t"
     "mtc1 %[tmp], %[shuf_50] \n\t"
     "ori %[tmp], $0, 0xfa \n\t"
     "mtc1 %[tmp], %[shuf_fa] \n\t"
     ".set pop \n\t"
-    :[zero]"=f"(zero), [mask0]"=f"(mask[0]),
-     [mask1]"=f"(mask[1]), [mask2]"=f"(mask[2]),
+    :[zero]"=f"(zero), [mask1]"=f"(mask[1]),
+     [mask2]"=f"(mask[2]), [mask3]"=f"(mask[3]),
      [shuf_50]"=f"(shuf_50), [shuf_fa]"=f"(shuf_fa),
      [tmp]"=&r"(tmp)
   );
 
   // Output one pixel each iteration, calculating all channels (RGBA) together.
-  for (int out_x = begin; out_x < end; out_x++) {
+  for (int out_x = 0; out_x < num_values; out_x++) {
     const ConvolutionFilter1D::Fixed* filter_values =
         filter.FilterForValue(out_x, &filter_offset, &filter_length);
     double accumh, accuml;
     // Compute the first pixel in this row that the filter affects. It will
     // touch |filter_length| pixels (4 bytes each) after this.
     const void *row_to_filter =
         reinterpret_cast<const void*>(&src_data[filter_offset << 2]);
 
@@ -198,17 +199,17 @@ void ConvolveHorizontally_LS3(const unsi
          [src8h]"=&f"(src8h), [src8l]"=&f"(src8l),
          [accumh]"+f"(accumh), [accuml]"+f"(accuml),
          [src16h]"=&f"(src16h), [src16l]"=&f"(src16l),
          [coeffh]"=&f"(coeffh), [coeffl]"=&f"(coeffl),
          [coeff16h]"=&f"(coeff16h), [coeff16l]"=&f"(coeff16l),
          [mul_hih]"=&f"(mul_hih), [mul_hil]"=&f"(mul_hil),
          [mul_loh]"=&f"(mul_loh), [mul_lol]"=&f"(mul_lol)
         :[fval]"r"(filter_values), [rtf]"r"(row_to_filter),
-         [zeroh]"f"(zero), [zerol]"f"(zero), [mask]"f"(mask[r-1]),
+         [zeroh]"f"(zero), [zerol]"f"(zero), [mask]"f"(mask[r]),
          [shuf_50]"f"(shuf_50), [shuf_fa]"f"(shuf_fa)
       );
     }
 
     double t, sra;
     asm volatile (
       ".set push \n\t"
       ".set arch=loongson3a \n\t"
@@ -230,53 +231,54 @@ void ConvolveHorizontally_LS3(const unsi
       :"memory"
     );
 
     out_row += 4;
   }
 }
 
 // Convolves horizontally along four rows. The row data is given in
-// |src_data| and continues for the [begin, end) of the filter.
+// |src_data| and continues for the num_values() of the filter.
 // The algorithm is almost same as |ConvolveHorizontally_LS3|. Please
 // refer to that function for detailed comments.
 void ConvolveHorizontally4_LS3(const unsigned char* src_data[4],
-                                int begin, int end,
                                 const ConvolutionFilter1D& filter,
                                 unsigned char* out_row[4]) {
+  int num_values = filter.num_values();
   int tmp, filter_offset, filter_length;
-  double zero, mask[3], shuf_50, shuf_fa;
+  double zero, mask[4], shuf_50, shuf_fa;
 
   asm volatile (
     ".set push \n\t"
     ".set arch=loongson3a \n\t"
     "xor %[zero], %[zero], %[zero] \n\t"
     // |mask| will be used to decimate all extra filter coefficients that are
     // loaded by SIMD when |filter_length| is not divisible by 4.
+    // mask[0] is not used in following algorithm.
     "li %[tmp], 1 \n\t"
     "dsll32 %[tmp], 0x10 \n\t"
     "daddiu %[tmp], -1 \n\t"
-    "dmtc1 %[tmp], %[mask2] \n\t"
+    "dmtc1 %[tmp], %[mask3] \n\t"
+    "dsrl %[tmp], 0x10 \n\t"
+    "mtc1 %[tmp], %[mask2] \n\t"
     "dsrl %[tmp], 0x10 \n\t"
     "mtc1 %[tmp], %[mask1] \n\t"
-    "dsrl %[tmp], 0x10 \n\t"
-    "mtc1 %[tmp], %[mask0] \n\t"
     "ori %[tmp], $0, 0x50 \n\t"
     "mtc1 %[tmp], %[shuf_50] \n\t"
     "ori %[tmp], $0, 0xfa \n\t"
     "mtc1 %[tmp], %[shuf_fa] \n\t"
     ".set pop \n\t"
-    :[zero]"=f"(zero), [mask0]"=f"(mask[0]),
-     [mask1]"=f"(mask[1]), [mask2]"=f"(mask[2]),
+    :[zero]"=f"(zero), [mask1]"=f"(mask[1]),
+     [mask2]"=f"(mask[2]), [mask3]"=f"(mask[3]),
      [shuf_50]"=f"(shuf_50), [shuf_fa]"=f"(shuf_fa),
      [tmp]"=&r"(tmp)
   );
 
   // Output one pixel each iteration, calculating all channels (RGBA) together.
-  for (int out_x = begin; out_x < end; out_x++) {
+  for (int out_x = 0; out_x < num_values; out_x++) {
     const ConvolutionFilter1D::Fixed* filter_values =
         filter.FilterForValue(out_x, &filter_offset, &filter_length);
     double accum0h, accum0l, accum1h, accum1l;
     double accum2h, accum2l, accum3h, accum3l;
 
     // four pixels in a column per iteration.
     asm volatile (
       ".set push \n\t"
@@ -380,17 +382,17 @@ void ConvolveHorizontally4_LS3(const uns
         /* c1 c1 c1 c1 c0 c0 c0 c0 */
         _mm_punpcklhw(coeff16lo, coeff16lo, coeff16lo)
         _mm_pshuflh(coeff16hi, coeff, shuf_fa)
         _mm_punpcklhw(coeff16hi, coeff16hi, coeff16hi)
         ".set pop \n\t"
         :[coeffh]"=&f"(coeffh), [coeffl]"=&f"(coeffl),
          [coeff16loh]"=&f"(coeff16loh), [coeff16lol]"=&f"(coeff16lol),
          [coeff16hih]"=&f"(coeff16hih), [coeff16hil]"=&f"(coeff16hil)
-        :[fval]"r"(filter_values), [mask]"f"(mask[r-1]),
+        :[fval]"r"(filter_values), [mask]"f"(mask[r]),
          [shuf_50]"f"(shuf_50), [shuf_fa]"f"(shuf_fa)
       );
 
       ITERATION(src_data[0] + start, accum0h, accum0l);
       ITERATION(src_data[1] + start, accum1h, accum1l);
       ITERATION(src_data[2] + start, accum2h, accum2l);
       ITERATION(src_data[3] + start, accum3h, accum3l);
     }
@@ -435,26 +437,27 @@ void ConvolveHorizontally4_LS3(const uns
     out_row[2] += 4;
     out_row[3] += 4;
   }
 }
 
 // Does vertical convolution to produce one output row. The filter values and
 // length are given in the first two parameters. These are applied to each
 // of the rows pointed to in the |source_data_rows| array, with each row
-// being |end - begin| wide.
+// being |pixel_width| wide.
 //
-// The output must have room for |(end - begin) * 4| bytes.
+// The output must have room for |pixel_width * 4| bytes.
 template<bool has_alpha>
 void ConvolveVertically_LS3_impl(const ConvolutionFilter1D::Fixed* filter_values,
                                   int filter_length,
                                   unsigned char* const* source_data_rows,
-                                  int begin, int end,
+                                  int pixel_width,
                                   unsigned char* out_row) {
   uint64_t tmp;
+  int width = pixel_width & ~3;
   double zero, sra, coeff16h, coeff16l;
   double accum0h, accum0l, accum1h, accum1l;
   double accum2h, accum2l, accum3h, accum3l;
   const void *src;
   int out_x;
 
   asm volatile (
     ".set push \n\t"
@@ -463,17 +466,17 @@ void ConvolveVertically_LS3_impl(const C
     "ori %[tmp], $0, %[sk_sra] \n\t"
     "mtc1 %[tmp], %[sra] \n\t"
     ".set pop \n\t"
     :[zero]"=f"(zero), [sra]"=f"(sra), [tmp]"=&r"(tmp)
     :[sk_sra]"i"(ConvolutionFilter1D::kShiftBits)
   );
 
   // Output four pixels per iteration (16 bytes).
-  for (out_x = begin; out_x + 3 < end; out_x += 4) {
+  for (out_x = 0; out_x < width; out_x += 4) {
     // Accumulated result for each pixel. 32 bits per RGBA channel.
     asm volatile (
       ".set push \n\t"
       ".set arch=loongson3a \n\t"
       _mm_xor(accum0, accum0, accum0)
       _mm_xor(accum1, accum1, accum1)
       _mm_xor(accum2, accum2, accum2)
       _mm_xor(accum3, accum3, accum3)
@@ -646,18 +649,17 @@ void ConvolveVertically_LS3_impl(const C
         [out_row]"r"(out_row)
       :"memory"
     );
     out_row += 16;
   }
 
   // When the width of the output is not divisible by 4, We need to save one
   // pixel (4 bytes) each time. And also the fourth pixel is always absent.
-  int r = end - out_x;
-  if (r > 0) {
+  if (pixel_width & 3) {
     asm volatile (
       ".set push \n\t"
       ".set arch=loongson3a \n\t"
       _mm_xor(accum0, accum0, accum0)
       _mm_xor(accum1, accum1, accum1)
       _mm_xor(accum2, accum2, accum2)
       ".set pop \n\t"
       :[accum0h]"=&f"(accum0h), [accum0l]"=&f"(accum0l),
@@ -788,17 +790,17 @@ void ConvolveVertically_LS3_impl(const C
       "li %[tmp], 4 \n\t"
       "mtc1 %[tmp], %[s4] \n\t"
       "li %[tmp], 64 \n\t"
       "mtc1 %[tmp], %[s64] \n\t"
       ".set pop \n\t"
       :[s4]"=f"(s4), [s64]"=f"(s64),
        [tmp]"=&r"(tmp)
     );
-    for (; out_x < end; out_x++) {
+    for (int out_x = width; out_x < pixel_width; out_x++) {
       double t;
 
       asm volatile (
         ".set push \n\t"
         ".set arch=loongson3a \n\t"
         "swc1 %[accum0l], (%[out_row]) \n\t"
         _mm_psrlq(accum0, accum0, s4, s64, t)
         ".set pop \n\t"
@@ -810,22 +812,22 @@ void ConvolveVertically_LS3_impl(const C
       out_row += 4;
     }
   }
 }
 
 void ConvolveVertically_LS3(const ConvolutionFilter1D::Fixed* filter_values,
                              int filter_length,
                              unsigned char* const* source_data_rows,
-                             int begin, int end,
+                             int pixel_width,
                              unsigned char* out_row, bool has_alpha) {
   if (has_alpha) {
     ConvolveVertically_LS3_impl<true>(filter_values, filter_length,
-                                       source_data_rows, begin, end, out_row);
+                                       source_data_rows, pixel_width, out_row);
   } else {
     ConvolveVertically_LS3_impl<false>(filter_values, filter_length,
-                                       source_data_rows, begin, end, out_row);
+                                       source_data_rows, pixel_width, out_row);
   }
 }
 
 }  // namespace skia
 
 #endif /* _MIPS_ARCH_LOONGSON3A */
--- a/gfx/2d/convolverLS3.h
+++ b/gfx/2d/convolverLS3.h
@@ -35,36 +35,34 @@
 
 #include "skia/include/core/SkTypes.h"
 
 namespace skia {
 
 // Convolves horizontally along a single row. The row data is given in
 // |src_data| and continues for the [begin, end) of the filter.
 void ConvolveHorizontally_LS3(const unsigned char* src_data,
-                               int begin, int end,
                                const ConvolutionFilter1D& filter,
                                unsigned char* out_row);
 
 // Convolves horizontally along four rows. The row data is given in
 // |src_data| and continues for the [begin, end) of the filter.
 // The algorithm is almost same as |ConvolveHorizontally_LS3|. Please
 // refer to that function for detailed comments.
 void ConvolveHorizontally4_LS3(const unsigned char* src_data[4],
-                                int begin, int end,
                                 const ConvolutionFilter1D& filter,
                                 unsigned char* out_row[4]);
 
 // Does vertical convolution to produce one output row. The filter values and
 // length are given in the first two parameters. These are applied to each
 // of the rows pointed to in the |source_data_rows| array, with each row
 // being |pixel_width| wide.
 //
 // The output must have room for |pixel_width * 4| bytes.
 void ConvolveVertically_LS3(const ConvolutionFilter1D::Fixed* filter_values,
                              int filter_length,
                              unsigned char* const* source_data_rows,
-                             int begin, int end,
+                             int pixel_width,
                              unsigned char* out_row, bool has_alpha);
 
 }  // namespace skia
 
 #endif  // SKIA_EXT_CONVOLVER_LS3_H_
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -1161,17 +1161,16 @@ OOMTest(JSContext* cx, unsigned argc, Va
             RootedValue result(cx);
             bool ok = JS_CallFunction(cx, cx->global(), function,
                                       HandleValueArray::empty(), &result);
 
             handledOOM = OOM_counter >= OOM_maxAllocations;
             OOM_maxAllocations = UINT32_MAX;
 
             MOZ_ASSERT_IF(ok, !cx->isExceptionPending());
-            MOZ_ASSERT_IF(!ok, cx->isExceptionPending());
 
             // Note that it is possible that the function throws an exception
             // unconnected to OOM, in which case we ignore it. More correct
             // would be to have the caller pass some kind of exception
             // specification and to check the exception against it.
 
             cx->clearPendingException();
             cx->runtime()->hadOutOfMemory = false;
--- a/js/src/jit-test/tests/debug/Script-getOffsetsCoverage-02.js
+++ b/js/src/jit-test/tests/debug/Script-getOffsetsCoverage-02.js
@@ -1,8 +1,9 @@
+// |jit-test| --ion-pgo=off;
 
 // This script check that when we enable / disable the code coverage collection,
 // then we have different results for the getOffsetsCoverage methods.
 
 var g = newGlobal();
 var dbg = Debugger(g);
 var coverageInfo = [];
 var num = 20;
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/bug1219905.js
@@ -0,0 +1,14 @@
+// |jit-test| allow-oom
+
+// We need allow-oom here because the debugger reports an uncaught exception if
+// it hits OOM calling the exception unwind hook. This causes the shell to exit
+// with the OOM reason.
+
+if (!('oomTest' in this))
+    quit();
+
+var g = newGlobal();
+g.parent = this;
+g.eval("new Debugger(parent).onExceptionUnwind = function() {}");
+let finished = false;
+oomTest(() => l);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1223021.js
@@ -0,0 +1,12 @@
+if (!('oomTest' in this))
+    quit();
+
+function f() {
+    return this === null;
+};
+
+function g() {
+    if (!f.apply(9)) {}
+}
+
+oomTest(g);
--- a/js/src/jit-test/tests/ion/recover-object-bug1175233.js
+++ b/js/src/jit-test/tests/ion/recover-object-bug1175233.js
@@ -1,19 +1,19 @@
-// |jit-test| test-join=--no-unboxed-objects
+// |jit-test| test-join=--no-unboxed-objects; --ion-pgo=on
 //
 // Unboxed object optimization might not trigger in all cases, thus we ensure
 // that Scalar Replacement optimization is working well independently of the
 // object representation.
 
 // Ion eager fails the test below because we have not yet created any
 // template object in baseline before running the content of the top-level
 // function.
-if (getJitCompilerOptions()["ion.warmup.trigger"] <= 30)
-    setJitCompilerOption("ion.warmup.trigger", 30);
+if (getJitCompilerOptions()["ion.warmup.trigger"] <= 90)
+    setJitCompilerOption("ion.warmup.trigger", 90);
 
 // This test checks that we are able to remove the getelem & setelem with scalar
 // replacement, so we should not force inline caches, as this would skip the
 // generation of getelem & setelem instructions.
 if (getJitCompilerOptions()["ion.forceinlineCaches"])
     setJitCompilerOption("ion.forceinlineCaches", 0);
 
 var uceFault = function (j) {
@@ -23,18 +23,21 @@ var uceFault = function (j) {
 }
 
 function f(j) {
     var i = Math.pow(2, j) | 0;
     var obj = {
       i: i,
       v: i + i
     };
-    assertRecoveredOnBailout(obj, false); // :TODO: Fixed by Bug 1165348
-    assertRecoveredOnBailout(obj.v, false); // :TODO: Fixed by Bug 1165348
+    // These can only be recovered on bailout iff either we have type
+    // information for the property access in the branch, or the branch is
+    // removed before scalar replacement.
+    assertRecoveredOnBailout(obj, true);
+    assertRecoveredOnBailout(obj.v, true);
     if (uceFault(j) || uceFault(j)) {
         // MObjectState::recover should neither fail,
         // nor coerce its result to an int32.
         assertEq(obj.v, 2 * i);
     }
     return 2 * obj.i;
 }
 
--- a/js/src/jit-test/tests/ion/recover-objects.js
+++ b/js/src/jit-test/tests/ion/recover-objects.js
@@ -1,9 +1,9 @@
-// |jit-test| test-join=--no-unboxed-objects
+// |jit-test| test-join=--no-unboxed-objects; --ion-pgo=on
 //
 // Unboxed object optimization might not trigger in all cases, thus we ensure
 // that Scalar Replacement optimization is working well independently of the
 // object representation.
 
 // Ion eager fails the test below because we have not yet created any
 // template object in baseline before running the content of the top-level
 // function.
@@ -20,17 +20,17 @@ function resumeHere() {}
 var uceFault = function (i) {
     if (i > 98)
         uceFault = function (i) { return true; };
     return false;
 };
 
 
 // Without "use script" in the inner function, the arguments might be
-// obersvable.
+// observable.
 function inline_notSoEmpty1(a, b, c, d) {
     // This function is not strict, so we might be able to observe its
     // arguments, if somebody ever called fun.arguments inside it.
     return { v: (a.v + b.v + c.v + d.v - 10) / 4 };
 }
 var uceFault_notSoEmpty1 = eval(uneval(uceFault).replace('uceFault', 'uceFault_notSoEmpty1'));
 function notSoEmpty1() {
     var a = { v: i };
@@ -45,19 +45,20 @@ function notSoEmpty1() {
     // optimizing these cases by executing recover instruction before the
     // execution of the bailout. This ensures that the observed objects are
     // allocated once and used by the unexpected observation and the bailout.
     assertRecoveredOnBailout(a, true);
     assertRecoveredOnBailout(b, true);
     assertRecoveredOnBailout(c, true);
     assertRecoveredOnBailout(d, true);
     assertRecoveredOnBailout(unused, true);
-    // Scalar Replacement is coming after the branch removal made by GVN, and
-    // the ucefault branch is not taken yet.
-    assertRecoveredOnBailout(res, false);
+    // This can only be recovered on bailout iff either we have type
+    // information for the property access in the branch, or the branch is
+    // removed before scalar replacement.
+    assertRecoveredOnBailout(res, true);
 }
 
 // Check that we can recover objects with their content.
 function inline_notSoEmpty2(a, b, c, d) {
     "use strict";
     return { v: (a.v + b.v + c.v + d.v - 10) / 4 };
 }
 var uceFault_notSoEmpty2 = eval(uneval(uceFault).replace('uceFault', 'uceFault_notSoEmpty2'));
@@ -70,19 +71,20 @@ function notSoEmpty2(i) {
     var res = inline_notSoEmpty2(a, b, c, d);
     if (uceFault_notSoEmpty2(i) || uceFault_notSoEmpty2(i))
         assertEq(i, res.v);
     assertRecoveredOnBailout(a, true);
     assertRecoveredOnBailout(b, true);
     assertRecoveredOnBailout(c, true);
     assertRecoveredOnBailout(d, true);
     assertRecoveredOnBailout(unused, true);
-    // Scalar Replacement is coming after the branch removal made by GVN, and
-    // the ucefault branch is not taken yet.
-    assertRecoveredOnBailout(res, false);
+    // This can only be recovered on bailout iff either we have type
+    // information for the property access in the branch, or the branch is
+    // removed before scalar replacement.
+    assertRecoveredOnBailout(res, true);
 }
 
 // Check that we can recover objects with their content.
 var argFault_observeArg = function (i) {
     if (i > 98)
         return inline_observeArg.arguments[0];
     return { test : i };
 };
--- a/js/src/jit/Bailouts.cpp
+++ b/js/src/jit/Bailouts.cpp
@@ -265,27 +265,31 @@ jit::EnsureHasScopeObjects(JSContext* cx
         !fp.hasCallObj())
     {
         return fp.initFunctionScopeObjects(cx);
     }
     return true;
 }
 
 bool
-jit::CheckFrequentBailouts(JSContext* cx, JSScript* script)
+jit::CheckFrequentBailouts(JSContext* cx, JSScript* script, BailoutKind bailoutKind)
 {
     if (script->hasIonScript()) {
         // Invalidate if this script keeps bailing out without invalidation. Next time
         // we compile this script LICM will be disabled.
         IonScript* ionScript = script->ionScript();
 
-        if (ionScript->numBailouts() >= js_JitOptions.frequentBailoutThreshold &&
-            !script->hadFrequentBailouts())
-        {
-            script->setHadFrequentBailouts();
+        if (ionScript->numBailouts() >= js_JitOptions.frequentBailoutThreshold) {
+            // If we bailout because of the first execution of a basic block,
+            // then we should record which basic block we are returning in,
+            // which should prevent this from happening again.  Also note that
+            // the first execution bailout can be related to an inlined script,
+            // so there is no need to penalize the caller.
+            if (bailoutKind != Bailout_FirstExecution && !script->hadFrequentBailouts())
+                script->setHadFrequentBailouts();
 
             JitSpew(JitSpew_IonInvalidate, "Invalidating due to too many bailouts");
 
             if (!Invalidate(cx, script))
                 return false;
         }
     }
 
--- a/js/src/jit/Bailouts.h
+++ b/js/src/jit/Bailouts.h
@@ -206,14 +206,14 @@ class ExceptionBailoutInfo
 // Returns a BAILOUT_* error code.
 uint32_t ExceptionHandlerBailout(JSContext* cx, const InlineFrameIterator& frame,
                                  ResumeFromException* rfe,
                                  const ExceptionBailoutInfo& excInfo,
                                  bool* overrecursed);
 
 uint32_t FinishBailoutToBaseline(BaselineBailoutInfo* bailoutInfo);
 
-bool CheckFrequentBailouts(JSContext* cx, JSScript* script);
+bool CheckFrequentBailouts(JSContext* cx, JSScript* script, BailoutKind bailoutKind);
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_Bailouts_h */
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -790,16 +790,26 @@ InitFromBailout(JSContext* cx, HandleScr
             return false;
     }
 
     // Get the pc. If we are handling an exception, resume at the pc of the
     // catch or finally block.
     jsbytecode* pc = catchingException ? excInfo->resumePC() : script->offsetToPC(iter.pcOffset());
     bool resumeAfter = catchingException ? false : iter.resumeAfter();
 
+    // When pgo is enabled, increment the counter of the block in which we
+    // resume, as Ion does not keep track of the code coverage.
+    //
+    // We need to do that when pgo is enabled, as after a specific number of
+    // FirstExecution bailouts, we invalidate and recompile the script with
+    // IonMonkey. Failing to increment the counter of the current basic block
+    // might lead to repeated bailouts and invalidations.
+    if (!js_JitOptions.disablePgo && script->hasScriptCounts())
+        script->incHitCount(pc);
+
     JSOp op = JSOp(*pc);
 
     // Fixup inlined JSOP_FUNCALL, JSOP_FUNAPPLY, and accessors on the caller side.
     // On the caller side this must represent like the function wasn't inlined.
     uint32_t pushedSlots = 0;
     AutoValueVector savedCallerArgs(cx);
     bool needToSaveArgs = op == JSOP_FUNAPPLY || IsGetPropPC(pc) || IsSetPropPC(pc);
     if (iter.moreFrames() && (op == JSOP_FUNCALL || needToSaveArgs))
@@ -1885,16 +1895,22 @@ jit::FinishBailoutToBaseline(BaselineBai
       case Bailout_NonSimdFloat32x4Input:
       case Bailout_InitialState:
       case Bailout_Debugger:
       case Bailout_UninitializedThis:
       case Bailout_BadDerivedConstructorReturn:
         // Do nothing.
         break;
 
+      case Bailout_FirstExecution:
+        // Do not return directly, as this was not frequent in the first place,
+        // thus rely on the check for frequent bailouts to recompile the current
+        // script.
+        break;
+
       // Invalid assumption based on baseline code.
       case Bailout_OverflowInvalidate:
       case Bailout_NonStringInputInvalidate:
       case Bailout_DoubleOutput:
       case Bailout_ObjectIdentityOrTypeGuard:
         if (!HandleBaselineInfoBailout(cx, outerScript, innerScript))
             return false;
         break;
@@ -1918,15 +1934,15 @@ jit::FinishBailoutToBaseline(BaselineBai
       case Bailout_IonExceptionDebugMode:
         // Return false to resume in HandleException with reconstructed
         // baseline frame.
         return false;
       default:
         MOZ_CRASH("Unknown bailout kind!");
     }
 
-    if (!CheckFrequentBailouts(cx, outerScript))
+    if (!CheckFrequentBailouts(cx, outerScript, bailoutKind))
         return false;
 
     // We're returning to JIT code, so we should clear the override pc.
     topFrame->clearOverridePc();
     return true;
 }
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -1498,16 +1498,27 @@ OptimizeMIR(MIRGenerator* mir)
     }
 
     gs.spewPass("BuildSSA");
     AssertBasicGraphCoherency(graph);
 
     if (mir->shouldCancel("Start"))
         return false;
 
+    if (!js_JitOptions.disablePgo && !mir->compilingAsmJS()) {
+        AutoTraceLog log(logger, TraceLogger_PruneUnusedBranches);
+        if (!PruneUnusedBranches(mir, graph))
+            return false;
+        gs.spewPass("Prune Unused Branches");
+        AssertBasicGraphCoherency(graph);
+
+        if (mir->shouldCancel("Prune Unused Branches"))
+            return false;
+    }
+
     {
         AutoTraceLog log(logger, TraceLogger_FoldTests);
         if (!FoldTests(graph))
             return false;
         gs.spewPass("Fold Tests");
         AssertBasicGraphCoherency(graph);
 
         if (mir->shouldCancel("Fold Tests"))
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -22,16 +22,400 @@
 
 #include "jit/shared/Lowering-shared-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::DebugOnly;
 
+typedef Vector<MPhi*, 16, SystemAllocPolicy> MPhiVector;
+
+static bool
+FlagPhiInputsAsHavingRemovedUses(MBasicBlock* block, MBasicBlock* succ, MPhiVector& worklist)
+{
+    // When removing an edge between 2 blocks, we might remove the ability of
+    // later phases to figure out that the uses of a Phi should be considered as
+    // a use of all its inputs. Thus we need to mark the Phi inputs as having
+    // removed uses iff the phi has any uses.
+    //
+    //
+    //        +--------------------+         +---------------------+
+    //        |12 MFoo 6           |         |32 MBar 5            |
+    //        |                    |         |                     |
+    //        |   ...              |         |   ...               |
+    //        |                    |         |                     |
+    //        |25 MGoto Block 4    |         |43 MGoto Block 4     |
+    //        +--------------------+         +---------------------+
+    //                   |                              |
+    //             |     |                              |
+    //             |     |                              |
+    //             |     +-----X------------------------+
+    //             |         Edge       |
+    //             |        Removed     |
+    //             |                    |
+    //             |       +------------v-----------+
+    //             |       |50 MPhi 12 32           |
+    //             |       |                        |
+    //             |       |   ...                  |
+    //             |       |                        |
+    //             |       |70 MReturn 50           |
+    //             |       +------------------------+
+    //             |
+    //   - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+    //             |
+    //             v
+    //
+    //    ^   +--------------------+         +---------------------+
+    //   /!\  |12 MConst opt-out   |         |32 MBar 5            |
+    //  '---' |                    |         |                     |
+    //        |   ...              |         |   ...               |
+    //        |78 MBail            |         |                     |
+    //        |80 MUnreachable     |         |43 MGoto Block 4     |
+    //        +--------------------+         +---------------------+
+    //                                                  |
+    //                                                  |
+    //                                                  |
+    //                                  +---------------+
+    //                                  |
+    //                                  |
+    //                                  |
+    //                     +------------v-----------+
+    //                     |50 MPhi 32              |
+    //                     |                        |
+    //                     |   ...                  |
+    //                     |                        |
+    //                     |70 MReturn 50           |
+    //                     +------------------------+
+    //
+    //
+    // If the inputs of the Phi are not flagged as having removed uses, then
+    // later compilation phase might optimize them out. The problem is that a
+    // bailout will use this value and give it back to baseline, which will then
+    // use the OptimizedOut magic value in a computation.
+
+    // Conservative upper limit for the number of Phi instructions which are
+    // visited while looking for uses.
+    const size_t conservativeUsesLimit = 128;
+
+    MOZ_ASSERT(worklist.empty());
+    size_t predIndex = succ->getPredecessorIndex(block);
+    MPhiIterator end = succ->phisEnd();
+    MPhiIterator it = succ->phisBegin();
+    for (; it != end; it++) {
+        MPhi* phi = *it;
+
+        // We are looking to mark the Phi inputs which are used across the edge
+        // between the |block| and its successor |succ|.
+        MDefinition* def = phi->getOperand(predIndex);
+        if (def->isUseRemoved())
+            continue;
+
+        phi->setInWorklist();
+        if (!worklist.append(phi))
+            return false;
+
+        // Fill the work list with all the Phi nodes uses until we reach either:
+        //  - A resume point which uses the Phi as an observable operand.
+        //  - An explicit use of the Phi instruction.
+        //  - An implicit use of the Phi instruction.
+        bool isUsed = false;
+        for (size_t idx = 0; !isUsed && idx < worklist.length(); idx++) {
+            phi = worklist[idx];
+            MUseIterator usesEnd(phi->usesEnd());
+            for (MUseIterator use(phi->usesBegin()); use != usesEnd; use++) {
+                MNode* consumer = (*use)->consumer();
+                if (consumer->isResumePoint()) {
+                    MResumePoint* rp = consumer->toResumePoint();
+                    if (rp->isObservableOperand(*use)) {
+                        // The phi is observable via a resume point operand.
+                        isUsed = true;
+                        break;
+                    }
+                    continue;
+                }
+
+                MDefinition* cdef = consumer->toDefinition();
+                if (!cdef->isPhi()) {
+                    // The phi is explicitly used.
+                    isUsed = true;
+                    break;
+                }
+
+                phi = cdef->toPhi();
+                if (phi->isInWorklist())
+                    continue;
+
+                if (phi->isUseRemoved() || phi->isImplicitlyUsed()) {
+                    // The phi is implicitly used.
+                    isUsed = true;
+                    break;
+                }
+
+                phi->setInWorklist();
+                if (!worklist.append(phi))
+                    return false;
+            }
+
+            // Use a conservative upper bound to avoid iterating too many times
+            // on very large graphs.
+            if (idx >= conservativeUsesLimit) {
+                isUsed = true;
+                break;
+            }
+        }
+
+        if (isUsed)
+            def->setUseRemoved();
+
+        // Remove all the InWorklist flags.
+        while (!worklist.empty()) {
+            phi = worklist.popCopy();
+            phi->setNotInWorklist();
+        }
+    }
+
+    return true;
+}
+
+static bool
+FlagAllOperandsAsHavingRemovedUses(MBasicBlock* block)
+{
+    // Flag all instructions operands as having removed uses.
+    MInstructionIterator end = block->end();
+    for (MInstructionIterator it = block->begin(); it != end; it++) {
+        MInstruction* ins = *it;
+        for (size_t i = 0, e = ins->numOperands(); i < e; i++)
+            ins->getOperand(i)->setUseRemovedUnchecked();
+
+        // Flag observable resume point operands as having removed uses.
+        if (MResumePoint* rp = ins->resumePoint()) {
+            // Note: no need to iterate over the caller's of the resume point as
+            // this is the same as the entry resume point.
+            for (size_t i = 0, e = rp->numOperands(); i < e; i++) {
+                if (!rp->isObservableOperand(i))
+                    continue;
+                rp->getOperand(i)->setUseRemovedUnchecked();
+            }
+        }
+    }
+
+    // Flag observable operands of the entry resume point as having removed uses.
+    MResumePoint* rp = block->entryResumePoint();
+    do {
+        for (size_t i = 0, e = rp->numOperands(); i < e; i++) {
+            if (!rp->isObservableOperand(i))
+                continue;
+            rp->getOperand(i)->setUseRemovedUnchecked();
+        }
+        rp = rp->caller();
+    } while (rp);
+
+    // Flag Phi inputs of the successors has having removed uses.
+    MPhiVector worklist;
+    for (size_t i = 0, e = block->numSuccessors(); i < e; i++) {
+        if (!FlagPhiInputsAsHavingRemovedUses(block, block->getSuccessor(i), worklist))
+            return false;
+    }
+
+    return true;
+}
+
+static void
+RemoveFromSuccessors(MBasicBlock* block)
+{
+    // Remove this block from its successors.
+    size_t numSucc = block->numSuccessors();
+    while (numSucc--) {
+        MBasicBlock* succ = block->getSuccessor(numSucc);
+        if (succ->isDead())
+            continue;
+        JitSpew(JitSpew_Prune, "Remove block edge %d -> %d.", block->id(), succ->id());
+        succ->removePredecessor(block);
+    }
+}
+
+static void
+ConvertToBailingBlock(TempAllocator& alloc, MBasicBlock* block)
+{
+    // Add a bailout instruction.
+    MBail* bail = MBail::New(alloc, Bailout_FirstExecution);
+    MInstruction* bailPoint = block->safeInsertTop();
+    block->insertBefore(block->safeInsertTop(), bail);
+
+    // Discard all remaining instructions.
+    MInstructionIterator clearStart = block->begin(bailPoint);
+    block->discardAllInstructionsStartingAt(clearStart);
+    if (block->outerResumePoint())
+        block->clearOuterResumePoint();
+
+    // And replace the last instruction by the unreachable control instruction.
+    block->end(MUnreachable::New(alloc));
+}
+
+bool
+jit::PruneUnusedBranches(MIRGenerator* mir, MIRGraph& graph)
+{
+    MOZ_ASSERT(!mir->compilingAsmJS(), "AsmJS compilation have no code coverage support.");
+
+    // We do a reverse-post-order traversal, marking basic blocks when the block
+    // have to be converted into bailing blocks, and flagging block as
+    // unreachable if all predecessors are flagged as bailing or unreachable.
+    bool someUnreachable = false;
+    for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); block++) {
+        JitSpew(JitSpew_Prune, "Investigate Block %d:", block->id());
+
+        // Do not touch entry basic blocks.
+        if (*block == graph.osrBlock() || *block == graph.entryBlock())
+            continue;
+
+        // Compute if all the predecessors of this block are either bailling out
+        // or are already flagged as unreachable.
+        bool isUnreachable = true;
+        bool isLoopHeader = block->isLoopHeader();
+        size_t numPred = block->numPredecessors();
+        size_t i = 0;
+        for (; i < numPred; i++) {
+            MBasicBlock* pred = block->getPredecessor(i);
+
+            // The backedge is visited after the loop header, but if the loop
+            // header is unreachable, then we can assume that the backedge would
+            // be unreachable too.
+            if (isLoopHeader && pred == block->backedge())
+                continue;
+
+            // Break if any of the predecessor can continue in this block.
+            if (!pred->isMarked() && !pred->unreachable()) {
+                isUnreachable = false;
+                break;
+            }
+        }
+
+        // Compute if the block should bailout, based on the trivial heuristic
+        // which is that if the block never got visited before, then it is
+        // likely to not be visited after.
+        bool shouldBailout =
+            block->getHitState() == MBasicBlock::HitState::Count &&
+            block->getHitCount() == 0;
+
+        // Check if the predecessors got accessed a large number of times in
+        // comparisons of the current block, in order to know if our attempt at
+        // removing this block is not premature.
+        if (shouldBailout) {
+            size_t p = numPred;
+            size_t predCount = 0;
+            bool isLoopExit = false;
+            while (p--) {
+                MBasicBlock* pred = block->getPredecessor(p);
+                if (pred->getHitState() == MBasicBlock::HitState::Count)
+                    predCount += pred->getHitCount();
+                isLoopExit |= pred->isLoopHeader() && pred->backedge() != *block;
+            }
+
+            // This assumes that instructions are numbered in sequence, which is
+            // the case after IonBuilder creation of basic blocks.
+            size_t numInst = block->rbegin()->id() - block->begin()->id();
+
+            // This sum is not homogeneous but gives good results on benchmarks
+            // like Kraken and Octane. The current block has not been executed
+            // yet, and ...
+            //
+            //   1. If the number of times the predecessor got executed is
+            //      larger, then we are less likely to hit this block.
+            //
+            //   2. If the block is large, then this is likely a corner case,
+            //      and thus we are less likely to hit this block.
+            if (predCount + numInst < 75)
+                shouldBailout = false;
+
+            // If this is the exit block of a loop, then keep this basic
+            // block. This heuristic is useful as a bailout is often much more
+            // costly than a simple exit sequence.
+            if (isLoopExit)
+                shouldBailout = false;
+        }
+
+        // Continue to the next basic block if the current basic block can
+        // remain unchanged.
+        if (!isUnreachable && !shouldBailout)
+            continue;
+
+        JitSpewIndent indent(JitSpew_Prune);
+        someUnreachable = true;
+        if (isUnreachable) {
+            JitSpew(JitSpew_Prune, "Mark block %d as unreachable.", block->id());
+            block->setUnreachable();
+            // If the block is unreachable, then there is no need to convert it
+            // to a bailing block.
+        } else if (shouldBailout) {
+            JitSpew(JitSpew_Prune, "Mark block %d as bailing block.", block->id());
+            block->markUnchecked();
+        }
+
+        // When removing a loop header, we should ensure that its backedge is
+        // removed first, otherwise this triggers an assertion in
+        // removePredecessorsWithoutPhiOperands.
+        if (block->isLoopHeader()) {
+            JitSpew(JitSpew_Prune, "Mark block %d as bailing block. (loop backedge)", block->backedge()->id());
+            block->backedge()->markUnchecked();
+        }
+    }
+
+    // Returns early if nothing changed.
+    if (!someUnreachable)
+        return true;
+
+    JitSpew(JitSpew_Prune, "Convert basic block to bailing blocks, and remove unreachable blocks:");
+    JitSpewIndent indent(JitSpew_Prune);
+
+    // As we are going to remove edges and basic block, we have to mark
+    // instructions which would be needed by baseline if we were to bailout.
+    for (PostorderIterator it(graph.poBegin()); it != graph.poEnd();) {
+        MBasicBlock* block = *it++;
+        if (!block->isMarked() && !block->unreachable())
+            continue;
+
+        FlagAllOperandsAsHavingRemovedUses(block);
+    }
+
+    // Remove the blocks in post-order such that consumers are visited before
+    // the predecessors, the only exception being the Phi nodes of loop headers.
+    for (PostorderIterator it(graph.poBegin()); it != graph.poEnd();) {
+        MBasicBlock* block = *it++;
+        if (!block->isMarked() && !block->unreachable())
+            continue;
+
+        JitSpew(JitSpew_Prune, "Remove / Replace block %d.", block->id());
+        JitSpewIndent indent(JitSpew_Prune);
+
+        // As we are going to replace/remove the last instruction, we first have
+        // to remove this block from the predecessor list of its successors.
+        RemoveFromSuccessors(block);
+
+        // Convert the current basic block to a bailing block which ends with an
+        // Unreachable control instruction.
+        if (block->isMarked()) {
+            JitSpew(JitSpew_Prune, "Convert Block %d to a bailing block.", block->id());
+            if (!graph.alloc().ensureBallast())
+                return false;
+            ConvertToBailingBlock(graph.alloc(), block);
+            block->unmark();
+        }
+
+        // Remove all instructions.
+        if (block->unreachable()) {
+            JitSpew(JitSpew_Prune, "Remove Block %d.", block->id());
+            JitSpewIndent indent(JitSpew_Prune);
+            graph.removeBlock(block);
+        }
+    }
+
+    return true;
+}
+
 static bool
 SplitCriticalEdgesForBlock(MIRGraph& graph, MBasicBlock* block)
 {
     if (block->numSuccessors() < 2)
         return true;
     for (size_t i = 0; i < block->numSuccessors(); i++) {
         MBasicBlock* target = block->getSuccessor(i);
         if (target->numPredecessors() < 2)
@@ -491,17 +875,17 @@ jit::EliminateDeadResumePointOperands(MI
             if (ins->isNewDerivedTypedObject() || ins->isRecoveredOnBailout()) {
                 MOZ_ASSERT(ins->canRecoverOnBailout());
                 continue;
             }
 
             // If the instruction's behavior has been constant folded into a
             // separate instruction, we can't determine precisely where the
             // instruction becomes dead and can't eliminate its uses.
-            if (ins->isImplicitlyUsed())
+            if (ins->isImplicitlyUsed() || ins->isUseRemoved())
                 continue;
 
             // Check if this instruction's result is only used within the
             // current block, and keep track of its last use in a definition
             // (not resume point). This requires the instructions in the block
             // to be numbered, ensured by running this immediately after alias
             // analysis.
             uint32_t maxDefinition = 0;
@@ -604,17 +988,17 @@ jit::EliminateDeadCode(MIRGenerator* mir
     return true;
 }
 
 static inline bool
 IsPhiObservable(MPhi* phi, Observability observe)
 {
     // If the phi has uses which are not reflected in SSA, then behavior in the
     // interpreter may be affected by removing the phi.
-    if (phi->isImplicitlyUsed())
+    if (phi->isImplicitlyUsed() || phi->isUseRemoved())
         return true;
 
     // Check for uses of this phi node outside of other phi nodes.
     // Note that, initially, we skip reading resume points, which we
     // don't count as actual uses. If the only uses are resume points,
     // then the SSA name is never consumed by the program.  However,
     // after optimizations have been performed, it's possible that the
     // actual uses in the program have been (incorrectly) optimized
--- a/js/src/jit/IonAnalysis.h
+++ b/js/src/jit/IonAnalysis.h
@@ -14,16 +14,19 @@
 
 namespace js {
 namespace jit {
 
 class MIRGenerator;
 class MIRGraph;
 
 bool
+PruneUnusedBranches(MIRGenerator* mir, MIRGraph& graph);
+
+bool
 FoldTests(MIRGraph& graph);
 
 bool
 SplitCriticalEdges(MIRGraph& graph);
 
 enum Observability {
     ConservativeObservability,
     AggressiveObservability
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -728,22 +728,19 @@ IonBuilder::analyzeNewLoopTypes(MBasicBl
         }
     }
     return true;
 }
 
 bool
 IonBuilder::pushLoop(CFGState::State initial, jsbytecode* stopAt, MBasicBlock* entry, bool osr,
                      jsbytecode* loopHead, jsbytecode* initialPc,
-                     jsbytecode* bodyStart, jsbytecode* bodyEnd, jsbytecode* exitpc,
-                     jsbytecode* continuepc)
-{
-    if (!continuepc)
-        continuepc = entry->pc();
-
+                     jsbytecode* bodyStart, jsbytecode* bodyEnd,
+                     jsbytecode* exitpc, jsbytecode* continuepc)
+{
     ControlFlowInfo loop(cfgStack_.length(), continuepc);
     if (!loops_.append(loop))
         return false;
 
     CFGState state;
     state.state = initial;
     state.stopAt = stopAt;
     state.loop.bodyStart = bodyStart;
@@ -3128,26 +3125,26 @@ IonBuilder::doWhileLoop(JSOp op, jssrcno
     MOZ_ASSERT(JSOp(*loopHead) == JSOP_LOOPHEAD);
     MOZ_ASSERT(loopHead == ifne + GetJumpOffset(ifne));
 
     jsbytecode* loopEntry = GetNextPc(loopHead);
     bool canOsr = LoopEntryCanIonOsr(loopEntry);
     bool osr = info().hasOsrAt(loopEntry);
 
     if (osr) {
-        MBasicBlock* preheader = newOsrPreheader(current, loopEntry);
+        MBasicBlock* preheader = newOsrPreheader(current, loopEntry, pc);
         if (!preheader)
             return ControlStatus_Error;
         current->end(MGoto::New(alloc(), preheader));
         if (!setCurrentAndSpecializePhis(preheader))
             return ControlStatus_Error;
     }
 
     unsigned stackPhiCount = 0;
-    MBasicBlock* header = newPendingLoopHeader(current, pc, osr, canOsr, stackPhiCount);
+    MBasicBlock* header = newPendingLoopHeader(current, loopEntry, osr, canOsr, stackPhiCount);
     if (!header)
         return ControlStatus_Error;
     current->end(MGoto::New(alloc(), header));
 
     jsbytecode* loophead = GetNextPc(pc);
     jsbytecode* bodyStart = GetNextPc(loophead);
     jsbytecode* bodyEnd = conditionpc;
     jsbytecode* exitpc = GetNextPc(ifne);
@@ -3193,46 +3190,47 @@ IonBuilder::whileOrForInLoop(jssrcnote* 
     MOZ_ASSERT(JSOp(*GetNextPc(pc)) == JSOP_LOOPHEAD);
     MOZ_ASSERT(GetNextPc(pc) == ifne + GetJumpOffset(ifne));
 
     jsbytecode* loopEntry = pc + GetJumpOffset(pc);
     bool canOsr = LoopEntryCanIonOsr(loopEntry);
     bool osr = info().hasOsrAt(loopEntry);
 
     if (osr) {
-        MBasicBlock* preheader = newOsrPreheader(current, loopEntry);
+        MBasicBlock* preheader = newOsrPreheader(current, loopEntry, pc);
         if (!preheader)
             return ControlStatus_Error;
         current->end(MGoto::New(alloc(), preheader));
         if (!setCurrentAndSpecializePhis(preheader))
             return ControlStatus_Error;
     }
 
     unsigned stackPhiCount;
     if (SN_TYPE(sn) == SRC_FOR_OF)
         stackPhiCount = 2;
     else if (SN_TYPE(sn) == SRC_FOR_IN)
         stackPhiCount = 1;
     else
         stackPhiCount = 0;
 
-    MBasicBlock* header = newPendingLoopHeader(current, pc, osr, canOsr, stackPhiCount);
+    MBasicBlock* header = newPendingLoopHeader(current, loopEntry, osr, canOsr, stackPhiCount);
     if (!header)
         return ControlStatus_Error;
     current->end(MGoto::New(alloc(), header));
 
     // Skip past the JSOP_LOOPHEAD for the body start.
     jsbytecode* loopHead = GetNextPc(pc);
     jsbytecode* bodyStart = GetNextPc(loopHead);
     jsbytecode* bodyEnd = pc + GetJumpOffset(pc);
     jsbytecode* exitpc = GetNextPc(ifne);
+    jsbytecode* continuepc = pc;
     if (!analyzeNewLoopTypes(header, bodyStart, exitpc))
         return ControlStatus_Error;
     if (!pushLoop(CFGState::WHILE_LOOP_COND, ifne, header, osr,
-                  loopHead, bodyEnd, bodyStart, bodyEnd, exitpc))
+                  loopHead, bodyEnd, bodyStart, bodyEnd, exitpc, continuepc))
     {
         return ControlStatus_Error;
     }
 
     // Parse the condition first.
     if (!setCurrentAndSpecializePhis(header))
         return ControlStatus_Error;
     if (!jsop_loophead(loopHead))
@@ -3294,26 +3292,26 @@ IonBuilder::forLoop(JSOp op, jssrcnote* 
     MOZ_ASSERT(JSOp(*bodyStart) == JSOP_LOOPHEAD);
     MOZ_ASSERT(ifne + GetJumpOffset(ifne) == bodyStart);
     bodyStart = GetNextPc(bodyStart);
 
     bool osr = info().hasOsrAt(loopEntry);
     bool canOsr = LoopEntryCanIonOsr(loopEntry);
 
     if (osr) {
-        MBasicBlock* preheader = newOsrPreheader(current, loopEntry);
+        MBasicBlock* preheader = newOsrPreheader(current, loopEntry, pc);
         if (!preheader)
             return ControlStatus_Error;
         current->end(MGoto::New(alloc(), preheader));
         if (!setCurrentAndSpecializePhis(preheader))
             return ControlStatus_Error;
     }
 
     unsigned stackPhiCount = 0;
-    MBasicBlock* header = newPendingLoopHeader(current, pc, osr, canOsr, stackPhiCount);
+    MBasicBlock* header = newPendingLoopHeader(current, loopEntry, osr, canOsr, stackPhiCount);
     if (!header)
         return ControlStatus_Error;
     current->end(MGoto::New(alloc(), header));
 
     // If there is no condition, we immediately parse the body. Otherwise, we
     // parse the condition.
     jsbytecode* stopAt;
     CFGState::State initial;
@@ -7275,16 +7273,18 @@ IonBuilder::jsop_initelem_getter_setter(
     return resumeAfter(init);
 }
 
 MBasicBlock*
 IonBuilder::addBlock(MBasicBlock* block, uint32_t loopDepth)
 {
     if (!block)
         return nullptr;
+    if (block->pc() && script()->hasScriptCounts())
+        block->setHitCount(script()->getHitCount(block->pc()));
     graph().addBlock(block);
     block->setLoopDepth(loopDepth);
     return block;
 }
 
 MBasicBlock*
 IonBuilder::newBlock(MBasicBlock* predecessor, jsbytecode* pc)
 {
@@ -7311,42 +7311,47 @@ IonBuilder::newBlockPopN(MBasicBlock* pr
 
 MBasicBlock*
 IonBuilder::newBlockAfter(MBasicBlock* at, MBasicBlock* predecessor, jsbytecode* pc)
 {
     MBasicBlock* block = MBasicBlock::New(graph(), &analysis(), info(), predecessor,
                                           bytecodeSite(pc), MBasicBlock::NORMAL);
     if (!block)
         return nullptr;
+    block->setHitCount(0); // osr block
     graph().insertBlockAfter(at, block);
     return block;
 }
 
 MBasicBlock*
 IonBuilder::newBlock(MBasicBlock* predecessor, jsbytecode* pc, uint32_t loopDepth)
 {
     MBasicBlock* block = MBasicBlock::New(graph(), &analysis(), info(), predecessor,
                                           bytecodeSite(pc), MBasicBlock::NORMAL);
     return addBlock(block, loopDepth);
 }
 
 MBasicBlock*
-IonBuilder::newOsrPreheader(MBasicBlock* predecessor, jsbytecode* loopEntry)
+IonBuilder::newOsrPreheader(MBasicBlock* predecessor, jsbytecode* loopEntry, jsbytecode* beforeLoopEntry)
 {
     MOZ_ASSERT(LoopEntryCanIonOsr(loopEntry));
     MOZ_ASSERT(loopEntry == info().osrPc());
 
     // Create two blocks: one for the OSR entry with no predecessors, one for
     // the preheader, which has the OSR entry block as a predecessor. The
     // OSR block is always the second block (with id 1).
     MBasicBlock* osrBlock  = newBlockAfter(*graph().begin(), loopEntry);
     MBasicBlock* preheader = newBlock(predecessor, loopEntry);
     if (!osrBlock || !preheader)
         return nullptr;
 
+    // Give the pre-header the same hit count as the code before the loop.
+    if (script()->hasScriptCounts())
+        preheader->setHitCount(script()->getHitCount(beforeLoopEntry));
+
     MOsrEntry* entry = MOsrEntry::New(alloc());
     osrBlock->add(entry);
 
     // Initialize |scopeChain|.
     {
         uint32_t slot = info().scopeChainSlot();
 
         MInstruction* scopev;
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -263,27 +263,28 @@ class IonBuilder
     ControlStatus processTryEnd(CFGState& state);
     ControlStatus processReturn(JSOp op);
     ControlStatus processThrow();
     ControlStatus processContinue(JSOp op);
     ControlStatus processBreak(JSOp op, jssrcnote* sn);
     ControlStatus maybeLoop(JSOp op, jssrcnote* sn);
     bool pushLoop(CFGState::State state, jsbytecode* stopAt, MBasicBlock* entry, bool osr,
                   jsbytecode* loopHead, jsbytecode* initialPc,
-                  jsbytecode* bodyStart, jsbytecode* bodyEnd, jsbytecode* exitpc,
-                  jsbytecode* continuepc = nullptr);
+                  jsbytecode* bodyStart, jsbytecode* bodyEnd,
+                  jsbytecode* exitpc, jsbytecode* continuepc);
     bool analyzeNewLoopTypes(MBasicBlock* entry, jsbytecode* start, jsbytecode* end);
 
     MBasicBlock* addBlock(MBasicBlock* block, uint32_t loopDepth);
     MBasicBlock* newBlock(MBasicBlock* predecessor, jsbytecode* pc);
     MBasicBlock* newBlock(MBasicBlock* predecessor, jsbytecode* pc, uint32_t loopDepth);
     MBasicBlock* newBlock(MBasicBlock* predecessor, jsbytecode* pc, MResumePoint* priorResumePoint);
     MBasicBlock* newBlockPopN(MBasicBlock* predecessor, jsbytecode* pc, uint32_t popped);
     MBasicBlock* newBlockAfter(MBasicBlock* at, MBasicBlock* predecessor, jsbytecode* pc);
-    MBasicBlock* newOsrPreheader(MBasicBlock* header, jsbytecode* loopEntry);
+    MBasicBlock* newOsrPreheader(MBasicBlock* header, jsbytecode* loopEntry,
+                                 jsbytecode* beforeLoopEntry);
     MBasicBlock* newPendingLoopHeader(MBasicBlock* predecessor, jsbytecode* pc, bool osr, bool canOsr,
                                       unsigned stackPhiCount);
     MBasicBlock* newBlock(jsbytecode* pc) {
         return newBlock(nullptr, pc);
     }
     MBasicBlock* newBlockAfter(MBasicBlock* at, jsbytecode* pc) {
         return newBlockAfter(at, nullptr, pc);
     }
--- a/js/src/jit/IonTypes.h
+++ b/js/src/jit/IonTypes.h
@@ -112,16 +112,19 @@ enum BailoutKind
     Bailout_Debugger,
 
     // |this| used uninitialized in a derived constructor
     Bailout_UninitializedThis,
 
     // Derived constructors must return object or undefined
     Bailout_BadDerivedConstructorReturn,
 
+    // We hit this code for the first time.
+    Bailout_FirstExecution,
+
     // END Normal bailouts
 
     // Bailouts caused by invalid assumptions based on Baseline code.
     // Causes immediate invalidation.
 
     // Like Bailout_Overflow, but causes immediate invalidation.
     Bailout_OverflowInvalidate,
 
@@ -213,16 +216,18 @@ BailoutKindString(BailoutKind kind)
       case Bailout_InitialState:
         return "Bailout_InitialState";
       case Bailout_Debugger:
         return "Bailout_Debugger";
       case Bailout_UninitializedThis:
         return "Bailout_UninitializedThis";
       case Bailout_BadDerivedConstructorReturn:
         return "Bailout_BadDerivedConstructorReturn";
+      case Bailout_FirstExecution:
+        return "Bailout_FirstExecution";
 
       // Bailouts caused by invalid assumptions.
       case Bailout_OverflowInvalidate:
         return "Bailout_OverflowInvalidate";
       case Bailout_NonStringInputInvalidate:
         return "Bailout_NonStringInputInvalidate";
       case Bailout_DoubleOutput:
         return "Bailout_DoubleOutput";
--- a/js/src/jit/JSONSpewer.cpp
+++ b/js/src/jit/JSONSpewer.cpp
@@ -254,16 +254,18 @@ JSONSpewer::spewMIR(MIRGraph* mir)
 {
     beginObjectProperty("mir");
     beginListProperty("blocks");
 
     for (MBasicBlockIterator block(mir->begin()); block != mir->end(); block++) {
         beginObject();
 
         integerProperty("number", block->id());
+        if (block->getHitState() == MBasicBlock::HitState::Count)
+            integerProperty("count", block->getHitCount());
 
         beginListProperty("attributes");
         if (block->isLoopBackedge())
             stringValue("backedge");
         if (block->isLoopHeader())
             stringValue("loopheader");
         if (block->isSplitEdge())
             stringValue("splitedge");
--- a/js/src/jit/JitOptions.cpp
+++ b/js/src/jit/JitOptions.cpp
@@ -93,16 +93,19 @@ JitOptions::JitOptions()
     SET_DEFAULT(disableInlining, false);
 
     // Toggles whether loop invariant code motion is globally disabled.
     SET_DEFAULT(disableLicm, false);
 
     // Toggles whether Loop Unrolling is globally disabled.
     SET_DEFAULT(disableLoopUnrolling, true);
 
+    // Toggle whether Profile Guided Optimization is globally disabled.
+    SET_DEFAULT(disablePgo, true);
+
     // Toggles whether instruction reordering is globally disabled.
     SET_DEFAULT(disableInstructionReordering, false);
 
     // Toggles whether Range Analysis is globally disabled.
     SET_DEFAULT(disableRangeAnalysis, false);
 
     // Toggle whether eager scalar replacement is globally disabled.
     SET_DEFAULT(disableScalarReplacement, false);
--- a/js/src/jit/JitOptions.h
+++ b/js/src/jit/JitOptions.h
@@ -50,16 +50,17 @@ struct JitOptions
     bool disableAma;
     bool disableEaa;
     bool disableEagerSimdUnbox;
     bool disableEdgeCaseAnalysis;
     bool disableGvn;
     bool disableInlining;
     bool disableLicm;
     bool disableLoopUnrolling;
+    bool disablePgo;
     bool disableInstructionReordering;
     bool disableRangeAnalysis;
     bool disableScalarReplacement;
     bool disableSharedStubs;
     bool disableSincos;
     bool disableSink;
     bool eagerCompilation;
     bool forceInlineCaches;
--- a/js/src/jit/JitSpewer.cpp
+++ b/js/src/jit/JitSpewer.cpp
@@ -83,17 +83,18 @@ class MOZ_RAII AutoLockIonSpewerOutput
     explicit AutoLockIonSpewerOutput(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM);
     ~AutoLockIonSpewerOutput();
 };
 
 // IonSpewer singleton.
 static IonSpewer ionspewer;
 
 static bool LoggingChecked = false;
-static uint32_t LoggingBits = 0;
+static_assert(JitSpew_Terminator <= 64, "Increase the size of the LoggingBits global.");
+static uint64_t LoggingBits = 0;
 static mozilla::Atomic<uint32_t, mozilla::Relaxed> filteredOutCompilations(0);
 
 static const char * const ChannelNames[] =
 {
 #define JITSPEW_CHANNEL(name) #name,
     JITSPEW_CHANNEL_LIST(JITSPEW_CHANNEL)
 #undef JITSPEW_CHANNEL
 };
@@ -395,16 +396,17 @@ jit::CheckLogging()
         fflush(nullptr);
         printf(
             "\n"
             "usage: IONFLAGS=option,option,option,... where options can be:\n"
             "\n"
             "  aborts     Compilation abort messages\n"
             "  scripts    Compiled scripts\n"
             "  mir        MIR information\n"
+            "  prune      Prune unused branches\n"
             "  escape     Escape analysis\n"
             "  alias      Alias analysis\n"
             "  gvn        Global Value Numbering\n"
             "  licm       Loop invariant code motion\n"
             "  sincos     Replace sin/cos by sincos\n"
             "  sink       Sink transformation\n"
             "  regalloc   Register allocation\n"
             "  inline     Inlining\n"
@@ -435,16 +437,18 @@ jit::CheckLogging()
             "  bl-all     All baseline spew\n"
             "\n"
         );
         exit(0);
         /*NOTREACHED*/
     }
     if (ContainsFlag(env, "aborts"))
         EnableChannel(JitSpew_IonAbort);
+    if (ContainsFlag(env, "prune"))
+        EnableChannel(JitSpew_Prune);
     if (ContainsFlag(env, "escape"))
         EnableChannel(JitSpew_Escape);
     if (ContainsFlag(env, "alias"))
         EnableChannel(JitSpew_Alias);
     if (ContainsFlag(env, "scripts"))
         EnableChannel(JitSpew_IonScripts);
     if (ContainsFlag(env, "mir"))
         EnableChannel(JitSpew_IonMIR);
@@ -484,17 +488,17 @@ jit::CheckLogging()
         EnableIonDebugAsyncLogging();
     if (ContainsFlag(env, "logs-sync"))
         EnableIonDebugSyncLogging();
     if (ContainsFlag(env, "profiling"))
         EnableChannel(JitSpew_Profiling);
     if (ContainsFlag(env, "trackopts"))
         EnableChannel(JitSpew_OptimizationTracking);
     if (ContainsFlag(env, "all"))
-        LoggingBits = uint32_t(-1);
+        LoggingBits = uint64_t(-1);
 
     if (ContainsFlag(env, "bl-aborts"))
         EnableChannel(JitSpew_BaselineAbort);
     if (ContainsFlag(env, "bl-scripts"))
         EnableChannel(JitSpew_BaselineScripts);
     if (ContainsFlag(env, "bl-op"))
         EnableChannel(JitSpew_BaselineOp);
     if (ContainsFlag(env, "bl-ic"))
@@ -620,27 +624,27 @@ jit::JitSpewHeader(JitSpewChannel channe
     for (size_t i = ChannelIndentLevel[channel]; i != 0; i--)
         out.put("  ");
 }
 
 bool
 jit::JitSpewEnabled(JitSpewChannel channel)
 {
     MOZ_ASSERT(LoggingChecked);
-    return (LoggingBits & (1 << uint32_t(channel))) && !filteredOutCompilations;
+    return (LoggingBits & (uint64_t(1) << uint32_t(channel))) && !filteredOutCompilations;
 }
 
 void
 jit::EnableChannel(JitSpewChannel channel)
 {
     MOZ_ASSERT(LoggingChecked);
-    LoggingBits |= (1 << uint32_t(channel));
+    LoggingBits |= uint64_t(1) << uint32_t(channel);
 }
 
 void
 jit::DisableChannel(JitSpewChannel channel)
 {
     MOZ_ASSERT(LoggingChecked);
-    LoggingBits &= ~(1 << uint32_t(channel));
+    LoggingBits &= ~(uint64_t(1) << uint32_t(channel));
 }
 
 #endif /* JS_JITSPEW */
 
--- a/js/src/jit/JitSpewer.h
+++ b/js/src/jit/JitSpewer.h
@@ -16,16 +16,18 @@
 #include "jit/JSONSpewer.h"
 #include "js/RootingAPI.h"
 
 namespace js {
 namespace jit {
 
 // New channels may be added below.
 #define JITSPEW_CHANNEL_LIST(_)             \
+    /* Information during sinking */        \
+    _(Prune)                                \
     /* Information during escape analysis */\
     _(Escape)                               \
     /* Information during alias analysis */ \
     _(Alias)                                \
     /* Information during GVN */            \
     _(GVN)                                  \
     /* Information during sincos */         \
     _(Sincos)                               \
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -597,27 +597,37 @@ MDefinition::replaceAllUsesWith(MDefinit
 }
 
 void
 MDefinition::justReplaceAllUsesWith(MDefinition* dom)
 {
     MOZ_ASSERT(dom != nullptr);
     MOZ_ASSERT(dom != this);
 
+    // Carry over the fact the value has uses which are no longer inspectable
+    // with the graph.
+    if (isUseRemoved())
+        dom->setUseRemovedUnchecked();
+
     for (MUseIterator i(usesBegin()), e(usesEnd()); i != e; ++i)
         i->setProducerUnchecked(dom);
     dom->uses_.takeElements(uses_);
 }
 
 void
 MDefinition::justReplaceAllUsesWithExcept(MDefinition* dom)
 {
     MOZ_ASSERT(dom != nullptr);
     MOZ_ASSERT(dom != this);
 
+    // Carry over the fact the value has uses which are no longer inspectable
+    // with the graph.
+    if (isUseRemoved())
+        dom->setUseRemovedUnchecked();
+
     // Move all uses to new dom. Save the use of the dominating instruction.
     MUse *exceptUse = nullptr;
     for (MUseIterator i(usesBegin()), e(usesEnd()); i != e; ++i) {
         if (i->consumer() != dom) {
             i->setProducerUnchecked(dom);
         } else {
             MOZ_ASSERT(!exceptUse);
             exceptUse = *i;
--- a/js/src/jit/MIRGraph.cpp
+++ b/js/src/jit/MIRGraph.cpp
@@ -389,26 +389,29 @@ MBasicBlock::MBasicBlock(MIRGraph& graph
   : unreachable_(false),
     graph_(graph),
     info_(info),
     predecessors_(graph.alloc()),
     stackPosition_(info_.firstStackSlot()),
     numDominated_(0),
     pc_(site->pc()),
     lir_(nullptr),
+    callerResumePoint_(nullptr),
     entryResumePoint_(nullptr),
     outerResumePoint_(nullptr),
     successorWithPhis_(nullptr),
     positionInPhiSuccessor_(0),
     loopDepth_(0),
     kind_(kind),
     mark_(false),
     immediatelyDominated_(graph.alloc()),
     immediateDominator_(nullptr),
-    trackedSite_(site)
+    trackedSite_(site),
+    hitCount_(0),
+    hitState_(HitState::NotDefined)
 #if defined (JS_ION_PERF)
     , lineno_(0u),
     columnIndex_(0u)
 #endif
 {
 }
 
 bool
--- a/js/src/jit/MIRGraph.h
+++ b/js/src/jit/MIRGraph.h
@@ -52,18 +52,16 @@ class MBasicBlock : public TempObject, p
     bool inherit(TempAllocator& alloc, BytecodeAnalysis* analysis, MBasicBlock* pred,
                  uint32_t popped, unsigned stackPhiCount = 0);
     bool inheritResumePoint(MBasicBlock* pred);
     void assertUsesAreNotWithin(MUseIterator use, MUseIterator end);
 
     // This block cannot be reached by any means.
     bool unreachable_;
 
-    MResumePoint* callerResumePoint_;
-
     // Pushes a copy of a local variable or argument.
     void pushVariable(uint32_t slot);
 
     // Sets a variable slot to the top of the stack, correctly creating copies
     // as needed.
     void setVariable(uint32_t slot);
 
     enum ReferencesType {
@@ -600,16 +598,42 @@ class MBasicBlock : public TempObject, p
     }
 
     void dumpStack(GenericPrinter& out);
     void dumpStack();
 
     void dump(GenericPrinter& out);
     void dump();
 
+    // Hit count
+    enum class HitState {
+        // Not hit information is attached to this basic block.
+        NotDefined,
+
+        // The hit information is a raw counter. Note that due to inlining this
+        // counter is not guaranteed to be consistent over the graph.
+        Count,
+
+        // The hit information is a frequency, which is a form of normalized
+        // counter, where a hit-count can be compared against any previous block
+        // in the graph.
+        Frequency
+    };
+    HitState getHitState() const {
+        return hitState_;
+    }
+    void setHitCount(uint64_t count) {
+        hitCount_ = count;
+        hitState_ = HitState::Count;
+    }
+    uint64_t getHitCount() const {
+        MOZ_ASSERT(hitState_ == HitState::Count);
+        return hitCount_;
+    }
+
     // Track bailouts by storing the current pc in MIR instruction added at
     // this cycle. This is also used for tracking calls and optimizations when
     // profiling.
     void updateTrackedSite(BytecodeSite* site) {
         MOZ_ASSERT(site->tree() == trackedSite_->tree());
         trackedSite_ = site;
     }
     BytecodeSite* trackedSite() const {
@@ -631,16 +655,21 @@ class MBasicBlock : public TempObject, p
     FixedList<MDefinition*> slots_;
     uint32_t stackPosition_;
     uint32_t id_;
     uint32_t domIndex_; // Index in the dominator tree.
     uint32_t numDominated_;
     jsbytecode* pc_;
     LBlock* lir_;
 
+    // Copy of a dominator block's outerResumePoint_ which holds the state of
+    // caller frame at the time of the call. If not null, this implies that this
+    // basic block corresponds to an inlined script.
+    MResumePoint* callerResumePoint_;
+
     // Resume point holding baseline-like frame for the PC corresponding to the
     // entry of this basic block.
     MResumePoint* entryResumePoint_;
 
     // Resume point holding baseline-like frame for the PC corresponding to the
     // beginning of the call-site which is being inlined after this block.
     MResumePoint* outerResumePoint_;
 
@@ -658,16 +687,21 @@ class MBasicBlock : public TempObject, p
     // Utility mark for traversal algorithms.
     bool mark_;
 
     Vector<MBasicBlock*, 1, JitAllocPolicy> immediatelyDominated_;
     MBasicBlock* immediateDominator_;
 
     BytecodeSite* trackedSite_;
 
+    // Record the number of times a block got visited. Note, due to inlined
+    // scripts these numbers might not be continuous.
+    uint64_t hitCount_;
+    HitState hitState_;
+
 #if defined(JS_ION_PERF) || defined(DEBUG)
     unsigned lineno_;
     unsigned columnIndex_;
 
   public:
     void setLineno(unsigned l) { lineno_ = l; }
     unsigned lineno() const { return lineno_; }
     void setColumnIndex(unsigned c) { columnIndex_ = c; }
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -13,16 +13,17 @@
 #include "jsfriendapi.h"
 #include "jsgc.h"
 #include "jsiter.h"
 #include "jswatchpoint.h"
 #include "jswrapper.h"
 
 #include "gc/Marking.h"
 #include "jit/JitCompartment.h"
+#include "jit/JitOptions.h"
 #include "js/Date.h"
 #include "js/Proxy.h"
 #include "js/RootingAPI.h"
 #include "proxy/DeadObjectProxy.h"
 #include "vm/Debugger.h"
 #include "vm/StopIterationObject.h"
 #include "vm/WrapperObject.h"
 
@@ -1070,16 +1071,25 @@ JSCompartment::updateDebuggerObservesCov
 
     // If code coverage is enabled by any other means, keep it.
     if (collectCoverage())
         return;
 
     clearScriptCounts();
 }
 
+bool
+JSCompartment::collectCoverage() const
+{
+    return !js_JitOptions.disablePgo ||
+           debuggerObservesCoverage() ||
+           runtimeFromAnyThread()->profilingScripts ||
+           runtimeFromAnyThread()->lcovOutput.isEnabled();
+}
+
 void
 JSCompartment::clearScriptCounts()
 {
     if (!scriptCountsMap)
         return;
 
     // Clear all hasScriptCounts_ flags of JSScript, in order to release all
     // ScriptCounts entry of the current compartment.
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -675,21 +675,17 @@ struct JSCompartment
     bool debuggerObservesCoverage() const {
         static const unsigned Mask = DebuggerObservesCoverage;
         return (debugModeBits & Mask) == Mask;
     }
     void updateDebuggerObservesCoverage();
 
     // The code coverage can be enabled either for each compartment, with the
     // Debugger API, or for the entire runtime.
-    bool collectCoverage() const {
-        return debuggerObservesCoverage() ||
-               runtimeFromAnyThread()->profilingScripts ||
-               runtimeFromAnyThread()->lcovOutput.isEnabled();
-    }
+    bool collectCoverage() const;
     void clearScriptCounts();
 
     bool needsDelazificationForDebugger() const {
         return debugModeBits & DebuggerNeedsDelazification;
     }
 
     /*
      * Schedule the compartment to be delazified. Called from
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1410,25 +1410,56 @@ const js::PCCounts*
 ScriptCounts::maybeGetPCCounts(size_t offset) const {
     PCCounts searched = PCCounts(offset);
     const PCCounts* elem = std::lower_bound(pcCounts_.begin(), pcCounts_.end(), searched);
     if (elem == pcCounts_.end() || elem->pcOffset() != offset)
         return nullptr;
     return elem;
 }
 
+js::PCCounts*
+ScriptCounts::getImmediatePrecedingPCCounts(size_t offset)
+{
+    PCCounts searched = PCCounts(offset);
+    PCCounts* elem = std::lower_bound(pcCounts_.begin(), pcCounts_.end(), searched);
+    if (elem == pcCounts_.end())
+        return &pcCounts_.back();
+    if (elem->pcOffset() == offset)
+        return elem;
+    if (elem != pcCounts_.begin())
+        return elem - 1;
+    return nullptr;
+}
+
 const js::PCCounts*
 ScriptCounts::maybeGetThrowCounts(size_t offset) const {
     PCCounts searched = PCCounts(offset);
     const PCCounts* elem = std::lower_bound(throwCounts_.begin(), throwCounts_.end(), searched);
     if (elem == throwCounts_.end() || elem->pcOffset() != offset)
         return nullptr;
     return elem;
 }
 
+const js::PCCounts*
+ScriptCounts::getImmediatePrecedingThrowCounts(size_t offset) const
+{
+    PCCounts searched = PCCounts(offset);
+    const PCCounts* elem = std::lower_bound(throwCounts_.begin(), throwCounts_.end(), searched);
+    if (elem == throwCounts_.end()) {
+        if (throwCounts_.begin() == throwCounts_.end())
+            return nullptr;
+        return &throwCounts_.back();
+    }
+    if (elem->pcOffset() == offset)
+        return elem;
+    if (elem != throwCounts_.begin())
+        return elem - 1;
+    return nullptr;
+}
+
 js::PCCounts*
 ScriptCounts::getThrowCounts(size_t offset) {
     PCCounts searched = PCCounts(offset);
     PCCounts* elem = std::lower_bound(throwCounts_.begin(), throwCounts_.end(), searched);
     if (elem == throwCounts_.end() || elem->pcOffset() != offset)
         elem = throwCounts_.insert(elem, searched);
     return elem;
 }
@@ -1457,16 +1488,57 @@ JSScript::maybeGetThrowCounts(jsbytecode
 }
 
 js::PCCounts*
 JSScript::getThrowCounts(jsbytecode* pc) {
     MOZ_ASSERT(containsPC(pc));
     return getScriptCounts().getThrowCounts(pcToOffset(pc));
 }
 
+uint64_t
+JSScript::getHitCount(jsbytecode* pc)
+{
+    MOZ_ASSERT(containsPC(pc));
+    if (pc < main())
+        pc = main();
+
+    ScriptCounts& sc = getScriptCounts();
+    size_t targetOffset = pcToOffset(pc);
+    const js::PCCounts* baseCount = sc.getImmediatePrecedingPCCounts(targetOffset);
+    if (!baseCount)
+        return 0;
+    if (baseCount->pcOffset() == targetOffset)
+        return baseCount->numExec();
+    MOZ_ASSERT(baseCount->pcOffset() < targetOffset);
+    uint64_t count = baseCount->numExec();
+    do {
+        const js::PCCounts* throwCount = sc.getImmediatePrecedingThrowCounts(targetOffset);
+        if (!throwCount)
+            return count;
+        if (throwCount->pcOffset() <= baseCount->pcOffset())
+            return count;
+        count -= throwCount->numExec();
+        targetOffset = throwCount->pcOffset() - 1;
+    } while (true);
+}
+
+void
+JSScript::incHitCount(jsbytecode* pc)
+{
+    MOZ_ASSERT(containsPC(pc));
+    if (pc < main())
+        pc = main();
+
+    ScriptCounts& sc = getScriptCounts();
+    js::PCCounts* baseCount = sc.getImmediatePrecedingPCCounts(pcToOffset(pc));
+    if (!baseCount)
+        return;
+    baseCount->numExec()++;
+}
+
 void
 JSScript::addIonCounts(jit::IonScriptCounts* ionCounts)
 {
     ScriptCounts& sc = getScriptCounts();
     if (sc.ionCounts_)
         ionCounts->setPrevious(sc.ionCounts_);
     sc.ionCounts_ = ionCounts;
 }
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -463,20 +463,32 @@ class ScriptCounts
 
     inline ScriptCounts& operator=(ScriptCounts&& src);
 
     // Return the counter used to count the number of visits. Returns null if
     // the element is not found.
     PCCounts* maybeGetPCCounts(size_t offset);
     const PCCounts* maybeGetPCCounts(size_t offset) const;
 
+    // PCCounts are stored at jump-target offsets. This function looks for the
+    // previous PCCount which is in the same basic block as the current offset.
+    PCCounts* getImmediatePrecedingPCCounts(size_t offset);
+
     // Return the counter used to count the number of throws. Returns null if
     // the element is not found.
     const PCCounts* maybeGetThrowCounts(size_t offset) const;
 
+    // Throw counts are stored at the location of each throwing
+    // instruction. This function looks for the previous throw count.
+    //
+    // Note: if the offset of the returned count is higher than the offset of
+    // the immediate preceding PCCount, then this throw happened in the same
+    // basic block.
+    const PCCounts* getImmediatePrecedingThrowCounts(size_t offset) const;
+
     // Return the counter used to count the number of throws. Allocate it if
     // none exists yet. Returns null if the allocation failed.
     PCCounts* getThrowCounts(size_t offset);
 
   private:
     friend class ::JSScript;
     friend struct ScriptAndCounts;
 
@@ -1666,16 +1678,18 @@ class JSScript : public js::gc::TenuredC
     void resetWarmUpResetCounter() { warmUpResetCount = 0; }
 
   public:
     bool initScriptCounts(JSContext* cx);
     js::ScriptCounts& getScriptCounts();
     js::PCCounts* maybeGetPCCounts(jsbytecode* pc);
     const js::PCCounts* maybeGetThrowCounts(jsbytecode* pc);
     js::PCCounts* getThrowCounts(jsbytecode* pc);
+    uint64_t getHitCount(jsbytecode* pc);
+    void incHitCount(jsbytecode* pc); // Used when we bailout out of Ion.
     void addIonCounts(js::jit::IonScriptCounts* ionCounts);
     js::jit::IonScriptCounts* getIonCounts();
     void releaseScriptCounts(js::ScriptCounts* counts);
     void destroyScriptCounts(js::FreeOp* fop);
     // The entry should be removed after using this function.
     void takeOverScriptCountsMapEntry(js::ScriptCounts* entryValue);
 
     jsbytecode* main() {
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -6240,16 +6240,25 @@ SetRuntimeOptions(JSRuntime* rt, const O
         if (strcmp(str, "on") == 0)
             jit::js_JitOptions.disableEdgeCaseAnalysis = false;
         else if (strcmp(str, "off") == 0)
             jit::js_JitOptions.disableEdgeCaseAnalysis = true;
         else
             return OptionFailure("ion-edgecase-analysis", str);
     }
 
+    if (const char* str = op.getStringOption("ion-pgo")) {
+        if (strcmp(str, "on") == 0)
+            jit::js_JitOptions.disablePgo = false;
+        else if (strcmp(str, "off") == 0)
+            jit::js_JitOptions.disablePgo = true;
+        else
+            return OptionFailure("ion-pgo", str);
+    }
+
     if (const char* str = op.getStringOption("ion-range-analysis")) {
         if (strcmp(str, "on") == 0)
             jit::js_JitOptions.disableRangeAnalysis = false;
         else if (strcmp(str, "off") == 0)
             jit::js_JitOptions.disableRangeAnalysis = true;
         else
             return OptionFailure("ion-range-analysis", str);
     }
@@ -6594,16 +6603,18 @@ main(int argc, char** argv, char** envp)
         || !op.addStringOption('\0', "ion-gvn", "[mode]",
                                "Specify Ion global value numbering:\n"
                                "  off: disable GVN\n"
                                "  on:  enable GVN (default)\n")
         || !op.addStringOption('\0', "ion-licm", "on/off",
                                "Loop invariant code motion (default: on, off to disable)")
         || !op.addStringOption('\0', "ion-edgecase-analysis", "on/off",
                                "Find edge cases where Ion can avoid bailouts (default: on, off to disable)")
+        || !op.addStringOption('\0', "ion-pgo", "on/off",
+                               "Profile guided optimization (default: off, on to enable)")
         || !op.addStringOption('\0', "ion-range-analysis", "on/off",
                                "Range analysis (default: on, off to disable)")
 #if defined(__APPLE__)
         || !op.addStringOption('\0', "ion-sincos", "on/off",
                                "Replace sin(x)/cos(x) to sincos(x) (default: on, off to disable)")
 #else
         || !op.addStringOption('\0', "ion-sincos", "on/off",
                                "Replace sin(x)/cos(x) to sincos(x) (default: off, on to enable)")
--- a/js/src/vm/TraceLogging.cpp
+++ b/js/src/vm/TraceLogging.cpp
@@ -677,16 +677,17 @@ TraceLoggerThreadState::init()
         enabledTextIds[TraceLogger_IrregexpExecute] = true;
         enabledTextIds[TraceLogger_Scripts] = true;
         enabledTextIds[TraceLogger_Engine] = true;
     }
 
     if (ContainsFlag(env, "IonCompiler")) {
         enabledTextIds[TraceLogger_IonCompilation] = true;
         enabledTextIds[TraceLogger_IonLinking] = true;
+        enabledTextIds[TraceLogger_PruneUnusedBranches] = true;
         enabledTextIds[TraceLogger_FoldTests] = true;
         enabledTextIds[TraceLogger_SplitCriticalEdges] = true;
         enabledTextIds[TraceLogger_RenumberBlocks] = true;
         enabledTextIds[TraceLogger_ScalarReplacement] = true;
         enabledTextIds[TraceLogger_DominatorTree] = true;
         enabledTextIds[TraceLogger_PhiAnalysis] = true;
         enabledTextIds[TraceLogger_MakeLoopsContiguous] = true;
         enabledTextIds[TraceLogger_ApplyTypes] = true;
--- a/js/src/vm/TraceLoggingTypes.h
+++ b/js/src/vm/TraceLoggingTypes.h
@@ -32,16 +32,17 @@
     _(ParserCompileFunction)                          \
     _(ParserCompileLazy)                              \
     _(ParserCompileScript)                            \
     _(ParserCompileModule)                            \
     _(Scripts)                                        \
     _(VM)                                             \
                                                       \
     /* Specific passes during ion compilation */      \
+    _(PruneUnusedBranches)                            \
     _(FoldTests)                                      \
     _(SplitCriticalEdges)                             \
     _(RenumberBlocks)                                 \
     _(ScalarReplacement)                              \
     _(DominatorTree)                                  \
     _(PhiAnalysis)                                    \
     _(MakeLoopsContiguous)                            \
     _(ApplyTypes)                                     \
new file mode 100644
--- /dev/null
+++ b/layout/style/crashtests/1223694-1.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<script>
+
+function boom()
+{
+    var sheet = document.createElement("style");
+    sheet.scoped = true;
+    document.documentElement.appendChild(sheet);
+}
+
+</script>
+</head>
+<body onload="boom();"></body>
+</html>
--- a/layout/style/crashtests/crashtests.list
+++ b/layout/style/crashtests/crashtests.list
@@ -123,12 +123,13 @@ load 1161320-1.html
 pref(dom.animations-api.core.enabled,true) load 1161320-2.html
 load 1161366-1.html
 load 1163446-1.html
 load 1164813-1.html
 load 1167782-1.html
 load 1186768-1.xhtml
 load 1200568-1.html
 load 1206105-1.html
+load 1223694-1.html
 load border-image-visited-link.html
 load font-face-truncated-src.html 
 load large_border_image_width.html
 load long-url-list-stack-overflow.html
--- a/layout/style/nsCSSRuleProcessor.cpp
+++ b/layout/style/nsCSSRuleProcessor.cpp
@@ -4121,16 +4121,21 @@ AncestorFilter::AssertHasAllAncestors(El
     MOZ_ASSERT(mElements.Contains(cur));
     cur = cur->GetParentElementCrossingShadowRoot();
   }
 }
 
 void
 TreeMatchContext::AssertHasAllStyleScopes(Element* aElement) const
 {
+  if (aElement->IsInNativeAnonymousSubtree()) {
+    // Document style sheets are never applied to native anonymous content,
+    // so it's not possible for them to be in a <style scoped> scope.
+    return;
+  }
   Element* cur = aElement->GetParentElementCrossingShadowRoot();
   while (cur) {
     if (cur->IsScopedStyleRoot()) {
       MOZ_ASSERT(mStyleScopes.Contains(cur));
     }
     cur = cur->GetParentElementCrossingShadowRoot();
   }
 }
--- a/layout/style/test/test_property_syntax_errors.html
+++ b/layout/style/test/test_property_syntax_errors.html
@@ -14,16 +14,17 @@
 <div id="content" style="display: none">
 
 <div id="testnode"></div>
   
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
+SimpleTest.requestLongerTimeout(2);
 SimpleTest.waitForExplicitFinish();
 
 function check_not_accepted(decl, property, info, badval)
 {
   decl.setProperty(property, badval, "");
 
   is(decl.getPropertyValue(property), "",
      "invalid value '" + badval + "' not accepted for '" + property +
--- a/mobile/android/base/toolbar/BrowserToolbar.java
+++ b/mobile/android/base/toolbar/BrowserToolbar.java
@@ -23,33 +23,37 @@ import org.mozilla.gecko.animation.ViewH
 import org.mozilla.gecko.lwt.LightweightTheme;
 import org.mozilla.gecko.lwt.LightweightThemeDrawable;
 import org.mozilla.gecko.menu.GeckoMenu;
 import org.mozilla.gecko.menu.MenuPopup;
 import org.mozilla.gecko.tabs.TabHistoryController;
 import org.mozilla.gecko.toolbar.ToolbarDisplayLayout.OnStopListener;
 import org.mozilla.gecko.toolbar.ToolbarDisplayLayout.OnTitleChangeListener;
 import org.mozilla.gecko.toolbar.ToolbarDisplayLayout.UpdateFlags;
+import org.mozilla.gecko.util.Clipboard;
 import org.mozilla.gecko.util.ColorUtils;
 import org.mozilla.gecko.util.HardwareUtils;
+import org.mozilla.gecko.util.MenuUtils;
 import org.mozilla.gecko.widget.themed.ThemedFrameLayout;
 import org.mozilla.gecko.widget.themed.ThemedImageButton;
 import org.mozilla.gecko.widget.themed.ThemedImageView;
 import org.mozilla.gecko.widget.themed.ThemedRelativeLayout;
 
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.StateListDrawable;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.view.ContextMenu;
 import android.view.LayoutInflater;
+import android.view.MenuInflater;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewTreeObserver.OnGlobalLayoutListener;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.Button;
 import android.widget.LinearLayout;
 import android.widget.PopupWindow;
 
@@ -114,16 +118,17 @@ public abstract class BrowserToolbar ext
 
     private ToolbarProgressView progressBar;
     protected final TabCounter tabsCounter;
     protected final ThemedFrameLayout menuButton;
     protected final ThemedImageView menuIcon;
     private MenuPopup menuPopup;
     protected final List<View> focusOrder;
 
+    private OnActivateListener activateListener;
     private OnFocusChangeListener focusChangeListener;
     private OnStartEditingListener startEditingListener;
     private OnStopEditingListener stopEditingListener;
 
     protected final BrowserApp activity;
     protected boolean hasSoftMenuButton;
 
     protected UIMode uiMode;
@@ -203,16 +208,59 @@ public abstract class BrowserToolbar ext
         shadowPaint.setColor(shadowColor);
         shadowPaint.setStrokeWidth(0.0f);
 
         setUIMode(UIMode.DISPLAY);
 
         prefs = new ToolbarPrefs();
         urlDisplayLayout.setToolbarPrefs(prefs);
         urlEditLayout.setToolbarPrefs(prefs);
+
+        setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() {
+            @Override
+            public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
+                // NOTE: Use MenuUtils.safeSetVisible because some actions might
+                // be on the Page menu
+                MenuInflater inflater = activity.getMenuInflater();
+                inflater.inflate(R.menu.titlebar_contextmenu, menu);
+
+                String clipboard = Clipboard.getText();
+                if (TextUtils.isEmpty(clipboard)) {
+                    menu.findItem(R.id.pasteandgo).setVisible(false);
+                    menu.findItem(R.id.paste).setVisible(false);
+                }
+
+                Tab tab = Tabs.getInstance().getSelectedTab();
+                if (tab != null) {
+                    String url = tab.getURL();
+                    if (url == null) {
+                        menu.findItem(R.id.copyurl).setVisible(false);
+                        menu.findItem(R.id.add_to_launcher).setVisible(false);
+                    }
+
+                    MenuUtils.safeSetVisible(menu, R.id.subscribe, tab.hasFeeds());
+                    MenuUtils.safeSetVisible(menu, R.id.add_search_engine, tab.hasOpenSearch());
+                } else {
+                    // if there is no tab, remove anything tab dependent
+                    menu.findItem(R.id.copyurl).setVisible(false);
+                    menu.findItem(R.id.add_to_launcher).setVisible(false);
+                    MenuUtils.safeSetVisible(menu, R.id.subscribe, false);
+                    MenuUtils.safeSetVisible(menu, R.id.add_search_engine, false);
+                }
+            }
+        });
+
+        setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (activateListener != null) {
+                    activateListener.onActivate();
+                }
+            }
+        });
     }
 
     @Override
     public void onAttachedToWindow() {
         super.onAttachedToWindow();
 
         prefs.open();
 
@@ -321,27 +369,16 @@ public abstract class BrowserToolbar ext
             cancelEdit();
             return true;
         }
 
         return urlDisplayLayout.dismissSiteIdentityPopup();
     }
 
     @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        // If the motion event has occurred below the toolbar (due to the scroll
-        // offset), let it pass through to the page.
-        if (event != null && event.getY() > getHeight() + ViewHelper.getTranslationY(this)) {
-            return false;
-        }
-
-        return super.onTouchEvent(event);
-    }
-
-    @Override
     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
         super.onSizeChanged(w, h, oldw, oldh);
 
         if (h != oldh) {
             // Post this to happen outside of onSizeChanged, as this may cause
             // a layout change and relayouts within a layout change don't work.
             post(new Runnable() {
                 @Override
@@ -630,18 +667,18 @@ public abstract class BrowserToolbar ext
 
         urlEditLayout.onEditSuggestion(suggestion);
     }
 
     public void setTitle(CharSequence title) {
         urlDisplayLayout.setTitle(title);
     }
 
-    public void setOnActivateListener(OnActivateListener listener) {
-        urlDisplayLayout.setOnActivateListener(listener);
+    public void setOnActivateListener(final OnActivateListener listener) {
+        activateListener = listener;
     }
 
     public void setOnCommitListener(OnCommitListener listener) {
         urlEditLayout.setOnCommitListener(listener);
     }
 
     public void setOnDismissListener(OnDismissListener listener) {
         urlEditLayout.setOnDismissListener(listener);
--- a/mobile/android/base/toolbar/ToolbarDisplayLayout.java
+++ b/mobile/android/base/toolbar/ToolbarDisplayLayout.java
@@ -10,46 +10,41 @@ import java.util.EnumSet;
 import java.util.List;
 
 import org.mozilla.gecko.AboutPages;
 import org.mozilla.gecko.AppConstants.Versions;
 import org.mozilla.gecko.BrowserApp;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.ReaderModeUtils;
 import org.mozilla.gecko.SiteIdentity;
+import org.mozilla.gecko.SiteIdentity.MixedMode;
 import org.mozilla.gecko.SiteIdentity.SecurityMode;
-import org.mozilla.gecko.SiteIdentity.MixedMode;
 import org.mozilla.gecko.SiteIdentity.TrackingMode;
 import org.mozilla.gecko.Tab;
-import org.mozilla.gecko.Tabs;
 import org.mozilla.gecko.animation.PropertyAnimator;
 import org.mozilla.gecko.animation.ViewHelper;
 import org.mozilla.gecko.favicons.Favicons;
 import org.mozilla.gecko.toolbar.BrowserToolbarTabletBase.ForwardButtonAnimation;
-import org.mozilla.gecko.util.Clipboard;
 import org.mozilla.gecko.util.ColorUtils;
 import org.mozilla.gecko.util.HardwareUtils;
-import org.mozilla.gecko.util.MenuUtils;
 import org.mozilla.gecko.util.StringUtils;
 import org.mozilla.gecko.widget.themed.ThemedLinearLayout;
 import org.mozilla.gecko.widget.themed.ThemedTextView;
 
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.os.SystemClock;
 import android.text.Spannable;
 import android.text.SpannableStringBuilder;
 import android.text.TextUtils;
 import android.text.style.ForegroundColorSpan;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.view.ContextMenu;
 import android.view.LayoutInflater;
-import android.view.MenuInflater;
 import android.view.View;
 import android.view.animation.AlphaAnimation;
 import android.view.animation.Animation;
 import android.view.animation.TranslateAnimation;
 import android.widget.Button;
 import android.widget.ImageButton;
 
 /**
@@ -141,18 +136,16 @@ public class ToolbarDisplayLayout extend
 
     private PropertyAnimator mForwardAnim;
 
     private final ForegroundColorSpan mUrlColor;
     private final ForegroundColorSpan mBlockedColor;
     private final ForegroundColorSpan mDomainColor;
     private final ForegroundColorSpan mPrivateDomainColor;
 
-    private BrowserToolbar.OnActivateListener mActivateListener;
-
     public ToolbarDisplayLayout(Context context, AttributeSet attrs) {
         super(context, attrs);
         setOrientation(HORIZONTAL);
 
         mActivity = (BrowserApp) context;
 
         LayoutInflater.from(context).inflate(R.layout.toolbar_display_layout, this);
 
@@ -234,66 +227,16 @@ public class ToolbarDisplayLayout extend
 
         mTitleSlideRight = new TranslateAnimation(-slideWidth, 0, 0, 0);
         mTitleSlideRight.setAnimationListener(this);
 
         final int lockAnimDuration = 300;
         mLockFadeIn.setDuration(lockAnimDuration);
         mTitleSlideLeft.setDuration(lockAnimDuration);
         mTitleSlideRight.setDuration(lockAnimDuration);
-
-        // Context menu
-        mTitle.setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() {
-            @Override
-            public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
-                // NOTE: Use MenuUtils.safeSetVisible because some actions might
-                // be on the Page menu
-                MenuInflater inflater = mActivity.getMenuInflater();
-                inflater.inflate(R.menu.titlebar_contextmenu, menu);
-
-                String clipboard = Clipboard.getText();
-                if (TextUtils.isEmpty(clipboard)) {
-                    menu.findItem(R.id.pasteandgo).setVisible(false);
-                    menu.findItem(R.id.paste).setVisible(false);
-                }
-
-                Tab tab = Tabs.getInstance().getSelectedTab();
-                if (tab != null) {
-                    String url = tab.getURL();
-                    if (url == null) {
-                        menu.findItem(R.id.copyurl).setVisible(false);
-                        menu.findItem(R.id.add_to_launcher).setVisible(false);
-                    }
-
-                    MenuUtils.safeSetVisible(menu, R.id.subscribe, tab.hasFeeds());
-                    MenuUtils.safeSetVisible(menu, R.id.add_search_engine, tab.hasOpenSearch());
-                }
-                else {
-                    // if there is no tab, remove anything tab dependent
-                    menu.findItem(R.id.copyurl).setVisible(false);
-                    menu.findItem(R.id.add_to_launcher).setVisible(false);
-                    MenuUtils.safeSetVisible(menu, R.id.subscribe, false);
-                    MenuUtils.safeSetVisible(menu, R.id.add_search_engine, false);
-                }
-            }
-        });
-
-        // Edit activation
-        mTitle.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                if (mActivateListener != null) {
-                    mActivateListener.onActivate();
-                }
-            }
-        });
-    }
-
-    public void setOnActivateListener(BrowserToolbar.OnActivateListener listener) {
-        mActivateListener = listener;
     }
 
     @Override
     public void onDetachedFromWindow() {
         super.onDetachedFromWindow();
         mIsAttached = false;
     }
 
--- a/netwerk/cache2/CacheIndex.cpp
+++ b/netwerk/cache2/CacheIndex.cpp
@@ -67,29 +67,29 @@ class CacheIndexEntryAutoManage
 {
 public:
   CacheIndexEntryAutoManage(const SHA1Sum::Hash *aHash, CacheIndex *aIndex)
     : mIndex(aIndex)
     , mOldRecord(nullptr)
     , mDoNotSearchInIndex(false)
     , mDoNotSearchInUpdates(false)
   {
-    mIndex->AssertOwnsLock();
+    CacheIndex::sLock.AssertCurrentThreadOwns();
 
     mHash = aHash;
     const CacheIndexEntry *entry = FindEntry();
     mIndex->mIndexStats.BeforeChange(entry);
     if (entry && entry->IsInitialized() && !entry->IsRemoved()) {
       mOldRecord = entry->mRec;
     }
   }
 
   ~CacheIndexEntryAutoManage()
   {
-    mIndex->AssertOwnsLock();
+    CacheIndex::sLock.AssertCurrentThreadOwns();
 
     const CacheIndexEntry *entry = FindEntry();
     mIndex->mIndexStats.AfterChange(entry);
     if (!entry || !entry->IsInitialized() || entry->IsRemoved()) {
       entry = nullptr;
     }
 
     if (entry && !mOldRecord) {
@@ -158,17 +158,17 @@ public:
   NS_DECL_THREADSAFE_ISUPPORTS
 
   explicit FileOpenHelper(CacheIndex* aIndex)
     : mIndex(aIndex)
     , mCanceled(false)
   {}
 
   void Cancel() {
-    mIndex->AssertOwnsLock();
+    CacheIndex::sLock.AssertCurrentThreadOwns();
     mCanceled = true;
   }
 
 private:
   virtual ~FileOpenHelper() {}
 
   NS_IMETHOD OnFileOpened(CacheFileHandle *aHandle, nsresult aResult) override;
   NS_IMETHOD OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
@@ -196,17 +196,17 @@ private:
 
   RefPtr<CacheIndex> mIndex;
   bool                 mCanceled;
 };
 
 NS_IMETHODIMP FileOpenHelper::OnFileOpened(CacheFileHandle *aHandle,
                                            nsresult aResult)
 {
-  CacheIndexAutoLock lock(mIndex);
+  StaticMutexAutoLock lock(CacheIndex::sLock);
 
   if (mCanceled) {
     if (aHandle) {
       CacheFileIOManager::DoomFile(aHandle, nullptr);
     }
 
     return NS_OK;
   }
@@ -215,95 +215,74 @@ NS_IMETHODIMP FileOpenHelper::OnFileOpen
 
   return NS_OK;
 }
 
 NS_IMPL_ISUPPORTS(FileOpenHelper, CacheFileIOListener);
 
 
 CacheIndex * CacheIndex::gInstance = nullptr;
+StaticMutex  CacheIndex::sLock;
 
 
 NS_IMPL_ADDREF(CacheIndex)
 NS_IMPL_RELEASE(CacheIndex)
 
 NS_INTERFACE_MAP_BEGIN(CacheIndex)
   NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileIOListener)
   NS_INTERFACE_MAP_ENTRY(nsIRunnable)
 NS_INTERFACE_MAP_END_THREADSAFE
 
 
 CacheIndex::CacheIndex()
-  : mLock("CacheIndex.mLock")
-  , mState(INITIAL)
+  : mState(INITIAL)
   , mShuttingDown(false)
   , mIndexNeedsUpdate(false)
   , mRemovingAll(false)
   , mIndexOnDiskIsValid(false)
   , mDontMarkIndexClean(false)
   , mIndexTimeStamp(0)
   , mUpdateEventPending(false)
   , mSkipEntries(0)
   , mProcessEntries(0)
   , mRWBuf(nullptr)
   , mRWBufSize(0)
   , mRWBufPos(0)
   , mJournalReadSuccessfully(false)
 {
+  sLock.AssertCurrentThreadOwns();
   LOG(("CacheIndex::CacheIndex [this=%p]", this));
   MOZ_COUNT_CTOR(CacheIndex);
   MOZ_ASSERT(!gInstance, "multiple CacheIndex instances!");
 }
 
 CacheIndex::~CacheIndex()
 {
   LOG(("CacheIndex::~CacheIndex [this=%p]", this));
   MOZ_COUNT_DTOR(CacheIndex);
 
   ReleaseBuffer();
 }
 
-void
-CacheIndex::Lock()
-{
-  mLock.Lock();
-
-  MOZ_ASSERT(!mIndexStats.StateLogged());
-}
-
-void
-CacheIndex::Unlock()
-{
-  MOZ_ASSERT(!mIndexStats.StateLogged());
-
-  mLock.Unlock();
-}
-
-inline void
-CacheIndex::AssertOwnsLock()
-{
-  mLock.AssertCurrentThreadOwns();
-}
-
 // static
 nsresult
 CacheIndex::Init(nsIFile *aCacheDirectory)
 {
   LOG(("CacheIndex::Init()"));
 
   MOZ_ASSERT(NS_IsMainThread());
 
+  StaticMutexAutoLock lock(sLock);
+
   if (gInstance) {
     return NS_ERROR_ALREADY_INITIALIZED;
   }
 
   RefPtr<CacheIndex> idx = new CacheIndex();
 
-  CacheIndexAutoLock lock(idx);
-
   nsresult rv = idx->InitInternal(aCacheDirectory);
   NS_ENSURE_SUCCESS(rv, rv);
 
   idx.swap(gInstance);
   return NS_OK;
 }
 
 nsresult
@@ -320,29 +299,29 @@ CacheIndex::InitInternal(nsIFile *aCache
 
   return NS_OK;
 }
 
 // static
 nsresult
 CacheIndex::PreShutdown()
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  StaticMutexAutoLock lock(sLock);
+
   LOG(("CacheIndex::PreShutdown() [gInstance=%p]", gInstance));
 
-  MOZ_ASSERT(NS_IsMainThread());
-
   nsresult rv;
   RefPtr<CacheIndex> index = gInstance;
 
   if (!index) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
-  CacheIndexAutoLock lock(index);
-
   LOG(("CacheIndex::PreShutdown() - [state=%d, indexOnDiskIsValid=%d, "
        "dontMarkIndexClean=%d]", index->mState, index->mIndexOnDiskIsValid,
        index->mDontMarkIndexClean));
 
   LOG(("CacheIndex::PreShutdown() - Closing iterators."));
   for (uint32_t i = 0; i < index->mIterators.Length(); ) {
     rv = index->mIterators[i]->CloseInternal(NS_ERROR_FAILURE);
     if (NS_FAILED(rv)) {
@@ -377,17 +356,17 @@ CacheIndex::PreShutdown()
   }
 
   return NS_OK;
 }
 
 void
 CacheIndex::PreShutdownInternal()
 {
-  CacheIndexAutoLock lock(this);
+  StaticMutexAutoLock lock(sLock);
 
   LOG(("CacheIndex::PreShutdownInternal() - [state=%d, indexOnDiskIsValid=%d, "
        "dontMarkIndexClean=%d]", mState, mIndexOnDiskIsValid,
        mDontMarkIndexClean));
 
   MOZ_ASSERT(mShuttingDown);
 
   if (mUpdateTimer) {
@@ -415,29 +394,29 @@ CacheIndex::PreShutdownInternal()
   // We should end up in READY state
   MOZ_ASSERT(mState == READY);
 }
 
 // static
 nsresult
 CacheIndex::Shutdown()
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  StaticMutexAutoLock lock(sLock);
+
   LOG(("CacheIndex::Shutdown() [gInstance=%p]", gInstance));
 
-  MOZ_ASSERT(NS_IsMainThread());
-
   RefPtr<CacheIndex> index;
   index.swap(gInstance);
 
   if (!index) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
-  CacheIndexAutoLock lock(index);
-
   bool sanitize = CacheObserver::ClearCacheOnShutdown();
 
   LOG(("CacheIndex::Shutdown() - [state=%d, indexOnDiskIsValid=%d, "
        "dontMarkIndexClean=%d, sanitize=%d]", index->mState,
        index->mIndexOnDiskIsValid, index->mDontMarkIndexClean, sanitize));
 
   MOZ_ASSERT(index->mShuttingDown);
 
@@ -481,26 +460,26 @@ CacheIndex::Shutdown()
 }
 
 // static
 nsresult
 CacheIndex::AddEntry(const SHA1Sum::Hash *aHash)
 {
   LOG(("CacheIndex::AddEntry() [hash=%08x%08x%08x%08x%08x]", LOGSHA1(aHash)));
 
+  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
+
+  StaticMutexAutoLock lock(sLock);
+
   RefPtr<CacheIndex> index = gInstance;
 
   if (!index) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
-  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
-
-  CacheIndexAutoLock lock(index);
-
   if (!index->IsIndexUsable()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   // Getters in CacheIndexStats assert when mStateLogged is true since the
   // information is incomplete between calls to BeforeChange() and AfterChange()
   // (i.e. while CacheIndexEntryAutoManage exists). We need to check whether
   // non-fresh entries exists outside the scope of CacheIndexEntryAutoManage.
@@ -596,26 +575,26 @@ CacheIndex::AddEntry(const SHA1Sum::Hash
 
 // static
 nsresult
 CacheIndex::EnsureEntryExists(const SHA1Sum::Hash *aHash)
 {
   LOG(("CacheIndex::EnsureEntryExists() [hash=%08x%08x%08x%08x%08x]",
        LOGSHA1(aHash)));
 
+  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
+
+  StaticMutexAutoLock lock(sLock);
+
   RefPtr<CacheIndex> index = gInstance;
 
   if (!index) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
-  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
-
-  CacheIndexAutoLock lock(index);
-
   if (!index->IsIndexUsable()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   {
     CacheIndexEntryAutoManage entryMng(aHash, index);
 
     CacheIndexEntry *entry = index->mIndex.GetEntry(*aHash);
@@ -707,26 +686,26 @@ CacheIndex::InitEntry(const SHA1Sum::Has
                       bool                 aAnonymous,
                       bool                 aInBrowser,
                       bool                 aPinned)
 {
   LOG(("CacheIndex::InitEntry() [hash=%08x%08x%08x%08x%08x, appId=%u, "
        "anonymous=%d, inBrowser=%d, pinned=%d]", LOGSHA1(aHash), aAppId,
        aAnonymous, aInBrowser, aPinned));
 
+  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
+
+  StaticMutexAutoLock lock(sLock);
+
   RefPtr<CacheIndex> index = gInstance;
 
   if (!index) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
-  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
-
-  CacheIndexAutoLock lock(index);
-
   if (!index->IsIndexUsable()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   {
     CacheIndexEntryAutoManage entryMng(aHash, index);
 
     CacheIndexEntry *entry = index->mIndex.GetEntry(*aHash);
@@ -816,26 +795,26 @@ CacheIndex::InitEntry(const SHA1Sum::Has
 
 // static
 nsresult
 CacheIndex::RemoveEntry(const SHA1Sum::Hash *aHash)
 {
   LOG(("CacheIndex::RemoveEntry() [hash=%08x%08x%08x%08x%08x]",
        LOGSHA1(aHash)));
 
+  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
+
+  StaticMutexAutoLock lock(sLock);
+
   RefPtr<CacheIndex> index = gInstance;
 
   if (!index) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
-  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
-
-  CacheIndexAutoLock lock(index);
-
   if (!index->IsIndexUsable()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   {
     CacheIndexEntryAutoManage entryMng(aHash, index);
 
     CacheIndexEntry *entry = index->mIndex.GetEntry(*aHash);
@@ -917,26 +896,26 @@ CacheIndex::UpdateEntry(const SHA1Sum::H
                         const uint32_t      *aSize)
 {
   LOG(("CacheIndex::UpdateEntry() [hash=%08x%08x%08x%08x%08x, "
        "frecency=%s, expirationTime=%s, size=%s]", LOGSHA1(aHash),
        aFrecency ? nsPrintfCString("%u", *aFrecency).get() : "",
        aExpirationTime ? nsPrintfCString("%u", *aExpirationTime).get() : "",
        aSize ? nsPrintfCString("%u", *aSize).get() : ""));
 
+  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
+
+  StaticMutexAutoLock lock(sLock);
+
   RefPtr<CacheIndex> index = gInstance;
 
   if (!index) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
-  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
-
-  CacheIndexAutoLock lock(index);
-
   if (!index->IsIndexUsable()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   {
     CacheIndexEntryAutoManage entryMng(aHash, index);
 
     CacheIndexEntry *entry = index->mIndex.GetEntry(*aHash);
@@ -1014,28 +993,28 @@ CacheIndex::UpdateEntry(const SHA1Sum::H
 }
 
 // static
 nsresult
 CacheIndex::RemoveAll()
 {
   LOG(("CacheIndex::RemoveAll()"));
 
-  RefPtr<CacheIndex> index = gInstance;
-
-  if (!index) {
-    return NS_ERROR_NOT_INITIALIZED;
-  }
-
   MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
 
   nsCOMPtr<nsIFile> file;
 
   {
-    CacheIndexAutoLock lock(index);
+    StaticMutexAutoLock lock(sLock);
+
+    RefPtr<CacheIndex> index = gInstance;
+
+    if (!index) {
+      return NS_ERROR_NOT_INITIALIZED;
+    }
 
     MOZ_ASSERT(!index->mRemovingAll);
 
     if (!index->IsIndexUsable()) {
       return NS_ERROR_NOT_AVAILABLE;
     }
 
     AutoRestore<bool> saveRemovingAll(index->mRemovingAll);
@@ -1120,24 +1099,24 @@ CacheIndex::HasEntry(const nsACString &a
 
   return HasEntry(hash, _retval, _pinned);
 }
 
 // static
 nsresult
 CacheIndex::HasEntry(const SHA1Sum::Hash &hash, EntryStatus *_retval, bool *_pinned)
 {
+  StaticMutexAutoLock lock(sLock);
+
   RefPtr<CacheIndex> index = gInstance;
 
   if (!index) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
-  CacheIndexAutoLock lock(index);
-
   if (!index->IsIndexUsable()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   if (_pinned) {
     *_pinned = false;
   }
 
@@ -1186,25 +1165,25 @@ CacheIndex::HasEntry(const SHA1Sum::Hash
 }
 
 // static
 nsresult
 CacheIndex::GetEntryForEviction(bool aIgnoreEmptyEntries, SHA1Sum::Hash *aHash, uint32_t *aCnt)
 {
   LOG(("CacheIndex::GetEntryForEviction()"));
 
+  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
+
+  StaticMutexAutoLock lock(sLock);
+
   RefPtr<CacheIndex> index = gInstance;
 
   if (!index)
     return NS_ERROR_NOT_INITIALIZED;
 
-  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
-
-  CacheIndexAutoLock lock(index);
-
   if (!index->IsIndexUsable()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   SHA1Sum::Hash hash;
   bool foundEntry = false;
   uint32_t i;
 
@@ -1262,69 +1241,69 @@ bool CacheIndex::IsForcedValidEntry(cons
 
 
 // static
 nsresult
 CacheIndex::GetCacheSize(uint32_t *_retval)
 {
   LOG(("CacheIndex::GetCacheSize()"));
 
+  StaticMutexAutoLock lock(sLock);
+
   RefPtr<CacheIndex> index = gInstance;
 
   if (!index)
     return NS_ERROR_NOT_INITIALIZED;
 
-  CacheIndexAutoLock lock(index);
-
   if (!index->IsIndexUsable()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   *_retval = index->mIndexStats.Size();
   LOG(("CacheIndex::GetCacheSize() - returning %u", *_retval));
   return NS_OK;
 }
 
 // static
 nsresult
 CacheIndex::GetEntryFileCount(uint32_t *_retval)
 {
   LOG(("CacheIndex::GetEntryFileCount()"));
 
+  StaticMutexAutoLock lock(sLock);
+
   RefPtr<CacheIndex> index = gInstance;
 
   if (!index) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
-  CacheIndexAutoLock lock(index);
-
   if (!index->IsIndexUsable()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   *_retval = index->mIndexStats.ActiveEntriesCount();
   LOG(("CacheIndex::GetEntryFileCount() - returning %u", *_retval));
   return NS_OK;
 }
 
 // static
 nsresult
 CacheIndex::GetCacheStats(nsILoadContextInfo *aInfo, uint32_t *aSize, uint32_t *aCount)
 {
   LOG(("CacheIndex::GetCacheStats() [info=%p]", aInfo));
 
+  StaticMutexAutoLock lock(sLock);
+
   RefPtr<CacheIndex> index = gInstance;
 
   if (!index) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
-  CacheIndexAutoLock lock(index);
-
   if (!index->IsIndexUsable()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   if (!aInfo) {
     return NS_ERROR_INVALID_ARG;
   }
 
@@ -1344,24 +1323,24 @@ CacheIndex::GetCacheStats(nsILoadContext
 }
 
 // static
 nsresult
 CacheIndex::AsyncGetDiskConsumption(nsICacheStorageConsumptionObserver* aObserver)
 {
   LOG(("CacheIndex::AsyncGetDiskConsumption()"));
 
+  StaticMutexAutoLock lock(sLock);
+
   RefPtr<CacheIndex> index = gInstance;
 
   if (!index) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
-  CacheIndexAutoLock lock(index);
-
   if (!index->IsIndexUsable()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   RefPtr<DiskConsumptionObserver> observer =
     DiskConsumptionObserver::Init(aObserver);
 
   NS_ENSURE_ARG(observer);
@@ -1383,24 +1362,24 @@ CacheIndex::AsyncGetDiskConsumption(nsIC
 
 // static
 nsresult
 CacheIndex::GetIterator(nsILoadContextInfo *aInfo, bool aAddNew,
                         CacheIndexIterator **_retval)
 {
   LOG(("CacheIndex::GetIterator() [info=%p, addNew=%d]", aInfo, aAddNew));
 
+  StaticMutexAutoLock lock(sLock);
+
   RefPtr<CacheIndex> index = gInstance;
 
   if (!index) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
-  CacheIndexAutoLock lock(index);
-
   if (!index->IsIndexUsable()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   RefPtr<CacheIndexIterator> iter;
   if (aInfo) {
     iter = new CacheIndexContextIterator(index, aAddNew, aInfo);
   } else {
@@ -1416,24 +1395,24 @@ CacheIndex::GetIterator(nsILoadContextIn
 }
 
 // static
 nsresult
 CacheIndex::IsUpToDate(bool *_retval)
 {
   LOG(("CacheIndex::IsUpToDate()"));
 
+  StaticMutexAutoLock lock(sLock);
+
   RefPtr<CacheIndex> index = gInstance;
 
   if (!index) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
-  CacheIndexAutoLock lock(index);
-
   if (!index->IsIndexUsable()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   *_retval = (index->mState == READY || index->mState == WRITING) &&
              !index->mIndexNeedsUpdate && !index->mShuttingDown;
 
   LOG(("CacheIndex::IsUpToDate() - returning %p", *_retval));
@@ -1508,17 +1487,17 @@ CacheIndex::HasEntryChanged(CacheIndexEn
   return false;
 }
 
 void
 CacheIndex::ProcessPendingOperations()
 {
   LOG(("CacheIndex::ProcessPendingOperations()"));
 
-  AssertOwnsLock();
+  sLock.AssertCurrentThreadOwns();
 
   for (auto iter = mPendingUpdates.Iter(); !iter.Done(); iter.Next()) {
     CacheIndexEntryUpdate* update = iter.Get();
 
     LOG(("CacheIndex::ProcessPendingOperations() [hash=%08x%08x%08x%08x%08x]",
          LOGSHA1(update->Hash())));
 
     MOZ_ASSERT(update->IsFresh());
@@ -1591,17 +1570,17 @@ CacheIndex::WriteIndexToDiskIfNeeded()
 void
 CacheIndex::WriteIndexToDisk()
 {
   LOG(("CacheIndex::WriteIndexToDisk()"));
   mIndexStats.Log();
 
   nsresult rv;
 
-  AssertOwnsLock();
+  sLock.AssertCurrentThreadOwns();
   MOZ_ASSERT(mState == READY);
   MOZ_ASSERT(!mRWBuf);
   MOZ_ASSERT(!mRWHash);
 
   ChangeState(WRITING);
 
   mProcessEntries = mIndexStats.ActiveEntriesCount();
 
@@ -1633,17 +1612,17 @@ CacheIndex::WriteIndexToDisk()
 
 void
 CacheIndex::WriteRecords()
 {
   LOG(("CacheIndex::WriteRecords()"));
 
   nsresult rv;
 
-  AssertOwnsLock();
+  sLock.AssertCurrentThreadOwns();
   MOZ_ASSERT(mState == WRITING);
 
   int64_t fileOffset;
 
   if (mSkipEntries) {
     MOZ_ASSERT(mRWBufPos == 0);
     fileOffset = sizeof(CacheIndexHeader);
     fileOffset += sizeof(CacheIndexRecord) * mSkipEntries;
@@ -1723,17 +1702,17 @@ CacheIndex::WriteRecords()
 
 void
 CacheIndex::FinishWrite(bool aSucceeded)
 {
   LOG(("CacheIndex::FinishWrite() [succeeded=%d]", aSucceeded));
 
   MOZ_ASSERT((!aSucceeded && mState == SHUTDOWN) || mState == WRITING);
 
-  AssertOwnsLock();
+  sLock.AssertCurrentThreadOwns();
 
   mIndexHandle = nullptr;
   mRWHash = nullptr;
   ReleaseBuffer();
 
   if (aSucceeded) {
     // Opening of the file must not be in progress if writing succeeded.
     MOZ_ASSERT(!mIndexFileOpener);
@@ -2002,17 +1981,17 @@ CacheIndex::WriteLogToDisk()
 
 void
 CacheIndex::ReadIndexFromDisk()
 {
   LOG(("CacheIndex::ReadIndexFromDisk()"));
 
   nsresult rv;
 
-  AssertOwnsLock();
+  sLock.AssertCurrentThreadOwns();
   MOZ_ASSERT(mState == INITIAL);
 
   ChangeState(READING);
 
   mIndexFileOpener = new FileOpenHelper(this);
   rv = CacheFileIOManager::OpenFile(NS_LITERAL_CSTRING(INDEX_NAME),
                                     CacheFileIOManager::SPECIAL_FILE |
                                     CacheFileIOManager::OPEN,
@@ -2049,17 +2028,17 @@ CacheIndex::ReadIndexFromDisk()
 
 void
 CacheIndex::StartReadingIndex()
 {
   LOG(("CacheIndex::StartReadingIndex()"));
 
   nsresult rv;
 
-  AssertOwnsLock();
+  sLock.AssertCurrentThreadOwns();
 
   MOZ_ASSERT(mIndexHandle);
   MOZ_ASSERT(mState == READING);
   MOZ_ASSERT(!mIndexOnDiskIsValid);
   MOZ_ASSERT(!mDontMarkIndexClean);
   MOZ_ASSERT(!mJournalReadSuccessfully);
   MOZ_ASSERT(mIndexHandle->FileSize() >= 0);
 
@@ -2089,17 +2068,17 @@ CacheIndex::StartReadingIndex()
 
 void
 CacheIndex::ParseRecords()
 {
   LOG(("CacheIndex::ParseRecords()"));
 
   nsresult rv;
 
-  AssertOwnsLock();
+  sLock.AssertCurrentThreadOwns();
 
   uint32_t entryCnt = (mIndexHandle->FileSize() - sizeof(CacheIndexHeader) -
                      sizeof(CacheHash::Hash32_t)) / sizeof(CacheIndexRecord);
   uint32_t pos = 0;
 
   if (!mSkipEntries) {
     CacheIndexHeader *hdr = reinterpret_cast<CacheIndexHeader *>(
                               moz_xmalloc(sizeof(CacheIndexHeader)));
@@ -2217,17 +2196,17 @@ CacheIndex::ParseRecords()
 
 void
 CacheIndex::StartReadingJournal()
 {
   LOG(("CacheIndex::StartReadingJournal()"));
 
   nsresult rv;
 
-  AssertOwnsLock();
+  sLock.AssertCurrentThreadOwns();
 
   MOZ_ASSERT(mJournalHandle);
   MOZ_ASSERT(mIndexOnDiskIsValid);
   MOZ_ASSERT(mTmpJournal.Count() == 0);
   MOZ_ASSERT(mJournalHandle->FileSize() >= 0);
 
   int64_t entriesSize = mJournalHandle->FileSize() -
                         sizeof(CacheHash::Hash32_t);
@@ -2254,17 +2233,17 @@ CacheIndex::StartReadingJournal()
 
 void
 CacheIndex::ParseJournal()
 {
   LOG(("CacheIndex::ParseJournal()"));
 
   nsresult rv;
 
-  AssertOwnsLock();
+  sLock.AssertCurrentThreadOwns();
 
   uint32_t entryCnt = (mJournalHandle->FileSize() -
                        sizeof(CacheHash::Hash32_t)) / sizeof(CacheIndexRecord);
 
   uint32_t pos = 0;
 
   while (pos + sizeof(CacheIndexRecord) <= mRWBufPos &&
          mSkipEntries != entryCnt) {
@@ -2329,17 +2308,17 @@ CacheIndex::ParseJournal()
   }
 }
 
 void
 CacheIndex::MergeJournal()
 {
   LOG(("CacheIndex::MergeJournal()"));
 
-  AssertOwnsLock();
+  sLock.AssertCurrentThreadOwns();
 
   for (auto iter = mTmpJournal.Iter(); !iter.Done(); iter.Next()) {
     CacheIndexEntry* entry = iter.Get();
 
     LOG(("CacheIndex::MergeJournal() [hash=%08x%08x%08x%08x%08x]",
          LOGSHA1(entry->Hash())));
 
     CacheIndexEntry* entry2 = mIndex.GetEntry(*entry->Hash());
@@ -2393,17 +2372,17 @@ CacheIndex::EnsureCorrectStats()
   MOZ_ASSERT(debugStats == mIndexStats);
 #endif
 }
 
 void
 CacheIndex::FinishRead(bool aSucceeded)
 {
   LOG(("CacheIndex::FinishRead() [succeeded=%d]", aSucceeded));
-  AssertOwnsLock();
+  sLock.AssertCurrentThreadOwns();
 
   MOZ_ASSERT((!aSucceeded && mState == SHUTDOWN) || mState == READING);
 
   MOZ_ASSERT(
     // -> rebuild
     (!aSucceeded && !mIndexOnDiskIsValid && !mJournalReadSuccessfully) ||
     // -> update
     (!aSucceeded && mIndexOnDiskIsValid && !mJournalReadSuccessfully) ||
@@ -2474,24 +2453,24 @@ CacheIndex::FinishRead(bool aSucceeded)
 
 // static
 void
 CacheIndex::DelayedUpdate(nsITimer *aTimer, void *aClosure)
 {
   LOG(("CacheIndex::DelayedUpdate()"));
 
   nsresult rv;
+  StaticMutexAutoLock lock(sLock);
+
   RefPtr<CacheIndex> index = gInstance;
 
   if (!index) {
     return;
   }
 
-  CacheIndexAutoLock lock(index);
-
   index->mUpdateTimer = nullptr;
 
   if (!index->IsIndexUsable()) {
     return;
   }
 
   if (index->mState == READY && index->mShuttingDown) {
     return;
@@ -2608,40 +2587,40 @@ CacheIndex::InitEntryFromDiskData(CacheI
   aEntry->SetFileSize(static_cast<uint32_t>(
                         std::min(static_cast<int64_t>(PR_UINT32_MAX),
                                  (aFileSize + 0x3FF) >> 10)));
 }
 
 bool
 CacheIndex::IsUpdatePending()
 {
-  AssertOwnsLock();
+  sLock.AssertCurrentThreadOwns();
 
   if (mUpdateTimer || mUpdateEventPending) {
     return true;
   }
 
   return false;
 }
 
 void
 CacheIndex::BuildIndex()
 {
   LOG(("CacheIndex::BuildIndex()"));
 
-  AssertOwnsLock();
+  sLock.AssertCurrentThreadOwns();
 
   MOZ_ASSERT(mPendingUpdates.Count() == 0);
 
   nsresult rv;
 
   if (!mDirEnumerator) {
     {
       // Do not do IO under the lock.
-      CacheIndexAutoUnlock unlock(this);
+      StaticMutexAutoUnlock unlock(sLock);
       rv = SetupDirectoryEnumerator();
     }
     if (mState == SHUTDOWN) {
       // The index was shut down while we released the lock. FinishUpdate() was
       // already called from Shutdown(), so just simply return here.
       return;
     }
 
@@ -2656,17 +2635,17 @@ CacheIndex::BuildIndex()
       LOG(("CacheIndex::BuildIndex() - Breaking loop for higher level events."));
       mUpdateEventPending = true;
       return;
     }
 
     nsCOMPtr<nsIFile> file;
     {
       // Do not do IO under the lock.
-      CacheIndexAutoUnlock unlock(this);
+      StaticMutexAutoUnlock unlock(sLock);
       rv = mDirEnumerator->GetNextFile(getter_AddRefs(file));
     }
     if (mState == SHUTDOWN) {
       return;
     }
     if (!file) {
       FinishUpdate(NS_SUCCEEDED(rv));
       return;
@@ -2718,17 +2697,17 @@ CacheIndex::BuildIndex()
 
     MOZ_ASSERT(!handle);
 
     RefPtr<CacheFileMetadata> meta = new CacheFileMetadata();
     int64_t size = 0;
 
     {
       // Do not do IO under the lock.
-      CacheIndexAutoUnlock unlock(this);
+      StaticMutexAutoUnlock unlock(sLock);
       rv = meta->SyncReadMetadata(file);
 
       if (NS_SUCCEEDED(rv)) {
         rv = file->GetFileSize(&size);
         if (NS_FAILED(rv)) {
           LOG(("CacheIndex::BuildIndex() - Cannot get filesize of file that was"
                " successfully parsed. [name=%s]", leaf.get()));
         }
@@ -2777,17 +2756,17 @@ CacheIndex::StartUpdatingIndexIfNeeded(b
   return false;
 }
 
 void
 CacheIndex::StartUpdatingIndex(bool aRebuild)
 {
   LOG(("CacheIndex::StartUpdatingIndex() [rebuild=%d]", aRebuild));
 
-  AssertOwnsLock();
+  sLock.AssertCurrentThreadOwns();
 
   nsresult rv;
 
   mIndexStats.Log();
 
   ChangeState(aRebuild ? BUILDING : UPDATING);
   mDontMarkIndexClean = false;
 
@@ -2833,26 +2812,26 @@ CacheIndex::StartUpdatingIndex(bool aReb
   }
 }
 
 void
 CacheIndex::UpdateIndex()
 {
   LOG(("CacheIndex::UpdateIndex()"));
 
-  AssertOwnsLock();
+  sLock.AssertCurrentThreadOwns();
 
   MOZ_ASSERT(mPendingUpdates.Count() == 0);
 
   nsresult rv;
 
   if (!mDirEnumerator) {
     {
       // Do not do IO under the lock.
-      CacheIndexAutoUnlock unlock(this);
+      StaticMutexAutoUnlock unlock(sLock);
       rv = SetupDirectoryEnumerator();
     }
     if (mState == SHUTDOWN) {
       // The index was shut down while we released the lock. FinishUpdate() was
       // already called from Shutdown(), so just simply return here.
       return;
     }
 
@@ -2868,17 +2847,17 @@ CacheIndex::UpdateIndex()
            "events."));
       mUpdateEventPending = true;
       return;
     }
 
     nsCOMPtr<nsIFile> file;
     {
       // Do not do IO under the lock.
-      CacheIndexAutoUnlock unlock(this);
+      StaticMutexAutoUnlock unlock(sLock);
       rv = mDirEnumerator->GetNextFile(getter_AddRefs(file));
     }
     if (mState == SHUTDOWN) {
       return;
     }
     if (!file) {
       FinishUpdate(NS_SUCCEEDED(rv));
       return;
@@ -2929,17 +2908,17 @@ CacheIndex::UpdateIndex()
     }
 
     MOZ_ASSERT(!handle);
 
     if (entry) {
       PRTime lastModifiedTime;
       {
         // Do not do IO under the lock.
-        CacheIndexAutoUnlock unlock(this);
+        StaticMutexAutoUnlock unlock(sLock);
         rv = file->GetLastModifiedTime(&lastModifiedTime);
       }
       if (mState == SHUTDOWN) {
         return;
       }
       if (NS_FAILED(rv)) {
         LOG(("CacheIndex::UpdateIndex() - Cannot get lastModifiedTime. "
              "[name=%s]", leaf.get()));
@@ -2958,17 +2937,17 @@ CacheIndex::UpdateIndex()
       }
     }
 
     RefPtr<CacheFileMetadata> meta = new CacheFileMetadata();
     int64_t size = 0;
 
     {
       // Do not do IO under the lock.
-      CacheIndexAutoUnlock unlock(this);
+      StaticMutexAutoUnlock unlock(sLock);
       rv = meta->SyncReadMetadata(file);
 
       if (NS_SUCCEEDED(rv)) {
         rv = file->GetFileSize(&size);
         if (NS_FAILED(rv)) {
           LOG(("CacheIndex::UpdateIndex() - Cannot get filesize of file that "
                "was successfully parsed. [name=%s]", leaf.get()));
         }
@@ -3009,17 +2988,17 @@ CacheIndex::UpdateIndex()
 void
 CacheIndex::FinishUpdate(bool aSucceeded)
 {
   LOG(("CacheIndex::FinishUpdate() [succeeded=%d]", aSucceeded));
 
   MOZ_ASSERT(mState == UPDATING || mState == BUILDING ||
              (!aSucceeded && mState == SHUTDOWN));
 
-  AssertOwnsLock();
+  sLock.AssertCurrentThreadOwns();
 
   if (mDirEnumerator) {
     if (NS_IsMainThread()) {
       LOG(("CacheIndex::FinishUpdate() - posting of PreShutdownInternal failed?"
            " Cannot safely release mDirEnumerator, leaking it!"));
       NS_WARNING(("CacheIndex::FinishUpdate() - Leaking mDirEnumerator!"));
       // This can happen only in case dispatching event to IO thread failed in
       // CacheIndex::PreShutdown().
@@ -3193,61 +3172,61 @@ CacheIndex::RemoveRecordFromFrecencyArra
   DebugOnly<bool> removed;
   removed = mFrecencyArray.RemoveElement(aRecord);
   MOZ_ASSERT(removed);
 }
 
 void
 CacheIndex::AddRecordToIterators(CacheIndexRecord *aRecord)
 {
-  AssertOwnsLock();
+  sLock.AssertCurrentThreadOwns();
 
   for (uint32_t i = 0; i < mIterators.Length(); ++i) {
     // Add a new record only when iterator is supposed to be updated.
     if (mIterators[i]->ShouldBeNewAdded()) {
       mIterators[i]->AddRecord(aRecord);
     }
   }
 }
 
 void
 CacheIndex::RemoveRecordFromIterators(CacheIndexRecord *aRecord)
 {
-  AssertOwnsLock();
+  sLock.AssertCurrentThreadOwns();
 
   for (uint32_t i = 0; i < mIterators.Length(); ++i) {
     // Remove the record from iterator always, it makes no sence to return
     // non-existing entries. Also the pointer to the record is no longer valid
     // once the entry is removed from index.
     mIterators[i]->RemoveRecord(aRecord);
   }
 }
 
 void
 CacheIndex::ReplaceRecordInIterators(CacheIndexRecord *aOldRecord,
                                      CacheIndexRecord *aNewRecord)
 {
-  AssertOwnsLock();
+  sLock.AssertCurrentThreadOwns();
 
   for (uint32_t i = 0; i < mIterators.Length(); ++i) {
     // We have to replace the record always since the pointer is no longer
     // valid after this point. NOTE: Replacing the record doesn't mean that
     // a new entry was added, it just means that the data in the entry was
     // changed (e.g. a file size) and we had to track this change in
     // mPendingUpdates since mIndex was read-only.
     mIterators[i]->ReplaceRecord(aOldRecord, aNewRecord);
   }
 }
 
 nsresult
 CacheIndex::Run()
 {
   LOG(("CacheIndex::Run()"));
 
-  CacheIndexAutoLock lock(this);
+  StaticMutexAutoLock lock(sLock);
 
   if (!IsIndexUsable()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   if (mState == READY && mShuttingDown) {
     return NS_OK;
   }
@@ -3272,17 +3251,17 @@ nsresult
 CacheIndex::OnFileOpenedInternal(FileOpenHelper *aOpener,
                                  CacheFileHandle *aHandle, nsresult aResult)
 {
   LOG(("CacheIndex::OnFileOpenedInternal() [opener=%p, handle=%p, "
        "result=0x%08x]", aOpener, aHandle, aResult));
 
   nsresult rv;
 
-  AssertOwnsLock();
+  sLock.AssertCurrentThreadOwns();
 
   if (!IsIndexUsable()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   if (mState == READY && mShuttingDown) {
     return NS_OK;
   }
@@ -3382,17 +3361,17 @@ nsresult
 CacheIndex::OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
                           nsresult aResult)
 {
   LOG(("CacheIndex::OnDataWritten() [handle=%p, result=0x%08x]", aHandle,
        aResult));
 
   nsresult rv;
 
-  CacheIndexAutoLock lock(this);
+  StaticMutexAutoLock lock(sLock);
 
   if (!IsIndexUsable()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   if (mState == READY && mShuttingDown) {
     return NS_OK;
   }
@@ -3432,17 +3411,17 @@ CacheIndex::OnDataWritten(CacheFileHandl
 }
 
 nsresult
 CacheIndex::OnDataRead(CacheFileHandle *aHandle, char *aBuf, nsresult aResult)
 {
   LOG(("CacheIndex::OnDataRead() [handle=%p, result=0x%08x]", aHandle,
        aResult));
 
-  CacheIndexAutoLock lock(this);
+  StaticMutexAutoLock lock(sLock);
 
   if (!IsIndexUsable()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   switch (mState) {
     case READING:
       MOZ_ASSERT(mIndexHandle == aHandle || mJournalHandle == aHandle);
@@ -3481,17 +3460,17 @@ CacheIndex::OnEOFSet(CacheFileHandle *aH
 }
 
 nsresult
 CacheIndex::OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult)
 {
   LOG(("CacheIndex::OnFileRenamed() [handle=%p, result=0x%08x]", aHandle,
        aResult));
 
-  CacheIndexAutoLock lock(this);
+  StaticMutexAutoLock lock(sLock);
 
   if (!IsIndexUsable()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   if (mState == READY && mShuttingDown) {
     return NS_OK;
   }
@@ -3536,17 +3515,17 @@ CacheIndex::OnFileRenamed(CacheFileHandl
   return NS_OK;
 }
 
 // Memory reporting
 
 size_t
 CacheIndex::SizeOfExcludingThisInternal(mozilla::MallocSizeOf mallocSizeOf) const
 {
-  CacheIndexAutoLock lock(const_cast<CacheIndex*>(this));
+  sLock.AssertCurrentThreadOwns();
 
   size_t n = 0;
   nsCOMPtr<nsISizeOf> sizeOf;
 
   // mIndexHandle and mJournalHandle are reported via SizeOfHandlesRunnable
   // in CacheFileIOManager::SizeOfExcludingThisInternal as part of special
   // handles array.
 
@@ -3573,26 +3552,30 @@ CacheIndex::SizeOfExcludingThisInternal(
 
   return n;
 }
 
 // static
 size_t
 CacheIndex::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
 {
+  sLock.AssertCurrentThreadOwns();
+
   if (!gInstance)
     return 0;
 
   return gInstance->SizeOfExcludingThisInternal(mallocSizeOf);
 }
 
 // static
 size_t
 CacheIndex::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
 {
+  StaticMutexAutoLock lock(sLock);
+
   return mallocSizeOf(gInstance) + SizeOfExcludingThis(mallocSizeOf);
 }
 
 namespace {
 
 class HashComparator
 {
 public:
--- a/netwerk/cache2/CacheIndex.h
+++ b/netwerk/cache2/CacheIndex.h
@@ -11,17 +11,17 @@
 #include "CacheHashUtils.h"
 #include "nsICacheStorageService.h"
 #include "nsICacheEntry.h"
 #include "nsILoadContextInfo.h"
 #include "nsTHashtable.h"
 #include "nsThreadUtils.h"
 #include "nsWeakReference.h"
 #include "mozilla/SHA1.h"
-#include "mozilla/Mutex.h"
+#include "mozilla/StaticMutex.h"
 #include "mozilla/Endian.h"
 #include "mozilla/TimeStamp.h"
 
 class nsIFile;
 class nsIDirectoryEnumerator;
 class nsITimer;
 
 
@@ -691,37 +691,31 @@ public:
   static nsresult IsUpToDate(bool *_retval);
 
   // Memory reporting
   static size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
   static size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
 
 private:
   friend class CacheIndexEntryAutoManage;
-  friend class CacheIndexAutoLock;
-  friend class CacheIndexAutoUnlock;
   friend class FileOpenHelper;
   friend class CacheIndexIterator;
 
   virtual ~CacheIndex();
 
   NS_IMETHOD OnFileOpened(CacheFileHandle *aHandle, nsresult aResult) override;
   nsresult   OnFileOpenedInternal(FileOpenHelper *aOpener,
                                   CacheFileHandle *aHandle, nsresult aResult);
   NS_IMETHOD OnDataWritten(CacheFileHandle *aHandle, const char *aBuf,
                            nsresult aResult) override;
   NS_IMETHOD OnDataRead(CacheFileHandle *aHandle, char *aBuf, nsresult aResult) override;
   NS_IMETHOD OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult) override;
   NS_IMETHOD OnEOFSet(CacheFileHandle *aHandle, nsresult aResult) override;
   NS_IMETHOD OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult) override;
 
-  void     Lock();
-  void     Unlock();
-  void     AssertOwnsLock();
-
   nsresult InitInternal(nsIFile *aCacheDirectory);
   void     PreShutdownInternal();
 
   // This method returns false when index is not initialized or is shut down.
   bool IsIndexUsable();
 
   // This method checks whether the entry has the same values of appId,
   // isAnonymous and isInBrowser. We don't expect to find a collision since
@@ -926,20 +920,20 @@ private:
                                 CacheIndexRecord *aNewRecord);
 
   // Memory reporting (private part)
   size_t SizeOfExcludingThisInternal(mozilla::MallocSizeOf mallocSizeOf) const;
 
   void ReportHashStats();
 
   static CacheIndex *gInstance;
+  static StaticMutex sLock;
 
   nsCOMPtr<nsIFile> mCacheDirectory;
 
-  mozilla::Mutex mLock;
   EState         mState;
   // Timestamp of time when the index was initialized. We use it to delay
   // initial update or build of index.
   TimeStamp      mStartTime;
   // Set to true in PreShutdown(), it is checked on variaous places to prevent
   // starting any process (write, update, etc.) during shutdown.
   bool           mShuttingDown;
   // When set to true, update process should start as soon as possible. This
@@ -1084,76 +1078,12 @@ private:
     nsWeakPtr mObserver;
     int64_t mSize;
   };
 
   // List of async observers that want to get disk consumption information
   nsTArray<RefPtr<DiskConsumptionObserver> > mDiskConsumptionObservers;
 };
 
-class CacheIndexAutoLock {
-public:
-  explicit CacheIndexAutoLock(CacheIndex *aIndex)
-    : mIndex(aIndex)
-    , mLocked(true)
-  {
-    mIndex->Lock();
-  }
-  ~CacheIndexAutoLock()
-  {
-    if (mLocked) {
-      mIndex->Unlock();
-    }
-  }
-  void Lock()
-  {
-    MOZ_ASSERT(!mLocked);
-    mIndex->Lock();
-    mLocked = true;
-  }
-  void Unlock()
-  {
-    MOZ_ASSERT(mLocked);
-    mIndex->Unlock();
-    mLocked = false;
-  }
-
-private:
-  RefPtr<CacheIndex> mIndex;
-  bool mLocked;
-};
-
-class CacheIndexAutoUnlock {
-public:
-  explicit CacheIndexAutoUnlock(CacheIndex *aIndex)
-    : mIndex(aIndex)
-    , mLocked(false)
-  {
-    mIndex->Unlock();
-  }
-  ~CacheIndexAutoUnlock()
-  {
-    if (!mLocked) {
-      mIndex->Lock();
-    }
-  }
-  void Lock()
-  {
-    MOZ_ASSERT(!mLocked);
-    mIndex->Lock();
-    mLocked = true;
-  }
-  void Unlock()
-  {
-    MOZ_ASSERT(mLocked);
-    mIndex->Unlock();
-    mLocked = false;
-  }
-
-private:
-  RefPtr<CacheIndex> mIndex;
-  bool mLocked;
-};
-
 } // namespace net
 } // namespace mozilla
 
 #endif
--- a/netwerk/cache2/CacheIndexIterator.cpp
+++ b/netwerk/cache2/CacheIndexIterator.cpp
@@ -27,17 +27,17 @@ CacheIndexIterator::~CacheIndexIterator(
   Close();
 }
 
 nsresult
 CacheIndexIterator::GetNextHash(SHA1Sum::Hash *aHash)
 {
   LOG(("CacheIndexIterator::GetNextHash() [this=%p]", this));
 
-  CacheIndexAutoLock lock(mIndex);
+  StaticMutexAutoLock lock(CacheIndex::sLock);
 
   if (NS_FAILED(mStatus)) {
     return mStatus;
   }
 
   if (!mRecords.Length()) {
     CloseInternal(NS_ERROR_NOT_AVAILABLE);
     return mStatus;
@@ -49,17 +49,17 @@ CacheIndexIterator::GetNextHash(SHA1Sum:
   return NS_OK;
 }
 
 nsresult
 CacheIndexIterator::Close()
 {
   LOG(("CacheIndexIterator::Close() [this=%p]", this));
 
-  CacheIndexAutoLock lock(mIndex);
+  StaticMutexAutoLock lock(CacheIndex::sLock);
 
   return CloseInternal(NS_ERROR_NOT_AVAILABLE);
 }
 
 nsresult
 CacheIndexIterator::CloseInternal(nsresult aStatus)
 {
   LOG(("CacheIndexIterator::CloseInternal() [this=%p, status=0x%08x]", this,
--- a/testing/marionette/client/docs/basics.rst
+++ b/testing/marionette/client/docs/basics.rst
@@ -1,9 +1,9 @@
-.. py:currentmodule:: marionette
+.. py:currentmodule:: marionette_driver.marionette
 
 Marionette Python Client
 ========================
 
 The Marionette python client library allows you to remotely control a
 Gecko-based browser or device which is running a Marionette_
 server. This includes desktop Firefox and FirefoxOS (support for
 Firefox for Android is planned, but not yet fully implemented).
--- a/testing/marionette/client/docs/reference.rst
+++ b/testing/marionette/client/docs/reference.rst
@@ -34,8 +34,13 @@ Wait
    :special-members:
 .. autoattribute marionette_driver.wait.DEFAULT_TIMEOUT
 .. autoattribute marionette_driver.wait.DEFAULT_INTERVAL
 
 Built-in Conditions
 ^^^^^^^^^^^^^^^^^^^
 .. automodule:: marionette_driver.expected
    :members:
+
+Addons
+------
+.. autoclass:: marionette_driver.addons.Addons
+   :members:
new file mode 100644
index 0000000000000000000000000000000000000000..c8877b55dec371fa6417fc626ef1a6f3113cc2bc
GIT binary patch
literal 1552
zc$^FHW@Zs#U|`^2xLcX!&XKXBU5t@|;S&(^Fvu_@<>!|amlP!y=w%g$hHx@4I|ev~
zfpBRBHv=Qf3uXoeFtN7(bk<=50oU*Uy1c{uJeFQcbqa5EQfzhD{W56U@r_Mu3@@!v
z<o)^ml;)SM@4Q}|n_Dcg_kj0%p?Q|>-Zu)oD;`)ZNYONB-^7&Y;V;nT%v65;yzqYO
z_q=60Kb{MVNO-N&>f*ge^UliX*5iC^(~e)helD+ju_uR4M(m^)vvjUJda*>mHR;Wq
z<xg3vpYmig?GSu?N|?j#LU)h$+IIi#k9J&dUD@rcty2;j$>&+%CieU?*X$ndcc%jn
zM{qb^+O=wz%o+<mkEBIUHqLt-voK_;_F9dvXTD8jpJnq<?b6m~nyRr6f)sYCzmJr?
zWH5XE>1R(;{7-$}x3ABu>gUUso43^P{MWi=y!~wFzZEe%?^W2()VrU_xBnA=05EL1
z7(hTQBFmit39tjhHaVjxKQ~n$9IR_ZoWis)gEasiIUob#4KJhw0(lugEPyZ|BQ+-{
z9~eKmaFak7%_MDaouGnVjt~4?g{7VReP2pHedi3@5ZV!T%H!<Gli`;F1H8_k_xD`o
z6WX=)nHIAaTQ_s4Mr(SAlclQUO4pTLUILXXiY9PvKeFgb(E_dsA`A$>1&3$3Prjz3
zFArpcFhAUBdbx>tnQ5uTC19870bQks>9XYfypq(s5{2A6-6EjU#G;a%)Z$_VsOPv4
zL68Ov0{z15Lsh`EX#mv74YVgS52zz2N3SR)4Qz*ls8bjSquG%=`LrK6dH&?meltO6
zd86;*E4o4oVmbk;3AvLpH+)duw0z6c*M&ZDtecA7&9N@F4LtQV(DM`LHO|H1AC=BN
zGVNYeR9)>~<GMTaPu~r(DRO-6Ukav5-M(<NXy=khzt1)szv>mL_x_oYQ7-7dq`GM%
zL+P{7XWkwsotte=uhU#{u2%4OVat^5545zSz8G@U8&}61tzO-IFO=i$1n-#|>yGlt
zRE3+bWC`t<r8zf3zo=Vi&TXE>T8q7YIro&*hfZ7C9Uy(r-JzH@$GWtdLG$eDjhpyt
zH2Mo{m(Hu^kP$4oKK)IPYFBhUi;Kbw#f7skWaY_OhlyE8oqkuMY|`-6pmNi)d*K>(
ztn-?R8x~#smM5}hPvi5q%CD!}MOPS{op|xNuz7=duD0vZ<toqRQ!X5<U2OQt`N6aA
z9f=p2SGCl={9ZkG`Siw_na_3X`KtK+4o&F%$0>TEUe7T0!xg5}^&8$<h~4XPY@fEB
z=dB!*f#=2fiK<`svvluc4De=Tl4HhIz({}-5Rl2Rq!GkIEpAvL#SKQD0GWs@TS-7!
z0t{~*V}J(3GZ`x+YhX1DR~AEd-du!XNEr^Fskm|nvZ)qK448R^6_Qu*I2AK1ARD>>
cXedg?V1;B13<Fu&K$=*Aa5hk9JPU{i03S06YXATM
new file mode 100644
--- /dev/null
+++ b/testing/marionette/client/marionette/tests/unit/test_addons.py
@@ -0,0 +1,50 @@
+# 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/.
+
+import os
+
+from marionette import MarionetteTestCase
+from marionette_driver.addons import Addons, AddonInstallException
+
+here = os.path.abspath(os.path.dirname(__file__))
+
+
+class TestAddons(MarionetteTestCase):
+    def setUp(self):
+        MarionetteTestCase.setUp(self)
+        self.addons = Addons(self.marionette)
+        self.addon_path = os.path.join(here, 'mn-restartless.xpi')
+
+
+    @property
+    def all_addon_ids(self):
+        with self.marionette.using_context('chrome'):
+            addons = self.marionette.execute_async_script("""
+              Components.utils.import("resource://gre/modules/AddonManager.jsm");
+              AddonManager.getAllAddons(function(addons){
+                let ids = addons.map(function(x) {
+                  return x.id;
+                });
+                marionetteScriptFinished(ids);
+              });
+            """)
+
+        return addons
+
+    def test_install_and_remove_unsigned_addon(self):
+        self.addons.signature_required = False
+
+        addon_id = self.addons.install(self.addon_path)
+        self.assertIn(addon_id, self.all_addon_ids)
+
+        self.addons.uninstall(addon_id)
+        self.assertNotIn(addon_id, self.all_addon_ids)
+
+        self.addons.signature_required = True
+
+    def test_install_unsigned_addon_with_signature_required(self):
+        self.signature_required = True
+
+        with self.assertRaises(AddonInstallException):
+            self.addons.install(self.addon_path)
--- a/testing/marionette/client/marionette/tests/unit/test_chrome_element_css.py
+++ b/testing/marionette/client/marionette/tests/unit/test_chrome_element_css.py
@@ -5,17 +5,17 @@
 from marionette import MarionetteTestCase
 
 
 class TestChromeElementCSS(MarionetteTestCase):
 
     def test_we_can_get_css_value_on_chrome_element(self):
         self.marionette.navigate("about:blank")
         with self.marionette.using_context("chrome"):
-            element = self.marionette.find_element("id", "page-proxy-favicon")
+            element = self.marionette.find_element("id", "identity-icon")
             favicon_image = element.value_of_css_property("list-style-image")
 
-            self.assertIn("identity-not-secure.svg", favicon_image)
+            self.assertIn("identity-icon.svg", favicon_image)
 
             element = self.marionette.find_element("id", "identity-box")
             background_colour = element.value_of_css_property("background-color")
 
             self.assertEqual("transparent", background_colour)
--- a/testing/marionette/client/marionette/tests/unit/unit-tests.ini
+++ b/testing/marionette/client/marionette/tests/unit/unit-tests.ini
@@ -157,8 +157,10 @@ skip-if = os == "win" # http://bugs.pyth
 [test_execute_sandboxes.py]
 [test_using_permissions.py]
 [test_using_prefs.py]
 
 [test_shadow_dom.py]
 
 [test_chrome.py]
 b2g = false
+
+[test_addons.py]
--- a/testing/marionette/driver/marionette_driver/__init__.py
+++ b/testing/marionette/driver/marionette_driver/__init__.py
@@ -1,15 +1,16 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 __version__ = '1.1.0'
 
 from marionette_driver import (
+    addons,
     by,
     date_time_value,
     decorators,
     errors,
     expected,
     geckoinstance,
     gestures,
     keys,
new file mode 100644
--- /dev/null
+++ b/testing/marionette/driver/marionette_driver/addons.py
@@ -0,0 +1,120 @@
+# 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/.
+
+from .errors import MarionetteException
+
+__all__ = ['Addons', 'AddonInstallException']
+
+
+# From https://developer.mozilla.org/en-US/Add-ons/Add-on_Manager/AddonManager#AddonInstall_errors
+ADDON_INSTALL_ERRORS = {
+    -1: "ERROR_NETWORK_FAILURE: A network error occured.",
+    -2: "ERROR_INCORECT_HASH: The downloaded file did not match the expected hash.",
+    -3: "ERROR_CORRUPT_FILE: The file appears to be corrupt.",
+    -4: "ERROR_FILE_ACCESS: There was an error accessing the filesystem.",
+    -5: "ERROR_SIGNEDSTATE_REQUIRED: The addon must be signed and isn't.",
+}
+
+
+class AddonInstallException(MarionetteException):
+    pass
+
+
+class Addons(object):
+    """
+    An API for installing and inspecting addons during Gecko runtime. This
+    is a partially implemented wrapper around Gecko's `AddonManager API`_.
+
+    For example::
+
+        from marionette_driver.addons import Addons
+        addons = Addons(marionette)
+        addons.install('path/to/extension.xpi')
+
+    .. _AddonManager API: https://developer.mozilla.org/en-US/Add-ons/Add-on_Manager
+    """
+
+    def __init__(self, marionette):
+        self._mn = marionette
+        self._signature_required = True
+
+        def on_restart():
+            self.signature_required = self._signature_required
+        self._mn.restart_handlers.append(on_restart)
+
+    @property
+    def signature_required(self):
+        """
+        Whether or not addons must be signed.
+        """
+        return self._signature_required
+
+    @signature_required.setter
+    def signature_required(self, val):
+        self._mn.set_pref('xpinstall.signatures.required', val)
+        self._signature_required = val
+
+    def install(self, path):
+        """Install an addon.
+
+        If the addon is restartless, it can be used right away. Otherwise
+        a restart using :func:`~marionette_driver.marionette.Marionette.restart`
+        will be needed.
+
+        :param path: A file path to the extension to be installed.
+        :returns: The addon ID string of the newly installed addon.
+        :raises: :exc:`AddonInstallException`
+        """
+        with self._mn.using_context('chrome'):
+            addon_id, status = self._mn.execute_async_script("""
+              let FileUtils = Components.utils.import("resource://gre/modules/FileUtils.jsm").FileUtils;
+              Components.utils.import("resource://gre/modules/AddonManager.jsm");
+              let listener = {
+                onInstallEnded: function(install, addon) {
+                  marionetteScriptFinished([addon.id, 0]);
+                },
+
+                onInstallFailed: function(install) {
+                  marionetteScriptFinished([null, install.error]);
+                }
+              }
+
+              let file = new FileUtils.File(arguments[0]);
+              AddonManager.getInstallForFile(file, function(aInstall) {
+                if (aInstall.error != 0) {
+                  marionetteScriptFinished([null, aInstall.error]);
+                }
+                aInstall.addListener(listener);
+                aInstall.install();
+              });
+            """, script_args=[path], debug_script=True)
+
+        if status:
+            if status in ADDON_INSTALL_ERRORS:
+                raise AddonInstallException(ADDON_INSTALL_ERRORS[status])
+            raise AddonInstallException("Addon failed to install with return code: %d" % status)
+        return addon_id
+
+    def uninstall(self, addon_id):
+        """Uninstall an addon.
+
+        If the addon is restartless, it will be uninstalled right away.
+        Otherwise a restart using :func:`~marionette_driver.marionette.Marionette.restart`
+        will be needed.
+
+        If the call to uninstall is resulting in a `ScriptTimeoutException`,
+        an invalid ID is likely being passed in. Unfortunately due to
+        AddonManager's implementation, it's hard to retrieve this error from
+        Python.
+
+        :param addon_id: The addon ID string to uninstall.
+        """
+        with self._mn.using_context('chrome'):
+            return self._mn.execute_async_script("""
+              Components.utils.import("resource://gre/modules/AddonManager.jsm");
+              AddonManager.getAddonByID(arguments[0], function(addon) {
+                addon.uninstall();
+                marionetteScriptFinished(0);
+              });
+            """, script_args=[addon_id])
--- a/testing/marionette/driver/marionette_driver/marionette.py
+++ b/testing/marionette/driver/marionette_driver/marionette.py
@@ -559,16 +559,17 @@ class Marionette(object):
         self.baseurl = baseurl
         self.no_window = no_window
         self._test_name = None
         self.timeout = timeout
         self.socket_timeout = socket_timeout
         self.device_serial = device_serial
         self.adb_host = adb_host
         self.adb_port = adb_port
+        self.restart_handlers = []
 
         startup_timeout = startup_timeout or self.DEFAULT_STARTUP_TIMEOUT
 
         if bin:
             port = int(self.port)
             if not Marionette.is_port_available(port, host=self.host):
                 ex_msg = "%s:%d is unavailable." % (self.host, port)
                 raise errors.MarionetteException(message=ex_msg)
@@ -1132,16 +1133,21 @@ class Marionette(object):
             self.instance.detached = True
         else:
             self.delete_session()
             self.instance.restart(clean=clean)
         assert(self.wait_for_port()), "Timed out waiting for port!"
         self.start_session(session_id=self.session_id)
         self._reset_timeouts()
 
+        # Give consumers who depended on the old session a
+        # chance to re-initialize and/or restore state.
+        for handler in self.restart_handlers:
+            handler()
+
     def absolute_url(self, relative_url):
         '''
         Returns an absolute url for files served from Marionette's www directory.
 
         :param relative_url: The url of a static file, relative to Marionette's www directory.
         '''
         return "%s%s" % (self.baseurl, relative_url)
 
--- a/tools/docs/mach_commands.py
+++ b/tools/docs/mach_commands.py
@@ -1,98 +1,117 @@
 # 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/.
 
-from __future__ import absolute_import, unicode_literals
+from __future__ import absolute_import, print_function, unicode_literals
 
 import os
+import sys
 
 from mach.decorators import (
+    Command,
     CommandArgument,
     CommandProvider,
-    Command,
 )
 
+import mozhttpd
+
 from mozbuild.base import MachCommandBase
 
 
 @CommandProvider
 class Documentation(MachCommandBase):
     """Helps manage in-tree documentation."""
 
     @Command('doc', category='devenv',
         description='Generate and display documentation from the tree.')
-    @CommandArgument('what', default=None, nargs='*',
+    @CommandArgument('what', nargs='*', metavar='DIRECTORY [, DIRECTORY]',
         help='Path(s) to documentation to build and display.')
     @CommandArgument('--format', default='html',
         help='Documentation format to write.')
-    @CommandArgument('--outdir', default=None,
+    @CommandArgument('--outdir', default=None, metavar='DESTINATION',
         help='Where to write output.')
     @CommandArgument('--no-open', dest='auto_open', default=True, action='store_false',
-        help='Don\'t automatically open HTML docs in a browser.')
-    def build_docs(self, what=None, format=None, outdir=None, auto_open=True):
+        help="Don't automatically open HTML docs in a browser.")
+    @CommandArgument('--http', const=':6666', metavar='ADDRESS', nargs='?',
+        help='Serve documentation on an HTTP server, e.g. ":6666".')
+    def build_docs(self, what=None, format=None, outdir=None, auto_open=True, http=None):
         self._activate_virtualenv()
         self.virtualenv_manager.install_pip_package('sphinx_rtd_theme==0.1.6')
 
         import sphinx
         import webbrowser
 
         if not outdir:
             outdir = os.path.join(self.topobjdir, 'docs')
-
         if not what:
             what = [os.path.join(self.topsrcdir, 'tools')]
+        outdir = os.path.join(outdir, format)
 
         generated = []
         failed = []
         for path in what:
             path = os.path.normpath(os.path.abspath(path))
             docdir = self._find_doc_dir(path)
 
             if not docdir:
                 failed.append((path, 'could not find docs at this location'))
                 continue
 
             # find project name to use as a namespace within `outdir`
             project = self._find_project_name(docdir)
-            savedir = os.path.join(outdir, format, project)
+            savedir = os.path.join(outdir, project)
 
             args = [
                 'sphinx',
                 '-b', format,
                 docdir,
                 savedir,
             ]
             result = sphinx.build_main(args)
             if result != 0:
                 failed.append((path, 'sphinx return code %d' % result))
             else:
                 generated.append(savedir)
 
-            # attempt to open html docs in a browser
             index_path = os.path.join(savedir, 'index.html')
-            if auto_open and os.path.isfile(index_path):
+            if not http and auto_open and os.path.isfile(index_path):
                 webbrowser.open(index_path)
 
         if generated:
-            print("\nGenerated documentation:\n%s\n" % '\n'.join(generated))
+            print('\nGenerated documentation:\n%s\n' % '\n'.join(generated))
 
         if failed:
-            failed = ['%s - %s' % (f[0], f[1]) for f in failed]
-            print("ERROR failed to generate documentation:\n%s" % '\n'.join(failed))
-            return 1
+            failed = ['%s: %s' % (f[0], f[1]) for f in failed]
+            return die('failed to generate documentation:\n%s' % '\n'.join(failed))
+
+        if http is not None:
+            host, port = http.split(':', 1)
+            addr = (host, int(port))
+            if len(addr) != 2:
+                return die('invalid address: %s' % http)
+
+            httpd = mozhttpd.MozHttpd(host=addr[0], port=addr[1], docroot=outdir)
+            print('listening on %s:%d' % addr)
+            httpd.start(block=True)
 
     def _find_project_name(self, path):
         import imp
         path = os.path.join(path, 'conf.py')
         with open(path, 'r') as fh:
             conf = imp.load_module('doc_conf', fh, path,
                                    ('.py', 'r', imp.PY_SOURCE))
 
         return conf.project.replace(' ', '_')
 
     def _find_doc_dir(self, path):
         search_dirs = ('doc', 'docs')
         for d in search_dirs:
             p = os.path.join(path, d)
             if os.path.isfile(os.path.join(p, 'conf.py')):
                 return p
+
+
+def die(msg, exit_code=1):
+    msg = '%s: %s' % (sys.argv[0], msg)
+    print(msg, file=sys.stderr)
+    return exit_code
--- a/tools/power/rapl.cpp
+++ b/tools/power/rapl.cpp
@@ -833,17 +833,19 @@ main(int argc, char** argv)
     Abort("new RAPL() failed");
   }
 
   // Install the signal handlers.
 
   struct sigaction sa;
   memset(&sa, 0, sizeof(sa));
   sa.sa_flags = SA_RESTART | SA_SIGINFO;
-  if (sigemptyset(&sa.sa_mask) < 0) {
+  // The extra parens around (0) suppress a -Wunreachable-code warning on OS X
+  // where sigemptyset() is a macro that can never fail and always returns 0.
+  if (sigemptyset(&sa.sa_mask) < (0)) {
     Abort("sigemptyset() failed");
   }
   sa.sa_sigaction = SigAlrmHandler;
   if (sigaction(SIGALRM, &sa, NULL) < 0) {
     Abort("sigaction(SIGALRM) failed");
   }
   sa.sa_sigaction = SigIntHandler;
   if (sigaction(SIGINT, &sa, NULL) < 0) {
@@ -872,9 +874,8 @@ main(int argc, char** argv)
       pause();
     }
   }
 
   Finish();
 
   return 0;
 }
-
--- a/tools/profiler/core/platform-linux.cc
+++ b/tools/profiler/core/platform-linux.cc
@@ -354,18 +354,19 @@ static void* SignalSender(void* arg) {
 
         // Profile from the signal handler for information which is signal safe
         // and needs to be precise too, such as the stack of the interrupted
         // thread.
         if (tgkill(vm_tgid_, threadId, SIGPROF) != 0) {
           printf_stderr("profiler failed to signal tid=%d\n", threadId);
 #ifdef DEBUG
           abort();
+#else
+          continue;
 #endif
-          continue;
         }
 
         // Wait for the signal handler to run before moving on to the next one
         sem_wait(&sSignalHandlingDone);
         isFirstProfiledThread = false;
 
         // The LUL unwind object accumulates frame statistics.
         // Periodically we should poke it to give it a chance to print
--- a/webapprt/locales/en-US/webapprt/overrides/dom.properties
+++ b/webapprt/locales/en-US/webapprt/overrides/dom.properties
@@ -146,8 +146,9 @@ Window_ContentWarning=window._content is
 # LOCALIZATION NOTE: Do not translate "XMLHttpRequest"
 SyncXMLHttpRequestWarning=Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help http://xhr.spec.whatwg.org/
 # LOCALIZATION NOTE: Do not translate "DataContainerEvent" or "CustomEvent"
 DataContainerEventWarning=Use of DataContainerEvent is deprecated. Use CustomEvent instead.
 # LOCALIZATION NOTE: Do not translate "Worker".
 EmptyWorkerSourceWarning=Attempting to create a Worker from an empty source. This is probably unintentional.
 WebrtcDeprecatedPrefixWarning=WebRTC interfaces with the "moz" prefix (mozRTCPeerConnection, mozRTCSessionDescription, mozRTCIceCandidate) have been deprecated.
 NavigatorGetUserMediaWarning=navigator.mozGetUserMedia has been replaced by navigator.mediaDevices.getUserMedia
+ExecCommandCutCopyDeniedNotInputDriven=document.execCommand('cut'/'copy') was denied because it was not called from inside a short running user-generated event handler.