Merge autoland to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Tue, 22 Aug 2017 15:40:16 -0700
changeset 650836 80fafbaaf8484104018976c2a0442c91805d3554
parent 650685 ab30809b4c8e53b8723c062340960680ca419c7f (current diff)
parent 650820 2f8718989f5420c21f78bd8b82cade79ffeb436a (diff)
child 650837 54210965d0dbedfd69aa63efc86d9a1101816422
child 650865 ec808fe61a74599e89ca63bb680cc9a5489b5ee8
child 650876 fa99c03ae65d9b58b0bf6795bf39e6f786471f33
child 650940 64a45ee1731c79da9850233a797bf80d3b1a52bd
push id75506
push userfmarier@mozilla.com
push dateTue, 22 Aug 2017 23:01:42 +0000
reviewersmerge
milestone57.0a1
Merge autoland to central, a=merge MozReview-Commit-ID: EGoWUCpbeu6
browser/components/downloads/test/browser/browser_overflow_anchor.js
browser/themes/linux/Info.png
browser/themes/linux/downloads/download-glow-menuPanel.png
browser/themes/linux/downloads/indicator.css
browser/themes/linux/notification-icons/geo-icon.inc.svg
browser/themes/osx/Info.png
browser/themes/osx/downloads/download-glow-menuPanel.png
browser/themes/osx/downloads/download-glow-menuPanel@2x.png
browser/themes/osx/downloads/indicator.css
browser/themes/osx/notification-icons/geo-icon.inc.svg
browser/themes/osx/urlbar-popup-blocked.png
browser/themes/osx/urlbar-popup-blocked@2x.png
browser/themes/osx/webRTC-sharingDevice-menubar.png
browser/themes/osx/webRTC-sharingDevice-menubar@2x.png
browser/themes/osx/webRTC-sharingMicrophone-menubar.png
browser/themes/osx/webRTC-sharingMicrophone-menubar@2x.png
browser/themes/osx/webRTC-sharingScreen-menubar.png
browser/themes/osx/webRTC-sharingScreen-menubar@2x.png
browser/themes/shared/notification-icons/camera-icon.inc.svg
browser/themes/shared/notification-icons/clip-path.inc.svg
browser/themes/shared/notification-icons/desktop-notification-icon.inc.svg
browser/themes/shared/notification-icons/indexedDB-icon.inc.svg
browser/themes/shared/notification-icons/microphone-icon.inc.svg
browser/themes/shared/notification-icons/persistent-storage-icon.inc.svg
browser/themes/shared/notification-icons/plugin-icon.inc.svg
browser/themes/shared/notification-icons/screen-icon.inc.svg
browser/themes/shared/notification-icons/strikeout.inc.svg
browser/themes/windows/Info.png
browser/themes/windows/downloads/download-glow-menuPanel-win7.png
browser/themes/windows/downloads/download-glow-menuPanel.png
browser/themes/windows/downloads/download-glow-win7.png
browser/themes/windows/downloads/download-glow.png
browser/themes/windows/downloads/indicator.css
browser/themes/windows/notification-icons/geo-icon.inc.svg
browser/themes/windows/urlbar-popup-blocked.png
testing/web-platform/meta/html/dom/documents/dom-tree-accessors/nameditem-04.html.ini
testing/web-platform/meta/html/dom/documents/dom-tree-accessors/nameditem-05.html.ini
testing/web-platform/meta/html/dom/documents/dom-tree-accessors/nameditem-06.html.ini
xpcom/threads/nsThread.cpp
--- a/browser/base/content/browser-media.js
+++ b/browser/base/content/browser-media.js
@@ -114,17 +114,17 @@ var gEMEHandler = {
       let btnAccessKeyId = msgPrefix + "button.accesskey";
       buttons.push({
         label: gNavigatorBundle.getString(btnLabelId),
         accessKey: gNavigatorBundle.getString(btnAccessKeyId),
         callback
       });
     }
 
-    let iconURL = "chrome://browser/skin/drm-icon.svg#chains-black";
+    let iconURL = "chrome://browser/skin/drm-icon.svg";
 
     // Do a little dance to get rich content into the notification:
     let fragment = document.createDocumentFragment();
     let descriptionContainer = document.createElement("description");
     // eslint-disable-next-line no-unsanitized/property
     descriptionContainer.innerHTML = message;
     while (descriptionContainer.childNodes.length) {
       fragment.appendChild(descriptionContainer.childNodes[0]);
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -498,20 +498,19 @@ toolbar:not(#TabsToolbar) > #personal-bo
 
 /* Hide menu elements intended for keyboard access support */
 #main-menubar[openedwithkey=false] .show-only-for-keyboard {
   display: none;
 }
 
 /* ::::: location bar & search bar ::::: */
 
-#urlbar-container {
-  min-width: 50ch;
-}
-
+/* url bar min-width is defined further down, together with the maximum size
+ * of the identity icon block, for different window sizes.
+ */
 #search-container {
   min-width: 25ch;
 }
 
 #urlbar,
 .searchbar-textbox {
   /* Setting a width and min-width to let the location & search bars maintain
      a constant width in case they haven't be resized manually. (bug 965772) */
@@ -680,51 +679,79 @@ html|input.urlbar-input[textoverflow]:no
   -moz-user-focus: normal;
 }
 
 #urlbar[pageproxystate="invalid"] > #identity-box {
   pointer-events: none;
   -moz-user-focus: ignore;
 }
 
+
+/* We leave 49ch plus whatever space the download button will need when it
+ * appears. Normally this should be 16px for the icon, plus 2 * 2px padding
+ * plus the toolbarbutton-inner-padding. We're adding 4px to ensure things
+ * like rounding on hidpi don't accidentally result in the button going
+ * into overflow.
+ */
+#urlbar-container {
+  min-width: calc(49ch + 24px + 2 * var(--toolbarbutton-inner-padding));
+}
+
+#nav-bar[hasdownloads] #urlbar-container {
+  min-width: 49ch;
+}
+
 #identity-icon-labels {
-  max-width: 18em;
+  max-width: 17em;
 }
 @media (max-width: 700px) {
   #urlbar-container {
-    min-width: 45ch;
+    min-width: calc(44ch + 24px + 2 * var(--toolbarbutton-inner-padding));
   }
+  #nav-bar[hasdownloads] #urlbar-container {
+    min-width: 44ch;
+  }
+
   #identity-icon-labels {
-    max-width: 70px;
+    max-width: 60px;
   }
 }
 @media (max-width: 600px) {
   #urlbar-container {
-    min-width: 40ch;
-  }
-  #identity-icon-labels {
-    max-width: 60px;
+    min-width: calc(39ch + 24px + 2 * var(--toolbarbutton-inner-padding));
   }
-}
-@media (max-width: 500px) {
-  #urlbar-container {
-    min-width: 35ch;
+  #nav-bar[hasdownloads] #urlbar-container {
+    min-width: 39ch;
   }
   #identity-icon-labels {
     max-width: 50px;
   }
 }
-@media (max-width: 400px) {
+@media (max-width: 500px) {
   #urlbar-container {
-    min-width: 28ch;
+    min-width: calc(34ch + 24px + 2 * var(--toolbarbutton-inner-padding));
+  }
+  #nav-bar[hasdownloads] #urlbar-container {
+    min-width: 34ch;
   }
   #identity-icon-labels {
     max-width: 40px;
   }
 }
+@media (max-width: 400px) {
+  #urlbar-container {
+    min-width: calc(27ch + 24px + 2 * var(--toolbarbutton-inner-padding));
+  }
+  #nav-bar[hasdownloads] #urlbar-container {
+    min-width: 27ch;
+  }
+  #identity-icon-labels {
+    max-width: 30px;
+  }
+}
 
 #identity-icon-country-label {
   direction: ltr;
 }
 
 #identity-box.verifiedIdentity > #identity-icon-labels > #identity-icon-label {
   margin-inline-end: 0.25em !important;
 }
@@ -1057,30 +1084,17 @@ notification[value="translation"] {
 
 /** See bug 872317 for why the following rule is necessary. */
 
 #downloads-button {
   -moz-binding: url("chrome://browser/content/downloads/download.xml#download-toolbarbutton");
 }
 
 /*** Visibility of downloads indicator controls ***/
-
-/* Bug 924050: If we've loaded the indicator, for now we hide it in the menu panel,
-   and just show the icon. This is a hack to side-step very weird layout bugs that
-   seem to be caused by the indicator stack interacting with the menu panel. */
-#downloads-button[indicator]:not([cui-areatype="menu-panel"]) > .toolbarbutton-badge-stack > image.toolbarbutton-icon,
-#downloads-button[indicator][cui-areatype="menu-panel"] > #downloads-indicator-anchor {
-  display: none;
-}
-
-toolbarpaletteitem[place="palette"] > #downloads-button[indicator] > .toolbarbutton-badge-stack > image.toolbarbutton-icon {
-  display: -moz-box;
-}
-
-toolbarpaletteitem[place="palette"] > #downloads-button[indicator] > #downloads-indicator-anchor {
+#downloads-button[indicator] > .toolbarbutton-badge-stack > image.toolbarbutton-icon {
   display: none;
 }
 
 /* Combobox dropdown renderer */
 #ContentSelectDropdown > menupopup {
   /* The menupopup itself should always be rendered LTR to ensure the scrollbar aligns with
    * the dropdown arrow on the dropdown widget. If a menuitem is RTL, its style will be set accordingly */
   direction: ltr;
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -668,17 +668,17 @@ var gPopupBlockerObserver = {
             label: popupButtonText,
             accessKey: popupButtonAccesskey,
             popup: "blockedPopupOptions",
             callback: null
           }];
 
           const priority = notificationBox.PRIORITY_WARNING_MEDIUM;
           notificationBox.appendNotification(message, "popup-blocked",
-                                             "chrome://browser/skin/Info.png",
+                                             "chrome://browser/skin/notification-icons/popup.svg",
                                              priority, buttons);
         }
       }
 
       // Record the fact that we've reported this blocked popup, so we don't
       // show it again.
       gBrowser.selectedBrowser.blockedPopups.reported = true;
     }
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -898,27 +898,28 @@
         <toolbaritem id="search-container" title="&searchItem.title;"
                      align="center" class="chromeclass-toolbar-additional panel-wide-item"
                      cui-areatype="toolbar"
                      flex="100" persist="width" removable="true">
           <searchbar id="searchbar" flex="1"/>
         </toolbaritem>
 
         <!-- This is a placeholder for the Downloads Indicator.  It is visible
-             during the customization of the toolbar, in the palette, and before
-             the Downloads Indicator overlay is loaded. -->
+             before the Downloads Indicator overlay is loaded. -->
         <toolbarbutton id="downloads-button"
+                       hidden="true"
                        class="toolbarbutton-1 chromeclass-toolbar-additional badged-button"
+                       skipintoolbarset="true"
+                       overflows="false"
                        key="key_openDownloads"
                        oncommand="DownloadsIndicatorView.onCommand(event);"
                        ondrop="DownloadsIndicatorView.onDrop(event);"
                        ondragover="DownloadsIndicatorView.onDragOver(event);"
                        ondragenter="DownloadsIndicatorView.onDragOver(event);"
                        label="&downloads.label;"
-                       removable="true"
                        cui-areatype="toolbar"
                        tooltip="dynamic-shortcut-tooltip"/>
 
         <toolbarbutton id="library-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
                        removable="true"
                        oncommand="PanelUI.showSubView('appMenu-libraryView', this, null, event);"
                        closemenu="none"
                        cui-areatype="toolbar"
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -5377,17 +5377,17 @@
                   callback() {
                     if (browser.messageManager) {
                       browser.messageManager.sendAsyncMessage("RefreshBlocker:Refresh", data);
                     }
                   }
                 }];
 
                 notificationBox.appendNotification(message, "refresh-blocked",
-                                                   "chrome://browser/skin/Info.png",
+                                                   "chrome://browser/skin/notification-icons/popup.svg",
                                                    notificationBox.PRIORITY_INFO_MEDIUM,
                                                    buttons);
               }
               break;
             }
 
             case "Prerender:Request": {
               let sendCancelPrerendering = () => {
--- a/browser/base/content/test/general/browser_documentnavigation.js
+++ b/browser/base/content/test/general/browser_documentnavigation.js
@@ -69,17 +69,17 @@ async function expectFocusOnF6(backward,
   }
 
   if (gMultiProcessBrowser && onContent) {
     expectedDocument = "main-window";
     expectedElement = gBrowser.selectedBrowser;
   }
 
   is(fm.focusedWindow.document.documentElement.id, expectedDocument, desc + " document matches");
-  is(fm.focusedElement, expectedElement, desc + " element matches");
+  is(fm.focusedElement, expectedElement, desc + " element matches (wanted: " + expectedElement.id + " got: " + fm.focusedElement.id + ")");
 
   if (onContent) {
     window.messageManager.removeMessageListener("BrowserTest:FocusChanged", focusChangedListener);
   }
 }
 
 // Load a page and navigate between it and the chrome window.
 add_task(async function() {
@@ -166,35 +166,52 @@ add_task(async function() {
                                false, "back focus with sidebar open sidebar");
   await expectFocusOnF6(true, "main-window", gURLBar.inputField,
                                false, "back focus with sidebar urlbar");
 
   SidebarUI.toggle("viewBookmarksSidebar");
 });
 
 // Navigate when the downloads panel is open
-add_task(async function() {
+add_task(async function test_download_focus() {
   await pushPrefs(["accessibility.tabfocus", 7]);
 
+  let testTargetFile = FileUtils.getFile("TmpD", ["dm-ui-test.file"]);
+  testTargetFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
+  registerCleanupFunction(() => testTargetFile.remove(false));
+  let downloadsList = await Downloads.getList(Downloads.PUBLIC);
+  let download = {
+    source: "http://www.example.com/test-download.txt",
+    target: testTargetFile.path,
+    succeeded: true,
+    startTime: new Date(),
+  };
+  await downloadsList.add(await Downloads.createDownload(download));
+
+  await BrowserTestUtils.waitForCondition(() => {
+    let btn = document.getElementById("downloads-button");
+    return !btn.hidden && btn.getBoundingClientRect().width > 0;
+  });
+
   let popupShownPromise = BrowserTestUtils.waitForEvent(document, "popupshown", true);
   EventUtils.synthesizeMouseAtCenter(document.getElementById("downloads-button"), { });
   await popupShownPromise;
 
   gURLBar.focus();
-  await expectFocusOnF6(false, "main-window", document.getElementById("downloadsHistory"),
+  await expectFocusOnF6(false, "main-window", document.getElementById("downloadsListBox"),
                                 false, "focus with downloads panel open panel");
   await expectFocusOnF6(false, "html1", "html1",
                                 true, "focus with downloads panel open");
   await expectFocusOnF6(false, "main-window", gURLBar.inputField,
                                 false, "focus downloads panel open urlbar");
 
   // Now go backwards
   await expectFocusOnF6(true, "html1", "html1",
                                true, "back focus with downloads panel open");
-  await expectFocusOnF6(true, "main-window", document.getElementById("downloadsHistory"),
+  await expectFocusOnF6(true, "main-window", document.getElementById("downloadsListBox"),
                                false, "back focus with downloads panel open");
   await expectFocusOnF6(true, "main-window", gURLBar.inputField,
                                false, "back focus downloads panel open urlbar");
 
   let downloadsPopup = document.getElementById("downloadsPanel");
   let popupHiddenPromise = BrowserTestUtils.waitForEvent(downloadsPopup, "popuphidden", true);
   downloadsPopup.hidePopup();
   await popupHiddenPromise;
--- a/browser/base/content/test/performance/browser_startup_images.js
+++ b/browser/base/content/test/performance/browser_startup_images.js
@@ -43,16 +43,20 @@ const whitelist = [
   },
 
   // Shared entries
   {
     file: "chrome://browser/skin/arrow-left.svg",
     platforms: ["linux", "win", "macosx"],
   },
   {
+    file: "chrome://browser/skin/fxa/sync-illustration.svg",
+    platforms: ["linux", "win", "macosx"],
+  },
+  {
     file: "chrome://browser/skin/tabbrowser/tab-overflow-indicator.png",
     platforms: ["linux", "win", "macosx"],
   },
 
   {
     file: "chrome://browser/skin/places/toolbarDropMarker.png",
     platforms: ["linux", "win", "macosx"],
   },
--- a/browser/base/content/test/urlbar/browser_dragdropURL.js
+++ b/browser/base/content/test/urlbar/browser_dragdropURL.js
@@ -4,35 +4,35 @@ const TEST_URL = "data:text/html,a test 
 const DRAG_URL = "http://www.example.com/";
 const DRAG_FORBIDDEN_URL = "chrome://browser/content/aboutDialog.xul";
 const DRAG_TEXT = "Firefox is awesome";
 const DRAG_WORD = "Firefox";
 
 add_task(async function checkDragURL() {
   await BrowserTestUtils.withNewTab(TEST_URL, function(browser) {
     // Have to use something other than the URL bar as a source, so picking the
-    // downloads button somewhat arbitrarily:
-    EventUtils.synthesizeDrop(document.getElementById("downloads-button"), gURLBar,
+    // home button somewhat arbitrarily:
+    EventUtils.synthesizeDrop(document.getElementById("home-button"), gURLBar,
                               [[{type: "text/plain", data: DRAG_URL}]], "copy", window);
     is(gURLBar.value, TEST_URL, "URL bar value should not have changed");
     is(gBrowser.selectedBrowser.userTypedValue, null, "Stored URL bar value should not have changed");
   });
 });
 
 add_task(async function checkDragForbiddenURL() {
   await BrowserTestUtils.withNewTab(TEST_URL, function(browser) {
-    EventUtils.synthesizeDrop(document.getElementById("downloads-button"), gURLBar,
+    EventUtils.synthesizeDrop(document.getElementById("home-button"), gURLBar,
                               [[{type: "text/plain", data: DRAG_FORBIDDEN_URL}]], "copy", window);
     isnot(gURLBar.value, DRAG_FORBIDDEN_URL, "Shouldn't be allowed to drop forbidden URL on URL bar");
   });
 });
 
 add_task(async function checkDragText() {
   await BrowserTestUtils.withNewTab(TEST_URL, function(browser) {
-    EventUtils.synthesizeDrop(document.getElementById("downloads-button"), gURLBar,
+    EventUtils.synthesizeDrop(document.getElementById("home-button"), gURLBar,
                               [[{type: "text/plain", data: DRAG_TEXT}]], "copy", window);
     is(gURLBar.value, DRAG_TEXT, "Dragging normal text should replace the URL bar value");
 
-    EventUtils.synthesizeDrop(document.getElementById("downloads-button"), gURLBar,
+    EventUtils.synthesizeDrop(document.getElementById("home-button"), gURLBar,
                               [[{type: "text/plain", data: DRAG_WORD}]], "copy", window);
     is(gURLBar.value, DRAG_WORD, "Dragging a single word should replace the URL bar value");
   });
 });
--- a/browser/components/customizableui/CustomizableUI.jsm
+++ b/browser/components/customizableui/CustomizableUI.jsm
@@ -52,17 +52,17 @@ const kSubviewEvents = [
   "ViewShowing",
   "ViewHiding"
 ];
 
 /**
  * The current version. We can use this to auto-add new default widgets as necessary.
  * (would be const but isn't because of testing purposes)
  */
-var kVersion = 10;
+var kVersion = 11;
 
 /**
  * Buttons removed from built-ins by version they were removed. kVersion must be
  * bumped any time a new id is added to this. Use the button id as key, and
  * version the button is removed in as the value.  e.g. "pocket-button": 5
  */
 var ObsoleteBuiltinButtons = {
 };
@@ -172,17 +172,17 @@ XPCOMUtils.defineLazyGetter(this, "log",
 
 var CustomizableUIInternal = {
   initialize() {
     log.debug("Initializing");
 
     this.addListener(this);
     this._defineBuiltInWidgets();
     this.loadSavedState();
-    this._introduceNewBuiltinWidgets();
+    this._updateForNewVersion();
     this._markObsoleteBuiltinButtonsSeen();
 
     this.registerArea(CustomizableUI.AREA_FIXED_OVERFLOW_PANEL, {
       type: CustomizableUI.TYPE_MENU_PANEL,
       defaultPlacements: [],
       anchor: "nav-bar-overflow-button",
     }, true);
 
@@ -190,17 +190,16 @@ var CustomizableUIInternal = {
       "back-button",
       "forward-button",
       "stop-reload-button",
       "home-button",
       "spring",
       "urlbar-container",
       "search-container",
       "spring",
-      "downloads-button",
       "library-button",
       "sidebar-button",
     ];
 
     if (AppConstants.MOZ_DEV_EDITION) {
       navbarPlacements.splice(2, 0, "developer-button");
     }
 
@@ -258,17 +257,17 @@ var CustomizableUIInternal = {
   },
 
   _defineBuiltInWidgets() {
     for (let widgetDefinition of CustomizableWidgets) {
       this.createBuiltinWidget(widgetDefinition);
     }
   },
 
-  _introduceNewBuiltinWidgets() {
+  _updateForNewVersion() {
     // We should still enter even if gSavedState.currentVersion >= kVersion
     // because the per-widget pref facility is independent of versioning.
     if (!gSavedState) {
       // Flip all the prefs so we don't try to re-introduce later:
       for (let [, widget] of gPalette) {
         if (widget.defaultArea && widget._introducedInVersion === "pref") {
           let prefId = "browser.toolbarbuttons.introduced." + widget.id;
           Services.prefs.setBoolPref(prefId, true);
@@ -411,16 +410,25 @@ var CustomizableUIInternal = {
     if (currentVersion < 10 && gSavedState && gSavedState.placements) {
       for (let placements of Object.values(gSavedState.placements)) {
         if (placements.includes("webcompat-reporter-button")) {
           placements.splice(placements.indexOf("webcompat-reporter-button"), 1);
           break;
         }
       }
     }
+
+    if (currentVersion < 11 && gSavedState && gSavedState.placements) {
+      for (let placements of Object.values(gSavedState.placements)) {
+        if (placements.includes("downloads-button")) {
+          placements.splice(placements.indexOf("downloads-button"), 1);
+          break;
+        }
+      }
+    }
   },
 
   /**
    * _markObsoleteBuiltinButtonsSeen
    * when upgrading, ensure obsoleted buttons are in seen state.
    */
   _markObsoleteBuiltinButtonsSeen() {
     if (!gSavedState)
--- a/browser/components/customizableui/content/panelUI.inc.xul
+++ b/browser/components/customizableui/content/panelUI.inc.xul
@@ -161,17 +161,17 @@
                     showMoreTooltipText="&appMenuRemoteTabs.showMore.tooltip;"
                     notabsforclientlabel="&appMenuRemoteTabs.notabs.label;"
                     />
             </vbox>
             <!-- Sync is ready to Sync but the "tabs" engine isn't enabled-->
             <hbox id="PanelUI-remotetabs-tabsdisabledpane" pack="center" flex="1">
               <vbox class="PanelUI-remotetabs-instruction-box" align="center">
                 <hbox pack="center">
-                  <image class="fxaSyncIllustration"/>
+                  <html:img class="fxaSyncIllustration" src="chrome://browser/skin/fxa/sync-illustration.svg"/>
                 </hbox>
                 <label class="PanelUI-remotetabs-instruction-label">&appMenuRemoteTabs.tabsnotsyncing.label;</label>
                 <hbox pack="center">
                   <toolbarbutton class="PanelUI-remotetabs-prefs-button"
                                  label="&appMenuRemoteTabs.openprefs.label;"
                                  oncommand="gSync.openPrefs('synced-tabs');"/>
                 </hbox>
               </vbox>
@@ -179,17 +179,17 @@
             <!-- Sync is ready to Sync but we are still fetching the tabs to show -->
             <vbox id="PanelUI-remotetabs-fetching">
               <!-- Show intentionally blank panel, see bug 1239845 -->
             </vbox>
             <!-- Sync has only 1 (ie, this) device connected -->
             <hbox id="PanelUI-remotetabs-nodevicespane" pack="center" flex="1">
               <vbox class="PanelUI-remotetabs-instruction-box">
                 <hbox pack="center">
-                  <image class="fxaSyncIllustration"/>
+                  <html:img class="fxaSyncIllustration" src="chrome://browser/skin/fxa/sync-illustration.svg"/>
                 </hbox>
                 <label class="PanelUI-remotetabs-instruction-title">&appMenuRemoteTabs.noclients.title;</label>
                 <label class="PanelUI-remotetabs-instruction-label">&appMenuRemoteTabs.noclients.subtitle;</label>
                 <!-- The inner HTML for PanelUI-remotetabs-mobile-promo is built at runtime -->
                 <label id="PanelUI-remotetabs-mobile-promo" fxAccountsBrand="&syncBrand.fxAccount.label;"/>
               </vbox>
             </hbox>
           </deck>
@@ -197,31 +197,31 @@
         <!-- a box to ensure contained boxes are centered horizonally -->
         <hbox pack="center" flex="1">
           <!-- When Sync is not configured -->
           <vbox id="PanelUI-remotetabs-setupsync"
                 flex="1"
                 align="center"
                 class="PanelUI-remotetabs-instruction-box"
                 observes="sync-setup-state">
-            <image class="fxaSyncIllustration"/>
+            <html:img class="fxaSyncIllustration" src="chrome://browser/skin/fxa/sync-illustration.svg"/>
             <label class="PanelUI-remotetabs-instruction-label">&appMenuRemoteTabs.notsignedin.label;</label>
             <toolbarbutton class="PanelUI-remotetabs-prefs-button"
                            label="&appMenuRemoteTabs.signin.label;"
                            oncommand="gSync.openPrefs('synced-tabs');"/>
           </vbox>
           <!-- When Sync needs re-authentication. This uses the exact same messaging
                as "Sync is not configured" but remains a separate box so we get
                the goodness of observing broadcasters to manage the hidden states -->
           <vbox id="PanelUI-remotetabs-reauthsync"
                 flex="1"
                 align="center"
                 class="PanelUI-remotetabs-instruction-box"
                 observes="sync-reauth-state">
-            <image class="fxaSyncIllustration"/>
+            <html:img class="fxaSyncIllustration" src="chrome://browser/skin/fxa/sync-illustration.svg"/>
             <label class="PanelUI-remotetabs-instruction-label">&appMenuRemoteTabs.notsignedin.label;</label>
             <toolbarbutton class="PanelUI-remotetabs-prefs-button"
                            label="&appMenuRemoteTabs.signin.label;"
                            oncommand="gSync.openPrefs('synced-tabs');"/>
           </vbox>
         </hbox>
       </vbox>
     </panelview>
--- a/browser/components/customizableui/test/browser_1042100_default_placements_update.js
+++ b/browser/components/customizableui/test/browser_1042100_default_placements_update.js
@@ -14,17 +14,17 @@ function test() {
 
   let oldState = CustomizableUIBSPass.gSavedState;
   registerCleanupFunction(() => CustomizableUIBSPass.gSavedState = oldState );
 
   is(CustomizableUIBSPass.gFuturePlacements.size, 0,
      "All future placements should be dealt with by now.");
 
   let {CustomizableUIInternal, gFuturePlacements, gPalette} = CustomizableUIBSPass;
-  CustomizableUIInternal._introduceNewBuiltinWidgets();
+  CustomizableUIInternal._updateForNewVersion();
   is(gFuturePlacements.size, 0,
      "No change to future placements initially.");
 
   let currentVersion = CustomizableUIBSPass.kVersion;
 
 
   // Add our widget to the defaults:
   let testWidgetNew = {
@@ -62,17 +62,17 @@ function test() {
   CustomizableUIBSPass.kVersion++;
 
   let hadSavedState = !!CustomizableUIBSPass.gSavedState
   if (!hadSavedState) {
     CustomizableUIBSPass.gSavedState = {currentVersion: CustomizableUIBSPass.kVersion - 1};
   }
 
   // Then call the re-init routine so we re-add the builtin widgets
-  CustomizableUIInternal._introduceNewBuiltinWidgets();
+  CustomizableUIInternal._updateForNewVersion();
   is(gFuturePlacements.size, 1,
      "Should have 1 more future placement");
   let haveNavbarPlacements = gFuturePlacements.has(CustomizableUI.AREA_NAVBAR);
   ok(haveNavbarPlacements, "Should have placements for nav-bar");
   if (haveNavbarPlacements) {
     let placements = [...gFuturePlacements.get(CustomizableUI.AREA_NAVBAR)];
 
     // Ignore widgets that are placed using the pref facility and not the
@@ -101,17 +101,17 @@ function test() {
 
   CustomizableUIBSPass.gSavedState = {
     currentVersion: 6,
     placements: {
       "nav-bar": ["urlbar-container", "bookmarks-menu-button"],
       "PanelUI-contents": ["panic-button", "edit-controls"],
     },
   };
-  CustomizableUIInternal._introduceNewBuiltinWidgets();
+  CustomizableUIInternal._updateForNewVersion();
   let navbarPlacements = CustomizableUIBSPass.gSavedState.placements["nav-bar"];
   let springs = navbarPlacements.filter(id => id.includes("spring"));
   is(springs.length, 2, "Should have 2 toolbarsprings in placements now");
   navbarPlacements = navbarPlacements.filter(id => !id.includes("spring"));
   is(navbarPlacements[0], "back-button", "Back button is in the right place.");
   is(navbarPlacements[1], "forward-button", "Fwd button is in the right place.");
   is(navbarPlacements[2], "stop-reload-button", "Stop/reload button is in the right place.");
   is(navbarPlacements[3], "home-button", "Home button is in the right place.");
--- a/browser/components/customizableui/test/browser_1161838_inserted_new_default_buttons.js
+++ b/browser/components/customizableui/test/browser_1161838_inserted_new_default_buttons.js
@@ -12,17 +12,17 @@ function test() {
      "All future placements should be dealt with by now.");
 
   let {CustomizableUIInternal, gFuturePlacements, gPalette} = CustomizableUIBSPass;
 
   // Force us to have a saved state:
   CustomizableUIInternal.saveState();
   CustomizableUIInternal.loadSavedState();
 
-  CustomizableUIInternal._introduceNewBuiltinWidgets();
+  CustomizableUIInternal._updateForNewVersion();
   is(gFuturePlacements.size, 0,
      "No change to future placements initially.");
 
   // Add our widget to the defaults:
   let testWidgetNew = {
     id: "test-messing-with-default-placements-new-pref",
     label: "Test messing with default placements - pref-based",
     defaultArea: CustomizableUI.AREA_NAVBAR,
@@ -39,17 +39,17 @@ function test() {
 
   // Now adjust default placements for area:
   let navbarArea = CustomizableUIBSPass.gAreas.get(CustomizableUI.AREA_NAVBAR);
   let navbarPlacements = navbarArea.get("defaultPlacements");
   navbarPlacements.splice(navbarPlacements.indexOf("bookmarks-menu-button") + 1, 0, testWidgetNew.id);
 
   let savedPlacements = CustomizableUIBSPass.gSavedState.placements[CustomizableUI.AREA_NAVBAR];
   // Then call the re-init routine so we re-add the builtin widgets
-  CustomizableUIInternal._introduceNewBuiltinWidgets();
+  CustomizableUIInternal._updateForNewVersion();
   is(gFuturePlacements.size, 1,
      "Should have 1 more future placement");
   let futureNavbarPlacements = gFuturePlacements.get(CustomizableUI.AREA_NAVBAR);
   ok(futureNavbarPlacements, "Should have placements for nav-bar");
   if (futureNavbarPlacements) {
     ok(futureNavbarPlacements.has(testWidgetNew.id), "widget should be in future placements");
   }
   CustomizableUIInternal._placeNewDefaultWidgetsInArea(CustomizableUI.AREA_NAVBAR);
--- a/browser/components/customizableui/test/browser_918049_skipintoolbarset_dnd.js
+++ b/browser/components/customizableui/test/browser_918049_skipintoolbarset_dnd.js
@@ -11,26 +11,26 @@ var skippedItem;
 add_task(async function() {
   navbar = document.getElementById("nav-bar");
   skippedItem = document.createElement("toolbarbutton");
   skippedItem.id = "test-skipintoolbarset-item";
   skippedItem.setAttribute("label", "Test");
   skippedItem.setAttribute("skipintoolbarset", "true");
   skippedItem.setAttribute("removable", "true");
   navbar.customizationTarget.appendChild(skippedItem);
-  let downloadsButton = document.getElementById("downloads-button");
+  let libraryButton = document.getElementById("library-button");
   await startCustomizing();
   ok(CustomizableUI.inDefaultState, "Should still be in default state");
-  simulateItemDrag(skippedItem, downloadsButton);
+  simulateItemDrag(skippedItem, libraryButton);
   ok(CustomizableUI.inDefaultState, "Should still be in default state");
   let skippedItemWrapper = skippedItem.parentNode;
   is(skippedItemWrapper.nextSibling && skippedItemWrapper.nextSibling.id,
-     downloadsButton.parentNode.id, "Should be next to downloads button");
-  simulateItemDrag(downloadsButton, skippedItem);
-  let downloadWrapper = downloadsButton.parentNode;
+     libraryButton.parentNode.id, "Should be next to downloads button");
+  simulateItemDrag(libraryButton, skippedItem);
+  let downloadWrapper = libraryButton.parentNode;
   is(downloadWrapper.nextSibling && downloadWrapper.nextSibling.id,
      skippedItem.parentNode.id, "Should be next to skipintoolbarset item");
   ok(CustomizableUI.inDefaultState, "Should still be in default state");
 });
 
 add_task(async function asyncCleanup() {
   await endCustomizing();
   skippedItem.remove();
--- a/browser/components/customizableui/test/browser_923857_customize_mode_event_wrapping_during_reset.js
+++ b/browser/components/customizableui/test/browser_923857_customize_mode_event_wrapping_during_reset.js
@@ -3,21 +3,21 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 // Customize mode reset button should revert correctly
 add_task(async function() {
   await startCustomizing();
   let devButton = document.getElementById("developer-button");
-  let downloadsButton = document.getElementById("downloads-button");
+  let libraryButton = document.getElementById("library-button");
   let searchBox = document.getElementById("search-container");
   let palette = document.getElementById("customization-palette");
-  ok(devButton && downloadsButton && searchBox && palette, "Stuff should exist");
-  simulateItemDrag(devButton, downloadsButton);
+  ok(devButton && libraryButton && searchBox && palette, "Stuff should exist");
+  simulateItemDrag(devButton, libraryButton);
   simulateItemDrag(searchBox, palette);
   await gCustomizeMode.reset();
   ok(CustomizableUI.inDefaultState, "Should be back in default state");
   await endCustomizing();
 });
 
 add_task(async function asyncCleanup() {
   await resetCustomization();
--- a/browser/components/customizableui/test/browser_927717_customize_drag_empty_toolbar.js
+++ b/browser/components/customizableui/test/browser_927717_customize_drag_empty_toolbar.js
@@ -5,21 +5,21 @@
 "use strict";
 
 const kTestToolbarId = "test-empty-drag";
 
 // Attempting to drag an item to an empty container should work.
 add_task(async function() {
   await createToolbarWithPlacements(kTestToolbarId, []);
   await startCustomizing();
-  let downloadButton = document.getElementById("downloads-button");
+  let libraryButton = document.getElementById("library-button");
   let customToolbar = document.getElementById(kTestToolbarId);
-  simulateItemDrag(downloadButton, customToolbar);
-  assertAreaPlacements(kTestToolbarId, ["downloads-button"]);
-  ok(downloadButton.parentNode && downloadButton.parentNode.parentNode == customToolbar,
+  simulateItemDrag(libraryButton, customToolbar);
+  assertAreaPlacements(kTestToolbarId, ["library-button"]);
+  ok(libraryButton.parentNode && libraryButton.parentNode.parentNode == customToolbar,
      "Button should really be in toolbar");
   await endCustomizing();
   removeCustomToolbars();
 });
 
 add_task(async function asyncCleanup() {
   await endCustomizing();
   await resetCustomization();
--- a/browser/components/customizableui/test/browser_968565_insert_before_hidden_items.js
+++ b/browser/components/customizableui/test/browser_968565_insert_before_hidden_items.js
@@ -39,18 +39,18 @@ add_task(async function() {
 
   // Make sure we have some hidden items at the end of the nav-bar.
   navbar.insertItem(hidden1.id);
   navbar.insertItem(hidden2.id);
 
   // Drag an item and drop it onto the nav-bar customization target, but
   // not over a particular item.
   await startCustomizing();
-  let downloadsButton = document.getElementById("downloads-button");
-  simulateItemDrag(downloadsButton, navbar.customizationTarget);
+  let homeButton = document.getElementById("home-button");
+  simulateItemDrag(homeButton, navbar.customizationTarget);
 
   await endCustomizing();
 
-  is(downloadsButton.previousSibling.id, lastVisible.id,
+  is(homeButton.previousSibling.id, lastVisible.id,
      "The downloads button should be placed after the last visible item.");
 
   await resetCustomization();
 });
--- a/browser/components/customizableui/test/browser_overflow_use_subviews.js
+++ b/browser/components/customizableui/test/browser_overflow_use_subviews.js
@@ -38,33 +38,8 @@ add_task(async function check_developer_
   button.click();
   await subviewShownPromise;
   let hasSubviews = !!kOverflowPanel.querySelector("photonpanelmultiview,panelmultiview");
   let expectedPanel = hasSubviews ? kOverflowPanel : document.getElementById("customizationui-widget-panel");
   is(developerView.closest("panel"), expectedPanel, "Should be inside the panel");
   expectedPanel.hidePopup();
   await Promise.resolve(); // wait for popup to hide fully.
 });
-
-/**
- * This checks that non-subview-compatible items still work correctly.
- * Ideally we should make the downloads panel and bookmarks/library item
- * proper subview items, then this test can go away, and potentially we can
- * simplify some of the subview anchoring code.
- */
-add_task(async function check_downloads_panel_in_overflow() {
-  let navbar = document.getElementById(CustomizableUI.AREA_NAVBAR);
-  ok(navbar.hasAttribute("overflowing"), "Should still be overflowing");
-  let chevron = document.getElementById("nav-bar-overflow-button");
-  let shownPanelPromise = promisePanelElementShown(window, kOverflowPanel);
-  chevron.click();
-  await shownPanelPromise;
-
-  let button = document.getElementById("downloads-button");
-  button.click();
-  await waitForCondition(() => {
-    let panel = document.getElementById("downloadsPanel");
-    return panel && panel.state != "closed";
-  });
-  let downloadsPanel = document.getElementById("downloadsPanel");
-  isnot(downloadsPanel.state, "closed", "Should be attempting to show the downloads panel.");
-  downloadsPanel.hidePopup();
-});
--- a/browser/components/downloads/DownloadsCommon.jsm
+++ b/browser/components/downloads/DownloadsCommon.jsm
@@ -37,17 +37,16 @@ const { classes: Cc, interfaces: Ci, uti
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyModuleGetters(this, {
   NetUtil: "resource://gre/modules/NetUtil.jsm",
   PluralForm: "resource://gre/modules/PluralForm.jsm",
   AppConstants: "resource://gre/modules/AppConstants.jsm",
   AppMenuNotifications: "resource://gre/modules/AppMenuNotifications.jsm",
-  CustomizableUI: "resource:///modules/CustomizableUI.jsm",
   DownloadHistory: "resource://gre/modules/DownloadHistory.jsm",
   Downloads: "resource://gre/modules/Downloads.jsm",
   DownloadUIHelper: "resource://gre/modules/DownloadUIHelper.jsm",
   DownloadUtils: "resource://gre/modules/DownloadUtils.jsm",
   FileUtils: "resource://gre/modules/FileUtils.jsm",
   OS: "resource://gre/modules/osfile.jsm",
   PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
   RecentWindow: "resource:///modules/RecentWindow.jsm",
@@ -1156,27 +1155,16 @@ DownloadsIndicatorDataCtor.prototype = {
   _updateViews() {
     // Do not update the status indicators during batch loads of download items.
     if (this._loading) {
       return;
     }
 
     this._refreshProperties();
 
-    let widgetGroup = CustomizableUI.getWidget("downloads-button");
-    let inMenu = widgetGroup.areaType == CustomizableUI.TYPE_MENU_PANEL;
-    if (inMenu) {
-      if (this._attention == DownloadsCommon.ATTENTION_NONE) {
-        AppMenuNotifications.removeNotification(/^download-/);
-      } else {
-        let badgeClass = "download-" + this._attention;
-        AppMenuNotifications.showBadgeOnlyNotification(badgeClass);
-      }
-    }
-
     this._views.forEach(this._updateView, this);
   },
 
   /**
    * Updates the specified view with the current aggregate values.
    *
    * @param aView
    *        DownloadsIndicatorView object to be updated.
--- a/browser/components/downloads/content/downloads.css
+++ b/browser/components/downloads/content/downloads.css
@@ -171,23 +171,16 @@ richlistitem.download button {
                                            > toolbarseparator,
 
 /* The "show blocked info" button is shown only in the downloads panel. */
 .downloadShowBlockedInfo
 {
   display: none;
 }
 
-/*** Downloads panel ***/
-
-#downloadsPanel[hasdownloads] #emptyDownloads,
-#downloadsPanel:not([hasdownloads]) #downloadsListBox {
-  display: none;
-}
-
 /*** Downloads panel multiview (main view and blocked-downloads subview) ***/
 
 /* Hide all the usual buttons. */
 #downloadsPanel-mainView .download-state[state="8"] .downloadCancel,
 #downloadsPanel-mainView .download-state[state="8"] .downloadConfirmBlock,
 #downloadsPanel-mainView .download-state[state="8"] .downloadChooseUnblock,
 #downloadsPanel-mainView .download-state[state="8"] .downloadChooseOpen,
 #downloadsPanel-mainView .download-state[state="8"] .downloadRetry,
--- a/browser/components/downloads/content/downloads.js
+++ b/browser/components/downloads/content/downloads.js
@@ -697,22 +697,18 @@ var DownloadsView = {
    * Called when the number of items in the list changes.
    */
   _itemCountChanged() {
     DownloadsCommon.log("The downloads item count has changed - we are tracking",
                         this._downloads.length, "downloads in total.");
     let count = this._downloads.length;
     let hiddenCount = count - this.kItemCountLimit;
 
-    if (count > 0) {
-      DownloadsCommon.log("Setting the panel's hasdownloads attribute to true.");
-      DownloadsPanel.panel.setAttribute("hasdownloads", "true");
-    } else {
-      DownloadsCommon.log("Removing the panel's hasdownloads attribute.");
-      DownloadsPanel.panel.removeAttribute("hasdownloads");
+    if (count <= 0) {
+      DownloadsButton.hide();
     }
 
     // If we've got some hidden downloads, we should activate the
     // DownloadsSummary. The DownloadsSummary will determine whether or not
     // it's appropriate to actually display the summary.
     DownloadsSummary.active = hiddenCount > 0;
   },
 
--- a/browser/components/downloads/content/downloadsOverlay.xul
+++ b/browser/components/downloads/content/downloadsOverlay.xul
@@ -112,19 +112,16 @@
         <panelview id="downloadsPanel-mainView">
           <vbox class="panel-view-body-unscrollable">
             <richlistbox id="downloadsListBox"
                          context="downloadsContextMenu"
                          onmouseover="DownloadsView.onDownloadMouseOver(event);"
                          onmouseout="DownloadsView.onDownloadMouseOut(event);"
                          oncontextmenu="DownloadsView.onDownloadContextMenu(event);"
                          ondragstart="DownloadsView.onDownloadDragStart(event);"/>
-            <description id="emptyDownloads"
-                         mousethrough="always"
-                         value="&downloadsPanelEmpty.label;"/>
           </vbox>
           <vbox id="downloadsFooter"
                 class="downloadsPanelFooter">
             <stack>
               <hbox id="downloadsSummary"
                     align="center"
                     orient="horizontal"
                     onkeydown="DownloadsSummary.onKeyDown(event);"
--- a/browser/components/downloads/content/indicator.js
+++ b/browser/components/downloads/content/indicator.js
@@ -103,46 +103,25 @@ const DownloadsButton = {
     if (!indicator) {
       // Exit now if the indicator overlay isn't loaded yet, or if the button
       // is not in the document.
       return null;
     }
 
     indicator.open = this._anchorRequested;
 
-    let widget = CustomizableUI.getWidget("downloads-button")
-                               .forWindow(window);
-     // Determine if the indicator is located on an invisible toolbar.
-     if (!isElementVisible(indicator.parentNode) && !widget.overflowed) {
-       return null;
-     }
+    // Determine if the indicator is located on an invisible toolbar.
+    if (!window.toolbar.visible) {
+      return null;
+    }
 
     return DownloadsIndicatorView.indicatorAnchor;
   },
 
   /**
-   * Checks whether the indicator is, or will soon be visible in the browser
-   * window.
-   *
-   * @param aCallback
-   *        Called once the indicator overlay has loaded. Gets a boolean
-   *        argument representing the indicator visibility.
-   */
-  checkIsVisible(aCallback) {
-    DownloadsOverlayLoader.ensureOverlayLoaded(this.kIndicatorOverlay, () => {
-      if (!this._placeholder) {
-        aCallback(false);
-      } else {
-        let element = DownloadsIndicatorView.indicator || this._placeholder;
-        aCallback(isElementVisible(element.parentNode));
-      }
-    });
-  },
-
-  /**
    * Indicates whether we should try and show the indicator temporarily as an
    * anchor for the panel, even if the indicator would be hidden by default.
    */
   _anchorRequested: false,
 
   /**
    * Ensures that there is an anchor available for the panel.
    *
@@ -159,16 +138,46 @@ const DownloadsButton = {
     }
 
     DownloadsOverlayLoader.ensureOverlayLoaded(this.kIndicatorOverlay, () => {
       this._anchorRequested = true;
       aCallback(this._getAnchorInternal());
     });
   },
 
+  unhideAndPlace() {
+    if (this._placeholder.hasAttribute("hidden")) {
+      this._navBar.setAttribute("hasdownloads", "true");
+      this._placeholder.removeAttribute("hidden");
+      let insertionPoint = document.getElementById("urlbar-container");
+      while (insertionPoint && insertionPoint.nextElementSibling) {
+        let next = insertionPoint.nextElementSibling;
+        if (!next.hidden &&
+            (next.nodeName == "toolbarspring" ||
+             next.id == "downloads-button" || /* also have to ignore ourselves... */
+             next.id == "search-container" ||
+             next.id == "urlbar-search-splitter")) {
+          insertionPoint = next;
+        } else {
+          break;
+        }
+      }
+      if (insertionPoint && insertionPoint.nextElementSibling != this._placeholder &&
+          insertionPoint.nextElementSibling != this._placeholder.nextElementSibling) {
+        insertionPoint.insertAdjacentElement("afterend", this._placeholder);
+      }
+    }
+  },
+
+  hide() {
+    DownloadsPanel.hidePanel();
+    this._navBar.removeAttribute("hasdownloads");
+    this._placeholder.setAttribute("hidden", "true");
+  },
+
   /**
    * Allows the temporary anchor to be hidden.
    */
   releaseAnchor() {
     this._anchorRequested = false;
     this._getAnchorInternal();
   },
 
@@ -247,22 +256,16 @@ const DownloadsIndicatorView = {
   _ensureOperational(aCallback) {
     if (this._operational) {
       if (aCallback) {
         aCallback();
       }
       return;
     }
 
-    // If we don't have a _placeholder, there's no chance that the overlay
-    // will load correctly: bail (and don't set _operational to true!)
-    if (!DownloadsButton._placeholder) {
-      return;
-    }
-
     DownloadsOverlayLoader.ensureOverlayLoaded(
       DownloadsButton.kIndicatorOverlay,
       () => {
         this._operational = true;
 
         // If the view is initialized, we need to update the elements now that
         // they are finally available in the document.
         if (this._initialized) {
@@ -334,42 +337,29 @@ const DownloadsIndicatorView = {
    *        Set to "start" for new downloads, "finish" for completed downloads.
    */
   _showNotification(aType) {
     // No need to show visual notification if the panel is visible.
     if (DownloadsPanel.isPanelShowing) {
       return;
     }
 
-    let anchor = DownloadsButton._placeholder;
-    let widgetGroup = CustomizableUI.getWidget("downloads-button");
-    let widget = widgetGroup.forWindow(window);
-    if (widget.overflowed || widgetGroup.areaType == CustomizableUI.TYPE_MENU_PANEL) {
-      if (anchor && this._isAncestorPanelOpen(anchor)) {
-        // If the containing panel is open, don't do anything, because the
-        // notification would appear under the open panel. See
-        // https://bugzilla.mozilla.org/show_bug.cgi?id=984023
-        return;
-      }
-
-      // Otherwise, try to use the anchor of the panel:
-      anchor = widget.anchor;
-    }
-    if (!anchor || !isElementVisible(anchor.parentNode)) {
+    if (!window.toolbar.visible) {
       // Our container isn't visible, so can't show the animation:
       return;
     }
 
     // The notification element is positioned to show in the same location as
     // the downloads button. It's not in the downloads button itself in order to
     // be able to anchor the notification elsewhere if required, and to ensure
     // the notification isn't clipped by overflow properties of the anchor's
     // container.
     // Note: no notifier animation for download finished in Photon
     let notifier = this.notifier;
+    let anchor = DownloadsButton._placeholder;
 
     if (aType == "start") {
       // Show the notifier before measuring for size/placement. Being hidden by default
       // avoids the interference with scrolling/APZ when the notifier element is
       // tall enough to overlap the tabbrowser element
       notifier.removeAttribute("hidden");
 
       // the anchor height may vary if font-size is changed or
@@ -418,17 +408,19 @@ const DownloadsIndicatorView = {
    * Indicates whether the indicator should be shown because there are some
    * downloads to be displayed.
    */
   set hasDownloads(aValue) {
     if (this._hasDownloads != aValue || (!this._operational && aValue)) {
       this._hasDownloads = aValue;
 
       // If there is at least one download, ensure that the view elements are
+      // operational
       if (aValue) {
+        DownloadsButton.unhideAndPlace();
         this._ensureOperational();
       }
     }
     return aValue;
   },
   get hasDownloads() {
     return this._hasDownloads;
   },
@@ -440,17 +432,16 @@ const DownloadsIndicatorView = {
    */
   set percentComplete(aValue) {
     if (!this._operational) {
       return this._percentComplete;
     }
 
     if (this._percentComplete !== aValue) {
       this._percentComplete = aValue;
-      this._refreshAttention();
 
       if (this._percentComplete >= 0) {
         this.indicator.setAttribute("progress", "true");
         // For arrow type only:
         // We set animationDelay to a minus value (0s ~ -100s) to show the
         // corresponding frame needed for progress.
         this._progressIcon.style.animationDelay = (-this._percentComplete) + "s";
       } else {
@@ -466,57 +457,35 @@ const DownloadsIndicatorView = {
    * Set when the indicator should draw user attention to itself.
    */
   set attention(aValue) {
     if (!this._operational) {
       return this._attention;
     }
     if (this._attention != aValue) {
       this._attention = aValue;
-      this._refreshAttention();
+      if (this._attention == DownloadsCommon.ATTENTION_NONE) {
+        this.indicator.removeAttribute("attention");
+      } else {
+        this.indicator.setAttribute("attention", this._attention);
+      }
     }
     return this._attention;
   },
-
-  _refreshAttention() {
-    // Check if the downloads button is in the menu panel, to determine which
-    // button needs to get a badge.
-    let widgetGroup = CustomizableUI.getWidget("downloads-button");
-    let inMenu = widgetGroup.areaType == CustomizableUI.TYPE_MENU_PANEL;
-
-    // For arrow-Styled indicator, suppress success attention if we have
-    // progress in toolbar
-    let suppressAttention = !inMenu &&
-      this._attention == DownloadsCommon.ATTENTION_SUCCESS &&
-      this._percentComplete >= 0;
-
-    if (suppressAttention || this._attention == DownloadsCommon.ATTENTION_NONE) {
-      this.indicator.removeAttribute("attention");
-    } else {
-      this.indicator.setAttribute("attention", this._attention);
-    }
-  },
   _attention: DownloadsCommon.ATTENTION_NONE,
 
   // User interface event functions
 
   onWindowUnload() {
     // This function is registered as an event listener, we can't use "this".
     DownloadsIndicatorView.ensureTerminated();
   },
 
   onCommand(aEvent) {
-    // If the downloads button is in the menu panel, open the Library
-    let widgetGroup = CustomizableUI.getWidget("downloads-button");
-    if (widgetGroup.areaType == CustomizableUI.TYPE_MENU_PANEL) {
-      DownloadsPanel.showDownloadsHistory();
-    } else {
-      DownloadsPanel.showPanel();
-    }
-
+    DownloadsPanel.showPanel();
     aEvent.stopPropagation();
   },
 
   onDragOver(aEvent) {
     browserDragAndDrop.dragOver(aEvent);
   },
 
   onDrop(aEvent) {
@@ -558,21 +527,16 @@ const DownloadsIndicatorView = {
     if (!indicator || indicator.getAttribute("indicator") != "true") {
       return null;
     }
 
     return this._indicator = indicator;
   },
 
   get indicatorAnchor() {
-    let widget = CustomizableUI.getWidget("downloads-button")
-                               .forWindow(window);
-    if (widget.overflowed) {
-      return widget.anchor;
-    }
     return document.getElementById("downloads-indicator-anchor");
   },
 
   get _progressIcon() {
     return this.__progressIcon ||
       (this.__progressIcon = document.getElementById("downloads-indicator-progress-inner"));
   },
 
--- a/browser/components/downloads/test/browser/browser.ini
+++ b/browser/components/downloads/test/browser/browser.ini
@@ -1,15 +1,13 @@
 [DEFAULT]
 support-files = head.js
 
 [browser_basic_functionality.js]
 [browser_first_download_panel.js]
 skip-if = os == "linux" # Bug 949434
-[browser_overflow_anchor.js]
-skip-if = os == "linux" # Bug 952422
 [browser_confirm_unblock_download.js]
 [browser_iframe_gone_mid_download.js]
 [browser_indicatorDrop.js]
 [browser_libraryDrop.js]
 [browser_downloads_panel_block.js]
 skip-if = true # Bug 1352792
 [browser_downloads_panel_height.js]
--- a/browser/components/downloads/test/browser/browser_downloads_panel_height.js
+++ b/browser/components/downloads/test/browser/browser_downloads_panel_height.js
@@ -5,25 +5,32 @@
 
 /**
  * This test exists because we use a <panelmultiview> element and it handles
  * some of the height changes for us. We need to verify that the height is
  * updated correctly if downloads are removed while the panel is hidden.
  */
 add_task(async function test_height_reduced_after_removal() {
   await task_addDownloads([
+    { state: DownloadsCommon.DOWNLOAD_PAUSED },
     { state: DownloadsCommon.DOWNLOAD_FINISHED },
   ]);
 
+  await BrowserTestUtils.waitForCondition(() => {
+    let btn = document.getElementById("downloads-button");
+    return !btn.hidden && btn.getBoundingClientRect().width > 0;
+  });
+
   await task_openPanel();
   let panel = document.getElementById("downloadsPanel");
   let heightBeforeRemoval = panel.getBoundingClientRect().height;
 
-  // We want to close the panel before we remove the download from the list.
+  // We want to close the panel before we remove a download from the list.
   DownloadsPanel.hidePanel();
-  await task_resetState();
+  let publicList = await Downloads.getList(Downloads.PUBLIC);
+  await publicList.removeFinished();
 
   await task_openPanel();
   let heightAfterRemoval = panel.getBoundingClientRect().height;
   Assert.greater(heightBeforeRemoval, heightAfterRemoval);
 
   await task_resetState();
 });
--- a/browser/components/downloads/test/browser/browser_first_download_panel.js
+++ b/browser/components/downloads/test/browser/browser_first_download_panel.js
@@ -27,17 +27,19 @@ add_task(async function test_first_downl
 
   // Ensure that state is reset in case previous tests didn't finish.
   await task_resetState();
 
   // With this set to false, we should automatically open the panel the first
   // time a download is started.
   DownloadsCommon.getData(window).panelHasShownBefore = false;
 
+  info("waiting for panel open");
   let promise = promisePanelOpened();
+  await task_addDownloads([{state: DownloadsCommon.DOWNLOAD_DOWNLOADING}]);
   DownloadsCommon.getData(window)._notifyDownloadEvent("start");
   await promise;
 
   // If we got here, that means the panel opened.
   DownloadsPanel.hidePanel();
 
   ok(DownloadsCommon.getData(window).panelHasShownBefore,
      "Should have recorded that the panel was opened on a download.")
deleted file mode 100644
--- a/browser/components/downloads/test/browser/browser_overflow_anchor.js
+++ /dev/null
@@ -1,114 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-registerCleanupFunction(async function() {
-  // Clean up when the test finishes.
-  await task_resetState();
-});
-
-/**
- * Make sure the downloads button and indicator overflows into the nav-bar
- * chevron properly, and then when those buttons are clicked in the overflow
- * panel that the downloads panel anchors to the chevron.
- */
-add_task(async function test_overflow_anchor() {
-  // Ensure that state is reset in case previous tests didn't finish.
-  await task_resetState();
-
-  // Record the original width of the window so we can put it back when
-  // this test finishes.
-  let oldWidth = window.outerWidth;
-
-  // The downloads button should not be overflowed to begin with.
-  let button = CustomizableUI.getWidget("downloads-button")
-                             .forWindow(window);
-  ok(!button.overflowed, "Downloads button should not be overflowed.");
-
-  // Hack - we lock the size of the default flex-y items in the nav-bar,
-  // namely, the URL and search inputs. That way we can resize the
-  // window without worrying about them flexing.
-  const kFlexyItems = ["urlbar-container", "search-container"];
-  registerCleanupFunction(() => unlockWidth(kFlexyItems));
-  lockWidth(kFlexyItems);
-
-  // Resize the window to half of its original size. That should
-  // be enough to overflow the downloads button.
-  window.resizeTo(oldWidth / 2, window.outerHeight);
-  await waitForOverflowed(button, true);
-
-  let promise = promisePanelOpened();
-  button.node.doCommand();
-  await promise;
-
-  let panel = DownloadsPanel.panel;
-  let chevron = document.getElementById("nav-bar-overflow-button");
-  is(panel.anchorNode, chevron, "Panel should be anchored to the chevron.");
-
-  DownloadsPanel.hidePanel();
-
-  // Unlock the widths on the flex-y items.
-  unlockWidth(kFlexyItems);
-
-  // Put the window back to its original dimensions.
-  window.resizeTo(oldWidth, window.outerHeight);
-
-  // The downloads button should eventually be un-overflowed.
-  await waitForOverflowed(button, false);
-
-  // Now try opening the panel again.
-  promise = promisePanelOpened();
-  button.node.doCommand();
-  await promise;
-
-  is(panel.anchorNode.id, "downloads-indicator-anchor");
-
-  DownloadsPanel.hidePanel();
-});
-
-/**
- * For some node IDs, finds the nodes and sets their min-width's to their
- * current width, preventing them from flex-shrinking.
- *
- * @param aItemIDs an array of item IDs to set min-width on.
- */
-function lockWidth(aItemIDs) {
-  for (let itemID of aItemIDs) {
-    let item = document.getElementById(itemID);
-    let curWidth = item.getBoundingClientRect().width + "px";
-    item.style.minWidth = curWidth;
-  }
-}
-
-/**
- * Clears the min-width's set on a set of IDs by lockWidth.
- *
- * @param aItemIDs an array of ItemIDs to remove min-width on.
- */
-function unlockWidth(aItemIDs) {
-  for (let itemID of aItemIDs) {
-    let item = document.getElementById(itemID);
-    item.style.minWidth = "";
-  }
-}
-
-/**
- * Waits for a node to enter or exit the overflowed state.
- *
- * @param aItem the node to wait for.
- * @param aIsOverflowed if we're waiting for the item to be overflowed.
- */
-function waitForOverflowed(aItem, aIsOverflowed) {
-  if (aItem.overflowed == aIsOverflowed) {
-    return Promise.resolve();
-  }
-
-  return new Promise(resolve => {
-    let observer = new MutationObserver(function(aMutations) {
-      if (aItem.overflowed == aIsOverflowed) {
-        observer.disconnect();
-        resolve();
-      }
-    });
-    observer.observe(aItem.node, {attributes: true});
-  });
-}
--- a/browser/components/extensions/test/browser/browser-common.ini
+++ b/browser/components/extensions/test/browser/browser-common.ini
@@ -68,17 +68,16 @@ skip-if = (os == 'win' && !debug) # bug 
 [browser_ext_currentWindow.js]
 [browser_ext_devtools_inspectedWindow.js]
 [browser_ext_devtools_inspectedWindow_eval_bindings.js]
 [browser_ext_devtools_inspectedWindow_reload.js]
 [browser_ext_devtools_network.js]
 [browser_ext_devtools_page.js]
 [browser_ext_devtools_panel.js]
 [browser_ext_devtools_panels_elements.js]
-skip-if = true # bug 1382487
 [browser_ext_geckoProfiler_symbolicate.js]
 [browser_ext_getViews.js]
 [browser_ext_identity_indication.js]
 [browser_ext_incognito_views.js]
 [browser_ext_incognito_popup.js]
 [browser_ext_lastError.js]
 [browser_ext_menus.js]
 [browser_ext_omnibox.js]
--- a/browser/components/extensions/test/browser/browser_ext_devtools_panels_elements.js
+++ b/browser/components/extensions/test/browser/browser_ext_devtools_panels_elements.js
@@ -6,35 +6,58 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource://devtools/client/framework/gDevTools.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "devtools",
                                   "resource://devtools/shared/Loader.jsm");
 
 add_task(async function test_devtools_panels_elements_onSelectionChanged() {
   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/");
 
   function devtools_page() {
-    let doTabReload = true;
+    let isReloading = false;
+    let collectedEvalResults = [];
 
     browser.devtools.panels.elements.onSelectionChanged.addListener(async () => {
       const [
         evalResult, exceptionInfo,
       ] = await browser.devtools.inspectedWindow.eval("$0 && $0.tagName");
 
       if (exceptionInfo) {
         browser.test.fail("Unexpected exceptionInfo on inspectedWindow.eval: " +
                           JSON.stringify(exceptionInfo));
       }
 
-      browser.test.sendMessage("devtools_eval_result", evalResult);
+      collectedEvalResults.push(evalResult);
+
+      // The eval results that are happening during the reload are going to
+      // be retrieved all at once using the "collected_devttols_eval_results:request".
+      if (!isReloading) {
+        browser.test.sendMessage("devtools_eval_result", evalResult);
+      }
+    });
 
-      if (doTabReload) {
-        // Force a reload to test that the expected onSelectionChanged events are sent
-        // while the page is navigating and once it has been fully reloaded.
-        doTabReload = false;
-        await browser.devtools.inspectedWindow.eval("window.location.reload();");
+    browser.test.onMessage.addListener(msg => {
+      switch (msg) {
+        case "inspectedWindow_reload": {
+          // Force a reload to test that the expected onSelectionChanged events are sent
+          // while the page is navigating and once it has been fully reloaded.
+          isReloading = true;
+          collectedEvalResults = [];
+          browser.devtools.inspectedWindow.eval("window.location.reload();");
+          break;
+        }
+
+        case "collected_devtools_eval_results:request": {
+          browser.test.sendMessage("collected_devtools_eval_results:reply",
+                                   collectedEvalResults);
+          break;
+        }
+
+        default: {
+          browser.test.fail(`Received unexpected test.onMesssage: ${msg}`);
+        }
       }
     });
 
     browser.test.sendMessage("devtools_page_loaded");
   }
 
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
@@ -58,33 +81,42 @@ add_task(async function test_devtools_pa
 
   let target = devtools.TargetFactory.forTab(tab);
 
   const toolbox = await gDevTools.showToolbox(target, "webconsole");
   info("developer toolbox opened");
 
   await extension.awaitMessage("devtools_page_loaded");
 
-  toolbox.selectTool("inspector");
+  await toolbox.selectTool("inspector");
+
+  const inspector = toolbox.getPanel("inspector");
 
   const evalResult = await extension.awaitMessage("devtools_eval_result");
 
   is(evalResult, "BODY", "Got the expected onSelectionChanged once the inspector is selected");
 
-  const evalResultNavigating = await extension.awaitMessage("devtools_eval_result");
-
-  is(evalResultNavigating, undefined, "Got the expected onSelectionChanged once the tab is navigating");
-
-  const evalResultNavigated = await extension.awaitMessage("devtools_eval_result");
+  // Reload the inspected tab and wait for the inspector markup view to have been
+  // fully reloaded.
+  const onceMarkupReloaded = inspector.once("markuploaded");
+  extension.sendMessage("inspectedWindow_reload");
+  await onceMarkupReloaded;
 
-  is(evalResultNavigated, undefined, "Got the expected onSelectionChanged once the tab navigated");
+  // Retrieve the first and last collected eval result (the first is related to the
+  // page navigating away, the last one is related to the updated inspector markup view
+  // fully reloaded and the selection updated).
+  extension.sendMessage("collected_devtools_eval_results:request");
+  const collectedEvalResults = await extension.awaitMessage("collected_devtools_eval_results:reply");
+  const evalResultNavigating = collectedEvalResults.shift();
+  const evalResultOnceMarkupReloaded = collectedEvalResults.pop();
 
-  const evalResultReloaded = await extension.awaitMessage("devtools_eval_result");
+  is(evalResultNavigating, undefined,
+     "Got the expected onSelectionChanged once the tab is navigating");
 
-  is(evalResultReloaded, "BODY",
+  is(evalResultOnceMarkupReloaded, "BODY",
      "Got the expected onSelectionChanged once the tab has been completely reloaded");
 
   await gDevTools.closeToolbox(target);
 
   await target.destroy();
 
   await extension.unload();
 
--- a/browser/extensions/formautofill/content/formfill-anchor.svg
+++ b/browser/extensions/formautofill/content/formfill-anchor.svg
@@ -1,11 +1,9 @@
 <!-- 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="context-fill">
-    <path d="M7.28 5h1.47A.25.25 0 0 0 9 4.75V.984a.984.984 0 0 0-1.97 0V4.75a.25.25 0 0 0 .25.25z"/>
-    <path d="M13.5 2H11a1 1 0 0 0 0 2h2.5a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-.5.5h-11a.5.5 0 0 1-.5-.5v-7a.5.5 0 0 1 .5-.5H5a1 1 0 0 0 0-2H2.5A2.5 2.5 0 0 0 0 4.5v7A2.5 2.5 0 0 0 2.5 14h11a2.5 2.5 0 0 0 2.5-2.5v-7A2.5 2.5 0 0 0 13.5 2z"/>
-    <rect x="3" y="6" width="4" height="4" rx=".577" ry=".577"/>
-    <path d="M9.5 7h3a.5.5 0 0 0 0-1h-3a.5.5 0 0 0 0 1zM9.5 8a.5.5 0 0 0 0 1h2a.5.5 0 0 0 0-1z"/>
-  </g>
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+  <path d="M7.28 5h1.47A.25.25 0 0 0 9 4.75V.984a.984.984 0 0 0-1.97 0V4.75a.25.25 0 0 0 .25.25z"/>
+  <path d="M13.5 2H11a1 1 0 0 0 0 2h2.5a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-.5.5h-11a.5.5 0 0 1-.5-.5v-7a.5.5 0 0 1 .5-.5H5a1 1 0 0 0 0-2H2.5A2.5 2.5 0 0 0 0 4.5v7A2.5 2.5 0 0 0 2.5 14h11a2.5 2.5 0 0 0 2.5-2.5v-7A2.5 2.5 0 0 0 13.5 2z"/>
+  <rect x="3" y="6" width="4" height="4" rx=".577" ry=".577"/>
+  <path d="M9.5 7h3a.5.5 0 0 0 0-1h-3a.5.5 0 0 0 0 1zM9.5 8a.5.5 0 0 0 0 1h2a.5.5 0 0 0 0-1z"/>
 </svg>
--- a/browser/extensions/webcompat-reporter/skin/lightbulb.svg
+++ b/browser/extensions/webcompat-reporter/skin/lightbulb.svg
@@ -1,8 +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"
-     width="32" height="32" fill="context-fill">
-  <path d="M11.5,8.3h.1l9-1a.8.8,0,0,0-.2-1.5l-9,1a.8.8,0,0,0,.1,1.5Zm2.4,13.5a.3.3,0,0,0,.1.2h.9a.3.3,0,0,0,.1-.2,1.8,1.8,0,0,0-.5-1.1A1.8,1.8,0,0,0,13.9,21.7ZM11.5,6.3h.1l8-1a.7.7,0,0,0,.7-.8.7.7,0,0,0-.3-.5A4.4,4.4,0,0,0,16,1c-3,0-4,3-4,3h5.5l-6,.8a.7.7,0,0,0,.1,1.5ZM17,21.8c0,.1,0,.3.5.3h.4a.4.4,0,0,0,.1-.3,1.8,1.8,0,0,0-.4-1A1.9,1.9,0,0,0,17,21.8Zm5.3-8.9-1.1-2.2a1,1,0,0,0-.9-.6H13.8l6.8-.8a.8.8,0,0,0-.2-1.5l-9,1a.8.8,0,0,0-.7.8.7.7,0,0,0,.4.6l-.4.5L9.7,12.8C8.5,15,6,17,6,21.5A9.6,9.6,0,0,0,16,31a9.6,9.6,0,0,0,10-9.5C26,17,23.5,15,22.3,12.8Zm0,7.7a.5.5,0,0,1-.6,0,3.8,3.8,0,0,0-2.2-.8l-1.1.3a2.7,2.7,0,0,1,.6,1.6,1.4,1.4,0,0,1-.4,1,1.6,1.6,0,0,1-1.1.4A1.3,1.3,0,0,1,16,21.8a2.8,2.8,0,0,1,.8-1.8,1.8,1.8,0,0,0-1.7,0,2.8,2.8,0,0,1,.7,1.7,1.3,1.3,0,0,1-.4.9,1.5,1.5,0,0,1-1,.4,1.8,1.8,0,0,1-1.2-.4,1.3,1.3,0,0,1-.4-1,2.7,2.7,0,0,1,.7-1.7,2.5,2.5,0,0,0-1.2-.3,3.8,3.8,0,0,0-2.1.7.5.5,0,0,1-.7-.6l4.6-8.5a.5.5,0,1,1,.9.5l-3.9,7.2,1.2-.2a3.6,3.6,0,0,1,2,.6,2.8,2.8,0,0,1,3.3,0,3.5,3.5,0,0,1,1.9-.5l1.3.2L17,11.8a.5.5,0,1,1,.9-.5l4.5,8.6A.5.5,0,0,1,22.3,20.6Z"/>
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+  <path d="M8 0C4.3 0 2 2.107 2 5.5c0 2.372 2.065 4.268 3 5V14c0 1.476 1.616 2 3 2s3-.524 3-2v-3.5c.935-.736 3-2.632 3-5C14 2.107 11.7 0 8 0zm1 12H7v-1h2zm-1 2a3.086 3.086 0 0 1-1-.172V13h2v.828A3.047 3.047 0 0 1 8 14zm1.445-4.832A1 1 0 0 0 9 10H7a1 1 0 0 0-.444-.831C5.845 8.691 4 7.1 4 5.5 4 2.607 6.175 2 8 2s4 .607 4 3.5c0 1.6-1.845 3.191-2.555 3.668z"/>
 </svg>
--- a/browser/installer/Makefile.in
+++ b/browser/installer/Makefile.in
@@ -58,16 +58,20 @@ DEFINES += -DJAREXT=
 
 ifdef MOZ_ANGLE_RENDERER
 DEFINES += -DMOZ_ANGLE_RENDERER=$(MOZ_ANGLE_RENDERER)
 ifdef MOZ_D3DCOMPILER_VISTA_DLL
 DEFINES += -DMOZ_D3DCOMPILER_VISTA_DLL=$(MOZ_D3DCOMPILER_VISTA_DLL)
 endif
 endif
 
+ifdef MOZ_ENABLE_SKIA_PDF
+DEFINES += -DMOZ_ENABLE_SKIA_PDF=$(MOZ_ENABLE_SKIA_PDF)
+endif
+
 DEFINES += -DMOZ_CHILD_PROCESS_NAME=$(MOZ_CHILD_PROCESS_NAME)
 
 # Set MSVC dlls version to package, if any.
 ifdef MOZ_NO_DEBUG_RTL
 ifdef WIN32_REDIST_DIR
 ifndef MOZ_ARTIFACT_BUILDS
 DEFINES += -DMOZ_PACKAGE_MSVC_DLLS=1
 DEFINES += -DMSVC_C_RUNTIME_DLL=$(MSVC_C_RUNTIME_DLL)
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -600,16 +600,20 @@
 @BINPATH@/libEGL.dll
 @BINPATH@/libGLESv2.dll
 
 #ifdef MOZ_D3DCOMPILER_VISTA_DLL
 @BINPATH@/@MOZ_D3DCOMPILER_VISTA_DLL@
 #endif
 #endif # MOZ_ANGLE_RENDERER
 
+#if defined(XP_WIN) && defined(MOZ_ENABLE_SKIA_PDF)
+@BINPATH@/pdfium.dll
+#endif
+
 ; [Browser Chrome Files]
 @RESPATH@/browser/chrome.manifest
 @RESPATH@/browser/chrome/browser@JAREXT@
 @RESPATH@/browser/chrome/browser.manifest
 @RESPATH@/browser/chrome/pdfjs.manifest
 @RESPATH@/browser/chrome/pdfjs/*
 @RESPATH@/browser/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/chrome.manifest
 @RESPATH@/browser/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/install.rdf
--- a/browser/installer/windows/nsis/defines.nsi.in
+++ b/browser/installer/windows/nsis/defines.nsi.in
@@ -23,16 +23,18 @@
 # These defines should match application.ini settings
 !define AppName               "Firefox"
 !define AppVersion            "@APP_VERSION@"
 !define GREVersion            @MOZILLA_VERSION@
 !define AB_CD                 "@AB_CD@"
 
 !define FileMainEXE           "@MOZ_APP_NAME@.exe"
 !define WindowClass           "FirefoxMessageWindow"
+!define MainWindowClass       "MozillaWindowClass"
+!define DialogWindowClass     "MozillaDialogClass"
 !define DDEApplication        "Firefox"
 !define AppRegName            "Firefox"
 
 !ifndef DEV_EDITION
 !define BrandShortName        "@MOZ_APP_DISPLAYNAME@"
 !endif
 !define BrandFullName         "${BrandFullNameInternal}"
 
--- a/browser/installer/windows/nsis/stub.nsi
+++ b/browser/installer/windows/nsis/stub.nsi
@@ -42,28 +42,25 @@ Var FontInstalling
 Var FontBlurb
 Var FontFooter
 Var FontCheckbox
 
 Var CanWriteToInstallDir
 Var HasRequiredSpaceAvailable
 Var IsDownloadFinished
 Var DownloadSizeBytes
-Var HalfOfDownload
 Var DownloadReset
 Var ExistingTopDir
 Var SpaceAvailableBytes
 Var InitialInstallDir
 Var HandleDownload
 Var CanSetAsDefault
 Var InstallCounterStep
-Var InstallStepSize
 Var InstallTotalSteps
 Var ProgressCompleted
-Var ProgressTotal
 
 Var ExitCode
 Var FirefoxLaunchCode
 
 Var StartDownloadPhaseTickCount
 ; Since the Intro and Options pages can be displayed multiple times the total
 ; seconds spent on each of these pages is reported.
 Var IntroPhaseSeconds
@@ -89,16 +86,17 @@ Var DownloadRetryCount
 Var OpenedDownloadPage
 Var DownloadServerIP
 Var PostSigningData
 Var PreviousInstallDir
 Var PreviousInstallArch
 Var ProfileCleanupPromptType
 Var ProfileCleanupHeaderString
 Var ProfileCleanupButtonString
+Var AppLaunchWaitTickCount
 
 ; Uncomment the following to prevent pinging the metrics server when testing
 ; the stub installer
 ;!define STUB_DEBUG
 
 !define StubURLVersion "v8"
 
 ; Successful install exit code
@@ -154,43 +152,43 @@ Var ProfileCleanupButtonString
 !define DownloadRetryIntervalMS 3000
 
 ; Interval for the download timer
 !define DownloadIntervalMS 200
 
 ; Interval for the install timer
 !define InstallIntervalMS 100
 
-; The first step for the install progress bar. By starting with a large step
-; immediate feedback is given to the user.
-!define InstallProgressFirstStep 20
-
-; The finish step size to quickly increment the progress bar after the
-; installation has finished.
-!define InstallProgressFinishStep 40
-
 ; Number of steps for the install progress.
 ; This might not be enough when installing on a slow network drive so it will
-; fallback to downloading the full installer if it reaches this number. The size
-; of the install progress step is increased when the full installer finishes
-; instead of waiting.
+; fallback to downloading the full installer if it reaches this number.
 
-; Approximately 150 seconds with a 100 millisecond timer and a first step of 20
-; as defined by InstallProgressFirstStep.
-!define /math InstallCleanTotalSteps ${InstallProgressFirstStep} + 1500
+; Approximately 150 seconds with a 100 millisecond timer.
+!define InstallCleanTotalSteps 1500
 
-; Approximately 165 seconds (minus 0.2 seconds for each file that is removed)
-; with a 100 millisecond timer and a first step of 20 as defined by
-; InstallProgressFirstStep .
-!define /math InstallPaveOverTotalSteps ${InstallProgressFirstStep} + 1800
+; Approximately 165 seconds with a 100 millisecond timer.
+!define InstallPaveOverTotalSteps 1650
 
 ; Blurb duty cycle
 !define BlurbDisplayMS 19500
 !define BlurbBlankMS 500
 
+; Interval between checks for the application window and progress bar updates.
+!define AppLaunchWaitIntervalMS 100
+
+; Total time to wait for the application to start before just exiting.
+!define AppLaunchWaitTimeoutMS 10000
+
+; Maximum value of the download/install/launch progress bar, and the end values
+; for each individual stage.
+!define PROGRESS_BAR_TOTAL_STEPS 500 
+!define PROGRESS_BAR_DOWNLOAD_END_STEP 300
+!define PROGRESS_BAR_INSTALL_END_STEP 475
+!define PROGRESS_BAR_APP_LAUNCH_END_STEP 500
+
 ; Amount of physical memory required for the 64-bit build to be selected (2 GB).
 ; Machines with this or less RAM get the 32-bit build, even with a 64-bit OS.
 !define RAM_NEEDED_FOR_64BIT 0x80000000
 
 ; Attempt to elevate Standard Users in addition to users that
 ; are a member of the Administrators group.
 !define NONADMIN_ELEVATE
 
@@ -543,17 +541,16 @@ Function .onGUIEnd
   ${UnloadUAC}
 FunctionEnd
 
 Function .onUserAbort
   ${NSD_KillTimer} StartDownload
   ${NSD_KillTimer} OnDownload
   ${NSD_KillTimer} CheckInstall
   ${NSD_KillTimer} FinishInstall
-  ${NSD_KillTimer} FinishProgressBar
   ${NSD_KillTimer} DisplayDownloadError
   ${NSD_KillTimer} NextBlurb
   ${NSD_KillTimer} ClearBlurb
 
   ${If} "$IsDownloadFinished" != ""
     Call DisplayDownloadError
   ${Else}
     Call SendPing
@@ -884,27 +881,17 @@ Function StartDownload
   ${NSD_CreateTimer} OnDownload ${DownloadIntervalMS}
   ${If} ${FileExists} "$INSTDIR\${TO_BE_DELETED}"
     RmDir /r "$INSTDIR\${TO_BE_DELETED}"
   ${EndIf}
 FunctionEnd
 
 Function SetProgressBars
   SendMessage $Progressbar ${PBM_SETPOS} $ProgressCompleted 0
-  ${ITBL3SetProgressValue} "$ProgressCompleted" "$ProgressTotal"
-FunctionEnd
-
-Function RemoveFileProgressCallback
-  IntOp $InstallCounterStep $InstallCounterStep + 2
-  System::Int64Op $ProgressCompleted + $InstallStepSize
-  Pop $ProgressCompleted
-  Call SetProgressBars
-  System::Int64Op $ProgressCompleted + $InstallStepSize
-  Pop $ProgressCompleted
-  Call SetProgressBars
+  ${ITBL3SetProgressValue} "$ProgressCompleted" "${PROGRESS_BAR_TOTAL_STEPS}"
 FunctionEnd
 
 Function NextBlurb
   ${NSD_KillTimer} NextBlurb
 
   IntOp $CurrentBlurbIdx $CurrentBlurbIdx + 1
   IntOp $CurrentBlurbIdx $CurrentBlurbIdx % 3
 
@@ -1000,26 +987,20 @@ Function OnDownload
         ${NSD_CreateTimer} DisplayDownloadError ${InstallIntervalMS}
       ${Else}
         ${NSD_CreateTimer} StartDownload ${DownloadIntervalMS}
       ${EndIf}
       Return
     ${EndIf}
 
     StrCpy $DownloadSizeBytes "$4"
-    System::Int64Op $4 / 2
-    Pop $HalfOfDownload
-    System::Int64Op $HalfOfDownload / $InstallTotalSteps
-    Pop $InstallStepSize
     SendMessage $Progressbar ${PBM_SETMARQUEE} 0 0 ; start=1|stop=0 interval(ms)=+N
     ${RemoveStyle} $Progressbar ${PBS_MARQUEE}
-    System::Int64Op $HalfOfDownload + $DownloadSizeBytes
-    Pop $ProgressTotal
     StrCpy $ProgressCompleted 0
-    SendMessage $Progressbar ${PBM_SETRANGE32} $ProgressCompleted $ProgressTotal
+    SendMessage $Progressbar ${PBM_SETRANGE32} $ProgressCompleted ${PROGRESS_BAR_TOTAL_STEPS}
   ${EndIf}
 
   ; Don't update the status until after the download starts
   ${If} $2 != 0
   ${AndIf} "$4" == ""
     Return
   ${EndIf}
 
@@ -1037,20 +1018,16 @@ Function OnDownload
     ${EndIf}
     Return
   ${EndIf}
 
   ${If} $IsDownloadFinished != "true"
     ${If} $2 == 0
       ${NSD_KillTimer} OnDownload
       StrCpy $IsDownloadFinished "true"
-      ; The first step of the install progress bar is determined by the
-      ; InstallProgressFirstStep define and provides the user with immediate
-      ; feedback.
-      StrCpy $InstallCounterStep "${InstallProgressFirstStep}"
       System::Call "kernel32::GetTickCount()l .s"
       Pop $EndDownloadPhaseTickCount
 
       StrCpy $DownloadedBytes "$DownloadSizeBytes"
 
       ; When a download has finished handle the case where the  downloaded size
       ; is less than the minimum expected size or greater than the maximum
       ; expected size during the download.
@@ -1065,23 +1042,19 @@ Function OnDownload
         ${Else}
           ${NSD_CreateTimer} StartDownload ${DownloadIntervalMS}
         ${EndIf}
         Return
       ${EndIf}
 
       ; Update the progress bars first in the UI change so they take affect
       ; before other UI changes.
-      StrCpy $ProgressCompleted "$DownloadSizeBytes"
+      StrCpy $ProgressCompleted "${PROGRESS_BAR_DOWNLOAD_END_STEP}"
       Call SetProgressBars
-      System::Int64Op $InstallStepSize * ${InstallProgressFirstStep}
-      Pop $R9
-      System::Int64Op $ProgressCompleted + $R9
-      Pop $ProgressCompleted
-      Call SetProgressBars
+
       ; Disable the Cancel button during the install
       GetDlgItem $5 $HWNDPARENT 2
       EnableWindow $5 0
 
       ; Open a handle to prevent modification of the full installer
       StrCpy $R9 "${INVALID_HANDLE_VALUE}"
       System::Call 'kernel32::CreateFileW(w "$PLUGINSDIR\download.exe", \
                                           i ${GENERIC_READ}, \
@@ -1150,18 +1123,17 @@ Function OnDownload
       WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "MaintenanceService" "false"
 !endif
 
       ; Delete the taskbar shortcut history to ensure we do the right thing based on
       ; the config file above.
       ${GetShortcutsLogPath} $0
       Delete "$0"
 
-      GetFunctionAddress $0 RemoveFileProgressCallback
-      ${RemovePrecompleteEntries} $0
+      ${RemovePrecompleteEntries} "false"
 
       ; Delete the install.log and let the full installer create it. When the
       ; installer closes it we can detect that it has completed.
       Delete "$INSTDIR\install.log"
 
       ; Delete firefox.exe.moz-upgrade and firefox.exe.moz-delete if it exists
       ; since it being present will require an OS restart for the full
       ; installer.
@@ -1169,29 +1141,31 @@ Function OnDownload
       Delete "$INSTDIR\${FileMainEXE}.moz-delete"
 
       System::Call "kernel32::GetTickCount()l .s"
       Pop $EndPreInstallPhaseTickCount
 
       Exec "$\"$PLUGINSDIR\download.exe$\" /INI=$PLUGINSDIR\${CONFIG_INI}"
       ${NSD_CreateTimer} CheckInstall ${InstallIntervalMS}
     ${Else}
-      ${If} $HalfOfDownload != "true"
-      ${AndIf} $3 > $HalfOfDownload
-        StrCpy $HalfOfDownload "true"
-      ${EndIf}
       StrCpy $DownloadedBytes "$3"
-      StrCpy $ProgressCompleted "$DownloadedBytes"
+      System::Int64Op $DownloadedBytes * ${PROGRESS_BAR_DOWNLOAD_END_STEP}
+      Pop $ProgressCompleted
+      System::Int64Op $ProgressCompleted / $DownloadSizeBytes
+      Pop $ProgressCompleted
       Call SetProgressBars
     ${EndIf}
   ${EndIf}
 FunctionEnd
 
 Function SendPing
+  ${NSD_KillTimer} NextBlurb
+  ${NSD_KillTimer} ClearBlurb
   HideWindow
+
   ${If} $CheckboxSendPing == 1
     ; Get the tick count for the completion of all phases.
     System::Call "kernel32::GetTickCount()l .s"
     Pop $EndFinishPhaseTickCount
 
     ; When the value of $IsDownloadFinished is false the download was started
     ; but didn't finish. In this case the tick count stored in
     ; $EndFinishPhaseTickCount is used to determine how long the download was
@@ -1457,18 +1431,17 @@ Function CheckInstall
     ; Close the handle that prevents modification of the full installer
     System::Call 'kernel32::CloseHandle(i $HandleDownload)'
     StrCpy $ExitCode "${ERR_INSTALL_TIMEOUT}"
     ; Use a timer so the UI has a chance to update
     ${NSD_CreateTimer} DisplayDownloadError ${InstallIntervalMS}
     Return
   ${EndIf}
 
-  System::Int64Op $ProgressCompleted + $InstallStepSize
-  Pop $ProgressCompleted
+  IntOp $ProgressCompleted $ProgressCompleted + 1
   Call SetProgressBars
 
   ${If} ${FileExists} "$INSTDIR\install.log"
     Delete "$INSTDIR\install.tmp"
     CopyFiles /SILENT "$INSTDIR\install.log" "$INSTDIR\install.tmp"
 
     ; The unfocus and refocus that happens approximately here is caused by the
     ; installer calling SHChangeNotify to refresh the shortcut icons.
@@ -1481,68 +1454,34 @@ Function CheckInstall
       ${NSD_KillTimer} CheckInstall
       ; Close the handle that prevents modification of the full installer
       System::Call 'kernel32::CloseHandle(i $HandleDownload)'
       Rename "$INSTDIR\install.tmp" "$INSTDIR\install.log"
       Delete "$PLUGINSDIR\download.exe"
       Delete "$PLUGINSDIR\${CONFIG_INI}"
       System::Call "kernel32::GetTickCount()l .s"
       Pop $EndInstallPhaseTickCount
-      System::Int64Op $InstallStepSize * ${InstallProgressFinishStep}
-      Pop $InstallStepSize
-      ${NSD_CreateTimer} FinishInstall ${InstallIntervalMS}
+      Call FinishInstall
     ${EndUnless}
   ${EndIf}
 FunctionEnd
 
 Function FinishInstall
-  ; The full installer has completed but the progress bar still needs to finish
-  ; so increase the size of the step.
-  IntOp $InstallCounterStep $InstallCounterStep + ${InstallProgressFinishStep}
-  ${If} $InstallTotalSteps < $InstallCounterStep
-    StrCpy $InstallCounterStep "$InstallTotalSteps"
-  ${EndIf}
-
-  ${If} $InstallTotalSteps != $InstallCounterStep
-    System::Int64Op $ProgressCompleted + $InstallStepSize
-    Pop $ProgressCompleted
-    Call SetProgressBars
-    Return
-  ${EndIf}
-
-  ${NSD_KillTimer} FinishInstall
-
-  StrCpy $ProgressCompleted "$ProgressTotal"
+  StrCpy $ProgressCompleted "${PROGRESS_BAR_INSTALL_END_STEP}"
   Call SetProgressBars
 
   ${If} ${FileExists} "$INSTDIR\${FileMainEXE}.moz-upgrade"
     Delete "$INSTDIR\${FileMainEXE}"
     Rename "$INSTDIR\${FileMainEXE}.moz-upgrade" "$INSTDIR\${FileMainEXE}"
   ${EndIf}
 
   StrCpy $ExitCode "${ERR_SUCCESS}"
 
-  StrCpy $InstallCounterStep 0
-  ${NSD_CreateTimer} FinishProgressBar ${InstallIntervalMS}
-FunctionEnd
-
-Function FinishProgressBar
-  IntOp $InstallCounterStep $InstallCounterStep + 1
-
-  ${If} $InstallCounterStep < 10
-    Return
-  ${EndIf}
-
-  ${NSD_KillTimer} FinishProgressBar
-  ${NSD_KillTimer} NextBlurb
-  ${NSD_KillTimer} ClearBlurb
-
   Call CopyPostSigningData
   Call LaunchApp
-  Call SendPing
 FunctionEnd
 
 Function RelativeGotoPage
   IntCmp $R9 0 0 Move Move
   StrCmp $R9 "X" 0 Move
   StrCpy $R9 "120"
 
   Move:
@@ -1651,28 +1590,58 @@ Function LaunchApp
     ${Else}
       Exec "$\"$INSTDIR\${FileMainEXE}$\""
     ${EndIf}
   ${Else}
     StrCpy $R1 $CheckboxCleanupProfile
     GetFunctionAddress $0 LaunchAppFromElevatedProcess
     UAC::ExecCodeSegment $0
   ${EndIf}
+
+  StrCpy $AppLaunchWaitTickCount 0
+  ${NSD_CreateTimer} WaitForAppLaunch ${AppLaunchWaitIntervalMS}
 FunctionEnd
 
 Function LaunchAppFromElevatedProcess
   ; Set the current working directory to the installation directory
   SetOutPath "$INSTDIR"
   ${If} $R1 == 1
     Exec "$\"$INSTDIR\${FileMainEXE}$\" -reset-profile -migration"
   ${Else}
     Exec "$\"$INSTDIR\${FileMainEXE}$\""
   ${EndIf}
 FunctionEnd
 
+Function WaitForAppLaunch
+  FindWindow $0 "${MainWindowClass}"
+  FindWindow $1 "${DialogWindowClass}"
+  ${If} $0 <> 0
+  ${OrIf} $1 <> 0
+    ${NSD_KillTimer} WaitForAppLaunch
+    StrCpy $ProgressCompleted "${PROGRESS_BAR_APP_LAUNCH_END_STEP}"
+    Call SetProgressBars
+    Call SendPing
+    Return
+  ${EndIf}
+
+  IntOp $AppLaunchWaitTickCount $AppLaunchWaitTickCount + 1
+  IntOp $0 $AppLaunchWaitTickCount * ${AppLaunchWaitIntervalMS}
+  ${If} $0 >= ${AppLaunchWaitTimeoutMS}
+    ; We've waited an unreasonably long time, so just exit.
+    ${NSD_KillTimer} WaitForAppLaunch
+    Call SendPing
+    Return
+  ${EndIf}
+
+  ${If} $ProgressCompleted < ${PROGRESS_BAR_APP_LAUNCH_END_STEP}
+    IntOp $ProgressCompleted $ProgressCompleted + 1
+    Call SetProgressBars
+  ${EndIf}
+FunctionEnd
+
 Function CopyPostSigningData
   ${LineRead} "$EXEDIR\postSigningData" "1" $PostSigningData
   ${If} ${Errors}
     ClearErrors
     StrCpy $PostSigningData "0"
   ${Else}
     CreateDirectory "$LOCALAPPDATA\Mozilla\Firefox"
     CopyFiles /SILENT "$EXEDIR\postSigningData" "$LOCALAPPDATA\Mozilla\Firefox"
--- a/browser/locales/en-US/chrome/browser/downloads/downloads.dtd
+++ b/browser/locales/en-US/chrome/browser/downloads/downloads.dtd
@@ -144,18 +144,13 @@
 <!ENTITY clearDownloadsButton.tooltip     "Clears completed, canceled and failed downloads">
 
 <!-- LOCALIZATION NOTE (downloadsListEmpty.label):
      This string is shown when there are no items in the Downloads view, when it
      is displayed inside a browser tab.
      -->
 <!ENTITY downloadsListEmpty.label         "There are no downloads.">
 
-<!-- LOCALIZATION NOTE (downloadsPanelEmpty.label):
-     This string is shown when there are no items in the Downloads Panel.
-     -->
-<!ENTITY downloadsPanelEmpty.label        "No downloads for this session.">
-
 <!-- LOCALIZATION NOTE (downloadsListNoMatch.label):
      This string is shown when some search terms are specified, but there are no
      results in the Downloads view.
      -->
 <!ENTITY downloadsListNoMatch.label       "Could not find any matching downloads.">
deleted file mode 100644
index d1447985258d7f8a34e385bb81c59f779979fb39..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -568,21 +568,16 @@ html|span.ac-emphasize-text-url {
   color: GrayText;
   font-size: smaller;
 }
 
 .autocomplete-treebody::-moz-tree-cell(suggesthint) {
   border-top: 1px solid GrayText;
 }
 
-/* Popup blocker button */
-#page-report-button {
-  list-style-image: url("chrome://browser/skin/Info.png");
-}
-
 /* Bookmarking panel */
 #editBookmarkPanelStarIcon {
   list-style-image: url("chrome://browser/skin/places/starred48.png");
   width: 48px;
   height: 48px;
 }
 
 #editBookmarkPanelStarIcon[unstarred] {
@@ -721,18 +716,16 @@ html|span.ac-emphasize-text-url {
   border-top-left-radius: .3em;
   margin-left: 1em;
 }
 
 %include ../shared/fullscreen/warning.inc.css
 %include ../shared/ctrlTab.inc.css
 %include ../shared/plugin-doorhanger.inc.css
 
-%include downloads/indicator.css
-
 .gcli-panel {
   padding: 0;
 }
 
 .gclitoolbar-input-node > .textbox-input-box > html|*.textbox-input::-moz-selection {
   color: hsl(210,11%,16%);
 }
 
deleted file mode 100644
index b8443f04557ac064b45d2a723f6b2c0123a31615..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
--- a/browser/themes/linux/downloads/indicator.css
+++ /dev/null
@@ -1,5 +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/. */
-
-%include ../../shared/downloads/indicator.inc.css
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -6,17 +6,16 @@ browser.jar:
 % skin browser classic/1.0 %skin/classic/browser/
 % override chrome://global/skin/icons/warning-16.png moz-icon://stock/gtk-dialog-warning?size=menu
 #include ../shared/jar.inc.mn
   skin/classic/browser/sanitizeDialog.css
   skin/classic/browser/aboutSessionRestore-window-icon.png
 * skin/classic/browser/syncedtabs/sidebar.css     (syncedtabs/sidebar.css)
 * skin/classic/browser/browser.css
 * skin/classic/browser/compacttheme.css
-  skin/classic/browser/Info.png
   skin/classic/browser/menuPanel-customize.png
   skin/classic/browser/menuPanel-customize@2x.png
   skin/classic/browser/menuPanel-exit.png
   skin/classic/browser/menuPanel-exit@2x.png
   skin/classic/browser/menuPanel-help.png
   skin/classic/browser/menuPanel-help@2x.png
   skin/classic/browser/monitor.png
   skin/classic/browser/monitor_16-10.png
@@ -28,25 +27,24 @@ browser.jar:
   skin/classic/browser/reload-stop-go@2x.png
   skin/classic/browser/searchbar.css
   skin/classic/browser/setDesktopBackground.css
   skin/classic/browser/slowStartup-16.png
   skin/classic/browser/webRTC-indicator.css  (../shared/webRTC-indicator.css)
 * skin/classic/browser/controlcenter/panel.css        (controlcenter/panel.css)
 * skin/classic/browser/customizableui/panelUI.css (customizableui/panelUI.css)
 * skin/classic/browser/downloads/allDownloadsViewOverlay.css   (downloads/allDownloadsViewOverlay.css)
-  skin/classic/browser/downloads/download-glow-menuPanel.png (downloads/download-glow-menuPanel.png)
 * skin/classic/browser/downloads/downloads.css        (downloads/downloads.css)
   skin/classic/browser/feeds/feedIcon.png             (feeds/feedIcon.png)
   skin/classic/browser/feeds/feedIcon16.png           (feeds/feedIcon16.png)
   skin/classic/browser/feeds/subscribe.css            (feeds/subscribe.css)
 * skin/classic/browser/newtab/newTab.css              (newtab/newTab.css)
-* skin/classic/browser/notification-icons/geo-blocked.svg  (notification-icons/geo-blocked.svg)
+  skin/classic/browser/notification-icons/geo-blocked.svg  (notification-icons/geo-blocked.svg)
   skin/classic/browser/notification-icons/geo-detailed.svg (notification-icons/geo-detailed.svg)
-* skin/classic/browser/notification-icons/geo.svg          (notification-icons/geo.svg)
+  skin/classic/browser/notification-icons/geo.svg          (notification-icons/geo.svg)
   skin/classic/browser/places/allBookmarks.png        (places/allBookmarks.png)
   skin/classic/browser/places/bookmarksMenu.png       (places/bookmarksMenu.png)
   skin/classic/browser/places/bookmarksToolbar.png    (places/bookmarksToolbar.png)
   skin/classic/browser/places/bookmarksToolbar-menuPanel.png (places/bookmarksToolbar-menuPanel.png)
   skin/classic/browser/places/bookmarks-menu-arrow.png           (places/bookmarks-menu-arrow.png)
   skin/classic/browser/places/calendar.png            (places/calendar.png)
 * skin/classic/browser/places/editBookmarkOverlay.css (places/editBookmarkOverlay.css)
   skin/classic/browser/places/livemark-item.png       (places/livemark-item.png)
--- a/browser/themes/linux/notification-icons/geo-blocked.svg
+++ b/browser/themes/linux/notification-icons/geo-blocked.svg
@@ -1,12 +1,6 @@
 <!-- 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 fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
-     width="32" height="32" viewBox="0 0 32 32">
-  <defs>
-#include ../../shared/notification-icons/clip-path.inc.svg
-#include geo-icon.inc.svg
-  </defs>
-  <use clip-path="url(#blocked-clipPath)" href="#geo-linux-icon"/>
-#include ../../shared/notification-icons/strikeout.inc.svg
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+  <path d="M12.7 6.3a4.953 4.953 0 0 1 .2.7H12l-1 1a1 1 0 0 0 1 1h.9A5.015 5.015 0 0 1 9 12.9V12a1 1 0 0 0-1-1l-1 1v.9a4.953 4.953 0 0 1-.7-.2l-1.512 1.512a6.989 6.989 0 0 0 9.425-9.425zm2.007-5.007a1 1 0 0 0-1.414 0L12.184 2.4A6.986 6.986 0 0 0 2.4 12.184l-1.107 1.109a1 1 0 1 0 1.414 1.414l12-12a1 1 0 0 0 0-1.414zM3.1 9H4a1 1 0 0 0 0-2h-.9A5.015 5.015 0 0 1 7 3.1V4a1 1 0 0 0 2 0v-.9a4.955 4.955 0 0 1 1.747.738l-6.908 6.909A4.957 4.957 0 0 1 3.1 9z"/>
 </svg>
deleted file mode 100644
--- a/browser/themes/linux/notification-icons/geo-icon.inc.svg
+++ /dev/null
@@ -1,1 +0,0 @@
-<path id="geo-linux-icon" d="m 2,15.9 a 14,14 0 1 1 0,0.2 z m 4,2.1 a 10,10 0 0 0 8,8 l 0,-4 4,0 0,4 a 10,10 0 0 0 8,-8 l -4,0 0,-4 4,0 a 10,10 0 0 0 -8,-8 l 0,4 -4,0 0,-4 a 10,10 0 0 0 -8,8 l 4,0 0,4 z"/>
--- a/browser/themes/linux/notification-icons/geo.svg
+++ b/browser/themes/linux/notification-icons/geo.svg
@@ -1,7 +1,6 @@
 <!-- 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 fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
-     width="32" height="32" viewBox="0 0 32 32">
-#include geo-icon.inc.svg
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+  <path d="M8 1a7 7 0 1 0 7 7 7.008 7.008 0 0 0-7-7zm1 11.9V12a1 1 0 0 0-2 0v.9A5.015 5.015 0 0 1 3.1 9H4a1 1 0 0 0 0-2h-.9A5.015 5.015 0 0 1 7 3.1V4a1 1 0 0 0 2 0v-.9A5.015 5.015 0 0 1 12.9 7H12a1 1 0 0 0 0 2h.9A5.015 5.015 0 0 1 9 12.9z"/>
 </svg>
deleted file mode 100644
index 7dd795ac92d4d0c9e84799bf5f864102374d4ab4..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -559,39 +559,16 @@ html|span.ac-emphasize-text-url {
 }
 
 #BMB_bookmarksPopup[side="left"],
 #BMB_bookmarksPopup[side="right"] {
   margin-top: -26px;
   margin-bottom: -26px;
 }
 
-/* POPUP BLOCKER BUTTON */
-#page-report-button {
-  list-style-image: url("chrome://browser/skin/urlbar-popup-blocked.png");
-  -moz-image-region: rect(0, 16px, 16px, 0);
-}
-
-#page-report-button:hover:active,
-#page-report-button[open="true"] {
-  -moz-image-region: rect(0, 32px, 16px, 16px);
-}
-
-@media (min-resolution: 2dppx) {
-  #page-report-button {
-    list-style-image: url("chrome://browser/skin/urlbar-popup-blocked@2x.png");
-    -moz-image-region: rect(0, 32px, 32px, 0);
-  }
-
-  #page-report-button:hover:active,
-  #page-report-button[open="true"] {
-    -moz-image-region: rect(0, 64px, 32px, 32px);
-  }
-}
-
 /* BOOKMARKING PANEL */
 #editBookmarkPanelStarIcon {
   list-style-image: url("chrome://browser/skin/places/starred48.png");
   width: 48px;
   height: 48px;
 }
 
 #editBookmarkPanelStarIcon[unstarred] {
@@ -1255,18 +1232,16 @@ html|*.addon-webext-perm-list {
   border-top-left-radius: .3em;
   margin-left: 1em;
 }
 
 %include ../shared/fullscreen/warning.inc.css
 %include ../shared/ctrlTab.inc.css
 %include ../shared/plugin-doorhanger.inc.css
 
-%include downloads/indicator.css
-
 /* On mac, the popup notification contents are indented by default and so
   the default closebutton margins from notification.css require adjustment */
 
 .click-to-play-plugins-notification-description-box > .popup-notification-closebutton {
   margin-inline-end: -6px;
   margin-top: -7px;
 }
 
deleted file mode 100644
index 583dac86eba3fcf8f06deec44b17bd30b1abe068..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 9b48fbd120a8f16d91d77d71a40da8791235855a..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
--- a/browser/themes/osx/downloads/indicator.css
+++ /dev/null
@@ -1,12 +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/. */
-
-%include ../../shared/downloads/indicator.inc.css
-
-
-@media (min-resolution: 2dppx) {
-  #downloads-button[cui-areatype="menu-panel"][attention="success"] {
-    list-style-image: url("chrome://browser/skin/downloads/download-glow-menuPanel@2x.png");
-  }
-}
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -5,17 +5,16 @@
 browser.jar:
 % skin browser classic/1.0 %skin/classic/browser/
 #include ../shared/jar.inc.mn
   skin/classic/browser/sanitizeDialog.css
   skin/classic/browser/aboutSessionRestore-window-icon.png
 * skin/classic/browser/syncedtabs/sidebar.css          (syncedtabs/sidebar.css)
 * skin/classic/browser/browser.css
 * skin/classic/browser/compacttheme.css
-  skin/classic/browser/Info.png
   skin/classic/browser/subtle-pattern.png
   skin/classic/browser/menu-back.png
   skin/classic/browser/menu-forward.png
   skin/classic/browser/menuPanel-customize.png
   skin/classic/browser/menuPanel-customize@2x.png
   skin/classic/browser/menuPanel-exit.png
   skin/classic/browser/menuPanel-exit@2x.png
   skin/classic/browser/menuPanel-help.png
@@ -33,40 +32,30 @@ browser.jar:
   skin/classic/browser/privatebrowsing-mask-short.png
   skin/classic/browser/privatebrowsing-mask-short@2x.png
   skin/classic/browser/reload-stop-go.png
   skin/classic/browser/reload-stop-go@2x.png
   skin/classic/browser/searchbar.css
   skin/classic/browser/slowStartup-16.png
   skin/classic/browser/toolbarbutton-dropmarker.png
   skin/classic/browser/toolbarbutton-dropmarker@2x.png
-  skin/classic/browser/urlbar-popup-blocked.png
-  skin/classic/browser/urlbar-popup-blocked@2x.png
-  skin/classic/browser/webRTC-sharingDevice-menubar.png
-  skin/classic/browser/webRTC-sharingDevice-menubar@2x.png
-  skin/classic/browser/webRTC-sharingMicrophone-menubar.png
-  skin/classic/browser/webRTC-sharingMicrophone-menubar@2x.png
-  skin/classic/browser/webRTC-sharingScreen-menubar.png
-  skin/classic/browser/webRTC-sharingScreen-menubar@2x.png
   skin/classic/browser/webRTC-indicator.css
 * skin/classic/browser/controlcenter/panel.css        (controlcenter/panel.css)
 * skin/classic/browser/customizableui/panelUI.css    (customizableui/panelUI.css)
 * skin/classic/browser/downloads/allDownloadsViewOverlay.css (downloads/allDownloadsViewOverlay.css)
-  skin/classic/browser/downloads/download-glow-menuPanel.png (downloads/download-glow-menuPanel.png)
-  skin/classic/browser/downloads/download-glow-menuPanel@2x.png (downloads/download-glow-menuPanel@2x.png)
 * skin/classic/browser/downloads/downloads.css              (downloads/downloads.css)
   skin/classic/browser/feeds/subscribe.css                  (feeds/subscribe.css)
   skin/classic/browser/feeds/feedIcon.png                   (feeds/feedIcon.png)
   skin/classic/browser/feeds/feedIcon16.png                 (feeds/feedIcon16.png)
 * skin/classic/browser/newtab/newTab.css                    (newtab/newTab.css)
   skin/classic/browser/setDesktopBackground.css
   skin/classic/browser/monitor.png
   skin/classic/browser/monitor_16-10.png
-* skin/classic/browser/notification-icons/geo-blocked.svg  (notification-icons/geo-blocked.svg)
-* skin/classic/browser/notification-icons/geo.svg          (notification-icons/geo.svg)
+  skin/classic/browser/notification-icons/geo-blocked.svg  (notification-icons/geo-blocked.svg)
+  skin/classic/browser/notification-icons/geo.svg          (notification-icons/geo.svg)
   skin/classic/browser/places/allBookmarks.png              (places/allBookmarks.png)
 * skin/classic/browser/places/places.css                    (places/places.css)
   skin/classic/browser/places/organizer.css                 (places/organizer.css)
   skin/classic/browser/places/query.png                     (places/query.png)
   skin/classic/browser/places/query@2x.png                  (places/query@2x.png)
   skin/classic/browser/places/bookmarksMenu.png             (places/bookmarksMenu.png)
   skin/classic/browser/places/bookmarksToolbar.png          (places/bookmarksToolbar.png)
   skin/classic/browser/places/bookmarksToolbar@2x.png       (places/bookmarksToolbar@2x.png)
--- a/browser/themes/osx/notification-icons/geo-blocked.svg
+++ b/browser/themes/osx/notification-icons/geo-blocked.svg
@@ -1,12 +1,6 @@
 <!-- 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 fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
-     width="32" height="32" viewBox="0 0 32 32">
-  <defs>
-#include ../../shared/notification-icons/clip-path.inc.svg
-#include geo-icon.inc.svg
-  </defs>
-  <use clip-path="url(#blocked-clipPath)" href="#geo-osx-icon"/>
-#include ../../shared/notification-icons/strikeout.inc.svg
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+  <path d="M8 16l3.75-8.75L8 11v5zm6.707-14.707a1 1 0 0 0-1.414 0l-1.768 1.768L0 8h6.586l-5.293 5.293a1 1 0 1 0 1.414 1.414l12-12a1 1 0 0 0 0-1.414z"/>
 </svg>
deleted file mode 100644
--- a/browser/themes/osx/notification-icons/geo-icon.inc.svg
+++ /dev/null
@@ -1,1 +0,0 @@
-<path id="geo-osx-icon" d="m 0,16 16,0 0,16 12,-28 z"/>
--- a/browser/themes/osx/notification-icons/geo.svg
+++ b/browser/themes/osx/notification-icons/geo.svg
@@ -1,7 +1,6 @@
 <!-- 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 fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
-     width="32" height="32" viewBox="0 0 32 32">
-#include geo-icon.inc.svg
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+  <path d="M0 8l14-6-6 14V8z"/>
 </svg>
deleted file mode 100644
index 8c4f4d96f06ed2067f62d012d1f91ab1b98503f8..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 4d6aa6f2009e0f025f4b759ed3014c199de36ee0..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/themes/osx/webRTC-indicator.css
+++ b/browser/themes/osx/webRTC-indicator.css
@@ -1,35 +1,28 @@
 /* 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/. */
 
+#webRTC-sharingCamera-menu,
+#webRTC-sharingMicrophone-menu,
+#webRTC-sharingScreen-menu {
+  -moz-context-properties: fill;
+  fill: currentColor;
+}
+
 #webRTC-sharingCamera-menu {
-  list-style-image: url("chrome://browser/skin/webRTC-sharingDevice-menubar.png");
+  list-style-image: url("chrome://browser/skin/notification-icons/camera.svg");
 }
 
 #webRTC-sharingMicrophone-menu {
-  list-style-image: url("chrome://browser/skin/webRTC-sharingMicrophone-menubar.png");
+  list-style-image: url("chrome://browser/skin/notification-icons/microphone.svg");
 }
 
 #webRTC-sharingScreen-menu {
-  list-style-image: url("chrome://browser/skin/webRTC-sharingScreen-menubar.png");
-}
-
-@media (min-resolution: 2dppx) {
-  #webRTC-sharingCamera-menu {
-    list-style-image: url("chrome://browser/skin/webRTC-sharingDevice-menubar@2x.png");
-  }
-
-  #webRTC-sharingMicrophone-menu {
-    list-style-image: url("chrome://browser/skin/webRTC-sharingMicrophone-menubar@2x.png");
-  }
-
-  #webRTC-sharingScreen-menu {
-    list-style-image: url("chrome://browser/skin/webRTC-sharingScreen-menubar@2x.png");
-  }
+  list-style-image: url("chrome://browser/skin/notification-icons/screen.svg");
 }
 
 #webRTC-sharingCamera-menu > menupopup,
 #webRTC-sharingMicrophone-menu > menupopup,
 #webRTC-sharingScreen-menu > menupopup {
   list-style-image: none; /* don't inherit into menu items */
 }
deleted file mode 100644
index fb74738dd14e67d3481a39b0aa6f766f29d59365..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 8f64887a8c5b6cd47ef7f2076bab83f03f307b5b..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index c47f5827d753d1da4a84b8795462cff48bd8bde1..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 2c45c8bccc3e23c3c0f7a0935556819e69c74793..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 6d9e49cfd6200eacb7df70ba1fe4160ff505e321..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 0f3d62ec5599ee6ab537e6241107727ce3aad823..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/themes/shared/browser.inc.css
+++ b/browser/themes/shared/browser.inc.css
@@ -11,16 +11,18 @@
 %else
 :root[tabsintitlebar][sizemode=normal] #TabsToolbar
 %endif
 {
   padding-inline-start: 40px;
 }
 %endif
 
+%include downloads/indicator.inc.css
+
 /* Toolbar / content area border */
 
 #navigator-toolbox::after {
   content: "";
   display: -moz-box;
   border-bottom: 1px solid var(--toolbox-border-bottom-color);
 }
 
--- a/browser/themes/shared/compacttheme.inc.css
+++ b/browser/themes/shared/compacttheme.inc.css
@@ -105,25 +105,16 @@ toolbar[brighttext] .toolbarbutton-1 {
 .searchbar-textbox:not([focused="true"]) {
   border-color: var(--chrome-nav-bar-controls-border-color);
 }
 
 #urlbar[pageproxystate="valid"] > #identity-box.verifiedIdentity > #identity-icon-labels:-moz-lwtheme-brighttext {
   color: #30e60b;
 }
 
-#identity-icon:-moz-lwtheme-brighttext,
-#tracking-protection-icon:-moz-lwtheme-brighttext,
-#connection-icon:-moz-lwtheme-brighttext,
-.notification-anchor-icon:-moz-lwtheme-brighttext,
-#blocked-permissions-container > .blocked-permission-icon:-moz-lwtheme-brighttext,
-#extension-icon:-moz-lwtheme-brighttext {
-  fill: rgba(255,255,255,.7);
-}
-
 #urlbar-zoom-button:-moz-lwtheme-brighttext:hover {
   background-color: rgba(255,255,255,.2);
 }
 
 #urlbar-zoom-button:-moz-lwtheme-brighttext:hover:active {
   background-color: rgba(255,255,255,.3);
 }
 
--- a/browser/themes/shared/contextmenu.inc.css
+++ b/browser/themes/shared/contextmenu.inc.css
@@ -42,10 +42,12 @@
 
 #context-back:-moz-locale-dir(rtl),
 #context-forward:-moz-locale-dir(rtl),
 #context-reload:-moz-locale-dir(rtl) {
   transform: scaleX(-1);
 }
 
 #context-media-eme-learnmore {
-  list-style-image: url("chrome://browser/skin/drm-icon.svg#chains");
+  list-style-image: url("chrome://browser/skin/drm-icon.svg");
+  -moz-context-properties: fill;
+  fill: currentColor;
 }
--- a/browser/themes/shared/customizableui/panelUI.inc.css
+++ b/browser/themes/shared/customizableui/panelUI.inc.css
@@ -931,17 +931,16 @@ toolbaritem[cui-areatype="menu-panel"][s
 
 #PanelUI-remotetabs[mainview] .PanelUI-remotetabs-notabsforclient-label {
   margin-left: 32px;
 }
 
 .fxaSyncIllustration {
   width: 180px;
   height: var(--panel-ui-sync-illustration-height);
-  list-style-image: url(chrome://browser/skin/fxa/sync-illustration.svg);
   -moz-context-properties: fill;
   fill: #cdcdcd;
 }
 
 .PanelUI-remotetabs-prefs-button > .toolbarbutton-text {
   /* !important to override ".cui-widget-panel toolbarbutton > .toolbarbutton-text" above. */
   text-align: center !important;
   text-shadow: none;
--- a/browser/themes/shared/downloads/indicator.inc.css
+++ b/browser/themes/shared/downloads/indicator.inc.css
@@ -1,18 +1,14 @@
 /* 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/. */
 
 /*** Status and progress indicator ***/
 
-#downloads-button {
-  --downloads-indicator-image: url("chrome://browser/skin/downloads/download-icons.svg#arrow-with-bar");
-}
-
 #downloads-indicator-anchor {
   min-width: 16px;
   min-height: 16px;
 }
 
 #downloads-indicator-progress-outer {
   width: 16px;
   height: 16px;
@@ -24,21 +20,16 @@
 #downloads-button[attention="success"] > #downloads-indicator-anchor > #downloads-indicator-icon,
 #downloads-button[attention="success"] > #downloads-indicator-anchor > #downloads-indicator-progress-outer {
   fill: var(--toolbarbutton-icon-fill-attention);
 }
 #downloads-button[progress] > #downloads-indicator-anchor > #downloads-indicator-progress-outer {
   background: url("chrome://browser/skin/downloads/download-icons.svg#progress-bar-bg") center no-repeat;
 }
 
-#downloads-button[attention="success"] {
-  list-style-image: url("chrome://browser/skin/downloads/download-glow-menuPanel.png");
-  -moz-image-region: auto;
-}
-
 #downloads-indicator-icon {
   -moz-context-properties: fill;
   background-image: url("chrome://browser/skin/downloads/download-icons.svg#arrow");
   width: 16px;
   height: 16px;
 }
 
 #downloads-indicator-progress-inner {
--- a/browser/themes/shared/drm-icon.svg
+++ b/browser/themes/shared/drm-icon.svg
@@ -1,38 +1,8 @@
-<?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" viewBox="0 0 16 16">
-  <style>
-    #chains {
-      fill: url(#baseGradient);
-    }
-    #chains-pressed {
-      fill: url(#pressedGradient);
-    }
-    #chains-black {
-      fill: #000;
-    }
-    use:not(:target) {
-      display: none;
-    }
-  </style>
-  <defs>
-    <linearGradient id="baseGradient" gradientUnits="userSpaceOnUse" x1="8" x2="8" y1="16" y2="0">
-      <stop offset="0" style="stop-color: #808080"/>
-      <stop offset="1" style="stop-color: #999"/>
-    </linearGradient>
-    <linearGradient id="pressedGradient" gradientUnits="userSpaceOnUse" x1="8" x2="8" y1="16" y2="0">
-      <stop offset="0" style="stop-color: #4d4d4d"/>
-      <stop offset="1" style="stop-color: #808080"/>
-    </linearGradient>
-    <g id="path">
-      <path d="M7.058,9.72c-0.245,0.245-0.62,0.27-0.834,0.056C6.01,9.562,6.035,9.186,6.28,8.942l0.218-0.218 c-0.245-0.245-0.645-0.245-0.89,0L4.496,9.836c-0.245,0.245-0.245,0.645,0,0.89l0.779,0.779c0.245,0.245,0.645,0.245,0.89,0 l1.112-1.112c0.245-0.245,0.245-0.645,0-0.89L7.058,9.72z"/>
-      <path d="M10.726,4.496c-0.245-0.245-0.645-0.245-0.89,0L8.723,5.608c-0.245,0.245-0.245,0.645,0,0.89 L8.95,6.272c0.245-0.245,0.62-0.27,0.834-0.056s0.189,0.59-0.056,0.834L9.502,7.277c0.245,0.245,0.645,0.245,0.89,0l1.112-1.112 c0.245-0.245,0.245-0.645,0-0.89L10.726,4.496z"/>
-      <path d="M8,0C3.582,0,0,3.582,0,8s3.582,8,8,8s8-3.582,8-8S12.418,0,8,0z M12.527,6.81l-1.489,1.489 c-0.631,0.631-1.663,0.631-2.293,0L8.612,8.167L8.167,8.612l0.133,0.133c0.631,0.631,0.631,1.663,0,2.293L6.81,12.527 c-0.631,0.631-1.663,0.631-2.293,0l-1.044-1.044c-0.631-0.631-0.631-1.663,0-2.293l1.489-1.489c0.631-0.631,1.663-0.631,2.293,0 l0.133,0.133l0.445-0.445L7.701,7.255c-0.631-0.631-0.631-1.663,0-2.293L9.19,3.473c0.631-0.631,1.663-0.631,2.293,0l1.044,1.044 C13.158,5.148,13.158,6.18,12.527,6.81z"/>
-    </g>
-  </defs>
-  <use xlink:href="#path" id="chains"/>
-  <use xlink:href="#path" id="chains-pressed"/>
-  <use xlink:href="#path" id="chains-black"/>
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+  <path d="M7.058,9.72c-0.245,0.245-0.62,0.27-0.834,0.056C6.01,9.562,6.035,9.186,6.28,8.942l0.218-0.218 c-0.245-0.245-0.645-0.245-0.89,0L4.496,9.836c-0.245,0.245-0.245,0.645,0,0.89l0.779,0.779c0.245,0.245,0.645,0.245,0.89,0 l1.112-1.112c0.245-0.245,0.245-0.645,0-0.89L7.058,9.72z"/>
+  <path d="M10.726,4.496c-0.245-0.245-0.645-0.245-0.89,0L8.723,5.608c-0.245,0.245-0.245,0.645,0,0.89 L8.95,6.272c0.245-0.245,0.62-0.27,0.834-0.056s0.189,0.59-0.056,0.834L9.502,7.277c0.245,0.245,0.645,0.245,0.89,0l1.112-1.112 c0.245-0.245,0.245-0.645,0-0.89L10.726,4.496z"/>
+  <path d="M8,0C3.582,0,0,3.582,0,8s3.582,8,8,8s8-3.582,8-8S12.418,0,8,0z M12.527,6.81l-1.489,1.489 c-0.631,0.631-1.663,0.631-2.293,0L8.612,8.167L8.167,8.612l0.133,0.133c0.631,0.631,0.631,1.663,0,2.293L6.81,12.527 c-0.631,0.631-1.663,0.631-2.293,0l-1.044-1.044c-0.631-0.631-0.631-1.663,0-2.293l1.489-1.489c0.631-0.631,1.663-0.631,2.293,0 l0.133,0.133l0.445-0.445L7.701,7.255c-0.631-0.631-0.631-1.663,0-2.293L9.19,3.473c0.631-0.631,1.663-0.631,2.293,0l1.044,1.044 C13.158,5.148,13.158,6.18,12.527,6.81z"/>
 </svg>
--- a/browser/themes/shared/incontentprefs-old/containers.css
+++ b/browser/themes/shared/incontentprefs-old/containers.css
@@ -18,15 +18,15 @@
   background: transparent;
 }
 
 #containersView richlistitem {
   margin: 0px;
   margin-inline-end: 8px;
   padding: 0;
   padding-block-end: 8px;
-  border-block-end: 1px solid var(--in-content-header-border-color);
+  border-block-end: 1px solid var(--in-content-box-border-color);
 }
 
 #containersView richlistitem:last-of-type {
   border-block-end: 0 none;
   margin-block-end: 8px;
 }
--- a/browser/themes/shared/incontentprefs-old/preferences.inc.css
+++ b/browser/themes/shared/incontentprefs-old/preferences.inc.css
@@ -460,17 +460,17 @@ description > html|a {
   margin-bottom: 27.5px;
 }
 
 #noFxaDescription {
   margin-bottom: 20px !important;
 }
 
 .separator {
-  border-bottom: 1px solid var(--in-content-header-border-color);
+  border-bottom: 1px solid var(--in-content-box-border-color);
 }
 
 .fxaAccountBox {
   border: 1px solid #D1D2D3;
   border-radius: 5px;
   padding: 14px 20px 14px 14px;
 }
 
--- a/browser/themes/shared/incontentprefs/containers.css
+++ b/browser/themes/shared/incontentprefs/containers.css
@@ -18,15 +18,15 @@
   background: transparent;
 }
 
 #containersView richlistitem {
   margin: 0px;
   margin-inline-end: 8px;
   padding: 0;
   padding-block-end: 8px;
-  border-block-end: 1px solid var(--in-content-header-border-color);
+  border-block-end: 1px solid var(--in-content-box-border-color);
 }
 
 #containersView richlistitem:last-of-type {
   border-block-end: 0 none;
   margin-block-end: 8px;
 }
--- a/browser/themes/shared/incontentprefs/preferences.inc.css
+++ b/browser/themes/shared/incontentprefs/preferences.inc.css
@@ -540,17 +540,17 @@ separator.thin:not([orient="vertical"]) 
   height: 136px;
 }
 
 #noFxaDescription {
   padding-inline-end: 52px;
 }
 
 .separator {
-  border-bottom: 1px solid var(--in-content-header-border-color);
+  border-bottom: 1px solid var(--in-content-box-border-color);
 }
 
 #fxaGroup {
   margin-top: 16px;
   margin-bottom: 32px;
 }
 
 #signedOutAccountBoxTitle {
--- a/browser/themes/shared/jar.inc.mn
+++ b/browser/themes/shared/jar.inc.mn
@@ -51,37 +51,37 @@
   skin/classic/browser/fullscreen/secure.svg                   (../shared/fullscreen/secure.svg)
   skin/classic/browser/connection-secure.svg                   (../shared/identity-block/connection-secure.svg)
   skin/classic/browser/connection-mixed-passive-loaded.svg     (../shared/identity-block/connection-mixed-passive-loaded.svg)
   skin/classic/browser/connection-mixed-active-loaded.svg      (../shared/identity-block/connection-mixed-active-loaded.svg)
   skin/classic/browser/identity-icon.svg                       (../shared/identity-block/identity-icon.svg)
   skin/classic/browser/identity-icon-notice.svg                (../shared/identity-block/identity-icon-notice.svg)
   skin/classic/browser/info.svg                                (../shared/info.svg)
 
-* skin/classic/browser/notification-icons/camera-blocked.svg                (../shared/notification-icons/camera-blocked.svg)
-* skin/classic/browser/notification-icons/camera.svg                        (../shared/notification-icons/camera.svg)
+  skin/classic/browser/notification-icons/camera-blocked.svg                (../shared/notification-icons/camera-blocked.svg)
+  skin/classic/browser/notification-icons/camera.svg                        (../shared/notification-icons/camera.svg)
   skin/classic/browser/notification-icons/default-info.svg                  (../shared/notification-icons/default-info.svg)
-* skin/classic/browser/notification-icons/desktop-notification-blocked.svg  (../shared/notification-icons/desktop-notification-blocked.svg)
-* skin/classic/browser/notification-icons/desktop-notification.svg          (../shared/notification-icons/desktop-notification.svg)
+  skin/classic/browser/notification-icons/desktop-notification-blocked.svg  (../shared/notification-icons/desktop-notification-blocked.svg)
+  skin/classic/browser/notification-icons/desktop-notification.svg          (../shared/notification-icons/desktop-notification.svg)
   skin/classic/browser/notification-icons/focus-tab-by-prompt.svg           (../shared/notification-icons/focus-tab-by-prompt.svg)
-* skin/classic/browser/notification-icons/indexedDB-blocked.svg             (../shared/notification-icons/indexedDB-blocked.svg)
-* skin/classic/browser/notification-icons/indexedDB.svg                     (../shared/notification-icons/indexedDB.svg)
+  skin/classic/browser/notification-icons/indexedDB-blocked.svg             (../shared/notification-icons/indexedDB-blocked.svg)
+  skin/classic/browser/notification-icons/indexedDB.svg                     (../shared/notification-icons/indexedDB.svg)
   skin/classic/browser/notification-icons/login-detailed.svg                (../shared/notification-icons/login-detailed.svg)
   skin/classic/browser/notification-icons/login.svg                         (../shared/notification-icons/login.svg)
-* skin/classic/browser/notification-icons/microphone-blocked.svg            (../shared/notification-icons/microphone-blocked.svg)
+  skin/classic/browser/notification-icons/microphone-blocked.svg            (../shared/notification-icons/microphone-blocked.svg)
   skin/classic/browser/notification-icons/microphone-detailed.svg           (../shared/notification-icons/microphone-detailed.svg)
-* skin/classic/browser/notification-icons/microphone.svg                    (../shared/notification-icons/microphone.svg)
-* skin/classic/browser/notification-icons/persistent-storage-blocked.svg    (../shared/notification-icons/persistent-storage-blocked.svg)
-* skin/classic/browser/notification-icons/persistent-storage.svg            (../shared/notification-icons/persistent-storage.svg)
+  skin/classic/browser/notification-icons/microphone.svg                    (../shared/notification-icons/microphone.svg)
+  skin/classic/browser/notification-icons/persistent-storage-blocked.svg    (../shared/notification-icons/persistent-storage-blocked.svg)
+  skin/classic/browser/notification-icons/persistent-storage.svg            (../shared/notification-icons/persistent-storage.svg)
   skin/classic/browser/notification-icons/plugin-badge.svg                  (../shared/notification-icons/plugin-badge.svg)
-* skin/classic/browser/notification-icons/plugin-blocked.svg                (../shared/notification-icons/plugin-blocked.svg)
-* skin/classic/browser/notification-icons/plugin.svg                        (../shared/notification-icons/plugin.svg)
+  skin/classic/browser/notification-icons/plugin-blocked.svg                (../shared/notification-icons/plugin-blocked.svg)
+  skin/classic/browser/notification-icons/plugin.svg                        (../shared/notification-icons/plugin.svg)
   skin/classic/browser/notification-icons/popup.svg                         (../shared/notification-icons/popup.svg)
-* skin/classic/browser/notification-icons/screen-blocked.svg                (../shared/notification-icons/screen-blocked.svg)
-* skin/classic/browser/notification-icons/screen.svg                        (../shared/notification-icons/screen.svg)
+  skin/classic/browser/notification-icons/screen-blocked.svg                (../shared/notification-icons/screen-blocked.svg)
+  skin/classic/browser/notification-icons/screen.svg                        (../shared/notification-icons/screen.svg)
   skin/classic/browser/notification-icons/update.svg                        (../shared/notification-icons/update.svg)
 
   skin/classic/browser/tracking-protection-16.svg              (../shared/identity-block/tracking-protection-16.svg)
   skin/classic/browser/newtab/close.png                        (../shared/newtab/close.png)
   skin/classic/browser/newtab/controls.svg                     (../shared/newtab/controls.svg)
   skin/classic/browser/panel-icon-arrow-left.svg               (../shared/panel-icon-arrow-left.svg)
   skin/classic/browser/panel-icon-arrow-right.svg              (../shared/panel-icon-arrow-right.svg)
   skin/classic/browser/panel-icon-cancel.svg                   (../shared/panel-icon-cancel.svg)
--- a/browser/themes/shared/notification-icons.inc.css
+++ b/browser/themes/shared/notification-icons.inc.css
@@ -149,21 +149,17 @@ html|*#webRTC-previewVideo {
 .popup-icon {
   list-style-image: url("chrome://browser/skin/notification-icons/popup.svg");
 }
 
 /* EME */
 
 .popup-notification-icon[popupid="drmContentPlaying"],
 .drm-icon {
-  list-style-image: url("chrome://browser/skin/drm-icon.svg#chains");
-}
-
-.drm-icon:hover:active {
-  list-style-image: url("chrome://browser/skin/drm-icon.svg#chains-pressed");
+  list-style-image: url("chrome://browser/skin/drm-icon.svg");
 }
 
 #eme-notification-icon[firstplay=true] {
   animation: emeTeachingMoment 0.2s linear 0s 5 normal;
 }
 
 @keyframes emeTeachingMoment {
   0% {transform: translateX(0); }
--- a/browser/themes/shared/notification-icons/camera-blocked.svg
+++ b/browser/themes/shared/notification-icons/camera-blocked.svg
@@ -1,12 +1,6 @@
 <!-- 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 fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
-     width="32" height="32" viewBox="0 0 32 32">
-  <defs>
-#include clip-path.inc.svg
-#include camera-icon.inc.svg
-  </defs>
-  <use clip-path="url(#blocked-clipPath)" href="#camera-icon"/>
-#include strikeout.inc.svg
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+  <path d="M9.7 13a1.345 1.345 0 0 0 1.3-1.389V9.552l3.037 2.648a1.007 1.007 0 0 0 .963.285V4l-9 9zm5.007-11.707a1 1 0 0 0-1.414 0l-2.452 2.452A1.284 1.284 0 0 0 9.7 3H2.3A1.345 1.345 0 0 0 1 4.389v7.222a1.4 1.4 0 0 0 .731 1.244l-.438.438a1 1 0 1 0 1.414 1.414l12-12a1 1 0 0 0 0-1.414z"/>
 </svg>
deleted file mode 100644
--- a/browser/themes/shared/notification-icons/camera-icon.inc.svg
+++ /dev/null
@@ -1,1 +0,0 @@
-<path id="camera-icon" d="m 2,23 a 3,3 0 0 0 3,3 l 14,0 a 3,3 0 0 0 3,-3 l 0,-4 6,5.5 c 0.5,0.5 1,0.7 2,0.5 l 0,-18 c -1,-0.2 -1.5,0 -2,0.5 l -6,5.5 0,-4 a 3,3 0 0 0 -3,-3 l -14,0 a 3,3 0 0 0 -3,3 z"/>
--- a/browser/themes/shared/notification-icons/camera.svg
+++ b/browser/themes/shared/notification-icons/camera.svg
@@ -1,7 +1,6 @@
 <!-- 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 fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
-     width="32" height="32" viewBox="0 0 32 32">
-#include camera-icon.inc.svg
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+  <path d="M14.037 3.828L11 6.479v-2.09A1.345 1.345 0 0 0 9.7 3H2.3A1.345 1.345 0 0 0 1 4.389v7.222A1.345 1.345 0 0 0 2.3 13h7.4a1.345 1.345 0 0 0 1.3-1.389V9.552l3.037 2.648a1.007 1.007 0 0 0 .963.285V3.542a1.007 1.007 0 0 0-.963.286z"/>
 </svg>
deleted file mode 100644
--- a/browser/themes/shared/notification-icons/clip-path.inc.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-<clipPath id="blocked-clipPath">
-    <path d="m 0,0 0,31 31,-31 z m 6,32 26,0 0,-26 z"/>
-</clipPath>
--- a/browser/themes/shared/notification-icons/default-info.svg
+++ b/browser/themes/shared/notification-icons/default-info.svg
@@ -1,16 +1,6 @@
 <!-- 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 fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
-     width="32" height="32" viewBox="0 0 32 32">
-  <defs>
-    <mask id="i-mask" fill-opacity="1">
-      <rect fill="white" width="32" height="32"/>
-      <circle fill="black" cx="16" cy="9" r="2.5"/>
-      <rect fill="black" x="14" y="14" width="4" height="10" rx="2" ry="2"/>
-    </mask>
-  </defs>
-  <g>
-    <circle cx="16" cy="16" r="14" mask="url(#i-mask)"/>
-  </g>
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+  <path fill-rule="evenodd" d="M8 1a7 7 0 1 1-7 7 7 7 0 0 1 7-7zm0 3a1 1 0 1 1-1 1 1 1 0 0 1 1-1zm0 3a1 1 0 0 1 1 1v3a1 1 0 0 1-2 0V8a1 1 0 0 1 1-1z"/>
 </svg>
--- a/browser/themes/shared/notification-icons/desktop-notification-blocked.svg
+++ b/browser/themes/shared/notification-icons/desktop-notification-blocked.svg
@@ -1,12 +1,6 @@
 <!-- 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 fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
-     width="32" height="32" viewBox="0 0 32 32">
-  <defs>
-#include clip-path.inc.svg
-#include desktop-notification-icon.inc.svg
-  </defs>
-  <use clip-path="url(#blocked-clipPath)" href="#desktop-notification-icon"/>
-#include strikeout.inc.svg
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+  <path d="M15.961 3.039L14 5v4.577a.423.423 0 0 1-.423.423H12a1 1 0 0 0-1 1v1.194l-1.491-1.827a.978.978 0 0 0-.558-.318L7 12h1.26l2.966 3.633A1 1 0 0 0 13 15v-3h.577A2.426 2.426 0 0 0 16 9.577V3.423a2.4 2.4 0 0 0-.039-.384zm-1.254-1.746A1.013 1.013 0 0 0 14 1H2.423A2.426 2.426 0 0 0 0 3.423v6.154A2.426 2.426 0 0 0 2.423 12h.163l-1.293 1.293a1 1 0 1 0 1.414 1.414l12-12a1 1 0 0 0 0-1.414zM2.423 10A.423.423 0 0 1 2 9.577V3.423A.423.423 0 0 1 2.423 3h9.163l-2 2H4.5a.5.5 0 0 0 0 1h4.086l-1 1H4.5a.5.5 0 0 0 0 1h2.086l-2 2z"/>
 </svg>
deleted file mode 100644
--- a/browser/themes/shared/notification-icons/desktop-notification-icon.inc.svg
+++ /dev/null
@@ -1,1 +0,0 @@
-<path id="desktop-notification-icon" d="m 2,20 a 4,4 0 0 0 4,4 l 13,0 7,7 0,-7 a 4,4 0 0 0 4,-4 l 0,-12 a 4,4 0 0 0 -4,-4 l -20,0 a 4,4 0 0 0 -4,4 z m 5,-2 a 1,1 0 1 1 0,-2 l 10,0 a 1,1 0 1 1 0,2 z m 0,-4 a 1,1 0 1 1 0,-2 l 14,0 a 1,1 0 1 1 0,2 z m 0,-4 a 1,1 0 1 1 0,-2 l 18,0 a 1,1 0 1 1 0,2 z"/>
--- a/browser/themes/shared/notification-icons/desktop-notification.svg
+++ b/browser/themes/shared/notification-icons/desktop-notification.svg
@@ -1,7 +1,7 @@
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
-     width="32" height="32" viewBox="0 0 32 32">
-#include desktop-notification-icon.inc.svg
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+  <path d="M13.577 1H2.423A2.426 2.426 0 0 0 0 3.423v6.154A2.426 2.426 0 0 0 2.423 12H8.26l2.966 3.633A1 1 0 0 0 13 15v-3h.577A2.426 2.426 0 0 0 16 9.577V3.423A2.426 2.426 0 0 0 13.577 1zM14 9.577a.423.423 0 0 1-.423.423H12a1 1 0 0 0-1 1v1.194l-1.491-1.827A1 1 0 0 0 8.734 10H2.423A.423.423 0 0 1 2 9.577V3.423A.423.423 0 0 1 2.423 3h11.154a.423.423 0 0 1 .423.423z"/>
+  <path d="M11.5 5h-7a.5.5 0 0 0 0 1h7a.5.5 0 0 0 0-1zm0 2h-7a.5.5 0 0 0 0 1h7a.5.5 0 0 0 0-1z"/>
 </svg>
--- a/browser/themes/shared/notification-icons/indexedDB-blocked.svg
+++ b/browser/themes/shared/notification-icons/indexedDB-blocked.svg
@@ -1,12 +1,6 @@
 <!-- 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 fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
-     width="32" height="32" viewBox="0 0 32 32">
-  <defs>
-#include clip-path.inc.svg
-#include indexedDB-icon.inc.svg
-  </defs>
-  <use clip-path="url(#blocked-clipPath)" href="#indexedDB-icon"/>
-#include strikeout.inc.svg
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+  <path d="M10.293 11.293L9 12.586V10l-2 2v.586l-.293-.293-1.414 1.414 2 2a1 1 0 0 0 1.414 0l3-3a1 1 0 0 0-1.414-1.414zM13 8h-1a1 1 0 0 0 0 2h2a1 1 0 0 0 1-1V4l-2 2zm1.707-6.707A1.016 1.016 0 0 0 14 1H2a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h2a1 1 0 0 0 0-2H3V3h8.586L1.293 13.293a1 1 0 1 0 1.414 1.414l12-12a1 1 0 0 0 0-1.414z"/>
 </svg>
deleted file mode 100644
--- a/browser/themes/shared/notification-icons/indexedDB-icon.inc.svg
+++ /dev/null
@@ -1,1 +0,0 @@
-<path id="indexedDB-icon" d="m 2,24 a 4,4 0 0 0 4,4 l 2,0 0,-4 -2,0 0,-16 20,0 0,16 -2,0 0,4 2,0 a 4,4 0 0 0 4,-4 l 0,-16 a 4,4 0 0 0 -4,-4 l -20,0 a 4,4 0 0 0 -4,4 z m 8,-2 6,7 6,-7 -4,0 0,-8 -4,0 0,8 z"/>
--- a/browser/themes/shared/notification-icons/indexedDB.svg
+++ b/browser/themes/shared/notification-icons/indexedDB.svg
@@ -1,7 +1,7 @@
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
-     width="32" height="32" viewBox="0 0 32 32">
-#include indexedDB-icon.inc.svg
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+  <path d="M14 1H2a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h2a1 1 0 0 0 0-2H3V3h10v5h-1a1 1 0 0 0 0 2h2a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1z"/>
+  <path d="M10.293 11.293L9 12.586V8a1 1 0 0 0-2 0v4.586l-1.293-1.293a1 1 0 0 0-1.414 1.414l3 3a1 1 0 0 0 1.414 0l3-3a1 1 0 0 0-1.414-1.414z"/>
 </svg>
--- a/browser/themes/shared/notification-icons/login.svg
+++ b/browser/themes/shared/notification-icons/login.svg
@@ -1,7 +1,6 @@
 <!-- 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 fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
-     width="32" height="32" viewBox="0 0 32 32">
-  <path d="m 2,26 0,4 6,0 0,-2 2,0 0,-2 1,0 0,-1 2,0 0,-3 2,0 2.5,-2.5 1.5,1.5 3,-3 a 8,8 0 1 0 -8,-8 l -3,3 2,2 z m 20,-18.1 a 2,2 0 1 1 0,0.2 z"/>
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+  <path d="M10.992 1a4.009 4.009 0 0 0-4.009 4.008c0 .1.022.187.028.282-.059.05-.119.087-.178.143L5.667 6.6a.366.366 0 0 0 0 .467A1.878 1.878 0 0 0 6 7.5.353.353 0 0 1 6 8l-5 5v1.767a.229.229 0 0 0 .233.233H3.77a.229.229 0 0 0 .23-.233v-.778h.75a.227.227 0 0 0 .233-.228v-.768H5.2s.28 0 .28-.235V12.5h.779s.233-.1.233-.244v-1.271h.855l1.12-1.118H8.7l.467.467c.233.233.233.233.365.233a.437.437 0 0 0 .275-.127l.993-1.273c.034-.053.054-.107.084-.161.036 0 .07.011.107.011a4.008 4.008 0 1 0 0-8.017zM12.5 4.489a1 1 0 1 1 1-1 1 1 0 0 1-1 1z"/>
 </svg>
--- a/browser/themes/shared/notification-icons/microphone-blocked.svg
+++ b/browser/themes/shared/notification-icons/microphone-blocked.svg
@@ -1,12 +1,6 @@
 <!-- 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 fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
-     width="32" height="32" viewBox="0 0 32 32">
-  <defs>
-#include clip-path.inc.svg
-#include microphone-icon.inc.svg
-  </defs>
-  <use clip-path="url(#blocked-clipPath)" href="#microphone-icon"/>
-#include strikeout.inc.svg
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+  <path d="M11 8v1a3 3 0 0 1-3 3 2.958 2.958 0 0 1-.859-.141l-.775.775a4.012 4.012 0 0 0 .634.224V14H5.5a.5.5 0 0 0 0 1h5a.5.5 0 0 0 0-1H9v-1.142A4 4 0 0 0 12 9V7.5a.5.5 0 0 0-.146-.354zm3.707-6.707a1 1 0 0 0-1.414 0L10 4.586V3a2 2 0 1 0-4 0v5.586l-.945.945A2.856 2.856 0 0 1 5 9V7.5a.5.5 0 0 0-1 0V9a3.843 3.843 0 0 0 .248 1.338l-2.955 2.955a1 1 0 1 0 1.414 1.414l12-12a1 1 0 0 0 0-1.414z"/>
 </svg>
deleted file mode 100644
--- a/browser/themes/shared/notification-icons/microphone-icon.inc.svg
+++ /dev/null
@@ -1,1 +0,0 @@
-<path id="microphone-icon" d="m 8,14 0,4 a 8,8 0 0 0 6,7.7 l 0,2.3 -2,0 a 2,2 0 0 0 -2,2 l 12,0 a 2,2 0 0 0 -2,-2 l -2,0 0,-2.3 a 8,8 0 0 0 6,-7.7 l 0,-4 -2,0 0,4 a 6,6 0 0 1 -12,0 l 0,-4 z m 4,4 a 4,4 0 0 0 8,0 l 0,-12 a 4,4 0 0 0 -8,0 z"/>
--- a/browser/themes/shared/notification-icons/microphone.svg
+++ b/browser/themes/shared/notification-icons/microphone.svg
@@ -1,7 +1,7 @@
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
-     width="32" height="32" viewBox="0 0 32 32">
-#include microphone-icon.inc.svg
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+  <path d="M8 11a2 2 0 0 0 2-2V3a2 2 0 1 0-4 0v6a2 2 0 0 0 2 2z"/>
+  <path d="M12 9V7.5a.5.5 0 0 0-1 0V9a3 3 0 0 1-6 0V7.5a.5.5 0 0 0-1 0V9a4 4 0 0 0 3 3.858V14H5.5a.5.5 0 0 0 0 1h5a.5.5 0 0 0 0-1H9v-1.142A4 4 0 0 0 12 9z"/>
 </svg>
--- a/browser/themes/shared/notification-icons/persistent-storage-blocked.svg
+++ b/browser/themes/shared/notification-icons/persistent-storage-blocked.svg
@@ -1,12 +1,6 @@
 <!-- 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 fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
-     width="32" height="32" viewBox="0 0 32 32">
-  <defs>
-#include clip-path.inc.svg
-#include persistent-storage-icon.inc.svg
-  </defs>
-  <use clip-path="url(#blocked-clipPath)" href="#persistent-storage-icon"/>
-#include strikeout.inc.svg
+<svg width="32" height="32" fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg">
+  <path d="M4 24.172V23.1c0-1.1.9-2 2-2h1.072l2.1-2.1H6c-.7 0-1.4.2-2 .5V6.2C4 4.4 5.3 3 7 3h18c.056 0 .112.002.167.005l1.42-1.42c.78-.78 2.046-.78 2.827 0 .78.782.78 2.048 0 2.83l-24 24c-.78.78-2.047.78-2.828 0-.78-.782-.78-2.048 0-2.83L4 24.173zM7.828 29l7.9-7.9H26c1.1 0 2 .9 2 2V27c0 1.1-.9 2-2 2H7.828zM28 8.828V19.5c-.6-.3-1.3-.5-2-.5h-8.172L28 8.828zM24.1 27c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2z" fill-rule="nonzero"/>
 </svg>
deleted file mode 100644
--- a/browser/themes/shared/notification-icons/persistent-storage-icon.inc.svg
+++ /dev/null
@@ -1,1 +0,0 @@
-<path id="persistent-storage-icon" d="M26 21.1H6c-1.1 0-2 .9-2 2V27c0 1.1.9 2 2 2h20c1.1 0 2-.9 2-2v-3.9c0-1.1-.9-2-2-2zM24.1 27c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zM25 3H7C5.3 3 4 4.4 4 6.2v13.3c.6-.3 1.3-.5 2-.5h20c.7 0 1.4.2 2 .5V6.2C28 4.4 26.7 3 25 3z"/>
--- a/browser/themes/shared/notification-icons/persistent-storage.svg
+++ b/browser/themes/shared/notification-icons/persistent-storage.svg
@@ -1,7 +1,6 @@
 <!-- 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 fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
-     width="32" height="32" viewBox="0 0 32 32">
-#include persistent-storage-icon.inc.svg
+<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg" width="32" height="32">
+  <path d="M26 21.1H6c-1.1 0-2 .9-2 2V27c0 1.1.9 2 2 2h20c1.1 0 2-.9 2-2v-3.9c0-1.1-.9-2-2-2zM24.1 27c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zM25 3H7C5.3 3 4 4.4 4 6.2v13.3c.6-.3 1.3-.5 2-.5h20c.7 0 1.4.2 2 .5V6.2C28 4.4 26.7 3 25 3z"/>
 </svg>
--- a/browser/themes/shared/notification-icons/plugin-blocked.svg
+++ b/browser/themes/shared/notification-icons/plugin-blocked.svg
@@ -1,12 +1,7 @@
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
-     width="32" height="32" viewBox="0 0 32 32">
-  <defs>
-#include clip-path.inc.svg
-#include plugin-icon.inc.svg
-  </defs>
-  <use clip-path="url(#blocked-clipPath)" href="#plugin-icon"/>
-#include strikeout.inc.svg
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+  <path d="M3 3h3a1 1 0 0 0 0-2H3a1 1 0 0 0 0 2zm11.707 1.293L5 14h9a1 1 0 0 0 1-1V5a1 1 0 0 0-.293-.707z"/>
+  <path d="M14.707 1.293a.986.986 0 0 0-1.207-.139A.973.973 0 0 0 13 1h-3a1 1 0 0 0 0 2h1.586l-1 1H2a1 1 0 0 0-1 1v8a.971.971 0 0 0 .154.5.986.986 0 0 0 .139 1.205 1 1 0 0 0 1.414 0l12-12a1 1 0 0 0 0-1.412z"/>
 </svg>
deleted file mode 100644
--- a/browser/themes/shared/notification-icons/plugin-icon.inc.svg
+++ /dev/null
@@ -1,1 +0,0 @@
-<path id="plugin-icon" d="m 2,26 a 2,2 0 0 0 2,2 l 24,0 a 2,2 0 0 0 2,-2 l 0,-16 a 2,2 0 0 0 -2,-2 l -24,0 a 2,2 0 0 0 -2,2 z m 2,-20 10,0 0,-2 a 2,2 0 0 0 -2,-2 l -6,0 a 2,2 0 0 0 -2,2 z m 14,0 10,0 0,-2 a 2,2 0 0 0 -2,-2 l -6,0 a 2,2 0 0 0 -2,2 z"/>
--- a/browser/themes/shared/notification-icons/plugin.svg
+++ b/browser/themes/shared/notification-icons/plugin.svg
@@ -1,7 +1,7 @@
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
-     width="32" height="32" viewBox="0 0 32 32">
-#include plugin-icon.inc.svg
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+  <rect x="1" y="4" width="14" height="10" rx="1" ry="1"/>
+  <path d="M6 1H3a1 1 0 0 0 0 2h3a1 1 0 0 0 0-2zm7 0h-3a1 1 0 0 0 0 2h3a1 1 0 0 0 0-2z"/>
 </svg>
--- a/browser/themes/shared/notification-icons/popup.svg
+++ b/browser/themes/shared/notification-icons/popup.svg
@@ -1,7 +1,7 @@
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
-     width="32" height="32" viewBox="0 0 32 32">
-  <path d="m 2,24 a 4,4 0 0 0 4,4 l 8,0 a 10,10 0 0 1 -2,-4 l -4,0 a 2,2 0 0 1 -2,-2 l 0,-12 18,0 0,2 a 10,10 0 0 1 4,2 l 0,-8 a 4,4 0 0 0 -4,-4 l -18,0 a 4,4 0 0 0 -4,4 z m 12,-2.1 a 8,8 0 1 1 0,0.2 m 10.7,-4.3 a 5,5 0 0 0 -6.9,6.9 z m -5.4,8.4 a 5,5 0 0 0 6.9,-6.9 z"/>
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+  <path d="M12 8a4 4 0 1 0 4 4 4 4 0 0 0-4-4zm0 1a2.975 2.975 0 0 1 1.733.56L9.56 13.733A2.991 2.991 0 0 1 12 9zm0 6a2.975 2.975 0 0 1-1.733-.56l4.173-4.173A2.991 2.991 0 0 1 12 15z"/>
+  <path d="M13 1H3a3 3 0 0 0-3 3v8a3 3 0 0 0 3 3h3a1 1 0 0 0 0-2H3a1 1 0 0 1-1-1V6h12a1 1 0 0 0 2 0V4a3 3 0 0 0-3-3zM2 5V4a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v1z"/>
 </svg>
--- a/browser/themes/shared/notification-icons/screen-blocked.svg
+++ b/browser/themes/shared/notification-icons/screen-blocked.svg
@@ -1,12 +1,7 @@
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
-     width="32" height="32" viewBox="0 0 32 32">
-  <defs>
-#include clip-path.inc.svg
-#include screen-icon.inc.svg
-  </defs>
-  <use clip-path="url(#blocked-clipPath)" href="#screen-icon"/>
-#include strikeout.inc.svg
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+  <path d="M4 10a1 1 0 0 0 0-2H2V3h6v1a1 1 0 0 0 2 0V2a1 1 0 0 0-1-1H1a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1zm11-4h-2l-2 2h3v5H8v-2l-2 2v1a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1z"/>
+  <path d="M14.707 1.293a1 1 0 0 0-1.414 0L8.586 6H7a1 1 0 0 0-1 1v1.586l-4.707 4.707a1 1 0 1 0 1.414 1.414l12-12a1 1 0 0 0 0-1.414z"/>
 </svg>
deleted file mode 100644
--- a/browser/themes/shared/notification-icons/screen-icon.inc.svg
+++ /dev/null
@@ -1,1 +0,0 @@
-<path id="screen-icon" d="m 2,18 a 2,2 0 0 0 2,2 l 2,0 0,-6 a 4,4 0 0 1 4,-4 l 14,0 0,-6 a 2,2 0 0 0 -2,-2 l -18,0 a 2,2 0 0 0 -2,2 z m 6,10 a 2,2 0 0 0 2,2 l 18,0 a 2,2 0 0 0 2,-2 l 0,-14 a 2,2 0 0 0 -2,-2 l -18,0 a 2,2 0 0 0 -2,2 z"/>
--- a/browser/themes/shared/notification-icons/screen.svg
+++ b/browser/themes/shared/notification-icons/screen.svg
@@ -1,7 +1,7 @@
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
-     width="32" height="32" viewBox="0 0 32 32">
-#include screen-icon.inc.svg
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+  <path d="M15 6H7a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1zm-1 7H8V8h6z"/>
+  <path d="M4 8H2V3h6v1a1 1 0 0 0 2 0V2a1 1 0 0 0-1-1H1a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h3a1 1 0 0 0 0-2z"/>
 </svg>
deleted file mode 100644
--- a/browser/themes/shared/notification-icons/strikeout.inc.svg
+++ /dev/null
@@ -1,1 +0,0 @@
-<path id="strikeout" d="m 2,28 2,2 26,-26 -2,-2 z"/>
--- a/browser/themes/shared/urlbar-searchbar.inc.css
+++ b/browser/themes/shared/urlbar-searchbar.inc.css
@@ -228,16 +228,22 @@
   list-style-image: url(chrome://browser/skin/readerMode.svg);
 }
 
 #reader-mode-button[readeractive] {
   fill: var(--toolbarbutton-icon-fill-attention);
   fill-opacity: 1;
 }
 
+/* Blocked popup icon */
+
+#page-report-button {
+  list-style-image: url(chrome://browser/skin/notification-icons/popup.svg);
+}
+
 /* Zoom button */
 
 #urlbar-zoom-button {
   margin: 0 3px;
   font-size: .8em;
   padding: 0 8px;
   border-radius: 1em;
   background-color: hsla(0,0%,0%,.05);
deleted file mode 100644
index f5dfb65a3f0c7ba09cdb64b5e1518b388445dc23..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -745,32 +745,16 @@ treechildren.searchbar-treebody::-moz-tr
   color: GrayText;
   font-size: smaller;
 }
 
 .autocomplete-treebody::-moz-tree-cell(suggesthint) {
   border-top: 1px solid GrayText;
 }
 
-/* popup blocker button */
-
-#page-report-button {
-  list-style-image: url("chrome://browser/skin/urlbar-popup-blocked.png");
-  -moz-image-region: rect(0, 16px, 16px, 0);
-}
-
-#page-report-button:hover {
-  -moz-image-region: rect(0, 32px, 16px, 16px);
-}
-
-#page-report-button:hover:active,
-#page-report-button[open="true"] {
-  -moz-image-region: rect(0, 48px, 16px, 32px);
-}
-
 /* bookmarking panel */
 
 #editBookmarkPanelStarIcon {
   list-style-image: url("chrome://browser/skin/places/starred48.png");
   width: 48px;
   height: 48px;
 }
 
@@ -1085,18 +1069,16 @@ notification[value="translation"] {
   */
   margin-left: 1em;
 }
 
 %include ../shared/fullscreen/warning.inc.css
 %include ../shared/ctrlTab.inc.css
 %include ../shared/plugin-doorhanger.inc.css
 
-%include downloads/indicator.css
-
 /* Error counter */
 
 #developer-toolbar-toolbox-button[error-count]:before {
   color: #FDF3DE;
   min-width: 16px;
   text-shadow: none;
   background-image: linear-gradient(#B4211B, #8A1915);
   border-radius: 1px;
deleted file mode 100644
index 7ff7e6a0330012061e5cf4477c196fdc766cecf0..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index d05b07b57fcc74749e7a04f0a39427a1f60763fe..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index e7415e83d8bbad00fcde9fb3926a2e51638eb6be..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 1239ada5273ee855ab939da76d6cad11583b24be..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
--- a/browser/themes/windows/downloads/indicator.css
+++ /dev/null
@@ -1,5 +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/. */
-
-%include ../../shared/downloads/indicator.inc.css
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -5,17 +5,16 @@
 browser.jar:
 % skin browser classic/1.0 %skin/classic/browser/
 #include ../shared/jar.inc.mn
   skin/classic/browser/sanitizeDialog.css
   skin/classic/browser/aboutSessionRestore-window-icon.png
 * skin/classic/browser/syncedtabs/sidebar.css     (syncedtabs/sidebar.css)
 * skin/classic/browser/browser.css
 * skin/classic/browser/compacttheme.css
-  skin/classic/browser/Info.png
   skin/classic/browser/livemark-folder.png
   skin/classic/browser/menu-back.png
   skin/classic/browser/menu-forward.png
   skin/classic/browser/menuPanel-customize.png
   skin/classic/browser/menuPanel-customize@2x.png
   skin/classic/browser/menuPanel-exit.png
   skin/classic/browser/menuPanel-exit@2x.png
   skin/classic/browser/menuPanel-help.png
@@ -34,32 +33,29 @@ browser.jar:
   skin/classic/browser/reload-stop-go-win7.png
   skin/classic/browser/reload-stop-go-win7@2x.png
   skin/classic/browser/searchbar.css
   skin/classic/browser/setDesktopBackground.css
   skin/classic/browser/slowStartup-16.png
   skin/classic/browser/sync-desktopIcon.svg  (../shared/sync-desktopIcon.svg)
   skin/classic/browser/sync-mobileIcon.svg  (../shared/sync-mobileIcon.svg)
   skin/classic/browser/toolbarbutton-dropdown-arrow-win7.png
-  skin/classic/browser/urlbar-popup-blocked.png
   skin/classic/browser/webRTC-indicator.css  (../shared/webRTC-indicator.css)
 * skin/classic/browser/controlcenter/panel.css                 (controlcenter/panel.css)
   skin/classic/browser/customizableui/menu-arrow.svg           (customizableui/menu-arrow.svg)
 * skin/classic/browser/customizableui/panelUI.css       (customizableui/panelUI.css)
 * skin/classic/browser/downloads/allDownloadsViewOverlay.css   (downloads/allDownloadsViewOverlay.css)
-  skin/classic/browser/downloads/download-glow-menuPanel.png   (downloads/download-glow-menuPanel.png)
-  skin/classic/browser/downloads/download-glow-menuPanel-win7.png   (downloads/download-glow-menuPanel-win7.png)
 * skin/classic/browser/downloads/downloads.css                 (downloads/downloads.css)
   skin/classic/browser/feeds/feedIcon.png                      (feeds/feedIcon.png)
   skin/classic/browser/feeds/feedIcon16.png                    (feeds/feedIcon16.png)
   skin/classic/browser/feeds/subscribe.css                     (feeds/subscribe.css)
 * skin/classic/browser/newtab/newTab.css                       (newtab/newTab.css)
-* skin/classic/browser/notification-icons/geo-blocked.svg      (notification-icons/geo-blocked.svg)
+  skin/classic/browser/notification-icons/geo-blocked.svg      (notification-icons/geo-blocked.svg)
   skin/classic/browser/notification-icons/geo-detailed.svg     (notification-icons/geo-detailed.svg)
-* skin/classic/browser/notification-icons/geo.svg              (notification-icons/geo.svg)
+  skin/classic/browser/notification-icons/geo.svg              (notification-icons/geo.svg)
 * skin/classic/browser/places/places.css                       (places/places.css)
 * skin/classic/browser/places/organizer.css                    (places/organizer.css)
   skin/classic/browser/places/query.png                        (places/query.png)
   skin/classic/browser/places/bookmarksMenu.png                (places/bookmarksMenu.png)
   skin/classic/browser/places/bookmarksToolbar.png             (places/bookmarksToolbar.png)
   skin/classic/browser/places/bookmarksToolbar-menuPanel.png   (places/bookmarksToolbar-menuPanel.png)
   skin/classic/browser/places/calendar.png                     (places/calendar.png)
   skin/classic/browser/places/toolbarDropMarker.png            (places/toolbarDropMarker.png)
@@ -106,16 +102,15 @@ browser.jar:
 % override chrome://browser/skin/feeds/videoFeedIcon.png              chrome://browser/skin/feeds/feedIcon.png
 % override chrome://browser/skin/feeds/videoFeedIcon16.png            chrome://browser/skin/feeds/feedIcon16.png
 
 % override chrome://browser/skin/privatebrowsing-mask-tabstrip.png    chrome://browser/skin/privatebrowsing-mask-tabstrip-win7.png  os=WINNT osversion<=6.1
 % override chrome://browser/skin/privatebrowsing-mask-titlebar.png    chrome://browser/skin/privatebrowsing-mask-titlebar-win7.png  os=WINNT osversion<=6.1
 % override chrome://browser/skin/reload-stop-go.png                   chrome://browser/skin/reload-stop-go-win7.png                 os=WINNT osversion<=6.1
 % override chrome://browser/skin/reload-stop-go@2x.png                chrome://browser/skin/reload-stop-go-win7@2x.png              os=WINNT osversion<=6.1
 % override chrome://browser/skin/toolbarbutton-dropdown-arrow.png     chrome://browser/skin/toolbarbutton-dropdown-arrow-win7.png   os=WINNT osversion<=6.1
-% override chrome://browser/skin/downloads/download-glow-menuPanel.png  chrome://browser/skin/downloads/download-glow-menuPanel-win7.png os=WINNT osversion<=6.1
 
 % override chrome://browser/skin/tabbrowser/tab-background-start.png     chrome://browser/skin/tabbrowser/tab-background-start-preWin10.png     os=WINNT osversion<=6.3
 % override chrome://browser/skin/tabbrowser/tab-background-start@2x.png  chrome://browser/skin/tabbrowser/tab-background-start-preWin10@2x.png  os=WINNT osversion<=6.3
 % override chrome://browser/skin/tabbrowser/tab-background-middle.png    chrome://browser/skin/tabbrowser/tab-background-middle-preWin10.png    os=WINNT osversion<=6.3
 % override chrome://browser/skin/tabbrowser/tab-background-middle@2x.png chrome://browser/skin/tabbrowser/tab-background-middle-preWin10@2x.png os=WINNT osversion<=6.3
 % override chrome://browser/skin/tabbrowser/tab-background-end.png       chrome://browser/skin/tabbrowser/tab-background-end-preWin10.png       os=WINNT osversion<=6.3
 % override chrome://browser/skin/tabbrowser/tab-background-end@2x.png    chrome://browser/skin/tabbrowser/tab-background-end-preWin10@2x.png    os=WINNT osversion<=6.3
--- a/browser/themes/windows/notification-icons/geo-blocked.svg
+++ b/browser/themes/windows/notification-icons/geo-blocked.svg
@@ -1,12 +1,6 @@
 <!-- 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 fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
-     width="32" height="32" viewBox="0 0 32 32">
-  <defs>
-#include ../../shared/notification-icons/clip-path.inc.svg
-#include geo-icon.inc.svg
-  </defs>
-  <use clip-path="url(#blocked-clipPath)" href="#geo-windows-icon"/>
-#include ../../shared/notification-icons/strikeout.inc.svg
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+  <path d="M14 7h-.091a5.931 5.931 0 0 0-.458-1.451l-1.559 1.559a3.972 3.972 0 0 1-4.784 4.784l-1.56 1.56A5.923 5.923 0 0 0 7 13.91V14a1 1 0 0 0 2 0v-.09A6 6 0 0 0 13.909 9H14a1 1 0 0 0 0-2zm.707-5.707a1 1 0 0 0-1.414 0l-1.826 1.826A5.955 5.955 0 0 0 9 2.09V2a1 1 0 0 0-2 0v.09A6 6 0 0 0 2.091 7H2a1 1 0 0 0 0 2h.091a5.963 5.963 0 0 0 1.029 2.467l-1.827 1.826a1 1 0 1 0 1.414 1.414l12-12a1 1 0 0 0 0-1.414zM4 8a3.976 3.976 0 0 1 6.02-3.434L8.512 6.074a1.974 1.974 0 0 0-2.438 2.438L4.566 10.02A3.961 3.961 0 0 1 4 8z"/>
 </svg>
deleted file mode 100644
--- a/browser/themes/windows/notification-icons/geo-icon.inc.svg
+++ /dev/null
@@ -1,1 +0,0 @@
-<path id="geo-windows-icon" d="m 2,14 0,4 2,0 a 12,12 0 0 0 10,10 l 0,2 4,0 0,-2 a 12,12 0 0 0 10,-10 l 2,0 0,-4 -2,0 a 12,12 0 0 0 -10,-10 l 0,-2 -4,0 0,2 a 12,12 0 0 0 -10,10 z m 4,1.9 a 10,10 0 1 1 0,0.2 z m 4,0 a 6,6 0 1 1 0,0.2 z"/>
--- a/browser/themes/windows/notification-icons/geo.svg
+++ b/browser/themes/windows/notification-icons/geo.svg
@@ -1,7 +1,6 @@
 <!-- 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 fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
-     width="32" height="32" viewBox="0 0 32 32">
-#include geo-icon.inc.svg
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+  <path d="M14 7h-.091A6 6 0 0 0 9 2.09V2a1 1 0 0 0-2 0v.09A6 6 0 0 0 2.091 7H2a1 1 0 0 0 0 2h.091A6 6 0 0 0 7 13.91V14a1 1 0 0 0 2 0v-.09A6 6 0 0 0 13.909 9H14a1 1 0 0 0 0-2zm-6 5a4 4 0 1 1 4-4 4 4 0 0 1-4 4zm0-6a2 2 0 1 0 2 2 2 2 0 0 0-2-2z"/>
 </svg>
deleted file mode 100644
index 56dbd2d0d2e266f864bf055cc2ab65383e790bc2..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
--- a/devtools/client/inspector/markup/test/browser.ini
+++ b/devtools/client/inspector/markup/test/browser.ini
@@ -78,17 +78,16 @@ skip-if = os == "mac" # Full keyboard na
 skip-if = os == "mac" # Full keyboard navigation on OSX only works if Full Keyboard Access setting is set to All Control in System Keyboard Preferences
 [browser_markup_accessibility_semantics.js]
 [browser_markup_anonymous_01.js]
 [browser_markup_anonymous_02.js]
 skip-if = e10s # scratchpad.xul is not loading in e10s window
 [browser_markup_anonymous_03.js]
 skip-if = stylo # Stylo doesn't support shadow DOM yet, bug 1293844
 [browser_markup_anonymous_04.js]
-skip-if = stylo && debug # Bug 1386865 - Triggers assertion
 [browser_markup_copy_image_data.js]
 subsuite = clipboard
 skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts
 [browser_markup_css_completion_style_attribute_01.js]
 [browser_markup_css_completion_style_attribute_02.js]
 [browser_markup_css_completion_style_attribute_03.js]
 [browser_markup_dragdrop_autoscroll_01.js]
 [browser_markup_dragdrop_autoscroll_02.js]
--- a/devtools/client/locales/en-US/webconsole.properties
+++ b/devtools/client/locales/en-US/webconsole.properties
@@ -292,8 +292,20 @@ webconsole.cssFilterButton.label=CSS
 # a fetch call.
 webconsole.xhrFilterButton.label=XHR
 
 # LOCALIZATION NOTE (webconsole.requestsFilterButton.label)
 # Label used as the text of the "Requests" button in the additional filter toolbar.
 # It shows or hides messages displayed when the page makes a network call, for example
 # when an image or a scripts is requested.
 webconsole.requestsFilterButton.label=Requests
+
+# LOCALIZATION NOTE (webconsole.filteredMessages.label)
+# Text of the "filtered messages" bar, shown when console messages are hidden
+# because the user has set non-default filters in the filter bar.
+# This is a semi-colon list of plural forms.
+# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
+# example: 345 items hidden by filters.
+webconsole.filteredMessages.label=#1 item hidden by filters;#1 items hidden by filters
+
+# Label used as the text of the "Reset filters" button in the "filtered messages" bar.
+# It resets the default filters of the console to their original values.
+webconsole.resetFiltersButton.label=Reset filters
--- a/devtools/client/themes/webconsole.css
+++ b/devtools/client/themes/webconsole.css
@@ -728,37 +728,86 @@ a.learn-more-link.webconsole-learn-more-
 
 .webconsole-output-wrapper {
   display: flex;
   flex-direction: column;
   height: 100%;
   -moz-user-focus: normal;
 }
 
-.webconsole-filterbar-wrapper {
-  flex-grow: 0;
+/*
+  This element contains the different toolbars in the console
+    - primary, containing the clear messages button and the text search input.
+      It can expand as much as it need.
+    - filtered messages, containing the "X items hidden by filters" and the reset filters button.
+      It should be on the same row than the primary bar if it fits there, or on its own 100% row if it is wrapped.
+    - secondary, containing the filter buttons (Error, Warning, …).
+      It should be on its own 100% row.
+
+  Basically here's what we can have :
+
+  -----------------------------------------------------------------------------------------------------------
+  | Clear button - Open filter bar button - Filter Input | X items hidden by filters - Reset Filters button |
+  -----------------------------------------------------------------------------------------------------------
+  | Error - Warning - Log - Info - Debug - CSS - Network - XHR                                              |
+  -----------------------------------------------------------------------------------------------------------
+
+  or
+
+  ------------------------------------------------------------------------------------
+  | Clear button - Open filter bar button - Filter Input                             |
+  ------------------------------------------------------------------------------------
+  |                                X items hidden by filters  - Reset Filters button |
+  ------------------------------------------------------------------------------------
+  | Error - Warning - Log - Info - Debug - CSS - Network - XHR                       |
+  ------------------------------------------------------------------------------------
+*/
+.webconsole-filteringbar-wrapper {
+  display: flex;
+  /* Wrap so the "Hidden messages" bar can go to its own row if needed */
+  flex-wrap: wrap;
+}
+
+.webconsole-filterbar-primary {
+  display: flex;
+  /* We want the toolbar (which contain the text search input) to be at least 200px
+  so we don't allow to shrink it */
+  flex: 1 0 200px;
+}
+
+.devtools-toolbar.webconsole-filterbar-secondary {
+  height: initial;
+  /* This toolbar should always take the whole horizontal space */
+  width: 100%;
+}
+
+.webconsole-filterbar-primary .devtools-plaininput {
+  flex: 1 1 100%;
+}
+
+.webconsole-filterbar-filtered-messages {
+  /* Needed so the bar takes the whole horizontal space when it is wrapped */
+  flex-grow: 1;
+  color: var(--theme-comment);
+  text-align: end;
+}
+
+.webconsole-filterbar-filtered-messages .filter-message-text {
+  font-style: italic;
+}
+
+.webconsole-filterbar-filtered-messages .reset-filters-button {
+  margin-inline-start: 0.5em;
 }
 
 .webconsole-output {
   flex: 1;
   overflow: auto;
 }
 
-.webconsole-filterbar-primary {
-  display: flex;
-}
-
-.devtools-toolbar.webconsole-filterbar-secondary {
-  height: initial;
-}
-
-.webconsole-filterbar-primary .devtools-plaininput {
-  flex: 1 1 100%;
-}
-
 .webconsole-output-wrapper .message {
   --border-size: 3px;
   border-inline-start: var(--border-size) solid transparent;
 }
 
 .webconsole-output-wrapper .message:hover {
   border-inline-start-color: #0a84ff; /* Photon Design System Blue 50 */
 }
--- a/devtools/client/webconsole/new-console-output/actions/filters.js
+++ b/devtools/client/webconsole/new-console-output/actions/filters.js
@@ -8,17 +8,20 @@
 
 const { getAllFilters } = require("devtools/client/webconsole/new-console-output/selectors/filters");
 const Services = require("Services");
 
 const {
   FILTER_TEXT_SET,
   FILTER_TOGGLE,
   FILTERS_CLEAR,
+  DEFAULT_FILTERS_RESET,
   PREFS,
+  FILTERS,
+  DEFAULT_FILTERS,
 } = require("devtools/client/webconsole/new-console-output/constants");
 
 function filterTextSet(text) {
   return {
     type: FILTER_TEXT_SET,
     text
   };
 }
@@ -38,18 +41,42 @@ function filterToggle(filter) {
 function filtersClear() {
   return (dispatch, getState) => {
     dispatch({
       type: FILTERS_CLEAR,
     });
 
     const filterState = getAllFilters(getState());
     for (let filter in filterState) {
-      Services.prefs.clearUserPref(PREFS.FILTER[filter.toUpperCase()]);
+      if (filter !== FILTERS.TEXT) {
+        Services.prefs.clearUserPref(PREFS.FILTER[filter.toUpperCase()]);
+      }
     }
   };
 }
 
+/**
+ * Set the default filters to their original values.
+ * This is different than filtersClear where we reset
+ * all the filters to their original values. Here we want
+ * to keep non-default filters the user might have set.
+ */
+function defaultFiltersReset() {
+  return (dispatch, getState) => {
+    dispatch({
+      type: DEFAULT_FILTERS_RESET,
+    });
+
+    const filterState = getAllFilters(getState());
+    DEFAULT_FILTERS.forEach(filter => {
+      if (filterState[filter] === false) {
+        Services.prefs.clearUserPref(PREFS.FILTER[filter.toUpperCase()]);
+      }
+    });
+  };
+}
+
 module.exports = {
   filterTextSet,
   filterToggle,
-  filtersClear
+  filtersClear,
+  defaultFiltersReset,
 };
--- a/devtools/client/webconsole/new-console-output/components/filter-bar.js
+++ b/devtools/client/webconsole/new-console-output/components/filter-bar.js
@@ -5,139 +5,243 @@
 
 const {
   createClass,
   DOM: dom,
   PropTypes
 } = require("devtools/client/shared/vendor/react");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 const { getAllFilters } = require("devtools/client/webconsole/new-console-output/selectors/filters");
+const { getFilteredMessagesCount } = require("devtools/client/webconsole/new-console-output/selectors/messages");
 const { getAllUi } = require("devtools/client/webconsole/new-console-output/selectors/ui");
 const {
+  filterBarToggle,
+  defaultFiltersReset,
   filterTextSet,
-  filterBarToggle,
-  messagesClear
+  messagesClear,
 } = require("devtools/client/webconsole/new-console-output/actions/index");
 const { l10n } = require("devtools/client/webconsole/new-console-output/utils/messages");
+const { PluralForm } = require("devtools/shared/plural-form");
 const {
-  MESSAGE_LEVEL
+  DEFAULT_FILTERS,
+  FILTERS,
 } = require("../constants");
+
 const FilterButton = require("devtools/client/webconsole/new-console-output/components/filter-button");
 
 const FilterBar = createClass({
 
   displayName: "FilterBar",
 
   propTypes: {
     dispatch: PropTypes.func.isRequired,
     filter: PropTypes.object.isRequired,
     serviceContainer: PropTypes.shape({
       attachRefToHud: PropTypes.func.isRequired,
     }).isRequired,
     filterBarVisible: PropTypes.bool.isRequired,
+    filteredMessagesCount: PropTypes.object.isRequired,
+  },
+
+  shouldComponentUpdate(nextProps, nextState) {
+    if (nextProps.filter !== this.props.filter) {
+      return true;
+    }
+
+    if (nextProps.filterBarVisible !== this.props.filterBarVisible) {
+      return true;
+    }
+
+    if (
+      JSON.stringify(nextProps.filteredMessagesCount)
+      !== JSON.stringify(this.props.filteredMessagesCount)
+    ) {
+      return true;
+    }
+
+    return false;
   },
 
   componentDidMount() {
     this.props.serviceContainer.attachRefToHud("filterBox",
       this.wrapperNode.querySelector(".text-filter"));
   },
 
   onClickMessagesClear: function () {
     this.props.dispatch(messagesClear());
   },
 
   onClickFilterBarToggle: function () {
     this.props.dispatch(filterBarToggle());
   },
 
+  onClickRemoveAllFilters: function () {
+    this.props.dispatch(defaultFiltersReset());
+  },
+
+  onClickRemoveTextFilter: function () {
+    this.props.dispatch(filterTextSet(""));
+  },
+
   onSearchInput: function (e) {
     this.props.dispatch(filterTextSet(e.target.value));
   },
 
-  render() {
-    const {dispatch, filter, filterBarVisible} = this.props;
-    let children = [];
+  renderFiltersConfigBar() {
+    const {
+      dispatch,
+      filter,
+      filteredMessagesCount,
+    } = this.props;
+
+    const getLabel = (baseLabel, filterKey) => {
+      const count = filteredMessagesCount[filterKey];
+      if (filter[filterKey] || count === 0) {
+        return baseLabel;
+      }
+      return `${baseLabel} (${count})`;
+    };
 
-    children.push(dom.div({className: "devtools-toolbar webconsole-filterbar-primary"},
-      dom.button({
-        className: "devtools-button devtools-clear-icon",
-        title: l10n.getStr("webconsole.clearButton.tooltip"),
-        onClick: this.onClickMessagesClear
+    return dom.div({
+      className: "devtools-toolbar webconsole-filterbar-secondary",
+      key: "config-bar",
+    },
+      FilterButton({
+        active: filter[FILTERS.ERROR],
+        label: getLabel(
+          l10n.getStr("webconsole.errorsFilterButton.label"),
+          FILTERS.ERROR
+        ),
+        filterKey: FILTERS.ERROR,
+        dispatch
+      }),
+      FilterButton({
+        active: filter[FILTERS.WARN],
+        label: getLabel(
+          l10n.getStr("webconsole.warningsFilterButton.label"),
+          FILTERS.WARN
+        ),
+        filterKey: FILTERS.WARN,
+        dispatch
+      }),
+      FilterButton({
+        active: filter[FILTERS.LOG],
+        label: getLabel(l10n.getStr("webconsole.logsFilterButton.label"), FILTERS.LOG),
+        filterKey: FILTERS.LOG,
+        dispatch
+      }),
+      FilterButton({
+        active: filter[FILTERS.INFO],
+        label: getLabel(l10n.getStr("webconsole.infoFilterButton.label"), FILTERS.INFO),
+        filterKey: FILTERS.INFO,
+        dispatch
+      }),
+      FilterButton({
+        active: filter[FILTERS.DEBUG],
+        label: getLabel(l10n.getStr("webconsole.debugFilterButton.label"), FILTERS.DEBUG),
+        filterKey: FILTERS.DEBUG,
+        dispatch
+      }),
+      dom.span({
+        className: "devtools-separator",
       }),
+      FilterButton({
+        active: filter[FILTERS.CSS],
+        label: l10n.getStr("webconsole.cssFilterButton.label"),
+        filterKey: FILTERS.CSS,
+        dispatch
+      }),
+      FilterButton({
+        active: filter[FILTERS.NETXHR],
+        label: l10n.getStr("webconsole.xhrFilterButton.label"),
+        filterKey: FILTERS.NETXHR,
+        dispatch
+      }),
+      FilterButton({
+        active: filter[FILTERS.NET],
+        label: l10n.getStr("webconsole.requestsFilterButton.label"),
+        filterKey: FILTERS.NET,
+        dispatch
+      })
+    );
+  },
+
+  renderFilteredMessagesBar() {
+    const {
+      filteredMessagesCount
+    } = this.props;
+    const {
+      global,
+    } = filteredMessagesCount;
+
+    let label = l10n.getStr("webconsole.filteredMessages.label");
+    label = PluralForm.get(global, label).replace("#1", global);
+
+    // Include all default filters that are hiding messages.
+    let title = DEFAULT_FILTERS.reduce((res, filter) => {
+      if (filteredMessagesCount[filter] > 0) {
+        return res.concat(`${filter}: ${filteredMessagesCount[filter]}`);
+      }
+      return res;
+    }, []).join(", ");
+
+    return dom.div({
+      className: "devtools-toolbar webconsole-filterbar-filtered-messages",
+      key: "filtered-messages-bar",
+    },
+      dom.span({
+        className: "filter-message-text",
+        title,
+      }, label),
       dom.button({
-        className: "devtools-button devtools-filter-icon" + (
-          filterBarVisible ? " checked" : ""),
-        title: l10n.getStr("webconsole.toggleFilterButton.tooltip"),
-        onClick: this.onClickFilterBarToggle
-      }),
-      dom.input({
-        className: "devtools-plaininput text-filter",
-        type: "search",
-        value: filter.text,
-        placeholder: l10n.getStr("webconsole.filterInput.placeholder"),
-        onInput: this.onSearchInput
-      })
-    ));
+        className: "devtools-button reset-filters-button",
+        onClick: this.onClickRemoveAllFilters
+      }, l10n.getStr("webconsole.resetFiltersButton.label"))
+    );
+  },
+
+  render() {
+    const {
+      filter,
+      filterBarVisible,
+      filteredMessagesCount,
+    } = this.props;
+
+    let children = [
+      dom.div({
+        className: "devtools-toolbar webconsole-filterbar-primary",
+        key: "primary-bar",
+      },
+        dom.button({
+          className: "devtools-button devtools-clear-icon",
+          title: l10n.getStr("webconsole.clearButton.tooltip"),
+          onClick: this.onClickMessagesClear
+        }),
+        dom.button({
+          className: "devtools-button devtools-filter-icon" + (
+            filterBarVisible ? " checked" : ""),
+          title: l10n.getStr("webconsole.toggleFilterButton.tooltip"),
+          onClick: this.onClickFilterBarToggle
+        }),
+        dom.input({
+          className: "devtools-plaininput text-filter",
+          type: "search",
+          value: filter.text,
+          placeholder: l10n.getStr("webconsole.filterInput.placeholder"),
+          onInput: this.onSearchInput
+        })
+      )
+    ];
+
+    if (filteredMessagesCount.global > 0) {
+      children.push(this.renderFilteredMessagesBar());
+    }
 
     if (filterBarVisible) {
-      children.push(
-        dom.div({className: "devtools-toolbar webconsole-filterbar-secondary"},
-          FilterButton({
-            active: filter.error,
-            label: l10n.getStr("webconsole.errorsFilterButton.label"),
-            filterKey: MESSAGE_LEVEL.ERROR,
-            dispatch
-          }),
-          FilterButton({
-            active: filter.warn,
-            label: l10n.getStr("webconsole.warningsFilterButton.label"),
-            filterKey: MESSAGE_LEVEL.WARN,
-            dispatch
-          }),
-          FilterButton({
-            active: filter.log,
-            label: l10n.getStr("webconsole.logsFilterButton.label"),
-            filterKey: MESSAGE_LEVEL.LOG,
-            dispatch
-          }),
-          FilterButton({
-            active: filter.info,
-            label: l10n.getStr("webconsole.infoFilterButton.label"),
-            filterKey: MESSAGE_LEVEL.INFO,
-            dispatch
-          }),
-          FilterButton({
-            active: filter.debug,
-            label: l10n.getStr("webconsole.debugFilterButton.label"),
-            filterKey: MESSAGE_LEVEL.DEBUG,
-            dispatch
-          }),
-          dom.span({
-            className: "devtools-separator",
-          }),
-          FilterButton({
-            active: filter.css,
-            label: l10n.getStr("webconsole.cssFilterButton.label"),
-            filterKey: "css",
-            dispatch
-          }),
-          FilterButton({
-            active: filter.netxhr,
-            label: l10n.getStr("webconsole.xhrFilterButton.label"),
-            filterKey: "netxhr",
-            dispatch
-          }),
-          FilterButton({
-            active: filter.net,
-            label: l10n.getStr("webconsole.requestsFilterButton.label"),
-            filterKey: "net",
-            dispatch
-          })
-        )
-      );
+      children.push(this.renderFiltersConfigBar());
     }
 
     return (
       dom.div({
         className: "webconsole-filteringbar-wrapper",
         ref: node => {
           this.wrapperNode = node;
         }
@@ -146,12 +250,13 @@ const FilterBar = createClass({
     );
   }
 });
 
 function mapStateToProps(state) {
   return {
     filter: getAllFilters(state),
     filterBarVisible: getAllUi(state).filterBarVisible,
+    filteredMessagesCount: getFilteredMessagesCount(state),
   };
 }
 
 module.exports = connect(mapStateToProps)(FilterBar);
--- a/devtools/client/webconsole/new-console-output/constants.js
+++ b/devtools/client/webconsole/new-console-output/constants.js
@@ -15,16 +15,17 @@ const actionTypes = {
   MESSAGE_TABLE_RECEIVE: "MESSAGE_TABLE_RECEIVE",
   MESSAGE_OBJECT_PROPERTIES_RECEIVE: "MESSAGE_OBJECT_PROPERTIES_RECEIVE",
   MESSAGE_OBJECT_ENTRIES_RECEIVE: "MESSAGE_OBJECT_ENTRIES_RECEIVE",
   REMOVED_ACTORS_CLEAR: "REMOVED_ACTORS_CLEAR",
   TIMESTAMPS_TOGGLE: "TIMESTAMPS_TOGGLE",
   FILTER_TOGGLE: "FILTER_TOGGLE",
   FILTER_TEXT_SET: "FILTER_TEXT_SET",
   FILTERS_CLEAR: "FILTERS_CLEAR",
+  DEFAULT_FILTERS_RESET: "DEFAULT_FILTERS_RESET",
   FILTER_BAR_TOGGLE: "FILTER_BAR_TOGGLE",
 };
 
 const prefs = {
   PREFS: {
     FILTER: {
       ERROR: "devtools.webconsole.filter.error",
       WARN: "devtools.webconsole.filter.warn",
@@ -36,16 +37,43 @@ const prefs = {
       NETXHR: "devtools.webconsole.filter.netxhr",
     },
     UI: {
       FILTER_BAR: "devtools.webconsole.ui.filterbar"
     }
   }
 };
 
+const FILTERS = {
+  CSS: "css",
+  DEBUG: "debug",
+  ERROR: "error",
+  INFO: "info",
+  LOG: "log",
+  NET: "net",
+  NETXHR: "netxhr",
+  TEXT: "text",
+  WARN: "warn",
+};
+
+const DEFAULT_FILTERS_VALUES = {
+  [FILTERS.TEXT]: "",
+  [FILTERS.ERROR]: true,
+  [FILTERS.WARN]: true,
+  [FILTERS.LOG]: true,
+  [FILTERS.INFO]: true,
+  [FILTERS.DEBUG]: true,
+  [FILTERS.CSS]: false,
+  [FILTERS.NET]: false,
+  [FILTERS.NETXHR]: false,
+};
+
+const DEFAULT_FILTERS = Object.keys(DEFAULT_FILTERS_VALUES)
+  .filter(filter => DEFAULT_FILTERS_VALUES[filter] !== false);
+
 const chromeRDPEnums = {
   MESSAGE_SOURCE: {
     XML: "xml",
     CSS: "css",
     JAVASCRIPT: "javascript",
     NETWORK: "network",
     CONSOLE_API: "console-api",
     STORAGE: "storage",
@@ -87,14 +115,18 @@ const chromeRDPEnums = {
 
 const jstermCommands = {
   JSTERM_COMMANDS: {
     INSPECT: "inspectObject"
   }
 };
 
 // Combine into a single constants object
-module.exports = Object.assign({},
+module.exports = Object.assign({
+  FILTERS,
+  DEFAULT_FILTERS,
+  DEFAULT_FILTERS_VALUES,
+},
   actionTypes,
   chromeRDPEnums,
   jstermCommands,
   prefs,
 );
--- a/devtools/client/webconsole/new-console-output/reducers/filters.js
+++ b/devtools/client/webconsole/new-console-output/reducers/filters.js
@@ -3,38 +3,34 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const Immutable = require("devtools/client/shared/vendor/immutable");
 const constants = require("devtools/client/webconsole/new-console-output/constants");
 
-const FilterState = Immutable.Record({
-  css: false,
-  debug: true,
-  error: true,
-  info: true,
-  log: true,
-  net: false,
-  netxhr: false,
-  text: "",
-  warn: true,
-});
+const FilterState = Immutable.Record(constants.DEFAULT_FILTERS_VALUES);
 
 function filters(state = new FilterState(), action) {
   switch (action.type) {
     case constants.FILTER_TOGGLE:
       const {filter} = action;
       const active = !state.get(filter);
       return state.set(filter, active);
     case constants.FILTERS_CLEAR:
       return new FilterState();
+    case constants.DEFAULT_FILTERS_RESET:
+      return state.withMutations(record => {
+        constants.DEFAULT_FILTERS.forEach(filterName => {
+          record.set(filterName, constants.DEFAULT_FILTERS_VALUES[filterName]);
+        });
+      });
     case constants.FILTER_TEXT_SET:
       let {text} = action;
-      return state.set("text", text);
+      return state.set(constants.FILTERS.TEXT, text);
   }
 
   return state;
 }
 
 exports.FilterState = FilterState;
 exports.filters = filters;
--- a/devtools/client/webconsole/new-console-output/reducers/messages.js
+++ b/devtools/client/webconsole/new-console-output/reducers/messages.js
@@ -1,32 +1,39 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const Immutable = require("devtools/client/shared/vendor/immutable");
-const { l10n } = require("devtools/client/webconsole/new-console-output/utils/messages");
+
+const {
+  isGroupType,
+  l10n,
+} = require("devtools/client/webconsole/new-console-output/utils/messages");
 
 const constants = require("devtools/client/webconsole/new-console-output/constants");
-const {isGroupType} = require("devtools/client/webconsole/new-console-output/utils/messages");
 const {
+  DEFAULT_FILTERS,
+  FILTERS,
   MESSAGE_TYPE,
-  MESSAGE_SOURCE
-} = require("devtools/client/webconsole/new-console-output/constants");
+  MESSAGE_SOURCE,
+} = constants;
 const { getGripPreviewItems } = require("devtools/client/shared/components/reps/reps");
 const { getSourceNames } = require("devtools/client/shared/source-utils");
 
 const MessageState = Immutable.Record({
   // List of all the messages added to the console.
   messagesById: Immutable.OrderedMap(),
   // Array of the visible messages.
   visibleMessages: [],
+  // Object for the filtered messages.
+  filteredMessagesCount: getDefaultFiltersCounter(),
   // List of the message ids which are opened.
   messagesUiById: Immutable.List(),
   // Map of the form {messageId : tableData}, which represent the data passed
   // as an argument in console.table calls.
   messagesTableDataById: Immutable.Map(),
   // Map of the form {messageId : {[actor]: properties}}, where `properties` is
   // a RDP packet containing the properties of the ${actor} grip.
   // This map is consumed by the ObjectInspector so we only load properties once,
@@ -60,16 +67,17 @@ function messages(state = new MessageSta
     messagesTableDataById,
     messagesObjectPropertiesById,
     messagesObjectEntriesById,
     networkMessagesUpdateById,
     groupsById,
     currentGroup,
     repeatById,
     visibleMessages,
+    filteredMessagesCount,
   } = state;
 
   const {logLimit} = prefsState;
 
   switch (action.type) {
     case constants.MESSAGE_ADD:
       let newMessage = action.message;
 
@@ -118,18 +126,28 @@ function messages(state = new MessageSta
           record.set("groupsById", groupsById.set(newMessage.id, parentGroups));
 
           if (newMessage.type === constants.MESSAGE_TYPE.START_GROUP) {
             // We want the group to be open by default.
             record.set("messagesUiById", messagesUiById.push(newMessage.id));
           }
         }
 
-        if (shouldMessageBeVisible(addedMessage, record, filtersState)) {
+        const {
+          visible,
+          cause
+        } = getMessageVisibility(addedMessage, record, filtersState);
+
+        if (visible) {
           record.set("visibleMessages", [...visibleMessages, newMessage.id]);
+        } else if (DEFAULT_FILTERS.includes(cause)) {
+          record.set("filteredMessagesCount", Object.assign({}, filteredMessagesCount, {
+            global: filteredMessagesCount.global + 1,
+            [cause]: filteredMessagesCount[cause] + 1
+          }));
         }
 
         // Remove top level message if the total count of top level messages
         // exceeds the current limit.
         if (record.messagesById.size > logLimit) {
           limitTopLevelMessageCount(state, record, logLimit);
         }
       });
@@ -150,24 +168,24 @@ function messages(state = new MessageSta
 
         // If the message is a group
         if (isGroupType(messagesById.get(action.id).type)) {
           // We want to make its children visible
           const messagesToShow = [...messagesById].reduce((res, [id, message]) => {
             if (
               !visibleMessages.includes(message.id)
               && getParentGroups(message.groupId, groupsById).includes(action.id)
-              && shouldMessageBeVisible(
+              && getMessageVisibility(
                 message,
                 record,
                 filtersState,
                 // We want to check if the message is in an open group
                 // only if it is not a direct child of the group we're opening.
                 message.groupId !== action.id
-              )
+              ).visible
             ) {
               res.push(id);
             }
             return res;
           }, []);
 
           // We can then insert the messages ids right after the one of the group.
           const insertIndex = visibleMessages.indexOf(action.id) + 1;
@@ -232,25 +250,37 @@ function messages(state = new MessageSta
         })
       );
 
     case constants.REMOVED_ACTORS_CLEAR:
       return state.set("removedActors", []);
 
     case constants.FILTER_TOGGLE:
     case constants.FILTER_TEXT_SET:
-      return state.set(
-        "visibleMessages",
-        [...messagesById].reduce((res, [messageId, message]) => {
-          if (shouldMessageBeVisible(message, state, filtersState)) {
-            res.push(messageId);
+    case constants.FILTERS_CLEAR:
+    case constants.DEFAULT_FILTERS_RESET:
+      return state.withMutations(function (record) {
+        const messagesToShow = [];
+        const filtered = getDefaultFiltersCounter();
+        messagesById.forEach((message, messageId) => {
+          const {
+            visible,
+            cause
+          } = getMessageVisibility(message, state, filtersState);
+          if (visible) {
+            messagesToShow.push(messageId);
+          } else if (DEFAULT_FILTERS.includes(cause)) {
+            filtered.global = filtered.global + 1;
+            filtered[cause] = filtered[cause] + 1;
           }
-          return res;
-        }, [])
-      );
+        });
+
+        record.set("visibleMessages", messagesToShow);
+        record.set("filteredMessagesCount", filtered);
+      });
   }
 
   return state;
 }
 
 function getNewCurrentGroup(currentGoup, groupsById) {
   let newCurrentGroup = null;
   if (currentGoup) {
@@ -427,66 +457,83 @@ function getAllActorsInMessage(message, 
  * Returns total count of top level messages (those which are not
  * within a group).
  */
 function getToplevelMessageCount(record) {
   return record.messagesById.count(message => !message.groupId);
 }
 
 /**
- * Returns true if given message should be visible, false otherwise.
+ * Check if a message should be visible in the console output, and if not, what
+ * causes it to be hidden.
+ *
+ * @return {Object} An object of the following form:
+ *         - visible {Boolean}: true if the message should be visible
+ *         - cause {String}: if visible is false, what causes the message to be hidden.
  */
-function shouldMessageBeVisible(message, messagesState, filtersState, checkGroup = true) {
+function getMessageVisibility(message, messagesState, filtersState, checkGroup = true) {
   // Do not display the message if it's in closed group.
   if (
     checkGroup
     && !isInOpenedGroup(message, messagesState.groupsById, messagesState.messagesUiById)
   ) {
-    return false;
+    return {
+      visible: false,
+      cause: "closedGroup"
+    };
   }
 
   // Some messages can't be filtered out (e.g. groups).
-  // So, always return true for those.
+  // So, always return visible: true for those.
   if (isUnfilterable(message)) {
-    return true;
+    return {
+      visible: true
+    };
   }
 
-  // Let's check all filters and hide the message if they say so.
-  if (!matchFilters(message, filtersState)) {
-    return false;
+  if (!passSearchFilters(message, filtersState)) {
+    return {
+      visible: false,
+      cause: FILTERS.TEXT
+    };
   }
 
-  // If there is a free text available for filtering use it to decide
-  // whether the message is displayed or not.
-  let text = (filtersState.text || "").trim();
-  if (text) {
-    return matchSearchFilters(message, text);
+  // Let's check all level filters (error, warn, log, …) and return visible: false
+  // and the message level as a cause if the function returns false.
+  if (!passLevelFilters(message, filtersState)) {
+    return {
+      visible: false,
+      cause: message.level
+    };
   }
 
-  return true;
-}
-
-function matchFilters(message, filtersState) {
-  if (matchLevelFilters(message, filtersState)) {
-    return true;
+  if (!passCssFilters(message, filtersState)) {
+    return {
+      visible: false,
+      cause: FILTERS.CSS
+    };
   }
 
-  // Return true if the message source is 'css'
-  // and CSS filter is enabled.
-  if (matchCssFilters(message, filtersState)) {
-    return true;
+  if (!passNetworkFilter(message, filtersState)) {
+    return {
+      visible: false,
+      cause: FILTERS.NET
+    };
   }
 
-  // Return true if the message is network event
-  // and Network and/or XHR filter is enabled.
-  if (matchNetworkFilters(message, filtersState)) {
-    return true;
+  if (!passXhrFilter(message, filtersState)) {
+    return {
+      visible: false,
+      cause: FILTERS.NETXHR
+    };
   }
 
-  return false;
+  return {
+    visible: true
+  };
 }
 
 function isUnfilterable(message) {
   return [
     MESSAGE_TYPE.COMMAND,
     MESSAGE_TYPE.RESULT,
     MESSAGE_TYPE.START_GROUP,
     MESSAGE_TYPE.START_GROUP_COLLAPSED,
@@ -504,40 +551,100 @@ function isInOpenedGroup(message, groups
 function hasClosedParentGroup(group, messagesUI) {
   return group.some(groupId => isGroupClosed(groupId, messagesUI));
 }
 
 function isGroupClosed(groupId, messagesUI) {
   return messagesUI.includes(groupId) === false;
 }
 
-function matchNetworkFilters(message, filters) {
+/**
+ * Returns true if the message shouldn't be hidden because of the network filter state.
+ *
+ * @param {Object} message - The message to check the filter against.
+ * @param {FilterState} filters - redux "filters" state.
+ * @returns {Boolean}
+ */
+function passNetworkFilter(message, filters) {
+  // The message passes the filter if it is not a network message,
+  // or if it is an xhr one,
+  // or if the network filter is on.
   return (
-    message.source === MESSAGE_SOURCE.NETWORK &&
-    (filters.get("net") === true && message.isXHR === false) ||
-    (filters.get("netxhr") === true && message.isXHR === true)
+    message.source !== MESSAGE_SOURCE.NETWORK ||
+    message.isXHR === true ||
+    filters.get(FILTERS.NET) === true
   );
 }
 
-function matchLevelFilters(message, filters) {
+/**
+ * Returns true if the message shouldn't be hidden because of the xhr filter state.
+ *
+ * @param {Object} message - The message to check the filter against.
+ * @param {FilterState} filters - redux "filters" state.
+ * @returns {Boolean}
+ */
+function passXhrFilter(message, filters) {
+  // The message passes the filter if it is not a network message,
+  // or if it is a non-xhr one,
+  // or if the xhr filter is on.
   return (
-    (message.source === MESSAGE_SOURCE.CONSOLE_API ||
-    message.source === MESSAGE_SOURCE.JAVASCRIPT) &&
+    message.source !== MESSAGE_SOURCE.NETWORK ||
+    message.isXHR === false ||
+    filters.get(FILTERS.NETXHR) === true
+  );
+}
+
+/**
+ * Returns true if the message shouldn't be hidden because of levels filter state.
+ *
+ * @param {Object} message - The message to check the filter against.
+ * @param {FilterState} filters - redux "filters" state.
+ * @returns {Boolean}
+ */
+function passLevelFilters(message, filters) {
+  // The message passes the filter if it is not a console call,
+  // or if its level matches the state of the corresponding filter.
+  return (
+    (message.source !== MESSAGE_SOURCE.CONSOLE_API &&
+    message.source !== MESSAGE_SOURCE.JAVASCRIPT) ||
     filters.get(message.level) === true
   );
 }
 
-function matchCssFilters(message, filters) {
+/**
+ * Returns true if the message shouldn't be hidden because of the CSS filter state.
+ *
+ * @param {Object} message - The message to check the filter against.
+ * @param {FilterState} filters - redux "filters" state.
+ * @returns {Boolean}
+ */
+function passCssFilters(message, filters) {
+  // The message passes the filter if it is not a CSS message,
+  // or if the CSS filter is on.
   return (
-    message.source === MESSAGE_SOURCE.CSS &&
+    message.source !== MESSAGE_SOURCE.CSS ||
     filters.get("css") === true
   );
 }
 
-function matchSearchFilters(message, text) {
+/**
+ * Returns true if the message shouldn't be hidden because of search filter state.
+ *
+ * @param {Object} message - The message to check the filter against.
+ * @param {FilterState} filters - redux "filters" state.
+ * @returns {Boolean}
+ */
+function passSearchFilters(message, filters) {
+  let text = (filters.get("text") || "").trim();
+
+  // If there is no search, the message passes the filter.
+  if (!text) {
+    return true;
+  }
+
   return (
     // Look for a match in parameters.
     isTextInParameters(text, message.parameters)
     // Look for a match in location.
     || isTextInFrame(text, message.frame)
     // Look for a match in net events.
     || isTextInNetEvent(text, message.request)
     // Look for a match in stack-trace.
@@ -665,9 +772,18 @@ function getAllProps(grips) {
   result = result.filter(grip =>
     typeof grip != "object" &&
     typeof grip != "undefined"
   );
 
   return [...new Set(result)];
 }
 
+function getDefaultFiltersCounter() {
+  const count = DEFAULT_FILTERS.reduce((res, filter) => {
+    res[filter] = 0;
+    return res;
+  }, {});
+  count.global = 0;
+  return count;
+}
+
 exports.messages = messages;
--- a/devtools/client/webconsole/new-console-output/selectors/messages.js
+++ b/devtools/client/webconsole/new-console-output/selectors/messages.js
@@ -36,29 +36,34 @@ function getAllGroupsById(state) {
 function getCurrentGroup(state) {
   return state.messages.currentGroup;
 }
 
 function getVisibleMessages(state) {
   return state.messages.visibleMessages;
 }
 
+function getFilteredMessagesCount(state) {
+  return state.messages.filteredMessagesCount;
+}
+
 function getAllRepeatById(state) {
   return state.messages.repeatById;
 }
 
 function getAllNetworkMessagesUpdateById(state) {
   return state.messages.networkMessagesUpdateById;
 }
 
 module.exports = {
   getMessage,
   getAllMessagesById,
   getAllMessagesUiById,
   getAllMessagesTableDataById,
   getAllGroupsById,
   getCurrentGroup,
   getVisibleMessages,
+  getFilteredMessagesCount,
   getAllRepeatById,
   getAllNetworkMessagesUpdateById,
   getAllMessagesObjectPropertiesById,
   getAllMessagesObjectEntriesById,
 };
--- a/devtools/client/webconsole/new-console-output/test/components/filter-bar.test.js
+++ b/devtools/client/webconsole/new-console-output/test/components/filter-bar.test.js
@@ -4,22 +4,24 @@
 
 const expect = require("expect");
 const sinon = require("sinon");
 const { render, mount, shallow } = require("enzyme");
 
 const { createFactory, DOM } = require("devtools/client/shared/vendor/react");
 const Provider = createFactory(require("react-redux").Provider);
 
+const actions = require("devtools/client/webconsole/new-console-output/actions/index");
 const FilterButton = require("devtools/client/webconsole/new-console-output/components/filter-button");
 const FilterBar = createFactory(require("devtools/client/webconsole/new-console-output/components/filter-bar"));
 const { getAllUi } = require("devtools/client/webconsole/new-console-output/selectors/ui");
+const { getAllFilters } = require("devtools/client/webconsole/new-console-output/selectors/filters");
 const {
   MESSAGES_CLEAR,
-  MESSAGE_LEVEL
+  FILTERS,
 } = require("devtools/client/webconsole/new-console-output/constants");
 
 const { setupStore } = require("devtools/client/webconsole/new-console-output/test/helpers");
 const serviceContainer = require("devtools/client/webconsole/new-console-output/test/fixtures/serviceContainer");
 
 describe("FilterBar component:", () => {
   it("initial render", () => {
     const store = setupStore([]);
@@ -42,16 +44,150 @@ describe("FilterBar component:", () => {
     // Text filter
     expect(toolbar.children().eq(2).attr("class"))
       .toBe("devtools-plaininput text-filter");
     expect(toolbar.children().eq(2).attr("placeholder")).toBe("Filter output");
     expect(toolbar.children().eq(2).attr("type")).toBe("search");
     expect(toolbar.children().eq(2).attr("value")).toBe("");
   });
 
+  it("displays the number of hidden messages when there are one hidden message", () => {
+    const store = setupStore([
+      "console.log('foobar', 'test')"
+    ]);
+    // Filter-out LOG messages
+    store.dispatch(actions.filterToggle(FILTERS.LOG));
+
+    const wrapper = mount(Provider({store}, FilterBar({ serviceContainer })));
+    const toolbar = wrapper.find(".webconsole-filterbar-filtered-messages");
+    expect(toolbar.exists()).toBeTruthy();
+
+    const message = toolbar.find(".filter-message-text");
+    expect(message.text()).toBe("1 item hidden by filters");
+    expect(message.prop("title")).toBe("log: 1");
+  });
+
+  it("Reset filters when the Reset filters button is clicked.", () => {
+    const store = setupStore([
+      "console.log('foobar', 'test')"
+    ]);
+    // Filter-out LOG messages
+    store.dispatch(actions.filterToggle(FILTERS.LOG));
+    const wrapper = mount(Provider({store}, FilterBar({serviceContainer})));
+
+    const resetFiltersButton = wrapper.find(
+      ".webconsole-filterbar-filtered-messages .devtools-button");
+    resetFiltersButton.simulate("click");
+
+    // Toolbar is now hidden
+    const toolbar = wrapper.find(".webconsole-filterbar-filtered-messages");
+    expect(toolbar.exists()).toBeFalsy();
+    expect(getAllFilters(store.getState()).get(FILTERS.LOG)).toBeTruthy();
+  });
+
+  it("displays the number of hidden messages when a search hide messages", () => {
+    const store = setupStore([
+      "console.log('foobar', 'test')",
+      "console.info('info message');",
+      "console.warn('danger, will robinson!')",
+      "console.debug('debug message');",
+      "console.error('error message');",
+    ]);
+    store.dispatch(actions.filterTextSet("qwerty"));
+
+    const wrapper = mount(Provider({store}, FilterBar({ serviceContainer })));
+    const toolbar = wrapper.find(".webconsole-filterbar-filtered-messages");
+    expect(toolbar.exists()).toBeTruthy();
+
+    const message = toolbar.find(".filter-message-text");
+    expect(message.text()).toBe("5 items hidden by filters");
+    expect(message.prop("title")).toBe("text: 5");
+  });
+
+  it("displays the number of hidden messages when there are multiple ones", () => {
+    const store = setupStore([
+      "console.log('foobar', 'test')",
+      "console.info('info message');",
+      "console.warn('danger, will robinson!')",
+      "console.debug('debug message');",
+      "console.error('error message');",
+      "console.log('foobar', 'test')",
+      "console.info('info message');",
+      "console.warn('danger, will robinson!')",
+      "console.debug('debug message');",
+      "console.error('error message');",
+    ]);
+
+    store.dispatch(actions.filterToggle(FILTERS.ERROR));
+    store.dispatch(actions.filterToggle(FILTERS.WARN));
+    store.dispatch(actions.filterToggle(FILTERS.LOG));
+    store.dispatch(actions.filterToggle(FILTERS.INFO));
+    store.dispatch(actions.filterToggle(FILTERS.DEBUG));
+    store.dispatch(actions.filterTextSet("qwerty"));
+
+    const wrapper = mount(Provider({store}, FilterBar({ serviceContainer })));
+    const message = wrapper.find(".filter-message-text");
+
+    expect(message.prop("title")).toBe("text: 10");
+  });
+
+  it("displays expected tooltip when there is text & level hidden-messages", () => {
+    const store = setupStore([
+      "console.log('foobar', 'test')",
+      "console.info('info message');",
+      "console.warn('danger, will robinson!')",
+      "console.debug('debug message');",
+      "console.error('error message');",
+      "console.log('foobar', 'test')",
+      "console.info('info message');",
+      "console.warn('danger, will robinson!')",
+      "console.debug('debug message');",
+      "console.error('error message');",
+    ]);
+
+    store.dispatch(actions.filterToggle(FILTERS.ERROR));
+    store.dispatch(actions.filterToggle(FILTERS.WARN));
+    store.dispatch(actions.filterToggle(FILTERS.LOG));
+    store.dispatch(actions.filterToggle(FILTERS.INFO));
+    store.dispatch(actions.filterToggle(FILTERS.DEBUG));
+
+    const wrapper = mount(Provider({store}, FilterBar({ serviceContainer })));
+    const toolbar = wrapper.find(".webconsole-filterbar-filtered-messages");
+    expect(toolbar.exists()).toBeTruthy();
+
+    const message = toolbar.find(".filter-message-text");
+    expect(message.text()).toBe("10 items hidden by filters");
+    expect(message.prop("title")).toBe("error: 2, warn: 2, log: 2, info: 2, debug: 2");
+  });
+
+  it("does not display the number of hidden messages when there are no messages", () => {
+    const store = setupStore([]);
+    const wrapper = mount(Provider({store}, FilterBar({ serviceContainer })));
+    const toolbar = wrapper.find(".webconsole-filterbar-filtered-messages");
+    expect(toolbar.exists()).toBeFalsy();
+  });
+
+  it("does not display the number of hidden non-default filters (CSS, Network,…)", () => {
+    const store = setupStore([
+      "Unknown property ‘such-unknown-property’.  Declaration dropped.",
+      "GET request",
+      "XHR GET request"
+    ]);
+    const wrapper = mount(Provider({store}, FilterBar({ serviceContainer })));
+
+    // Let's make sure those non-default filters are off.
+    const filters = getAllFilters(store.getState());
+    expect(filters.get(FILTERS.CSS)).toBe(false);
+    expect(filters.get(FILTERS.NET)).toBe(false);
+    expect(filters.get(FILTERS.NETXHR)).toBe(false);
+
+    const toolbar = wrapper.find(".webconsole-filterbar-filtered-messages");
+    expect(toolbar.exists()).toBeFalsy();
+  });
+
   it("displays filter bar when button is clicked", () => {
     const store = setupStore([]);
 
     expect(getAllUi(store.getState()).filterBarVisible).toBe(false);
 
     const wrapper = mount(Provider({store}, FilterBar({ serviceContainer })));
     wrapper.find(".devtools-filter-icon").simulate("click");
 
@@ -64,25 +200,25 @@ describe("FilterBar component:", () => {
     const filterBtn = props => FilterButton(
       Object.assign({}, {
         active: true,
         dispatch: store.dispatch
       }, props)
     );
 
     let buttons = [
-      filterBtn({ label: "Errors", filterKey: MESSAGE_LEVEL.ERROR }),
-      filterBtn({ label: "Warnings", filterKey: MESSAGE_LEVEL.WARN }),
-      filterBtn({ label: "Logs", filterKey: MESSAGE_LEVEL.LOG }),
-      filterBtn({ label: "Info", filterKey: MESSAGE_LEVEL.INFO }),
-      filterBtn({ label: "Debug", filterKey: MESSAGE_LEVEL.DEBUG }),
+      filterBtn({ label: "Errors", filterKey: FILTERS.ERROR }),
+      filterBtn({ label: "Warnings", filterKey: FILTERS.WARN }),
+      filterBtn({ label: "Logs", filterKey: FILTERS.LOG }),
+      filterBtn({ label: "Info", filterKey: FILTERS.INFO }),
+      filterBtn({ label: "Debug", filterKey: FILTERS.DEBUG }),
       DOM.span({
         className: "devtools-separator",
       }),
-      filterBtn({ label: "CSS", filterKey: "css" }),
+      filterBtn({ label: "CSS", filterKey: "css", active: false }),
       filterBtn({ label: "XHR", filterKey: "netxhr", active: false }),
       filterBtn({ label: "Requests", filterKey: "net", active: false }),
     ];
 
     secondaryBar.children().forEach((child, index) => {
       expect(child.html()).toEqual(shallow(buttons[index]).html());
     });
   });
--- a/devtools/client/webconsole/new-console-output/test/fixtures/L10n.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/L10n.js
@@ -32,16 +32,20 @@ class L10n {
       case "webconsole.debugFilterButton.label":
         return "Debug";
       case "webconsole.cssFilterButton.label":
         return "CSS";
       case "webconsole.xhrFilterButton.label":
         return "XHR";
       case "webconsole.requestsFilterButton.label":
         return "Requests";
+      case "messageRepeats.tooltip2":
+        return "#1 repeat;#1 repeats";
+      case "webconsole.filteredMessages.label":
+        return "#1 item hidden by filters;#1 items hidden by filters";
       default:
         return str;
     }
   }
 
   getFormatStr(str) {
     return this.getStr(str);
   }
--- a/devtools/client/webconsole/new-console-output/test/fixtures/PluralForm.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/PluralForm.js
@@ -1,18 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 module.exports = {
   PluralForm: {
     get: function (occurence, str) {
-      // @TODO Remove when loading the actual strings from webconsole.properties
-      // is done in the L10n fixture.
-      if (str === "messageRepeats.tooltip2") {
-        return `${occurence} repeats`;
+      if (str.includes(";")) {
+        const [singular, plural] = str.split(";");
+        return occurence > 1 ? plural : singular;
       }
 
       return str;
     }
   }
 };
--- a/devtools/client/webconsole/new-console-output/test/fixtures/Services.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/Services.js
@@ -11,16 +11,17 @@ module.exports = {
       switch (pref) {
         case "devtools.hud.loglimit":
           return 1000;
       }
       return null;
     },
     getBoolPref: pref => {
       const falsey = [
+        PREFS.FILTER.CSS,
         PREFS.FILTER.NET,
         PREFS.FILTER.NETXHR,
         PREFS.UI.FILTER_BAR,
       ];
       return !falsey.includes(pref);
     },
     setBoolPref: () => {},
     clearUserPref: () => {},
--- a/devtools/client/webconsole/new-console-output/test/helpers.js
+++ b/devtools/client/webconsole/new-console-output/test/helpers.js
@@ -29,17 +29,17 @@ function setupActions() {
   };
 
   return wrappedActions;
 }
 
 /**
  * Prepare the store for use in testing.
  */
-function setupStore(input, hud, options) {
+function setupStore(input = [], hud, options) {
   const store = configureStore(hud, options);
 
   // Add the messages from the input commands to the store.
   input.forEach((cmd) => {
     store.dispatch(actions.messageAdd(stubPackets.get(cmd)));
   });
 
   return store;
--- a/devtools/client/webconsole/new-console-output/test/store/filters.test.js
+++ b/devtools/client/webconsole/new-console-output/test/store/filters.test.js
@@ -6,17 +6,17 @@
 const expect = require("expect");
 
 const actions = require("devtools/client/webconsole/new-console-output/actions/index");
 const { messageAdd } = require("devtools/client/webconsole/new-console-output/actions/index");
 const { ConsoleCommand } = require("devtools/client/webconsole/new-console-output/types");
 const { getVisibleMessages } = require("devtools/client/webconsole/new-console-output/selectors/messages");
 const { getAllFilters } = require("devtools/client/webconsole/new-console-output/selectors/filters");
 const { setupStore } = require("devtools/client/webconsole/new-console-output/test/helpers");
-const { MESSAGE_LEVEL } = require("devtools/client/webconsole/new-console-output/constants");
+const { FILTERS } = require("devtools/client/webconsole/new-console-output/constants");
 const { stubPackets } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
 const { stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
 
 describe("Filtering", () => {
   let store;
   let numMessages;
   // Number of messages in prepareBaseStore which are not filtered out, i.e. Evaluation
   // Results, console commands and console.groups .
@@ -32,56 +32,56 @@ describe("Filtering", () => {
    * Tests for filter buttons in Console toolbar. The test switches off
    * all filters and consequently tests one by one on the list of messages
    * created in `prepareBaseStore` method.
    */
   describe("Level filter", () => {
     beforeEach(() => {
       // Switch off all filters (include those which are on by default).
       store.dispatch(actions.filtersClear());
-      store.dispatch(actions.filterToggle(MESSAGE_LEVEL.DEBUG));
-      store.dispatch(actions.filterToggle(MESSAGE_LEVEL.ERROR));
-      store.dispatch(actions.filterToggle(MESSAGE_LEVEL.INFO));
-      store.dispatch(actions.filterToggle(MESSAGE_LEVEL.LOG));
-      store.dispatch(actions.filterToggle(MESSAGE_LEVEL.WARN));
+      store.dispatch(actions.filterToggle(FILTERS.DEBUG));
+      store.dispatch(actions.filterToggle(FILTERS.ERROR));
+      store.dispatch(actions.filterToggle(FILTERS.INFO));
+      store.dispatch(actions.filterToggle(FILTERS.LOG));
+      store.dispatch(actions.filterToggle(FILTERS.WARN));
 
       let messages = getVisibleMessages(store.getState());
       expect(messages.length).toEqual(numUnfilterableMessages);
     });
 
     it("filters log messages", () => {
-      store.dispatch(actions.filterToggle(MESSAGE_LEVEL.LOG));
+      store.dispatch(actions.filterToggle(FILTERS.LOG));
 
       let messages = getVisibleMessages(store.getState());
       expect(messages.length).toEqual(numUnfilterableMessages + 5);
     });
 
     it("filters debug messages", () => {
-      store.dispatch(actions.filterToggle(MESSAGE_LEVEL.DEBUG));
+      store.dispatch(actions.filterToggle(FILTERS.DEBUG));
 
       let messages = getVisibleMessages(store.getState());
       expect(messages.length).toEqual(numUnfilterableMessages + 1);
     });
 
     it("filters info messages", () => {
-      store.dispatch(actions.filterToggle(MESSAGE_LEVEL.INFO));
+      store.dispatch(actions.filterToggle(FILTERS.INFO));
 
       let messages = getVisibleMessages(store.getState());
       expect(messages.length).toEqual(numUnfilterableMessages + 1);
     });
 
     it("filters warning messages", () => {
-      store.dispatch(actions.filterToggle(MESSAGE_LEVEL.WARN));
+      store.dispatch(actions.filterToggle(FILTERS.WARN));
 
       let messages = getVisibleMessages(store.getState());
       expect(messages.length).toEqual(numUnfilterableMessages + 1);
     });
 
     it("filters error messages", () => {
-      store.dispatch(actions.filterToggle(MESSAGE_LEVEL.ERROR));
+      store.dispatch(actions.filterToggle(FILTERS.ERROR));
 
       let messages = getVisibleMessages(store.getState());
       expect(messages.length).toEqual(numUnfilterableMessages + 3);
     });
 
     it("filters css messages", () => {
       let message = stubPreparedMessages.get(
         "Unknown property ‘such-unknown-property’.  Declaration dropped."
@@ -201,30 +201,34 @@ describe("Filtering", () => {
   });
 });
 
 describe("Clear filters", () => {
   it("clears all filters", () => {
     const store = setupStore([]);
 
     // Setup test case
-    store.dispatch(actions.filterToggle(MESSAGE_LEVEL.ERROR));
-    store.dispatch(actions.filterToggle("netxhr"));
+    store.dispatch(actions.filterToggle(FILTERS.ERROR));
+    store.dispatch(actions.filterToggle(FILTERS.CSS));
+    store.dispatch(actions.filterToggle(FILTERS.NET));
+    store.dispatch(actions.filterToggle(FILTERS.NETXHR));
     store.dispatch(actions.filterTextSet("foobar"));
 
     let filters = getAllFilters(store.getState());
     expect(filters.toJS()).toEqual({
-      "css": true,
-      "debug": true,
-      "error": false,
+      // default
+      "warn": true,
+      "log": true,
       "info": true,
-      "log": true,
-      "net": false,
+      "debug": true,
+      "css": true,
+      // changed
+      "error": false,
+      "net": true,
       "netxhr": true,
-      "warn": true,
       "text": "foobar",
     });
 
     store.dispatch(actions.filtersClear());
 
     filters = getAllFilters(store.getState());
     expect(filters.toJS()).toEqual({
       "css": false,
@@ -235,16 +239,62 @@ describe("Clear filters", () => {
       "net": false,
       "netxhr": false,
       "warn": true,
       "text": "",
     });
   });
 });
 
+describe("Resets filters", () => {
+  it("resets default filters value to their original one.", () => {
+    const store = setupStore([]);
+
+    // Setup test case
+    store.dispatch(actions.filterToggle(FILTERS.ERROR));
+    store.dispatch(actions.filterToggle(FILTERS.LOG));
+    store.dispatch(actions.filterToggle(FILTERS.CSS));
+    store.dispatch(actions.filterToggle(FILTERS.NET));
+    store.dispatch(actions.filterToggle(FILTERS.NETXHR));
+    store.dispatch(actions.filterTextSet("foobar"));
+
+    let filters = getAllFilters(store.getState());
+    expect(filters.toJS()).toEqual({
+      // default
+      "warn": true,
+      "info": true,
+      "debug": true,
+      // changed
+      "error": false,
+      "log": false,
+      "css": true,
+      "net": true,
+      "netxhr": true,
+      "text": "foobar",
+    });
+
+    store.dispatch(actions.defaultFiltersReset());
+
+    filters = getAllFilters(store.getState());
+    expect(filters.toJS()).toEqual({
+      // default
+      "error": true,
+      "warn": true,
+      "log": true,
+      "info": true,
+      "debug": true,
+      "text": "",
+      // non-default filters weren't changed
+      "css": true,
+      "net": true,
+      "netxhr": true,
+    });
+  });
+});
+
 function prepareBaseStore() {
   const store = setupStore([
     // Console API
     "console.log('foobar', 'test')",
     "console.warn('danger, will robinson!')",
     "console.log(undefined)",
     "console.count('bar')",
     "console.log('鼬')",
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/new-console-output/test/store/hidden-messages.test.js
@@ -0,0 +1,173 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const expect = require("expect");
+
+const actions = require("devtools/client/webconsole/new-console-output/actions/index");
+const { getFilteredMessagesCount } = require("devtools/client/webconsole/new-console-output/selectors/messages");
+const { setupStore } = require("devtools/client/webconsole/new-console-output/test/helpers");
+const { FILTERS } = require("devtools/client/webconsole/new-console-output/constants");
+const { stubPackets } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
+
+describe("Filtering - Hidden messages", () => {
+  let store;
+
+  beforeEach(() => {
+    store = prepareBaseStore();
+    // Switch off all filters (include those which are on by default).
+    store.dispatch(actions.filtersClear());
+    store.dispatch(actions.filterToggle(FILTERS.DEBUG));
+    store.dispatch(actions.filterToggle(FILTERS.ERROR));
+    store.dispatch(actions.filterToggle(FILTERS.INFO));
+    store.dispatch(actions.filterToggle(FILTERS.LOG));
+    store.dispatch(actions.filterToggle(FILTERS.WARN));
+  });
+
+  it("has the expected numbers", () => {
+    let counter = getFilteredMessagesCount(store.getState());
+    expect(counter).toEqual(BASIC_TEST_CASE_FILTERED_MESSAGE_COUNT);
+  });
+
+  it("has the expected numbers when there is a text search", () => {
+    store.dispatch(actions.filterTextSet("danger, will robinson!"));
+    let counter = getFilteredMessagesCount(store.getState());
+    expect(counter).toEqual({
+      [FILTERS.ERROR]: 0,
+      // The warning message matches the text search.
+      [FILTERS.WARN]: 1,
+      [FILTERS.LOG]: 0,
+      [FILTERS.INFO]: 0,
+      [FILTERS.DEBUG]: 0,
+      [FILTERS.TEXT]: 10,
+      global: 11
+    });
+
+    // Numbers update if the text search is cleared.
+    store.dispatch(actions.filterTextSet(""));
+    counter = getFilteredMessagesCount(store.getState());
+    expect(counter).toEqual(BASIC_TEST_CASE_FILTERED_MESSAGE_COUNT);
+  });
+
+  it("updates when messages are added", () => {
+    MESSAGES.forEach(message =>
+      store.dispatch(actions.messageAdd(stubPackets.get(message))));
+
+    let counter = getFilteredMessagesCount(store.getState());
+    expect(counter).toEqual({
+      [FILTERS.ERROR]: 6,
+      [FILTERS.WARN]: 2,
+      [FILTERS.LOG]: 10,
+      [FILTERS.INFO]: 2,
+      [FILTERS.DEBUG]: 2,
+      [FILTERS.TEXT]: 0,
+      global: 22
+    });
+  });
+
+  it("updates when filters are toggled", () => {
+    store.dispatch(actions.filterToggle(FILTERS.LOG));
+
+    let counter = getFilteredMessagesCount(store.getState());
+    expect(counter).toEqual(Object.assign({}, BASIC_TEST_CASE_FILTERED_MESSAGE_COUNT, {
+      [FILTERS.LOG]: 0,
+      global: 6
+    }));
+
+    store.dispatch(actions.filterToggle(FILTERS.ERROR));
+
+    counter = getFilteredMessagesCount(store.getState());
+    expect(counter).toEqual(Object.assign({}, BASIC_TEST_CASE_FILTERED_MESSAGE_COUNT, {
+      [FILTERS.ERROR]: 0,
+      [FILTERS.LOG]: 0,
+      global: 3
+    }));
+
+    store.dispatch(actions.filterToggle(FILTERS.LOG));
+    store.dispatch(actions.filterToggle(FILTERS.ERROR));
+    counter = getFilteredMessagesCount(store.getState());
+    expect(counter).toEqual(BASIC_TEST_CASE_FILTERED_MESSAGE_COUNT);
+  });
+
+  it("has the expected numbers after message clear", () => {
+    // Add a text search to make sure it is handled as well.
+    store.dispatch(actions.filterTextSet("danger, will robinson!"));
+    store.dispatch(actions.messagesClear());
+    let counter = getFilteredMessagesCount(store.getState());
+    expect(counter).toEqual({
+      [FILTERS.ERROR]: 0,
+      [FILTERS.WARN]: 0,
+      [FILTERS.LOG]: 0,
+      [FILTERS.INFO]: 0,
+      [FILTERS.DEBUG]: 0,
+      [FILTERS.TEXT]: 0,
+      global: 0,
+    });
+  });
+
+  it("has the expected numbers after filter reset", () => {
+    // Add a text search to make sure it is handled as well.
+    store.dispatch(actions.filterTextSet("danger, will robinson!"));
+    store.dispatch(actions.defaultFiltersReset());
+    let counter = getFilteredMessagesCount(store.getState());
+    expect(counter).toEqual({
+      [FILTERS.ERROR]: 0,
+      [FILTERS.WARN]: 0,
+      [FILTERS.LOG]: 0,
+      [FILTERS.INFO]: 0,
+      [FILTERS.DEBUG]: 0,
+      [FILTERS.TEXT]: 0,
+      global: 0,
+    });
+  });
+
+  it("has the expected numbers after filter clear", () => {
+    // Add a text search to make sure it is handled as well.
+    store.dispatch(actions.filterTextSet("danger, will robinson!"));
+    store.dispatch(actions.filtersClear());
+    let counter = getFilteredMessagesCount(store.getState());
+    expect(counter).toEqual({
+      [FILTERS.ERROR]: 0,
+      [FILTERS.WARN]: 0,
+      [FILTERS.LOG]: 0,
+      [FILTERS.INFO]: 0,
+      [FILTERS.DEBUG]: 0,
+      [FILTERS.TEXT]: 0,
+      global: 0,
+    });
+  });
+});
+
+const MESSAGES = [
+  // Error
+  "ReferenceError: asdf is not defined",
+  "console.error('error message');",
+  "console.assert(false, {message: 'foobar'})",
+  // Warning
+  "console.warn('danger, will robinson!')",
+  // Log
+  "console.log('foobar', 'test')",
+  "console.log(undefined)",
+  "console.count('bar')",
+  "console.log('鼬')",
+  "console.table(['red', 'green', 'blue']);",
+  // Info
+  "console.info('info message');",
+  // Debug
+  "console.debug('debug message');",
+];
+
+const BASIC_TEST_CASE_FILTERED_MESSAGE_COUNT = {
+  [FILTERS.ERROR]: 3,
+  [FILTERS.WARN]: 1,
+  [FILTERS.LOG]: 5,
+  [FILTERS.INFO]: 1,
+  [FILTERS.DEBUG]: 1,
+  [FILTERS.TEXT]: 0,
+  global: 11
+};
+
+function prepareBaseStore() {
+  return setupStore(MESSAGES);
+}
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -191,16 +191,17 @@
 #include "mozilla/dom/nsCSPUtils.h"
 #include "nsHTMLStyleSheet.h"
 #include "nsHTMLCSSStyleSheet.h"
 #include "mozilla/dom/DOMImplementation.h"
 #include "mozilla/dom/ShadowRoot.h"
 #include "mozilla/dom/Comment.h"
 #include "nsTextNode.h"
 #include "mozilla/dom/Link.h"
+#include "mozilla/dom/HTMLCollectionBinding.h"
 #include "mozilla/dom/HTMLElementBinding.h"
 #include "nsXULAppAPI.h"
 #include "mozilla/dom/Touch.h"
 #include "mozilla/dom/TouchEvent.h"
 
 #include "mozilla/Preferences.h"
 
 #include "imgILoader.h"
@@ -507,21 +508,140 @@ nsIdentifierMapEntry::SetImageElement(El
   Element* oldElement = GetImageIdElement();
   mImageElement = aElement;
   Element* newElement = GetImageIdElement();
   if (oldElement != newElement) {
     FireChangeCallbacks(oldElement, newElement, true);
   }
 }
 
+namespace mozilla {
+namespace dom {
+class SimpleHTMLCollection final : public nsSimpleContentList
+                                 , public nsIHTMLCollection
+{
+public:
+  explicit SimpleHTMLCollection(nsINode* aRoot) : nsSimpleContentList(aRoot) {}
+
+  NS_DECL_ISUPPORTS_INHERITED
+
+  // nsIDOMHTMLCollection
+  NS_DECL_NSIDOMHTMLCOLLECTION
+
+  virtual nsINode* GetParentObject() override
+  {
+    return nsSimpleContentList::GetParentObject();
+  }
+  virtual Element* GetElementAt(uint32_t aIndex) override
+  {
+    return mElements.SafeElementAt(aIndex)->AsElement();
+  }
+
+  virtual Element* GetFirstNamedElement(const nsAString& aName,
+                                        bool& aFound) override
+  {
+    aFound = false;
+    nsCOMPtr<nsIAtom> name = NS_Atomize(aName);
+    for (uint32_t i = 0; i < mElements.Length(); i++) {
+      MOZ_DIAGNOSTIC_ASSERT(mElements[i]);
+      Element* element = mElements[i]->AsElement();
+      if (element->GetID() == name ||
+          (element->HasName() &&
+           element->GetParsedAttr(nsGkAtoms::name)->GetAtomValue() == name)) {
+        aFound = true;
+        return element;
+      }
+    }
+    return nullptr;
+  }
+
+  virtual void GetSupportedNames(nsTArray<nsString>& aNames) override
+  {
+    AutoTArray<nsIAtom*, 8> atoms;
+    for (uint32_t i = 0; i < mElements.Length(); i++) {
+      MOZ_DIAGNOSTIC_ASSERT(mElements[i]);
+      Element* element = mElements[i]->AsElement();
+
+      nsIAtom* id = element->GetID();
+      MOZ_ASSERT(id != nsGkAtoms::_empty);
+      if (id && !atoms.Contains(id)) {
+        atoms.AppendElement(id);
+      }
+
+      if (element->HasName()) {
+        nsIAtom* name = element->GetParsedAttr(nsGkAtoms::name)->GetAtomValue();
+        MOZ_ASSERT(name && name != nsGkAtoms::_empty);
+        if (name && !atoms.Contains(name)) {
+          atoms.AppendElement(name);
+        }
+      }
+    }
+
+    nsString* names = aNames.AppendElements(atoms.Length());
+    for (uint32_t i = 0; i < atoms.Length(); i++) {
+      atoms[i]->ToString(names[i]);
+    }
+  }
+
+  virtual JSObject* GetWrapperPreserveColorInternal() override
+  {
+    return nsWrapperCache::GetWrapperPreserveColor();
+  }
+  virtual void PreserveWrapperInternal(nsISupports* aScriptObjectHolder) override
+  {
+    nsWrapperCache::PreserveWrapper(aScriptObjectHolder);
+  }
+  virtual JSObject* WrapObject(JSContext *aCx,
+                               JS::Handle<JSObject*> aGivenProto) override
+  {
+    return HTMLCollectionBinding::Wrap(aCx, this, aGivenProto);
+  }
+
+  using nsBaseContentList::Length;
+  using nsBaseContentList::Item;
+  using nsIHTMLCollection::NamedItem;
+
+private:
+  virtual ~SimpleHTMLCollection() {}
+};
+
+NS_IMPL_ISUPPORTS_INHERITED(SimpleHTMLCollection, nsSimpleContentList,
+                            nsIHTMLCollection, nsIDOMHTMLCollection)
+
+NS_IMETHODIMP
+SimpleHTMLCollection::GetLength(uint32_t* aLength)
+{
+  *aLength = Length();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SimpleHTMLCollection::Item(uint32_t aIdx, nsIDOMNode** aRetVal)
+{
+  nsCOMPtr<nsIDOMNode> retVal = Item(aIdx)->AsDOMNode();
+  retVal.forget(aRetVal);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SimpleHTMLCollection::NamedItem(const nsAString& aName, nsIDOMNode** aRetVal)
+{
+  nsCOMPtr<nsIDOMNode> retVal = NamedItem(aName)->AsDOMNode();
+  retVal.forget(aRetVal);
+  return NS_OK;
+}
+
+} // namespace dom
+} // namespace mozilla
+
 void
 nsIdentifierMapEntry::AddNameElement(nsINode* aNode, Element* aElement)
 {
   if (!mNameContentList) {
-    mNameContentList = new nsSimpleContentList(aNode);
+    mNameContentList = new SimpleHTMLCollection(aNode);
   }
 
   mNameContentList->AppendElement(aElement);
 }
 
 void
 nsIdentifierMapEntry::RemoveNameElement(Element* aElement)
 {
--- a/dom/base/nsTextFragment.cpp
+++ b/dom/base/nsTextFragment.cpp
@@ -332,31 +332,31 @@ nsTextFragment::Append(const char16_t* a
   // This is a common case because some callsites create a textnode
   // with a value by creating the node and then calling AppendData.
   if (mState.mLength == 0) {
     return SetTo(aBuffer, aLength, aUpdateBidi, aForce2b);
   }
 
   // Should we optimize for aData.Length() == 0?
 
-  CheckedUint32 length = mState.mLength;
-  length += aLength;
-
-  if (!length.isValid()) {
-    return false;
+  // FYI: Don't use CheckedInt in this method since here is very hot path
+  //      in some performance tests.
+  if (NS_MAX_TEXT_FRAGMENT_LENGTH - mState.mLength < aLength) {
+    return false;  // Would be overflown if we'd keep handling.
   }
 
   if (mState.mIs2b) {
-    length *= sizeof(char16_t);
-    if (!length.isValid()) {
-      return false;
+    size_t size = mState.mLength + aLength;
+    if (SIZE_MAX / sizeof(char16_t) < size) {
+      return false;  // Would be overflown if we'd keep handling.
     }
+    size *= sizeof(char16_t);
 
     // Already a 2-byte string so the result will be too
-    char16_t* buff = static_cast<char16_t*>(realloc(m2b, length.value()));
+    char16_t* buff = static_cast<char16_t*>(realloc(m2b, size));
     if (!buff) {
       return false;
     }
 
     memcpy(buff + mState.mLength, aBuffer, aLength * sizeof(char16_t));
     mState.mLength += aLength;
     m2b = buff;
 
@@ -366,24 +366,25 @@ nsTextFragment::Append(const char16_t* a
 
     return true;
   }
 
   // Current string is a 1-byte string, check if the new data fits in one byte too.
   int32_t first16bit = aForce2b ? 0 : FirstNon8Bit(aBuffer, aBuffer + aLength);
 
   if (first16bit != -1) { // aBuffer contains no non-8bit character
-    length *= sizeof(char16_t);
-    if (!length.isValid()) {
-      return false;
+    size_t size = mState.mLength + aLength;
+    if (SIZE_MAX / sizeof(char16_t) < size) {
+      return false;  // Would be overflown if we'd keep handling.
     }
+    size *= sizeof(char16_t);
 
     // The old data was 1-byte, but the new is not so we have to expand it
     // all to 2-byte
-    char16_t* buff = static_cast<char16_t*>(malloc(length.value()));
+    char16_t* buff = static_cast<char16_t*>(malloc(size));
     if (!buff) {
       return false;
     }
 
     // Copy data into buff
     LossyConvertEncoding8to16 converter(buff);
     copy_string(m1b, m1b+mState.mLength, converter);
 
@@ -401,25 +402,27 @@ nsTextFragment::Append(const char16_t* a
     if (aUpdateBidi) {
       UpdateBidiFlag(aBuffer + first16bit, aLength - first16bit);
     }
 
     return true;
   }
 
   // The new and the old data is all 1-byte
+  size_t size = mState.mLength + aLength;
+  MOZ_ASSERT(sizeof(char) == 1);
   char* buff;
   if (mState.mInHeap) {
-    buff = static_cast<char*>(realloc(const_cast<char*>(m1b), length.value()));
+    buff = static_cast<char*>(realloc(const_cast<char*>(m1b), size));
     if (!buff) {
       return false;
     }
   }
   else {
-    buff = static_cast<char*>(malloc(length.value()));
+    buff = static_cast<char*>(malloc(size));
     if (!buff) {
       return false;
     }
 
     memcpy(buff, m1b, mState.mLength);
     mState.mInHeap = true;
   }
 
--- a/dom/base/nsTextFragment.h
+++ b/dom/base/nsTextFragment.h
@@ -216,19 +216,23 @@ public:
     // uint32_t to ensure that the values are unsigned, because we
     // want 0/1, not 0/-1!
     // Making these bool causes Windows to not actually pack them,
     // which causes crashes because we assume this structure is no more than
     // 32 bits!
     uint32_t mInHeap : 1;
     uint32_t mIs2b : 1;
     uint32_t mIsBidi : 1;
+    // Note that when you change the bits of mLength, you also need to change
+    // NS_MAX_TEXT_FRAGMENT_LENGTH.
     uint32_t mLength : 29;
   };
 
+#define NS_MAX_TEXT_FRAGMENT_LENGTH (static_cast<uint32_t>(0x1FFFFFFF))
+
   size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
 private:
   void ReleaseText();
 
   /**
    * Scan the contents of the fragment and turn on mState.mIsBidi if it
    * includes any Bidi characters.
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -6297,16 +6297,26 @@ CanvasRenderingContext2D::InitializeCanv
   CanvasInitializeData data;
   data.mSize = GetSize();
   data.mHasAlpha = !mOpaque;
   data.mPreTransCallback = CanvasRenderingContext2DUserData::PreTransactionCallback;
   data.mPreTransCallbackData = this;
   data.mDidTransCallback = CanvasRenderingContext2DUserData::DidTransactionCallback;
   data.mDidTransCallbackData = this;
 
+  if (!mBufferProvider) {
+    // Force the creation of a buffer provider.
+    EnsureTarget();
+    ReturnTarget();
+    if (!mBufferProvider) {
+      MarkContextClean();
+      return false;
+    }
+  }
+
   if (mIsSkiaGL) {
       GLuint skiaGLTex = SkiaGLTex();
       if (skiaGLTex) {
         SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
         MOZ_ASSERT(glue);
         data.mGLContext = glue->GetGLContext();
         data.mFrontbufferGLTex = skiaGLTex;
       }
--- a/dom/html/HTMLLinkElement.h
+++ b/dom/html/HTMLLinkElement.h
@@ -102,16 +102,24 @@ public:
     SetOrRemoveNullableStringAttr(nsGkAtoms::crossorigin, aCrossOrigin, aError);
   }
   // XPCOM GetRel is fine.
   void SetRel(const nsAString& aRel, ErrorResult& aRv)
   {
     SetHTMLAttr(nsGkAtoms::rel, aRel, aRv);
   }
   nsDOMTokenList* RelList();
+  void GetNonce(nsAString& aNonce) const
+  {
+    GetHTMLAttr(nsGkAtoms::nonce, aNonce);
+  }
+  void SetNonce(const nsAString& aNonce, ErrorResult& aRv)
+  {
+    SetHTMLAttr(nsGkAtoms::nonce, aNonce, aRv);
+  }
   // XPCOM GetMedia is fine.
   void SetMedia(const nsAString& aMedia, ErrorResult& aRv)
   {
     SetHTMLAttr(nsGkAtoms::media, aMedia, aRv);
   }
   // XPCOM GetHreflang is fine.
   void SetHreflang(const nsAString& aHreflang, ErrorResult& aRv)
   {
--- a/dom/html/HTMLScriptElement.h
+++ b/dom/html/HTMLScriptElement.h
@@ -76,16 +76,24 @@ public:
     // always parse to an enum value, so we don't need an invalid
     // default, and we _want_ the missing default to be null.
     GetEnumAttr(nsGkAtoms::crossorigin, nullptr, aResult);
   }
   void SetCrossOrigin(const nsAString& aCrossOrigin, ErrorResult& aError)
   {
     SetOrRemoveNullableStringAttr(nsGkAtoms::crossorigin, aCrossOrigin, aError);
   }
+  void GetNonce(nsAString& aNonce) const
+  {
+    GetHTMLAttr(nsGkAtoms::nonce, aNonce);
+  }
+  void SetNonce(const nsAString& aNonce, ErrorResult& aRv)
+  {
+    SetHTMLAttr(nsGkAtoms::nonce, aNonce, aRv);
+  }
   void GetIntegrity(nsAString& aIntegrity)
   {
     GetHTMLAttr(nsGkAtoms::integrity, aIntegrity);
   }
   void SetIntegrity(const nsAString& aIntegrity, ErrorResult& rv)
   {
     SetHTMLAttr(nsGkAtoms::integrity, aIntegrity, rv);
   }
--- a/dom/html/HTMLStyleElement.h
+++ b/dom/html/HTMLStyleElement.h
@@ -57,16 +57,24 @@ public:
   // nsIMutationObserver
   NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
 
   bool Disabled();
   void SetDisabled(bool aDisabled);
+  void GetNonce(nsAString& aNonce) const
+  {
+    GetHTMLAttr(nsGkAtoms::nonce, aNonce);
+  }
+  void SetNonce(const nsAString& aNonce, ErrorResult& aRv)
+  {
+    SetHTMLAttr(nsGkAtoms::nonce, aNonce, aRv);
+  }
   void SetMedia(const nsAString& aMedia, ErrorResult& aError)
   {
     SetHTMLAttr(nsGkAtoms::media, aMedia, aError);
   }
   void SetType(const nsAString& aType, ErrorResult& aError)
   {
     SetHTMLAttr(nsGkAtoms::type, aType, aError);
   }
--- a/dom/media/MediaResource.h
+++ b/dom/media/MediaResource.h
@@ -170,27 +170,16 @@ public:
   virtual nsresult ReadAt(int64_t aOffset, char* aBuffer,
                           uint32_t aCount, uint32_t* aBytes) = 0;
   // Indicate whether caching data in advance of reads is worth it.
   // E.g. Caching lockless and memory-based MediaResource subclasses would be a
   // waste, but caching lock/IO-bound resources means reducing the impact of
   // each read.
   virtual bool ShouldCacheReads() = 0;
 
-  already_AddRefed<MediaByteBuffer> CachedReadAt(int64_t aOffset, uint32_t aCount)
-  {
-    RefPtr<MediaByteBuffer> bytes = new MediaByteBuffer();
-    bool ok = bytes->SetLength(aCount, fallible);
-    NS_ENSURE_TRUE(ok, nullptr);
-    char* curr = reinterpret_cast<char*>(bytes->Elements());
-    nsresult rv = ReadFromCache(curr, aOffset, aCount);
-    NS_ENSURE_SUCCESS(rv, nullptr);
-    return bytes.forget();
-  }
-
   // Report the current offset in bytes from the start of the stream.
   // This is used to approximate where we currently are in the playback of a
   // media.
   // A call to ReadAt will update this position.
   virtual int64_t Tell() = 0;
 
   // These can be called on any thread.
   // Cached blocks associated with this stream will not be evicted
@@ -772,16 +761,29 @@ public:
         break;
       }
       aCount -= bytesRead;
       curr += bytesRead;
     }
     bytes->SetLength(curr - start);
     return bytes.forget();
   }
+
+  already_AddRefed<MediaByteBuffer> CachedMediaReadAt(int64_t aOffset,
+                                                      uint32_t aCount) const
+  {
+    RefPtr<MediaByteBuffer> bytes = new MediaByteBuffer();
+    bool ok = bytes->SetLength(aCount, fallible);
+    NS_ENSURE_TRUE(ok, nullptr);
+    char* curr = reinterpret_cast<char*>(bytes->Elements());
+    nsresult rv = mResource->ReadFromCache(curr, aOffset, aCount);
+    NS_ENSURE_SUCCESS(rv, nullptr);
+    return bytes.forget();
+  }
+
   // Get the length of the stream in bytes. Returns -1 if not known.
   // This can change over time; after a seek operation, a misbehaving
   // server may give us a resource of a different length to what it had
   // reported previously --- or it may just lie in its Content-Length
   // header and give us more or less data than it reported. We will adjust
   // the result of GetLength to reflect the data that's actually arriving.
   int64_t GetLength() const { return mResource->GetLength(); }
 
--- a/dom/media/webm/WebMBufferedParser.cpp
+++ b/dom/media/webm/WebMBufferedParser.cpp
@@ -441,20 +441,22 @@ void WebMBufferedState::UpdateIndex(cons
         length -= uint32_t(adjust);
       } else {
         mRangeParsers.InsertElementAt(idx, WebMBufferedParser(offset));
         if (idx) {
           mRangeParsers[idx].SetTimecodeScale(mRangeParsers[0].GetTimecodeScale());
         }
       }
     }
+
+    MediaResourceIndex res(aResource);
     while (length > 0) {
       static const uint32_t BLOCK_SIZE = 1048576;
       uint32_t block = std::min(length, BLOCK_SIZE);
-      RefPtr<MediaByteBuffer> bytes = aResource->CachedReadAt(offset, block);
+      RefPtr<MediaByteBuffer> bytes = res.CachedMediaReadAt(offset, block);
       if (!bytes) {
         break;
       }
       NotifyDataArrived(bytes->Elements(), bytes->Length(), offset);
       length -= bytes->Length();
       offset += bytes->Length();
     }
   }
--- a/dom/webidl/HTMLLinkElement.webidl
+++ b/dom/webidl/HTMLLinkElement.webidl
@@ -22,16 +22,18 @@ interface HTMLLinkElement : HTMLElement 
            attribute DOMString? crossOrigin;
   [CEReactions, SetterThrows, Pure]
            attribute DOMString rel;
   [PutForwards=value]
   readonly attribute DOMTokenList relList;
   [CEReactions, SetterThrows, Pure]
            attribute DOMString media;
   [CEReactions, SetterThrows, Pure]
+           attribute DOMString nonce;
+  [CEReactions, SetterThrows, Pure]
            attribute DOMString hreflang;
   [CEReactions, SetterThrows, Pure]
            attribute DOMString type;
   [CEReactions, SetterThrows, Pure]
            attribute DOMString referrerPolicy;
   [PutForwards=value] readonly attribute DOMTokenList sizes;
 };
 HTMLLinkElement implements LinkStyle;
--- a/dom/webidl/HTMLScriptElement.webidl
+++ b/dom/webidl/HTMLScriptElement.webidl
@@ -21,23 +21,21 @@ interface HTMLScriptElement : HTMLElemen
   [CEReactions, SetterThrows]
   attribute boolean async;
   [CEReactions, SetterThrows]
   attribute boolean defer;
   [CEReactions, SetterThrows]
   attribute DOMString? crossOrigin;
   [CEReactions, SetterThrows]
   attribute DOMString text;
+  [CEReactions, SetterThrows, Pure]
+  attribute DOMString nonce;
+  [CEReactions, SetterThrows, Pure]
+  attribute DOMString integrity;
 };
 
 // http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
 partial interface HTMLScriptElement {
   [CEReactions, SetterThrows]
   attribute DOMString event;
   [CEReactions, SetterThrows]
   attribute DOMString htmlFor;
 };
-
-// https://w3c.github.io/webappsec/specs/subresourceintegrity/#htmlscriptelement-1
-partial interface HTMLScriptElement {
-  [CEReactions, SetterThrows]
-  attribute DOMString integrity;
-};
--- a/dom/webidl/HTMLStyleElement.webidl
+++ b/dom/webidl/HTMLStyleElement.webidl
@@ -10,14 +10,16 @@
 
 [HTMLConstructor]
 interface HTMLStyleElement : HTMLElement {
            [Pure]
            attribute boolean disabled;
            [CEReactions, SetterThrows, Pure]
            attribute DOMString media;
            [CEReactions, SetterThrows, Pure]
+           attribute DOMString nonce;
+           [CEReactions, SetterThrows, Pure]
            attribute DOMString type;
            [SetterThrows, Pure, Pref="layout.css.scoped-style.enabled"]
            attribute boolean scoped;
 };
 HTMLStyleElement implements LinkStyle;
 
--- a/dom/xbl/nsXBLPrototypeResources.cpp
+++ b/dom/xbl/nsXBLPrototypeResources.cpp
@@ -174,16 +174,21 @@ nsXBLPrototypeResources::ComputeServoSty
                "This should only be called with Servo-flavored style backend!");
     // The XBL style sheets aren't document level sheets, but we need to
     // decide a particular SheetType to add them to style set. This type
     // doesn't affect the place where we pull those rules from
     // stylist::push_applicable_declarations_as_xbl_only_stylist().
     mServoStyleSet->AppendStyleSheet(SheetType::Doc, sheet->AsServo());
   }
   mServoStyleSet->UpdateStylistIfNeeded();
+
+  // The PresContext of the bound document could be destroyed anytime later,
+  // which shouldn't be used for XBL styleset, so we clear it here to avoid
+  // dangling pointer.
+  mServoStyleSet->ClearPresContext();
 }
 
 void
 nsXBLPrototypeResources::AppendStyleSheet(StyleSheet* aSheet)
 {
   mStyleSheetList.AppendElement(aSheet);
 }
 
--- a/gfx/layers/wr/StackingContextHelper.cpp
+++ b/gfx/layers/wr/StackingContextHelper.cpp
@@ -74,17 +74,17 @@ StackingContextHelper::StackingContextHe
                                              float* aOpacityPtr,
                                              gfx::Matrix4x4* aTransformPtr,
                                              gfx::Matrix4x4* aPerspectivePtr,
                                              const nsTArray<wr::WrFilterOp>& aFilters,
                                              const gfx::CompositionOp& aMixBlendMode)
   : mBuilder(&aBuilder)
 {
   nsRect visibleRect;
-  bool is2d = aTransformPtr && aTransformPtr->Is2D() && !aPerspectivePtr;
+  bool is2d = !aTransformPtr || (aTransformPtr->Is2D() && !aPerspectivePtr);
   if (is2d) {
     nsRect itemBounds = aDisplayList->GetClippedBoundsWithRespectToASR(aDisplayListBuilder, aItem->GetActiveScrolledRoot());
     nsRect childrenVisible = aItem->GetVisibleRectForChildren();
     visibleRect = itemBounds.Intersect(childrenVisible);
   } else {
     visibleRect = aDisplayList->GetBounds(aDisplayListBuilder);
     // The position of bounds are calculated by transform and perspective matrix in 3d case. reset it to (0, 0)
     visibleRect.MoveTo(0, 0);
--- a/intl/unicharutil/util/nsBidiUtils.cpp
+++ b/intl/unicharutil/util/nsBidiUtils.cpp
@@ -1,15 +1,19 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #include "nsBidiUtils.h"
 
+namespace mozilla {
+static const uint32_t kMinRTLChar = 0x0590;
+} // namespace mozilla;
+
 #define ARABIC_TO_HINDI_DIGIT_INCREMENT (START_HINDI_DIGITS - START_ARABIC_DIGITS)
 #define PERSIAN_TO_HINDI_DIGIT_INCREMENT (START_HINDI_DIGITS - START_FARSI_DIGITS)
 #define ARABIC_TO_PERSIAN_DIGIT_INCREMENT (START_FARSI_DIGITS - START_ARABIC_DIGITS)
 #define NUM_TO_ARABIC(c) \
   ((((c)>=START_HINDI_DIGITS) && ((c)<=END_HINDI_DIGITS)) ? \
    ((c) - (uint16_t)ARABIC_TO_HINDI_DIGIT_INCREMENT) : \
    ((((c)>=START_FARSI_DIGITS) && ((c)<=END_FARSI_DIGITS)) ? \
     ((c) - (uint16_t)ARABIC_TO_PERSIAN_DIGIT_INCREMENT) : \
@@ -85,16 +89,19 @@ nsresult HandleNumbers(char16_t* aBuffer
 bool HasRTLChars(const char16_t* aText, uint32_t aLength)
 {
   // This is used to determine whether a string has right-to-left characters
   // that mean it will require bidi processing.
   const char16_t* cp = aText;
   const char16_t* end = cp + aLength;
   while (cp < end) {
     uint32_t ch = *cp++;
+    if (ch < mozilla::kMinRTLChar) {
+      continue;
+    }
     if (NS_IS_HIGH_SURROGATE(ch) && cp < end && NS_IS_LOW_SURROGATE(*cp)) {
       ch = SURROGATE_TO_UCS4(ch, *cp++);
     }
     if (UTF32_CHAR_IS_BIDI(ch) || IsBidiControlRTL(ch)) {
       return true;
     }
   }
   return false;
--- a/js/xpconnect/idl/xpccomponents.idl
+++ b/js/xpconnect/idl/xpccomponents.idl
@@ -39,16 +39,18 @@ interface nsIXPCComponents_Interfaces : 
 
 /**
 * interface of Components.classes
 * (interesting stuff only reflected into JavaScript)
 */
 [scriptable, uuid(978ff520-d26c-11d2-9842-006008962422)]
 interface nsIXPCComponents_Classes : nsISupports
 {
+  // Make it so that |cid| gets mapped to |idString|.
+  void initialize(in nsIJSCID cid, in string idString);
 };
 
 /**
 * interface of Components.classesByID
 * (interesting stuff only reflected into JavaScript)
 */
 [scriptable, uuid(336a9590-4d19-11d3-9893-006008962422)]
 interface nsIXPCComponents_ClassesByID : nsISupports
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -749,16 +749,24 @@ nsXPCComponents_Classes::Resolve(nsIXPCo
                                                      JSPROP_RESOLVING);
                 }
             }
         }
     }
     return NS_OK;
 }
 
+NS_IMETHODIMP
+nsXPCComponents_Classes::Initialize(nsIJSCID* cid,
+                                    const char* str)
+{
+    return cid->Initialize(str);
+}
+
+
 /***************************************************************************/
 /***************************************************************************/
 /***************************************************************************/
 
 class nsXPCComponents_ClassesByID final :
   public nsIXPCComponents_ClassesByID,
   public nsIXPCScriptable,
   public nsIClassInfo
--- a/js/xpconnect/src/XPCJSID.cpp
+++ b/js/xpconnect/src/XPCJSID.cpp
@@ -565,18 +565,36 @@ NS_IMETHODIMP_(const nsID*) nsJSCID::Get
     {return &mDetails->ID();}
 
 NS_IMETHODIMP nsJSCID::GetValid(bool* aValid)
     {return mDetails->GetValid(aValid);}
 
 NS_IMETHODIMP nsJSCID::Equals(nsIJSID* other, bool* _retval)
     {return mDetails->Equals(other, _retval);}
 
-NS_IMETHODIMP nsJSCID::Initialize(const char* idString)
-    {return mDetails->Initialize(idString);}
+NS_IMETHODIMP
+nsJSCID::Initialize(const char* str)
+{
+    if (str[0] == '{') {
+        NS_ENSURE_SUCCESS(mDetails->Initialize(str), NS_ERROR_FAILURE);
+    } else {
+        nsCOMPtr<nsIComponentRegistrar> registrar;
+        NS_GetComponentRegistrar(getter_AddRefs(registrar));
+        NS_ENSURE_TRUE(registrar, NS_ERROR_FAILURE);
+
+        nsCID* cid;
+        if (NS_FAILED(registrar->ContractIDToCID(str, &cid)))
+            return NS_ERROR_FAILURE;
+        bool success = mDetails->InitWithName(*cid, str);
+        free(cid);
+        if (!success)
+            return NS_ERROR_FAILURE;
+    }
+    return NS_OK;
+}
 
 NS_IMETHODIMP nsJSCID::ToString(char** _retval)
     {ResolveName(); return mDetails->ToString(_retval);}
 
 void
 nsJSCID::ResolveName()
 {
     if (!mDetails->NameIsSet())
@@ -588,31 +606,18 @@ already_AddRefed<nsJSCID>
 nsJSCID::NewID(const char* str)
 {
     if (!str) {
         NS_ERROR("no string");
         return nullptr;
     }
 
     RefPtr<nsJSCID> idObj = new nsJSCID();
-    if (str[0] == '{') {
-        NS_ENSURE_SUCCESS(idObj->Initialize(str), nullptr);
-    } else {
-        nsCOMPtr<nsIComponentRegistrar> registrar;
-        NS_GetComponentRegistrar(getter_AddRefs(registrar));
-        NS_ENSURE_TRUE(registrar, nullptr);
-
-        nsCID* cid;
-        if (NS_FAILED(registrar->ContractIDToCID(str, &cid)))
-            return nullptr;
-        bool success = idObj->mDetails->InitWithName(*cid, str);
-        free(cid);
-        if (!success)
-            return nullptr;
-    }
+    if (NS_FAILED(idObj->Initialize(str)))
+        return nullptr;
     return idObj.forget();
 }
 
 static const nsID*
 GetIIDArg(uint32_t argc, const JS::Value& val, JSContext* cx)
 {
     const nsID* iid;
 
--- a/layout/generic/nsHTMLCanvasFrame.cpp
+++ b/layout/generic/nsHTMLCanvasFrame.cpp
@@ -135,17 +135,17 @@ public:
       case CanvasContextType::WebGL2:
       {
         bool isRecycled;
         RefPtr<WebRenderCanvasData> canvasData =
           aManager->CreateOrRecycleWebRenderUserData<WebRenderCanvasData>(this, &isRecycled);
         WebRenderCanvasRendererAsync* data =
           static_cast<WebRenderCanvasRendererAsync*>(canvasData->GetCanvasRenderer());
 
-        if (isRecycled) {
+        if (!isRecycled) {
           nsHTMLCanvasFrame* canvasFrame = static_cast<nsHTMLCanvasFrame*>(mFrame);
           if (!canvasFrame->InitializeCanvasRenderer(aDisplayListBuilder, data)) {
             return true;
           }
         }
 
         data->UpdateCompositableClient();
 
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -5145,28 +5145,30 @@ nsDisplayText::nsDisplayText(nsDisplayLi
 
   if (gfxPrefs::LayersAllowTextLayers() &&
       CanUseAdvancedLayer(aBuilder->GetWidgetLayerManager())) {
     mTextDrawer = new TextDrawTarget();
     RefPtr<gfxContext> captureCtx = gfxContext::CreateOrNull(mTextDrawer);
 
     // TODO: Paint() checks mDisableSubpixelAA, we should too.
     RenderToContext(captureCtx, mTextDrawer, aBuilder, true);
+
+    if (!mTextDrawer->CanSerializeFonts()) {
+      mTextDrawer = nullptr;
+    }
   }
 }
 
 LayerState
 nsDisplayText::GetLayerState(nsDisplayListBuilder* aBuilder,
                              LayerManager* aManager,
                              const ContainerLayerParameters& aParameters)
 {
   // Basic things that all advanced backends need
-  if (!mTextDrawer ||
-      !mTextDrawer->CanSerializeFonts() ||
-      XRE_IsParentProcess()) {
+  if (!mTextDrawer) {
     return mozilla::LAYER_NONE;
   }
 
   // If we're using the webrender backend, then we're good to go!
   if (aManager->GetBackendType() == layers::LayersBackend::LAYERS_WR) {
     return mozilla::LAYER_ACTIVE;
   }
 
--- a/layout/style/ServoStyleSet.cpp
+++ b/layout/style/ServoStyleSet.cpp
@@ -1450,19 +1450,21 @@ ServoStyleSet::RunPostTraversalTasks()
   }
 }
 
 ServoStyleRuleMap*
 ServoStyleSet::StyleRuleMap()
 {
   if (!mStyleRuleMap) {
     mStyleRuleMap = new ServoStyleRuleMap(this);
-    nsIDocument* doc = mPresContext->Document();
-    doc->AddObserver(mStyleRuleMap);
-    doc->CSSLoader()->AddObserver(mStyleRuleMap);
+    if (mPresContext) {
+      nsIDocument* doc = mPresContext->Document();
+      doc->AddObserver(mStyleRuleMap);
+      doc->CSSLoader()->AddObserver(mStyleRuleMap);
+    }
   }
   return mStyleRuleMap;
 }
 
 bool
 ServoStyleSet::MightHaveAttributeDependency(const Element& aElement,
                                             nsIAtom* aAttribute) const
 {
--- a/layout/style/ServoStyleSet.h
+++ b/layout/style/ServoStyleSet.h
@@ -416,16 +416,21 @@ public:
   // its inner.
   void SetNeedsRestyleAfterEnsureUniqueInner() {
     mNeedsRestyleAfterEnsureUniqueInner = true;
   }
 
   // Returns the style rule map.
   ServoStyleRuleMap* StyleRuleMap();
 
+  // Clear mPresContext. This is needed after XBL ServoStyleSet is created.
+  void ClearPresContext() {
+    mPresContext = nullptr;
+  }
+
   /**
    * Returns true if a modification to an an attribute with the specified
    * local name might require us to restyle the element.
    *
    * This function allows us to skip taking a an attribute snapshot when
    * the modified attribute doesn't appear in an attribute selector in
    * a style sheet.
    */
--- a/media/webrtc/trunk/webrtc/modules/audio_device/android/audio_device_template.h
+++ b/media/webrtc/trunk/webrtc/modules/audio_device/android/audio_device_template.h
@@ -99,17 +99,17 @@ class AudioDeviceTemplate : public Audio
     LOG(INFO) << __FUNCTION__;
     return 1;
   }
 
   int32_t PlayoutDeviceName(
       uint16_t index,
       char name[kAdmMaxDeviceNameSize],
       char guid[kAdmMaxGuidSize]) override {
-    FATAL() << "Should never be called";
+    LOG(INFO) << __FUNCTION__;
     return -1;
   }
 
   int32_t RecordingDeviceName(
       uint16_t index,
       char name[kAdmMaxDeviceNameSize],
       char guid[kAdmMaxGuidSize]) override {
     LOG(INFO) << __FUNCTION__;
@@ -120,30 +120,30 @@ class AudioDeviceTemplate : public Audio
     // OK to use but it has no effect currently since device selection is
     // done using Andoid APIs instead.
     LOG(INFO) << __FUNCTION__;
     return 0;
   }
 
   int32_t SetPlayoutDevice(
       AudioDeviceModule::WindowsDeviceType device) override {
-    FATAL() << "Should never be called";
+    LOG(INFO) << __FUNCTION__;
     return -1;
   }
 
   int32_t SetRecordingDevice(uint16_t index) override {
     // OK to use but it has no effect currently since device selection is
     // done using Andoid APIs instead.
     LOG(INFO) << __FUNCTION__;
     return 0;
   }
 
   int32_t SetRecordingDevice(
       AudioDeviceModule::WindowsDeviceType device) override {
-    FATAL() << "Should never be called";
+    LOG(INFO) << __FUNCTION__;
     return -1;
   }
 
   int32_t PlayoutIsAvailable(bool& available) override {
     LOG(INFO) << __FUNCTION__;
     available = true;
     return 0;
   }
@@ -215,36 +215,34 @@ class AudioDeviceTemplate : public Audio
     return err;
   }
 
   bool Recording() const override {
     return input_.Recording() ;
   }
 
   int32_t SetAGC(bool enable) override {
-    if (enable) {
-      FATAL() << "Should never be called";
-    }
+    LOG(INFO) << __FUNCTION__;
     return -1;
   }
 
   bool AGC() const override {
     LOG(INFO) << __FUNCTION__;
     return false;
   }
 
   int32_t SetWaveOutVolume(
       uint16_t volumeLeft, uint16_t volumeRight) override {
-     FATAL() << "Should never be called";
+    LOG(INFO) << __FUNCTION__;
     return -1;
   }
 
   int32_t WaveOutVolume(
       uint16_t& volumeLeft, uint16_t& volumeRight) const override {
-    FATAL() << "Should never be called";
+    LOG(INFO) << __FUNCTION__;
     return -1;
   }
 
   int32_t InitSpeaker() override {
     LOG(INFO) << __FUNCTION__;
     return 0;
   }
 
@@ -284,92 +282,98 @@ class AudioDeviceTemplate : public Audio
   }
 
   int32_t MinSpeakerVolume(uint32_t& minVolume) const override {
     LOG(INFO) << __FUNCTION__;
     return output_.MinSpeakerVolume(minVolume);
   }
 
   int32_t SpeakerVolumeStepSize(uint16_t& stepSize) const override {
-    FATAL() << "Should never be called";
+    LOG(INFO) << __FUNCTION__;
     return -1;
   }
 
   int32_t MicrophoneVolumeIsAvailable(bool& available) override{
     available = false;
     return -1;
   }
 
   int32_t SetMicrophoneVolume(uint32_t volume) override {
-    FATAL() << "Should never be called";
+    LOG(INFO) << __FUNCTION__;
     return -1;
   }
 
   int32_t MicrophoneVolume(uint32_t& volume) const override {
-    FATAL() << "Should never be called";
+    LOG(INFO) << __FUNCTION__;
     return -1;
   }
 
   int32_t MaxMicrophoneVolume(uint32_t& maxVolume) const override {
-    FATAL() << "Should never be called";
+    LOG(INFO) << __FUNCTION__;
     return -1;
   }
 
   int32_t MinMicrophoneVolume(uint32_t& minVolume) const override {
-    FATAL() << "Should never be called";
+    LOG(INFO) << __FUNCTION__;
     return -1;
   }
 
   int32_t MicrophoneVolumeStepSize(uint16_t& stepSize) const override {
-    FATAL() << "Should never be called";
+    LOG(INFO) << __FUNCTION__;
     return -1;
   }
 
   int32_t SpeakerMuteIsAvailable(bool& available) override {
-    FATAL() << "Should never be called";
+    LOG(INFO) << __FUNCTION__;
+    available = false;
     return -1;
   }
 
   int32_t SetSpeakerMute(bool enable) override {
-    FATAL() << "Should never be called";
+    LOG(INFO) << __FUNCTION__;
     return -1;
   }
 
   int32_t SpeakerMute(bool& enabled) const override {
-    FATAL() << "Should never be called";
+    LOG(INFO) << __FUNCTION__;
+    enabled = false;
     return -1;
   }
 
   int32_t MicrophoneMuteIsAvailable(bool& available) override {
-    FATAL() << "Not implemented";
+    LOG(INFO) << __FUNCTION__;
+    available = false;
     return -1;
   }
 
   int32_t SetMicrophoneMute(bool enable) override {
-    FATAL() << "Not implemented";
+    LOG(INFO) << __FUNCTION__;
     return -1;
   }
 
   int32_t MicrophoneMute(bool& enabled) const override {
-    FATAL() << "Not implemented";
+    LOG(INFO) << __FUNCTION__;
+    enabled = false;
     return -1;
   }
 
   int32_t MicrophoneBoostIsAvailable(bool& available) override {
-    FATAL() << "Should never be called";
+    LOG(INFO) << __FUNCTION__;
+    available = false;
     return -1;
   }
 
   int32_t SetMicrophoneBoost(bool enable) override {
-    FATAL() << "Should never be called";
+    LOG(INFO) << __FUNCTION__;
     return -1;
   }
 
   int32_t MicrophoneBoost(bool& enabled) const override {
-    FATAL() << "Should never be called";
+    LOG(INFO) << __FUNCTION__;
+    enabled = false;
     return -1;
   }
 
   int32_t StereoPlayoutIsAvailable(bool& available) override {
     LOG(INFO) << __FUNCTION__;
     available = false;
     return 0;
   }
@@ -378,17 +382,17 @@ class AudioDeviceTemplate : public Audio
   int32_t SetStereoPlayout(bool enable) override {
     LOG(INFO) << __FUNCTION__;
     return -1;
   }
 
   // TODO(henrika): add support.
   int32_t StereoPlayout(bool& enabled) const override {
     enabled = false;
-    FATAL() << "Should never be called";
+    LOG(INFO) << __FUNCTION__;
     return -1;
   }
 
   int32_t StereoRecordingIsAvailable(bool& available) override {
     LOG(INFO) << __FUNCTION__;
     available = false;
     return 0;
   }
@@ -401,23 +405,23 @@ class AudioDeviceTemplate : public Audio
   int32_t StereoRecording(bool& enabled) const override {
     LOG(INFO) << __FUNCTION__;
     enabled = false;
     return 0;
   }
 
   int32_t SetPlayoutBuffer(
       const AudioDeviceModule::BufferType type, uint16_t sizeMS) override {
-    FATAL() << "Should never be called";
+    LOG(INFO) << __FUNCTION__;
     return -1;
   }
 
   int32_t PlayoutBuffer(
       AudioDeviceModule::BufferType& type, uint16_t& sizeMS) const override {
-    FATAL() << "Should never be called";
+    LOG(INFO) << __FUNCTION__;
     return -1;
   }
 
   int32_t PlayoutDelay(uint16_t& delay_ms) const override {
     // Best guess we can do is to use half of the estimated total delay.
     delay_ms = audio_manager_->GetDelayEstimateInMilliseconds() / 2;
     RTC_DCHECK_GT(delay_ms, 0);
     return 0;
@@ -427,17 +431,17 @@ class AudioDeviceTemplate : public Audio
     // Best guess we can do is to use half of the estimated total delay.
     LOG(INFO) << __FUNCTION__;
     delay_ms = audio_manager_->GetDelayEstimateInMilliseconds() / 2;
     RTC_DCHECK_GT(delay_ms, 0);
     return 0;
   }
 
   int32_t CPULoad(uint16_t& load) const override {
-    FATAL() << "Should never be called";
+    LOG(INFO) << __FUNCTION__;
     return -1;
   }
 
   bool PlayoutWarning() const override {
     return false;
   }
 
   bool PlayoutError() const override {
@@ -463,27 +467,27 @@ class AudioDeviceTemplate : public Audio
   void AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) override {
     LOG(INFO) << __FUNCTION__;
     output_.AttachAudioBuffer(audioBuffer);
     input_.AttachAudioBuffer(audioBuffer);
   }
 
   // TODO(henrika): remove
   int32_t SetPlayoutSampleRate(const uint32_t samplesPerSec) override {
-    FATAL() << "Should never be called";
+    LOG(INFO) << __FUNCTION__;
     return -1;
   }
 
   int32_t SetLoudspeakerStatus(bool enable) override {
-    FATAL() << "Should never be called";
+    LOG(INFO) << __FUNCTION__;
     return -1;
   }
 
   int32_t GetLoudspeakerStatus(bool& enable) const override {
-    FATAL() << "Should never be called";
+    LOG(INFO) << __FUNCTION__;
     return -1;
   }
 
   // Returns true if the device both supports built in AEC and the device
   // is not blacklisted.
   // Currently, if OpenSL ES is used in both directions, this method will still
   // report the correct value and it has the correct effect. As an example:
   // a device supports built in AEC and this method returns true. Libjingle
--- a/mobile/android/components/extensions/ext-browserAction.js
+++ b/mobile/android/components/extensions/ext-browserAction.js
@@ -79,17 +79,17 @@ class BrowserAction {
       let properties = this.tabContext.get(tab.id);
       if (value) {
         properties[prop] = value;
       } else {
         delete properties[prop];
       }
     }
 
-    if (!tab || tab.selected) {
+    if (!tab || tab.getActive()) {
       BrowserActions.update(this.uuid, {[prop]: value});
     }
   }
 
   /**
    * Retreives a property of the browser action for the specified tab.
    *
    * @param {Object} tab The tab to retrieve the property from. If null, the default value is returned.
--- a/mobile/android/components/extensions/test/mochitest/test_ext_browserAction_getTitle_setTitle.html
+++ b/mobile/android/components/extensions/test/mochitest/test_ext_browserAction_getTitle_setTitle.html
@@ -135,12 +135,43 @@ add_task(async function test_setTitle_an
   await checkTab(tabs.tab3, "tab 3");
   await checkTab(tabs.tab1, "Updated Title");
 
   extension.sendMessage("finish");
   await extension.awaitFinish("browserAction.setTitleAndGetTitle");
 
   await extension.unload();
 });
+
+add_task(async function test_setTitle_activeTab() {
+  async function background() {
+    const tabs = await browser.tabs.query({active: true});
+    const tabId = tabs[0].id;
+
+    const title = "Customized browserAction title";
+    await browser.browserAction.setTitle({tabId, title});
+
+    browser.test.notifyPass("browserAction_setTitle_activeTab.done");
+  }
+
+  const extension = ExtensionTestUtils.loadExtension({
+    background,
+    manifest: {
+      "browser_action": {
+        "default_title": "Browser Action default title",
+      },
+    },
+  });
+
+  await extension.startup();
+
+  await extension.awaitFinish("browserAction_setTitle_activeTab.done");
+
+  is(BrowserActions.getNameForActiveTab(`{${extension.uuid}}`),
+     "Customized browserAction title",
+     "The browserAction title has been updated on the currently activeTab");
+
+  await extension.unload();
+});
 </script>
 
 </body>
 </html>
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -3673,25 +3673,19 @@ pref("font.name-list.sans-serif.el", "Ar
 pref("font.name-list.monospace.el", "Courier New");
 pref("font.name-list.cursive.el", "Comic Sans MS");
 
 pref("font.name-list.serif.he", "Narkisim, David");
 pref("font.name-list.sans-serif.he", "Arial");
 pref("font.name-list.monospace.he", "Fixed Miriam Transparent, Miriam Fixed, Rod, Courier New");
 pref("font.name-list.cursive.he", "Guttman Yad, Ktav, Arial");
 
-#ifdef EARLY_BETA_OR_EARLIER
 pref("font.name-list.serif.ja", "Yu Mincho, MS PMincho, MS Mincho, Meiryo, Yu Gothic, MS PGothic, MS Gothic");
 pref("font.name-list.sans-serif.ja", "Meiryo, Yu Gothic, MS PGothic, MS Gothic, Yu Mincho, MS PMincho, MS Mincho");
 pref("font.name-list.monospace.ja", "MS Gothic, MS Mincho, Meiryo, Yu Gothic, Yu Mincho, MS PGothic, MS PMincho");
-#else
-pref("font.name-list.serif.ja", "MS PMincho, MS Mincho, MS PGothic, MS Gothic,Meiryo");
-pref("font.name-list.sans-serif.ja", "MS PGothic, MS Gothic, MS PMincho, MS Mincho,Meiryo");
-pref("font.name-list.monospace.ja", "MS Gothic, MS Mincho, MS PGothic, MS PMincho,Meiryo");
-#endif
 
 pref("font.name-list.serif.ko", "Batang, Gulim");
 pref("font.name-list.sans-serif.ko", "Gulim");
 pref("font.name-list.monospace.ko", "GulimChe");
 pref("font.name-list.cursive.ko", "Gungsuh");
 
 pref("font.name-list.serif.th", "Tahoma");
 pref("font.name-list.sans-serif.th", "Tahoma");
--- a/modules/pdfium/moz.build
+++ b/modules/pdfium/moz.build
@@ -554,9 +554,12 @@ LOCAL_INCLUDES += [
 ]
 USE_LIBS += [
     'media_libjpeg',
 ]
 
 # We allow warnings for third-party code that can be updated from upstream.
 ALLOW_COMPILER_WARNINGS = True
 
-FINAL_LIBRARY = 'xul'
+GeckoSharedLibrary('pdfium', linkage=None)
+
+if CONFIG['OS_TARGET'] == 'WINNT':
+    DEFFILE = SRCDIR + '/pdfium.def'
new file mode 100644
--- /dev/null
+++ b/modules/pdfium/pdfium.def
@@ -0,0 +1,14 @@
+; 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/.
+
+LIBRARY pdfium
+EXPORTS
+    FPDF_InitLibrary
+    FPDF_DestroyLibrary
+    FPDF_LoadDocument
+    FPDF_CloseDocument
+    FPDF_GetPageCount
+    FPDF_LoadPage
+    FPDF_ClosePage
+    FPDF_RenderPage
--- a/servo/Cargo.lock
+++ b/servo/Cargo.lock
@@ -1710,27 +1710,32 @@ dependencies = [
  "toml 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "metrics"
 version = "0.0.1"
 dependencies = [
  "gfx 0.0.1",
+ "gfx_traits 0.0.1",
+ "ipc-channel 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "msg 0.0.1",
  "profile_traits 0.0.1",
+ "script_traits 0.0.1",
  "servo_config 0.0.1",
- "time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "metrics_tests"
 version = "0.0.1"
 dependencies = [
  "euclid 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "gfx 0.0.1",
+ "gfx_traits 0.0.1",
  "ipc-channel 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "metrics 0.0.1",
  "msg 0.0.1",
  "net_traits 0.0.1",
  "profile_traits 0.0.1",
  "style 0.0.1",
  "time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
@@ -2592,17 +2597,16 @@ dependencies = [
  "euclid 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "gfx_traits 0.0.1",
  "heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize_derive 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "hyper 0.10.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "hyper_serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "ipc-channel 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
- "metrics 0.0.1",
  "msg 0.0.1",
  "net_traits 0.0.1",
  "profile_traits 0.0.1",
  "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "servo_atoms 0.0.1",
  "servo_url 0.0.1",
  "style_traits 0.0.1",
--- a/servo/components/compositing/compositor.rs
+++ b/servo/components/compositing/compositor.rs
@@ -181,16 +181,23 @@ pub struct IOCompositor<Window: WindowMe
     /// The active webrender document.
     webrender_document: webrender_api::DocumentId,
 
     /// The webrender interface, if enabled.
     webrender_api: webrender_api::RenderApi,
 
     /// GL functions interface (may be GL or GLES)
     gl: Rc<gl::Gl>,
+
+    /// Map of the pending paint metrics per layout thread.
+    /// The layout thread for each specific pipeline expects the compositor to
+    /// paint frames with specific given IDs (epoch). Once the compositor paints
+    /// these frames, it records the paint time for each of them and sends the
+    /// metric to the corresponding layout thread.
+    pending_paint_metrics: HashMap<PipelineId, Epoch>,
 }
 
 #[derive(Copy, Clone)]
 struct ScrollZoomEvent {
     /// Change the pinch zoom level by this factor
     magnification: f32,
     /// Scroll by this offset, or to Start or End
     scroll_location: ScrollLocation,
@@ -366,16 +373,17 @@ impl<Window: WindowMethods> IOCompositor
             time_profiler_chan: state.time_profiler_chan,
             last_composite_time: 0,
             ready_to_save_state: ReadyState::Unknown,
             scroll_in_progress: false,
             in_scroll_transaction: None,
             webrender: state.webrender,
             webrender_document: state.webrender_document,
             webrender_api: state.webrender_api,
+            pending_paint_metrics: HashMap::new(),
         }
     }
 
     pub fn create(window: Rc<Window>, state: InitialCompositorState) -> IOCompositor<Window> {
         let mut compositor = IOCompositor::new(window, state);
 
         let compositor_proxy_for_webrender = compositor.channel_to_self
                                                        .clone_compositor_proxy();
@@ -588,16 +596,20 @@ impl<Window: WindowMethods> IOCompositor
                 // But if we start running more complex code here, we should really catch panic here.
                 func();
             }
 
             (Msg::SetFullscreenState(top_level_browsing_context_id, state), ShutdownState::NotShuttingDown) => {
                 self.window.set_fullscreen_state(top_level_browsing_context_id, state);
             }
 
+            (Msg::PendingPaintMetric(pipeline_id, epoch), _) => {
+                self.pending_paint_metrics.insert(pipeline_id, epoch);
+            }
+
             // When we are shutting_down, we need to avoid performing operations
             // such as Paint that may crash because we have begun tearing down
             // the rest of our resources.
             (_, ShutdownState::ShuttingDown) => { }
         }
 
         true
     }
@@ -1422,16 +1434,48 @@ impl<Window: WindowMethods> IOCompositor
 
         profile(ProfilerCategory::Compositing, None, self.time_profiler_chan.clone(), || {
             debug!("compositor: compositing");
 
             // Paint the scene.
             self.webrender.render(self.frame_size);
         });
 
+        // If there are pending paint metrics, we check if any of the painted epochs is
+        // one of the ones that the paint metrics recorder is expecting . In that case,
+        // we get the current time, inform the layout thread about it and remove the
+        // pending metric from the list.
+        if !self.pending_paint_metrics.is_empty() {
+            let paint_time = precise_time_ns() as f64;
+            let mut to_remove = Vec::new();
+            // For each pending paint metrics pipeline id
+            for (id, pending_epoch) in &self.pending_paint_metrics {
+                // we get the last painted frame id from webrender
+                if let Some(webrender_api::Epoch(epoch)) = self.webrender.current_epoch(id.to_webrender()) {
+                    // and check if it is the one the layout thread is expecting,
+                    let epoch = Epoch(epoch);
+                    if *pending_epoch != epoch {
+                        continue;
+                    }
+                    // in which case, we remove it from the list of pending metrics,
+                    to_remove.push(id.clone());
+                    if let Some(pipeline) = self.pipeline(*id) {
+                        // and inform the layout thread with the measured paint time.
+                        let msg = LayoutControlMsg::PaintMetric(epoch, paint_time);
+                        if let Err(e)  = pipeline.layout_chan.send(msg) {
+                            warn!("Sending PaintMetric message to layout failed ({}).", e);
+                        }
+                    }
+                }
+            }
+            for id in to_remove.iter() {
+                self.pending_paint_metrics.remove(id);
+            }
+        }
+
         let rv = match target {
             CompositeTarget::Window => None,
             CompositeTarget::WindowAndPng => {
                 let img = self.draw_img(render_target_info,
                                         width,
                                         height);
                 Some(Image {
                     width: img.width(),
--- a/servo/components/compositing/compositor_thread.rs
+++ b/servo/components/compositing/compositor_thread.rs
@@ -2,16 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Communication with the compositor thread.
 
 use SendableFrameTree;
 use compositor::CompositingReason;
 use euclid::{Point2D, Size2D};
+use gfx_traits::Epoch;
 use ipc_channel::ipc::IpcSender;
 use msg::constellation_msg::{Key, KeyModifiers, KeyState, PipelineId, TopLevelBrowsingContextId};
 use net_traits::image::base::Image;
 use profile_traits::mem;
 use profile_traits::time;
 use script_traits::{AnimationState, ConstellationMsg, EventResult, LoadData};
 use servo_url::ServoUrl;
 use std::fmt::{Debug, Error, Formatter};
@@ -138,16 +139,20 @@ pub enum Msg {
     // tear down the other threads associated with this pipeline.
     PipelineExited(PipelineId, IpcSender<()>),
     /// Runs a closure in the compositor thread.
     /// It's used to dispatch functions from webrender to the main thread's event loop.
     /// Required to allow WGL GLContext sharing in Windows.
     Dispatch(Box<Fn() + Send>),
     /// Enter or exit fullscreen
     SetFullscreenState(TopLevelBrowsingContextId, bool),
+    /// Indicates to the compositor that it needs to record the time when the frame with
+    /// the given ID (epoch) is painted and report it to the layout thread of the given
+    /// pipeline ID.
+    PendingPaintMetric(PipelineId, Epoch),
 }
 
 impl Debug for Msg {
     fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
         match *self {
             Msg::Exit => write!(f, "Exit"),
             Msg::ShutdownComplete => write!(f, "ShutdownComplete"),
             Msg::ScrollFragmentPoint(..) => write!(f, "ScrollFragmentPoint"),
@@ -171,16 +176,17 @@ impl Debug for Msg {
             Msg::GetClientWindow(..) => write!(f, "GetClientWindow"),
             Msg::MoveTo(..) => write!(f, "MoveTo"),
             Msg::ResizeTo(..) => write!(f, "ResizeTo"),
             Msg::PipelineVisibilityChanged(..) => write!(f, "PipelineVisibilityChanged"),
             Msg::PipelineExited(..) => write!(f, "PipelineExited"),
             Msg::NewScrollFrameReady(..) => write!(f, "NewScrollFrameReady"),
             Msg::Dispatch(..) => write!(f, "Dispatch"),
             Msg::SetFullscreenState(..) => write!(f, "SetFullscreenState"),
+            Msg::PendingPaintMetric(..) => write!(f, "PendingPaintMetric"),
         }
     }
 }
 
 /// Data used to construct a compositor.
 pub struct InitialCompositorState {
     /// A channel to the compositor.
     pub sender: CompositorProxy,
--- a/servo/components/constellation/constellation.rs
+++ b/servo/components/constellation/constellation.rs
@@ -1250,16 +1250,20 @@ impl<Message, LTF, STF> Constellation<Me
                 self.handle_change_running_animations_state(pipeline_id, animation_state)
             }
             // Layout sends new sizes for all subframes. This needs to be reflected by all
             // frame trees in the navigation context containing the subframe.
             FromLayoutMsg::IFrameSizes(iframe_sizes) => {
                 debug!("constellation got iframe size message");
                 self.handle_iframe_size_msg(iframe_sizes);
             }
+            FromLayoutMsg::PendingPaintMetric(pipeline_id, epoch) => {
+                debug!("constellation got a pending paint metric message");
+                self.handle_pending_paint_metric(pipeline_id, epoch);
+            }
             FromLayoutMsg::SetCursor(cursor) => {
                 self.handle_set_cursor_msg(cursor)
             }
             FromLayoutMsg::ViewportConstrained(pipeline_id, constraints) => {
                 debug!("constellation got viewport-constrained event message");
                 self.handle_viewport_constrained_msg(pipeline_id, constraints);
             }
         }
@@ -1695,16 +1699,20 @@ impl<Message, LTF, STF> Constellation<Me
             top_level_browsing_context_id: top_level_browsing_context_id,
             browsing_context_id: browsing_context_id,
             new_pipeline_id: new_pipeline_id,
             load_data: load_data,
             replace_instant: replace_instant,
         });
     }
 
+    fn handle_pending_paint_metric(&self, pipeline_id: PipelineId, epoch: Epoch) {
+        self.compositor_proxy.send(ToCompositorMsg::PendingPaintMetric(pipeline_id, epoch))
+    }
+
     fn handle_set_cursor_msg(&mut self, cursor: Cursor) {
         self.compositor_proxy.send(ToCompositorMsg::SetCursor(cursor))
     }
 
     fn handle_change_running_animations_state(&mut self,
                                               pipeline_id: PipelineId,
                                               animation_state: AnimationState) {
         self.compositor_proxy.send(ToCompositorMsg::ChangeRunningAnimationsState(pipeline_id,
--- a/servo/components/constellation/pipeline.rs
+++ b/servo/components/constellation/pipeline.rs
@@ -481,17 +481,19 @@ pub struct UnprivilegedPipelineContent {
 }
 
 impl UnprivilegedPipelineContent {
     pub fn start_all<Message, LTF, STF>(self, wait_for_completion: bool)
         where LTF: LayoutThreadFactory<Message=Message>,
               STF: ScriptThreadFactory<Message=Message>
     {
         let image_cache = Arc::new(ImageCacheImpl::new(self.webrender_api_sender.create_api()));
-        let paint_time_metrics = PaintTimeMetrics::new(self.time_profiler_chan.clone());
+        let paint_time_metrics = PaintTimeMetrics::new(self.id,
+                                                       self.time_profiler_chan.clone(),
+                                                       self.layout_to_constellation_chan.clone());
         let layout_pair = STF::create(InitialScriptState {
             id: self.id,
             browsing_context_id: self.browsing_context_id,
             top_level_browsing_context_id: self.top_level_browsing_context_id,
             parent_info: self.parent_info,
             control_chan: self.script_chan.clone(),
             control_port: self.script_port,
             script_to_constellation_chan: self.script_to_constellation_chan.clone(),
--- a/servo/components/gfx_traits/lib.rs
+++ b/servo/components/gfx_traits/lib.rs
@@ -13,17 +13,17 @@ extern crate heapsize;
 #[macro_use] extern crate serde;
 
 pub mod print_tree;
 
 use range::RangeIndex;
 use std::sync::atomic::{ATOMIC_USIZE_INIT, AtomicUsize, Ordering};
 
 /// A newtype struct for denoting the age of messages; prevents race conditions.
-#[derive(PartialEq, Eq, Debug, Copy, Clone, PartialOrd, Ord, Deserialize, Serialize)]
+#[derive(PartialEq, Eq, Debug, Copy, Clone, PartialOrd, Ord, Deserialize, Serialize, Hash)]
 pub struct Epoch(pub u32);
 
 impl Epoch {
     pub fn next(&mut self) {
         self.0 += 1;
     }
 }
 
--- a/servo/components/layout_thread/lib.rs
+++ b/servo/components/layout_thread/lib.rs
@@ -124,80 +124,46 @@ use std::thread;
 use style::animation::Animation;
 use style::context::{QuirksMode, ReflowGoal, SharedStyleContext};
 use style::context::{StyleSystemOptions, ThreadLocalStyleContextCreationInfo};
 use style::context::RegisteredSpeculativePainter;
 use style::context::RegisteredSpeculativePainters;
 use style::dom::{ShowSubtree, ShowSubtreeDataAndPrimaryValues, TElement, TNode};
 use style::error_reporting::{NullReporter, RustLogReporter};
 use style::invalidation::element::restyle_hints::RestyleHint;
-use style::invalidation::media_queries::{MediaListKey, ToMediaListKey};
 use style::logical_geometry::LogicalPoint;
 use style::media_queries::{Device, MediaList, MediaType};
 use style::properties::PropertyId;
 use style::selector_parser::SnapshotMap;
 use style::servo::restyle_damage::{REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, REPOSITION, STORE_OVERFLOW};
 use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard, StylesheetGuards};
-use style::stylesheet_set::StylesheetSet;
-use style::stylesheets::{Origin, Stylesheet, StylesheetContents, StylesheetInDocument, UserAgentStylesheets};
+use style::stylesheets::{Origin, OriginSet, Stylesheet, DocumentStyleSheet, StylesheetInDocument, UserAgentStylesheets};
 use style::stylist::Stylist;
 use style::thread_state;
 use style::timer::Timer;
 use style::traversal::{DomTraversal, TraversalDriver};
 use style::traversal_flags::TraversalFlags;
 use style_traits::CSSPixel;
 use style_traits::DevicePixel;
 use style_traits::SpeculativePainter;
 
-#[derive(Clone)]
-struct DocumentStyleSheet(ServoArc<Stylesheet>);
-
-impl PartialEq for DocumentStyleSheet {
-    fn eq(&self, other: &Self) -> bool {
-        ServoArc::ptr_eq(&self.0, &other.0)
-    }
-}
-
-impl ToMediaListKey for DocumentStyleSheet {
-    fn to_media_list_key(&self) -> MediaListKey {
-        self.0.to_media_list_key()
-    }
-}
-
-impl StylesheetInDocument for DocumentStyleSheet {
-    fn contents(&self, guard: &SharedRwLockReadGuard) -> &StylesheetContents {
-        self.0.contents(guard)
-    }
-
-    fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
-        self.0.media(guard)
-    }
-
-    fn enabled(&self) -> bool {
-        self.0.enabled()
-    }
-}
-
 /// Information needed by the layout thread.
 pub struct LayoutThread {
     /// The ID of the pipeline that we belong to.
     id: PipelineId,
 
     /// The ID of the top-level browsing context that we belong to.
     top_level_browsing_context_id: TopLevelBrowsingContextId,
 
     /// The URL of the pipeline that we belong to.
     url: ServoUrl,
 
     /// Performs CSS selector matching and style resolution.
     stylist: Stylist,
 
-    /// The list of stylesheets synchronized with the document.
-    stylesheets: StylesheetSet<DocumentStyleSheet>,
-
     /// Is the current reflow of an iframe, as opposed to a root window?
     is_iframe: bool,
 
     /// The port on which we receive messages from the script thread.
     port: Receiver<Msg>,
 
     /// The port on which we receive messages from the constellation.
     pipeline_port: Receiver<LayoutControlMsg>,
@@ -544,17 +510,16 @@ impl LayoutThread {
             document_shared_lock: None,
             running_animations: ServoArc::new(RwLock::new(FnvHashMap::default())),
             expired_animations: ServoArc::new(RwLock::new(FnvHashMap::default())),
             epoch: Cell::new(Epoch(0)),
             viewport_size: Size2D::new(Au(0), Au(0)),
             webrender_api: webrender_api_sender.create_api(),
             webrender_document,
             stylist: Stylist::new(device, QuirksMode::NoQuirks),
-            stylesheets: StylesheetSet::new(),
             rw_data: Arc::new(Mutex::new(
                 LayoutThreadData {
                     constellation_chan: constellation_chan,
                     display_list: None,
                     content_box_response: None,
                     content_boxes_response: Vec::new(),
                     client_rect_response: Rect::zero(),
                     hit_test_response: (None, false),
@@ -669,16 +634,20 @@ impl LayoutThread {
             },
             Request::FromPipeline(LayoutControlMsg::GetWebFontLoadState(sender)) => {
                 self.handle_request_helper(Msg::GetWebFontLoadState(sender),
                                            possibly_locked_rw_data)
             },
             Request::FromPipeline(LayoutControlMsg::ExitNow) => {
                 self.handle_request_helper(Msg::ExitNow, possibly_locked_rw_data)
             },
+            Request::FromPipeline(LayoutControlMsg::PaintMetric(epoch, paint_time)) => {
+                self.paint_time_metrics.maybe_set_metric(epoch, paint_time);
+                true
+            },
             Request::FromScript(msg) => {
                 self.handle_request_helper(msg, possibly_locked_rw_data)
             },
             Request::FromFontCache => {
                 let _rw_data = possibly_locked_rw_data.lock();
                 self.outstanding_web_fonts.fetch_sub(1, Ordering::SeqCst);
                 font_context::invalidate_font_caches();
                 self.script_chan.send(ConstellationControlMsg::WebFontLoaded(self.id)).unwrap();
@@ -695,36 +664,33 @@ impl LayoutThread {
     ) -> bool {
         match request {
             Msg::AddStylesheet(stylesheet, before_stylesheet) => {
                 let guard = stylesheet.shared_lock.read();
                 self.handle_add_stylesheet(&stylesheet, &guard);
 
                 match before_stylesheet {
                     Some(insertion_point) => {
-                        self.stylesheets.insert_stylesheet_before(
-                            Some(self.stylist.device()),
+                        self.stylist.insert_stylesheet_before(
                             DocumentStyleSheet(stylesheet.clone()),
                             DocumentStyleSheet(insertion_point),
                             &guard,
                         )
                     }
                     None => {
-                        self.stylesheets.append_stylesheet(
-                            Some(self.stylist.device()),
+                        self.stylist.append_stylesheet(
                             DocumentStyleSheet(stylesheet.clone()),
                             &guard,
                         )
                     }
                 }
             }
             Msg::RemoveStylesheet(stylesheet) => {
                 let guard = stylesheet.shared_lock.read();
-                self.stylesheets.remove_stylesheet(
-                    Some(self.stylist.device()),
+                self.stylist.remove_stylesheet(
                     DocumentStyleSheet(stylesheet.clone()),
                     &guard,
                 );
             }
             Msg::SetQuirksMode(mode) => self.handle_set_quirks_mode(mode),
             Msg::GetRPC(response_chan) => {
                 response_chan.send(box LayoutRPCImpl(self.rw_data.clone()) as
                                    Box<LayoutRPC + Send>).unwrap();
@@ -1074,20 +1040,20 @@ impl LayoutThread {
                                             self.viewport_size.height.to_f32_px());
 
             let mut epoch = self.epoch.get();
             epoch.next();
             self.epoch.set(epoch);
 
             let viewport_size = webrender_api::LayoutSize::from_untyped(&viewport_size);
 
-            // Set paint metrics if needed right before sending the display list to WebRender.
-            // XXX At some point, we may want to set this metric from WebRender itself.
-            self.paint_time_metrics.maybe_set_first_paint(self);
-            self.paint_time_metrics.maybe_set_first_contentful_paint(self, &display_list);
+            // Observe notifications about rendered frames if needed right before
+            // sending the display list to WebRender in order to set time related
+            // Progressive Web Metrics.
+            self.paint_time_metrics.maybe_observe_paint_time(self, epoch, &display_list);
 
             self.webrender_api.set_display_list(
                 self.webrender_document,
                 webrender_api::Epoch(epoch.0),
                 Some(get_root_flow_background_color(layout_root)),
                 viewport_size,
                 builder.finalize(),
                 true,
@@ -1179,18 +1145,19 @@ impl LayoutThread {
 
         // Calculate the actual viewport as per DEVICE-ADAPT § 6
 
         let document_shared_lock = document.style_shared_lock();
         self.document_shared_lock = Some(document_shared_lock.clone());
         let author_guard = document_shared_lock.read();
         let device = Device::new(MediaType::screen(), initial_viewport, device_pixel_ratio);
         let sheet_origins_affected_by_device_change =
-            self.stylist.set_device(device, &author_guard, self.stylesheets.iter());
+            self.stylist.set_device(device, &author_guard);
 
+        self.stylist.force_stylesheet_origins_dirty(sheet_origins_affected_by_device_change);
         self.viewport_size =
             self.stylist.viewport_constraints().map_or(current_screen_size, |constraints| {
                 debug!("Viewport constraints: {:?}", constraints);
 
                 // other rules are evaluated against the actual viewport
                 Size2D::new(Au::from_f32_px(constraints.size.width),
                             Au::from_f32_px(constraints.size.height))
             });
@@ -1198,17 +1165,17 @@ impl LayoutThread {
         let viewport_size_changed = self.viewport_size != old_viewport_size;
         if viewport_size_changed {
             if let Some(constraints) = self.stylist.viewport_constraints() {
                 // let the constellation know about the viewport constraints
                 rw_data.constellation_chan
                        .send(ConstellationMsg::ViewportConstrained(self.id, constraints.clone()))
                        .unwrap();
             }
-            if self.stylesheets.iter().any(|sheet| sheet.0.dirty_on_viewport_size_change()) {
+            if self.stylist.iter_stylesheets().any(|sheet| sheet.0.dirty_on_viewport_size_change()) {
                 let mut iter = element.as_node().traverse_preorder();
 
                 let mut next = iter.next();
                 while let Some(node) = next {
                     if node.needs_dirty_on_viewport_size_changed() {
                         let el = node.as_element().unwrap();
                         if let Some(mut d) = element.mutate_data() {
                             if d.has_styles() {
@@ -1232,75 +1199,51 @@ impl LayoutThread {
         // If the entire flow tree is invalid, then it will be reflowed anyhow.
         let ua_stylesheets = &*UA_STYLESHEETS;
         let ua_or_user_guard = ua_stylesheets.shared_lock.read();
         let guards = StylesheetGuards {
             author: &author_guard,
             ua_or_user: &ua_or_user_guard,
         };
 
-        let needs_dirtying = {
-            debug!("Flushing stylist");
-
-            let mut origins_dirty = sheet_origins_affected_by_device_change;
-
-            debug!("Device changes: {:?}", origins_dirty);
-
+        {
             if self.first_reflow.get() {
                 debug!("First reflow, rebuilding user and UA rules");
-
-                origins_dirty |= Origin::User;
-                origins_dirty |= Origin::UserAgent;
+                let mut ua_and_user = OriginSet::empty();
+                ua_and_user |= Origin::User;
+                ua_and_user |= Origin::UserAgent;
+                self.stylist.force_stylesheet_origins_dirty(ua_and_user);
                 for stylesheet in &ua_stylesheets.user_or_user_agent_stylesheets {
                     self.handle_add_stylesheet(stylesheet, &ua_or_user_guard);
                 }
             }
 
-            let (iter, invalidation_origins_dirty) = self.stylesheets.flush(Some(element));
-            debug!("invalidation: {:?}", invalidation_origins_dirty);
-
-            origins_dirty |= invalidation_origins_dirty;
-
             if data.stylesheets_changed {
                 debug!("Doc sheets changed, flushing author sheets too");
-                origins_dirty |= Origin::Author;
+                self.stylist.force_stylesheet_origins_dirty(Origin::Author.into());
             }
 
-            if !origins_dirty.is_empty() {
-                let mut extra_data = Default::default();
-                self.stylist.rebuild(
-                    iter,
-                    &guards,
-                    Some(ua_stylesheets),
-                    /* author_style_disabled = */ false,
-                    &mut extra_data,
-                    origins_dirty,
-                );
-            }
+            let mut extra_data = Default::default();
+            self.stylist.flush(
+                &guards,
+                Some(ua_stylesheets),
+                &mut extra_data,
+                Some(element),
+            );
+        }
 
-            !origins_dirty.is_empty()
-        };
-
-        let needs_reflow = viewport_size_changed && !needs_dirtying;
-        if needs_dirtying {
-            if let Some(mut d) = element.mutate_data() {
-                if d.has_styles() {
-                    d.restyle.hint.insert(RestyleHint::restyle_subtree());
-                }
-            }
-        }
-        if needs_reflow {
+        if viewport_size_changed {
             if let Some(mut flow) = self.try_get_layout_root(element.as_node()) {
                 LayoutThread::reflow_all_nodes(FlowRef::deref_mut(&mut flow));
             }
         }
 
         let restyles = document.drain_pending_restyles();
-        debug!("Draining restyles: {} (needs dirtying? {:?})",
-               restyles.len(), needs_dirtying);
+        debug!("Draining restyles: {}", restyles.len());
+
         let mut map = SnapshotMap::new();
         let elements_with_snapshot: Vec<_> =
             restyles
                 .iter()
                 .filter(|r| r.1.snapshot.is_some())
                 .map(|r| r.0)
                 .collect();
 
--- a/servo/components/metrics/Cargo.toml
+++ b/servo/components/metrics/Cargo.toml
@@ -6,11 +6,15 @@ license = "MPL-2.0"
 publish = false
 
 [lib]
 name = "metrics"
 path = "lib.rs"
 
 [dependencies]
 gfx = {path = "../gfx"}
+gfx_traits = {path = "../gfx_traits"}
+ipc-channel = "0.8"
+log = "0.3.5"
+msg = {path = "../msg"}
 profile_traits = {path = "../profile_traits"}
+script_traits = {path = "../script_traits"}
 servo_config = {path = "../config"}
-time = "0.1.12"
--- a/servo/components/metrics/lib.rs
+++ b/servo/components/metrics/lib.rs
@@ -1,119 +1,161 @@
 /* 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/. */
 
 extern crate gfx;
+extern crate gfx_traits;
+extern crate ipc_channel;
+#[macro_use]
+extern crate log;
+extern crate msg;
 extern crate profile_traits;
+extern crate script_traits;
 extern crate servo_config;
-extern crate time;
 
 use gfx::display_list::{DisplayItem, DisplayList};
+use gfx_traits::Epoch;
+use ipc_channel::ipc::IpcSender;
+use msg::constellation_msg::PipelineId;
 use profile_traits::time::{ProfilerChan, ProfilerCategory, send_profile_data};
 use profile_traits::time::TimerMetadata;
+use script_traits::LayoutMsg;
 use servo_config::opts;
-use std::cell::Cell;
+use std::cell::{Cell, RefCell};
+use std::collections::HashMap;
 
 pub trait ProfilerMetadataFactory {
     fn new_metadata(&self) -> Option<TimerMetadata>;
 }
 
 macro_rules! make_time_setter(
     ( $attr:ident, $func:ident, $category:ident, $label:expr ) => (
-        fn $func<T>(&self, profiler_metadata_factory: &T)
-            where T: ProfilerMetadataFactory {
+        fn $func(&self, profiler_metadata: Option<TimerMetadata>, paint_time: f64) {
+            if self.$attr.get().is_some() {
+                return;
+            }
+
             let navigation_start = match self.navigation_start {
                 Some(time) => time,
                 None => {
-                    println!("Trying to set metric before navigation start");
+                    warn!("Trying to set metric before navigation start");
                     return;
                 }
             };
 
-            let now = time::precise_time_ns() as f64;
-            let time = now - navigation_start;
+            let time = paint_time - navigation_start;
             self.$attr.set(Some(time));
 
             // Send the metric to the time profiler.
             send_profile_data(ProfilerCategory::$category,
-                              profiler_metadata_factory.new_metadata(),
+                              profiler_metadata,
                               &self.time_profiler_chan,
                               time as u64, time as u64, 0, 0);
 
             // Print the metric to console if the print-pwm option was given.
             if opts::get().print_pwm {
                 println!("{:?} {:?}", $label, time);
             }
         }
     );
 );
 
 pub struct PaintTimeMetrics {
+    pending_metrics: RefCell<HashMap<Epoch, (Option<TimerMetadata>, bool)>>,
     navigation_start: Option<f64>,
     first_paint: Cell<Option<f64>>,
     first_contentful_paint: Cell<Option<f64>>,
+    pipeline_id: PipelineId,
     time_profiler_chan: ProfilerChan,
+    constellation_chan: IpcSender<LayoutMsg>,
 }
 
 impl PaintTimeMetrics {
-    pub fn new(time_profiler_chan: ProfilerChan)
+    pub fn new(pipeline_id: PipelineId,
+               time_profiler_chan: ProfilerChan,
+               constellation_chan: IpcSender<LayoutMsg>)
         -> PaintTimeMetrics {
         PaintTimeMetrics {
+            pending_metrics: RefCell::new(HashMap::new()),
             navigation_start: None,
             first_paint: Cell::new(None),
             first_contentful_paint: Cell::new(None),
-            time_profiler_chan: time_profiler_chan,
+            pipeline_id,
+            time_profiler_chan,
+            constellation_chan,
         }
     }
 
     pub fn set_navigation_start(&mut self, time: f64) {
         self.navigation_start = Some(time);
     }
 
     make_time_setter!(first_paint, set_first_paint,
                       TimeToFirstPaint,
                       "first-paint");
     make_time_setter!(first_contentful_paint, set_first_contentful_paint,
                       TimeToFirstContentfulPaint,
                       "first-contentful-paint");
 
-    pub fn maybe_set_first_paint<T>(&self, profiler_metadata_factory: &T)
+    pub fn maybe_observe_paint_time<T>(&self,
+                                       profiler_metadata_factory: &T,
+                                       epoch: Epoch,
+                                       display_list: &DisplayList)
         where T: ProfilerMetadataFactory {
-        {
-            if self.first_paint.get().is_some() {
-                return;
-            }
+        if self.first_paint.get().is_some() && self.first_contentful_paint.get().is_some() {
+            // If we already set all paint metrics, we just bail out.
+            return;
         }
 
-        self.set_first_paint(profiler_metadata_factory);
-    }
-
-    pub fn maybe_set_first_contentful_paint<T>(&self, profiler_metadata_factory: &T,
-                                               display_list: &DisplayList)
-        where T: ProfilerMetadataFactory {
-        {
-            if self.first_contentful_paint.get().is_some() {
-                return;
-            }
-        }
-
-        // Analyze display list to figure out if this is the first contentful
-        // paint (i.e. the display list contains items of type text, image,
-        // non-white canvas or SVG)
+        let mut is_contentful = false;
+        // Analyze the display list to figure out if this may be the first
+        // contentful paint (i.e. the display list contains items of type text,
+        // image, non-white canvas or SVG).
         for item in &display_list.list {
             match item {
                 &DisplayItem::Text(_) |
                 &DisplayItem::Image(_) => {
-                    self.set_first_contentful_paint(profiler_metadata_factory);
-                    return;
+                    is_contentful = true;
+                    break;
                 },
                 _ => (),
             }
         }
+
+        self.pending_metrics.borrow_mut().insert(
+            epoch,
+            (profiler_metadata_factory.new_metadata(), is_contentful)
+        );
+
+        // Send the pending metric information to the compositor thread.
+        // The compositor will record the current time after painting the
+        // frame with the given ID and will send the metric back to us.
+        let msg = LayoutMsg::PendingPaintMetric(self.pipeline_id, epoch);
+        if let Err(e) = self.constellation_chan.send(msg) {
+            warn!("Failed to send PendingPaintMetric {:?}", e);
+        }
+    }
+
+    pub fn maybe_set_metric(&mut self, epoch: Epoch, paint_time: f64) {
+        if (self.first_paint.get().is_some() && self.first_contentful_paint.get().is_some()) ||
+           self.navigation_start.is_none() {
+            // If we already set all paint metrics or we have not set navigation start yet,
+            // we just bail out.
+            return;
+        }
+
+        if let Some(pending_metric) = self.pending_metrics.borrow_mut().remove(&epoch) {
+            let profiler_metadata = pending_metric.0;
+            self.set_first_paint(profiler_metadata.clone(), paint_time);
+            if pending_metric.1 {
+                self.set_first_contentful_paint(profiler_metadata, paint_time);
+            }
+        }
+
     }
 
     pub fn get_navigation_start(&self) -> Option<f64> {
         self.navigation_start
     }
 
     pub fn get_first_paint(&self) -> Option<f64> {
         self.first_paint.get()
--- a/servo/components/script/dom/document.rs
+++ b/servo/components/script/dom/document.rs
@@ -2333,16 +2333,21 @@ impl Document {
     pub fn style_shared_lock(&self) -> &StyleSharedRwLock {
         &self.style_shared_lock
     }
 
     /// Flushes the stylesheet list, and returns whether any stylesheet changed.
     pub fn flush_stylesheets_for_reflow(&self) -> bool {
         // NOTE(emilio): The invalidation machinery is used on the replicated
         // list on the layout thread.
+        //
+        // FIXME(emilio): This really should differentiate between CSSOM changes
+        // and normal stylesheets additions / removals, because in the last case
+        // the layout thread already has that information and we could avoid
+        // dirtying the whole thing.
         let mut stylesheets = self.stylesheets.borrow_mut();
         let have_changed = stylesheets.has_changed();
         stylesheets.flush_without_invalidation();
         have_changed
     }
 
     /// Returns a `Device` suitable for media query evaluation.
     ///
@@ -2365,21 +2370,20 @@ impl Document {
     #[allow(unrooted_must_root)] // Owner needs to be rooted already necessarily.
     pub fn remove_stylesheet(&self, owner: &Element, s: &Arc<Stylesheet>) {
         self.window()
             .layout_chan()
             .send(Msg::RemoveStylesheet(s.clone()))
             .unwrap();
 
         let guard = s.shared_lock.read();
-        let device = self.device();
 
         // FIXME(emilio): Would be nice to remove the clone, etc.
         self.stylesheets.borrow_mut().remove_stylesheet(
-            device.as_ref(),
+            None,
             StyleSheetInDocument {
                 sheet: s.clone(),
                 owner: JS::from_ref(owner),
             },
             &guard,
         );
     }
 
@@ -2409,23 +2413,22 @@ impl Document {
         let sheet = StyleSheetInDocument {
             sheet,
             owner: JS::from_ref(owner),
         };
 
         let lock = self.style_shared_lock();
         let guard = lock.read();
 
-        let device = self.device();
         match insertion_point {
             Some(ip) => {
-                stylesheets.insert_stylesheet_before(device.as_ref(), sheet, ip, &guard);
+                stylesheets.insert_stylesheet_before(None, sheet, ip, &guard);
             }
             None => {
-                stylesheets.append_stylesheet(device.as_ref(), sheet, &guard);
+                stylesheets.append_stylesheet(None, sheet, &guard);
             }
         }
     }
 
     /// Returns the number of document stylesheets.
     pub fn stylesheet_count(&self) -> usize {
         self.stylesheets.borrow().len()
     }
--- a/servo/components/script/script_thread.rs
+++ b/servo/components/script/script_thread.rs
@@ -1475,17 +1475,19 @@ impl ScriptThread {
             is_parent: false,
             layout_pair: layout_pair,
             pipeline_port: pipeline_port,
             constellation_chan: self.layout_to_constellation_chan.clone(),
             script_chan: self.control_chan.clone(),
             image_cache: self.image_cache.clone(),
             content_process_shutdown_chan: content_process_shutdown_chan,
             layout_threads: layout_threads,
-            paint_time_metrics: PaintTimeMetrics::new(self.time_profiler_chan.clone()),
+            paint_time_metrics: PaintTimeMetrics::new(new_pipeline_id,
+                                                      self.time_profiler_chan.clone(),
+                                                      self.layout_to_constellation_chan.clone()),
         });
 
         // Pick a layout thread, any layout thread
         let current_layout_chan = self.documents.borrow().iter().next()
             .map(|(_, document)| document.window().layout_chan().clone())
             .or_else(|| self.incomplete_loads.borrow().first().map(|load| load.layout_chan.clone()));
 
         match current_layout_chan {
--- a/servo/components/script_traits/Cargo.toml
+++ b/servo/components/script_traits/Cargo.toml
@@ -17,17 +17,16 @@ devtools_traits = {path = "../devtools_t
 euclid = "0.15"
 gfx_traits = {path = "../gfx_traits"}
 heapsize = "0.4"
 heapsize_derive = "0.1"
 hyper = "0.10"
 hyper_serde = "0.7"
 ipc-channel = "0.8"
 libc = "0.2"
-metrics = {path = "../metrics"}
 msg = {path = "../msg"}
 net_traits = {path = "../net_traits"}
 profile_traits = {path = "../profile_traits"}
 rustc-serialize = "0.3.4"
 serde = "1.0"
 servo_atoms = {path = "../atoms"}
 servo_url = {path = "../url"}
 style_traits = {path = "../style_traits", features = ["servo"]}
--- a/servo/components/script_traits/lib.rs
+++ b/servo/components/script_traits/lib.rs
@@ -119,16 +119,18 @@ pub enum LayoutControlMsg {
     GetCurrentEpoch(IpcSender<Epoch>),
     /// Asks layout to run another step in its animation.
     TickAnimations,
     /// Tells layout about the new scrolling offsets of each scrollable stacking context.
     SetScrollStates(Vec<ScrollState>),
     /// Requests the current load state of Web fonts. `true` is returned if fonts are still loading
     /// and `false` is returned if all fonts have loaded.
     GetWebFontLoadState(IpcSender<bool>),
+    /// Send the paint time for a specific epoch to the layout thread.
+    PaintMetric(Epoch, f64),
 }
 
 /// can be passed to `LoadUrl` to load a page with GET/POST
 /// parameters or headers
 #[derive(Clone, Debug, Deserialize, Serialize)]
 pub struct LoadData {
     /// The URL.
     pub url: ServoUrl,
--- a/servo/components/script_traits/script_msg.rs
+++ b/servo/components/script_traits/script_msg.rs
@@ -10,16 +10,17 @@ use IFrameLoadInfoWithData;
 use LayoutControlMsg;
 use LoadData;
 use MozBrowserEvent;
 use WorkerGlobalScopeInit;
 use WorkerScriptLoadOrigin;
 use canvas_traits::canvas::CanvasMsg;
 use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId};
 use euclid::{Point2D, Size2D, TypedSize2D};
+use gfx_traits::Epoch;
 use ipc_channel::ipc::IpcSender;
 use msg::constellation_msg::{BrowsingContextId, FrameType, PipelineId, TraversalDirection};
 use msg::constellation_msg::{Key, KeyModifiers, KeyState};
 use net_traits::CoreResourceMsg;
 use net_traits::request::RequestInit;
 use net_traits::storage_thread::StorageType;
 use servo_url::ImmutableOrigin;
 use servo_url::ServoUrl;
@@ -30,16 +31,19 @@ use webrender_api::ClipId;
 
 /// Messages from the layout to the constellation.
 #[derive(Deserialize, Serialize)]
 pub enum LayoutMsg {
     /// Indicates whether this pipeline is currently running animations.
     ChangeRunningAnimationsState(PipelineId, AnimationState),
     /// Inform the constellation of the size of the iframe's viewport.
     IFrameSizes(Vec<(BrowsingContextId, TypedSize2D<f32, CSSPixel>)>),
+    /// Requests that the constellation inform the compositor that it needs to record
+    /// the time when the frame with the given ID (epoch) is painted.
+    PendingPaintMetric(PipelineId, Epoch),
     /// Requests that the constellation inform the compositor of the a cursor change.
     SetCursor(Cursor),
     /// Notifies the constellation that the viewport has been constrained in some manner
     ViewportConstrained(PipelineId, ViewportConstraints),
 }
 
 /// Whether a DOM event was prevented by web content
 #[derive(Deserialize, Serialize)]
--- a/servo/components/style/data.rs
+++ b/servo/components/style/data.rs
@@ -67,27 +67,29 @@ impl RestyleData {
             damage: RestyleDamage::empty(),
         }
     }
 
     /// Clear all the restyle state associated with this element.
     ///
     /// FIXME(bholley): The only caller of this should probably just assert that
     /// the hint is empty and call clear_flags_and_damage().
+    #[inline]
     fn clear_restyle_state(&mut self) {
         self.clear_restyle_flags_and_damage();
         self.hint = RestyleHint::empty();
     }
 
     /// Clear restyle flags and damage.
     ///
     /// Note that we don't touch the TRAVERSED_WITHOUT_STYLING bit, which gets
     /// set to the correct value on each traversal. There's no reason anyone
     /// needs to clear it, and clearing it accidentally mid-traversal could
     /// cause incorrect style sharing behavior.
+    #[inline]
     fn clear_restyle_flags_and_damage(&mut self) {
         self.damage = RestyleDamage::empty();
         self.flags = self.flags & TRAVERSED_WITHOUT_STYLING;
     }
 
     /// Returns whether this element or any ancestor is going to be
     /// reconstructed.
     pub fn reconstructed_self_or_ancestor(&self) -> bool {
@@ -119,32 +121,34 @@ impl RestyleData {
     /// Mark this element as restyled, which is useful to know whether we need
     /// to do a post-traversal.
     pub fn set_restyled(&mut self) {
         self.flags.insert(WAS_RESTYLED);
         self.flags.remove(TRAVERSED_WITHOUT_STYLING);
     }
 
     /// Returns true if this element was restyled.
+    #[inline]
     pub fn is_restyle(&self) -> bool {
         self.flags.contains(WAS_RESTYLED)
     }
 
     /// Mark that we traversed this element without computing any style for it.
     pub fn set_traversed_without_styling(&mut self) {
         self.flags.insert(TRAVERSED_WITHOUT_STYLING);
     }
 
     /// Returns whether the element was traversed without computing any style for
     /// it.
     pub fn traversed_without_styling(&self) -> bool {
         self.flags.contains(TRAVERSED_WITHOUT_STYLING)
     }
 
     /// Returns whether this element has been part of a restyle.
+    #[inline]
     pub fn contains_restyle_data(&self) -> bool {
         self.is_restyle() || !self.hint.is_empty() || !self.damage.is_empty()
     }
 }
 
 /// A lazily-allocated list of styles for eagerly-cascaded pseudo-elements.
 ///
 /// We use an Arc so that sharing these styles via the style sharing cache does
@@ -348,16 +352,17 @@ impl ElementData {
             );
             invalidator.invalidate();
             unsafe { element.set_handled_snapshot() }
             debug_assert!(element.handled_snapshot());
         }
     }
 
     /// Returns true if this element has styles.
+    #[inline]
     pub fn has_styles(&self) -> bool {
         self.styles.primary.is_some()
     }
 
     /// Returns the kind of restyling that we're going to need to do on this
     /// element, based of the stored restyle hint.
     pub fn restyle_kind(
         &self,
@@ -424,21 +429,23 @@ impl ElementData {
         debug_assert!(self.has_styles());
         let (important_rules, _custom) =
             self.styles.primary().rules().get_properties_overriding_animations(&guards);
         let (other_important_rules, _custom) = rules.get_properties_overriding_animations(&guards);
         important_rules != other_important_rules
     }
 
     /// Drops any restyle state from the element.
+    #[inline]
     pub fn clear_restyle_state(&mut self) {
         self.restyle.clear_restyle_state();
     }
 
     /// Drops restyle flags and damage from the element.
+    #[inline]
     pub fn clear_restyle_flags_and_damage(&mut self) {
         self.restyle.clear_restyle_flags_and_damage();
     }
 
     /// Measures memory usage.
     #[cfg(feature = "gecko")]
     pub fn malloc_size_of_children_excluding_cvs(&self, state: &mut SizeOfState) -> usize {
         let n = self.styles.malloc_size_of_children_excluding_cvs(state);
--- a/servo/components/style/gecko/data.rs
+++ b/servo/components/style/gecko/data.rs
@@ -11,17 +11,16 @@ use gecko_bindings::structs::{ServoStyle
 use gecko_bindings::structs::RawGeckoPresContextOwned;
 use gecko_bindings::structs::nsIDocument;
 use gecko_bindings::sugar::ownership::{HasArcFFI, HasBoxFFI, HasFFI, HasSimpleFFI};
 use invalidation::media_queries::{MediaListKey, ToMediaListKey};
 use media_queries::{Device, MediaList};
 use properties::ComputedValues;
 use servo_arc::Arc;
 use shared_lock::{Locked, StylesheetGuards, SharedRwLockReadGuard};
-use stylesheet_set::StylesheetSet;
 use stylesheets::{PerOrigin, StylesheetContents, StylesheetInDocument};
 use stylist::{ExtraStyleData, Stylist};
 
 /// Little wrapper to a Gecko style sheet.
 #[derive(PartialEq, Eq, Debug)]
 pub struct GeckoStyleSheet(*const ServoStyleSheet);
 
 impl ToMediaListKey for ::gecko::data::GeckoStyleSheet {
@@ -109,19 +108,16 @@ impl StylesheetInDocument for GeckoStyle
 }
 
 /// The container for data that a Servo-backed Gecko document needs to style
 /// itself.
 pub struct PerDocumentStyleDataImpl {
     /// Rule processor.
     pub stylist: Stylist,
 
-    /// List of stylesheets, mirrored from Gecko.
-    pub stylesheets: StylesheetSet<GeckoStyleSheet>,
-
     /// List of effective @font-face and @counter-style rules.
     pub extra_style_data: PerOrigin<ExtraStyleData>,
 }
 
 /// The data itself is an `AtomicRefCell`, which guarantees the proper semantics
 /// and unexpected races while trying to mutate it.
 pub struct PerDocumentStyleData(AtomicRefCell<PerDocumentStyleDataImpl>);
 
@@ -130,17 +126,16 @@ impl PerDocumentStyleData {
     pub fn new(pres_context: RawGeckoPresContextOwned) -> Self {
         let device = Device::new(pres_context);
         let quirks_mode = unsafe {
             (*device.pres_context().mDocument.raw::<nsIDocument>()).mCompatMode
         };
 
         PerDocumentStyleData(AtomicRefCell::new(PerDocumentStyleDataImpl {
             stylist: Stylist::new(device, quirks_mode.into()),
-            stylesheets: StylesheetSet::new(),
             extra_style_data: Default::default(),
         }))
     }
 
     /// Get an immutable reference to this style data.
     pub fn borrow(&self) -> AtomicRef<PerDocumentStyleDataImpl> {
         self.0.borrow()
     }
@@ -148,36 +143,30 @@ impl PerDocumentStyleData {
     /// Get an mutable reference to this style data.
     pub fn borrow_mut(&self) -> AtomicRefMut<PerDocumentStyleDataImpl> {
         self.0.borrow_mut()
     }
 }
 
 impl PerDocumentStyleDataImpl {
     /// Recreate the style data if the stylesheets have changed.
-    pub fn flush_stylesheets<E>(&mut self,
-                                guard: &SharedRwLockReadGuard,
-                                document_element: Option<E>)
-        where E: TElement,
+    pub fn flush_stylesheets<E>(
+        &mut self,
+        guard: &SharedRwLockReadGuard,
+        document_element: Option<E>,
+    )
+    where
+        E: TElement,
     {
-        if !self.stylesheets.has_changed() {
-            return;
-        }
-
-        let author_style_disabled = self.stylesheets.author_style_disabled();
-
-        let (iter, dirty_origins) = self.stylesheets.flush(document_element);
-        self.stylist.rebuild(
-            iter,
+        self.stylist.flush(
             &StylesheetGuards::same(guard),
             /* ua_sheets = */ None,
-            author_style_disabled,
             &mut self.extra_style_data,
-            dirty_origins,
-        );
+            document_element,
+        )
     }
 
     /// Returns whether private browsing is enabled.
     pub fn is_private_browsing_enabled(&self) -> bool {
         let doc =
             self.stylist.device().pres_context().mDocument.raw::<nsIDocument>();
         unsafe { bindings::Gecko_IsPrivateBrowsingEnabled(doc) }
     }
--- a/servo/components/style/gecko/restyle_damage.rs
+++ b/servo/components/style/gecko/restyle_damage.rs
@@ -13,31 +13,35 @@ use std::ops::{BitAnd, BitOr, BitOrAssig
 
 /// The representation of Gecko's restyle damage is just a wrapper over
 /// `nsChangeHint`.
 #[derive(Clone, Copy, Debug, PartialEq)]
 pub struct GeckoRestyleDamage(nsChangeHint);
 
 impl GeckoRestyleDamage {
     /// Trivially construct a new `GeckoRestyleDamage`.
+    #[inline]
     pub fn new(raw: nsChangeHint) -> Self {
         GeckoRestyleDamage(raw)
     }
 
     /// Get the inner change hint for this damage.
+    #[inline]
     pub fn as_change_hint(&self) -> nsChangeHint {
         self.0
     }
 
     /// Get an empty change hint, that is (`nsChangeHint(0)`).
+    #[inline]
     pub fn empty() -> Self {
         GeckoRestyleDamage(nsChangeHint(0))
     }
 
     /// Returns whether this restyle damage represents the empty damage.
+    #[inline]
     pub fn is_empty(&self) -> bool {
         self.0 == nsChangeHint(0)
     }
 
     /// Computes the `StyleDifference` (including the appropriate change hint)
     /// given an old style (in the form of a `nsStyleContext`, and a new style
     /// (in the form of `ComputedValues`).
     ///
--- a/servo/components/style/gecko/wrapper.rs
+++ b/servo/components/style/gecko/wrapper.rs
@@ -289,16 +289,17 @@ impl<'ln> TNode for GeckoNode<'ln> {
         let ptr: usize = self.0 as *const _ as usize;
         OpaqueNode(ptr)
     }
 
     fn debug_id(self) -> usize {
         unimplemented!()
     }
 
+    #[inline]
     fn as_element(&self) -> Option<GeckoElement<'ln>> {
         if self.is_element() {
             unsafe { Some(GeckoElement(&*(self.0 as *const _ as *const RawGeckoElement))) }
         } else {
             None
         }
     }
 
@@ -1014,43 +1015,47 @@ impl<'le> TElement for GeckoElement<'le>
     fn each_class<F>(&self, callback: F)
         where F: FnMut(&Atom)
     {
         snapshot_helpers::each_class(self.0,
                                      callback,
                                      Gecko_ClassOrClassList)
     }
 
+    #[inline]
     fn has_snapshot(&self) -> bool {
         self.flags() & (ELEMENT_HAS_SNAPSHOT as u32) != 0
     }
 
+    #[inline]
     fn handled_snapshot(&self) -> bool {
         self.flags() & (ELEMENT_HANDLED_SNAPSHOT as u32) != 0
     }
 
     unsafe fn set_handled_snapshot(&self) {
         debug_assert!(self.get_data().is_some());
         self.set_flags(ELEMENT_HANDLED_SNAPSHOT as u32)
     }
 
+    #[inline]
     fn has_dirty_descendants(&self) -> bool {
         self.flags() & (ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO as u32) != 0
     }
 
     unsafe fn set_dirty_descendants(&self) {
         debug_assert!(self.get_data().is_some());
         debug!("Setting dirty descendants: {:?}", self);
         self.set_flags(ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO as u32)
     }
 
     unsafe fn unset_dirty_descendants(&self) {
         self.unset_flags(ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO as u32)
     }
 
+    #[inline]
     fn has_animation_only_dirty_descendants(&self) -> bool {
         self.flags() & (ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO as u32) != 0
     }
 
     unsafe fn set_animation_only_dirty_descendants(&self) {
         self.set_flags(ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO as u32)
     }
 
@@ -1059,21 +1064,23 @@ impl<'le> TElement for GeckoElement<'le>
     }
 
     unsafe fn clear_descendants_bits(&self) {
         self.unset_flags(ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO as u32 |
                          ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO as u32 |
                          NODE_DESCENDANTS_NEED_FRAMES as u32)
     }
 
+    #[inline]
     fn is_visited_link(&self) -> bool {
         use element_state::IN_VISITED_STATE;
         self.get_state().intersects(IN_VISITED_STATE)
     }
 
+    #[inline]
     fn is_native_anonymous(&self) -> bool {
         use gecko_bindings::structs::NODE_IS_NATIVE_ANONYMOUS;
         self.flags() & (NODE_IS_NATIVE_ANONYMOUS as u32) != 0
     }
 
     fn implemented_pseudo_element(&self) -> Option<PseudoElement> {
         if !self.is_native_anonymous() {
             return None;
@@ -1091,16 +1098,17 @@ impl<'le> TElement for GeckoElement<'le>
     fn store_children_to_process(&self, _: isize) {
         // This is only used for bottom-up traversal, and is thus a no-op for Gecko.
     }
 
     fn did_process_child(&self) -> isize {
         panic!("Atomic child count not implemented in Gecko");
     }
 
+    #[inline(always)]
     fn get_data(&self) -> Option<&AtomicRefCell<ElementData>> {
         unsafe { self.0.mServoData.get().as_ref() }
     }
 
     unsafe fn ensure_data(&self) -> AtomicRefMut<ElementData> {
         if self.get_data().is_none() {
             debug!("Creating ElementData for {:?}", self);
             let ptr = Box::into_raw(Box::new(AtomicRefCell::new(ElementData::default())));
@@ -1122,16 +1130,17 @@ impl<'le> TElement for GeckoElement<'le>
 
             // Perform a mutable borrow of the data in debug builds. This
             // serves as an assertion that there are no outstanding borrows
             // when we destroy the data.
             debug_assert!({ let _ = data.borrow_mut(); true });
         }
     }
 
+    #[inline]
     fn skip_root_and_item_based_display_fixup(&self) -> bool {
         // We don't want to fix up display values of native anonymous content.
         // Additionally, we want to skip root-based display fixup for document
         // level native anonymous content subtree roots, since they're not
         // really roots from the style fixup perspective.  Checking that we
         // are NAC handles both cases.
         self.is_native_anonymous()
     }
@@ -1396,25 +1405,25 @@ impl<'le> TElement for GeckoElement<'le>
 
         // Check if we have to cancel the running transition because this is not a matching
         // transition-property value.
         transitions_to_keep.map_or(false, |set| {
             existing_transitions.keys().any(|property| !set.contains(property))
         })
     }
 
-    fn needs_transitions_update_per_property(&self,
-                                             property: &TransitionProperty,
-                                             combined_duration: f32,
-                                             before_change_style: &ComputedValues,
-                                             after_change_style: &ComputedValues,
-                                             existing_transitions: &HashMap<TransitionProperty,
-                                                                            Arc<AnimationValue>>)
-                                             -> bool {
-        use properties::animated_properties::Animatable;
+    fn needs_transitions_update_per_property(
+        &self,
+        property: &TransitionProperty,
+        combined_duration: f32,
+        before_change_style: &ComputedValues,
+        after_change_style: &ComputedValues,
+        existing_transitions: &HashMap<TransitionProperty, Arc<AnimationValue>>,
+    ) -> bool {
+        use values::animated::{Animate, Procedure};
 
         // |property| should be an animatable longhand
         let animatable_longhand = AnimatableLonghand::from_transition_property(property).unwrap();
 
         if existing_transitions.contains_key(property) {
             // If there is an existing transition, update only if the end value differs.
             // If the end value has not changed, we should leave the currently running
             // transition as-is since we don't want to interrupt its timing function.
@@ -1426,17 +1435,17 @@ impl<'le> TElement for GeckoElement<'le>
 
         let from = AnimationValue::from_computed_values(&animatable_longhand,
                                                         before_change_style);
         let to = AnimationValue::from_computed_values(&animatable_longhand,
                                                       after_change_style);
 
         combined_duration > 0.0f32 &&
         from != to &&
-        from.interpolate(&to, 0.5).is_ok()
+        from.animate(&to, Procedure::Interpolate { progress: 0.5 }).is_ok()
     }
 
     #[inline]
     fn lang_attr(&self) -> Option<AttrValue> {
         let ptr = unsafe { bindings::Gecko_LangValue(self.0) };
         if ptr.is_null() {
             None
         } else {
--- a/servo/components/style/macros.rs
+++ b/servo/components/style/macros.rs
@@ -82,20 +82,23 @@ macro_rules! add_impls_for_keyword_enum 
 
 macro_rules! define_keyword_type {
     ($name: ident, $css: expr) => {
         #[allow(missing_docs)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         #[derive(Clone, ComputeSquaredDistance, Copy, PartialEq, ToCss)]
         pub struct $name;
 
-        impl $crate::properties::animated_properties::Animatable for $name {
+        impl $crate::values::animated::Animate for $name {
             #[inline]
-            fn add_weighted(&self, _other: &Self, _self_progress: f64, _other_progress: f64)
-                -> Result<Self, ()> {
+            fn animate(
+                &self,
+                _other: &Self,
+                _procedure: $crate::values::animated::Procedure,
+            ) -> Result<Self, ()> {
                 Ok($name)
             }
         }
 
         impl fmt::Debug for $name {
             fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                 write!(f, $css)
             }
--- a/servo/components/style/properties/data.py
+++ b/servo/components/style/properties/data.py
@@ -206,17 +206,17 @@ class Longhand(object):
         self.is_animatable_with_computed_value = animation_value_type == "ComputedValue" \
             or animation_value_type == "discrete"
         if self.logical:
             # Logical properties will be animatable (i.e. the animation type is
             # discrete). For now, it is still non-animatable.
             self.animatable = False
             self.transitionable = False
             self.animation_type = None
-        # NB: Animatable implies clone because a property animation requires a
+        # NB: Animate implies clone because a property animation requires a
         # copy of the computed value.
         #
         # See components/style/helpers/animated_properties.mako.rs.
         self.need_clone = need_clone or self.animatable
 
 
 class Shorthand(object):
     def __init__(self, name, sub_properties, spec=None, experimental=False, internal=False,
--- a/servo/components/style/properties/helpers.mako.rs
+++ b/servo/components/style/properties/helpers.mako.rs
@@ -128,27 +128,22 @@
                 % if allow_empty and allow_empty != "NotInitial":
                 pub Vec<single_value::T>,
                 % else:
                 pub SmallVec<[single_value::T; 1]>,
                 % endif
             );
 
             % if need_animatable or animation_value_type == "ComputedValue":
-                use properties::animated_properties::Animatable;
-                use values::animated::ToAnimatedZero;
+                use values::animated::{Animate, Procedure, ToAnimatedZero};
 
-                impl Animatable for T {
-                    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64)
-                        -> Result<Self, ()> {
-                        self.0.add_weighted(&other.0, self_portion, other_portion).map(T)
-                    }
-
-                    fn add(&self, other: &Self) -> Result<Self, ()> {
-                        self.0.add(&other.0).map(T)
+                impl Animate for T {
+                    #[inline]
+                    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
+                        Ok(T(self.0.animate(&other.0, procedure)?))
                     }
                 }
 
                 impl ToAnimatedZero for T {
                     #[inline]
                     fn to_animated_zero(&self) -> Result<Self, ()> { Err(()) }
                 }
             % endif
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -3,46 +3,48 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
 <% from data import to_idl_name, SYSTEM_FONT_LONGHANDS %>
 
 use app_units::Au;
 use cssparser::Parser;
-use euclid::{Point2D, Point3D, Size2D};
+use euclid::Point3D;
 #[cfg(feature = "gecko")] use gecko_bindings::bindings::RawServoAnimationValueMap;
 #[cfg(feature = "gecko")] use gecko_bindings::structs::RawGeckoGfxMatrix4x4;
 #[cfg(feature = "gecko")] use gecko_bindings::structs::nsCSSPropertyID;
 #[cfg(feature = "gecko")] use gecko_bindings::sugar::ownership::{HasFFI, HasSimpleFFI};
 #[cfg(feature = "gecko")] use gecko_string_cache::Atom;
+use itertools::{EitherOrBoth, Itertools};
 use properties::{CSSWideKeyword, PropertyDeclaration};
 use properties::longhands;
 use properties::longhands::background_size::computed_value::T as BackgroundSizeList;
 use properties::longhands::border_spacing::computed_value::T as BorderSpacing;
 use properties::longhands::font_weight::computed_value::T as FontWeight;
 use properties::longhands::font_stretch::computed_value::T as FontStretch;
 use properties::longhands::line_height::computed_value::T as LineHeight;
 use properties::longhands::transform::computed_value::ComputedMatrix;
 use properties::longhands::transform::computed_value::ComputedOperation as TransformOperation;
 use properties::longhands::transform::computed_value::T as TransformList;
 use properties::longhands::vertical_align::computed_value::T as VerticalAlign;
 use properties::longhands::visibility::computed_value::T as Visibility;
 #[cfg(feature = "gecko")] use properties::{PropertyId, PropertyDeclarationId, LonghandId};
 #[cfg(feature = "gecko")] use properties::{ShorthandId};
 use selectors::parser::SelectorParseError;
 use smallvec::SmallVec;
+use std::borrow::Cow;
 use std::cmp;
 #[cfg(feature = "gecko")] use fnv::FnvHashMap;
 use style_traits::ParseError;
 use super::ComputedValues;
 #[cfg(feature = "gecko")]
 use values::Auto;
 use values::{CSSFloat, CustomIdent, Either};
-use values::animated::{ToAnimatedValue, ToAnimatedZero};
+use values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero};
 use values::animated::color::{Color as AnimatedColor, RGBA as AnimatedRGBA};
 use values::animated::effects::BoxShadowList as AnimatedBoxShadowList;
 use values::animated::effects::Filter as AnimatedFilter;
 use values::animated::effects::FilterList as AnimatedFilterList;
 use values::animated::effects::TextShadowList as AnimatedTextShadowList;
 use values::computed::{Angle, BorderCornerRadius, CalcLengthOrPercentage};
 use values::computed::{ClipRect, Context, ComputedUrl, ComputedValueAsSpecified};
 use values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto};
@@ -53,49 +55,18 @@ use values::computed::length::{NonNegati
 use values::computed::length::NonNegativeLengthOrPercentage;
 use values::distance::{ComputeSquaredDistance, SquaredDistance};
 use values::generics::{GreaterThanOrEqualToOne, NonNegative};
 use values::generics::effects::Filter;
 use values::generics::position as generic_position;
 use values::generics::svg::{SVGLength,  SvgLengthOrPercentageOrNumber, SVGPaint};
 use values::generics::svg::{SVGPaintKind, SVGStrokeDashArray, SVGOpacity};
 
-/// A trait used to implement various procedures used during animation.
-pub trait Animatable: Sized {
-    /// Performs a weighted sum of this value and |other|. This is used for
-    /// interpolation and addition of animation values.
-    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64)
-        -> Result<Self, ()>;
-
-    /// [Interpolates][interpolation] a value with another for a given property.
-    ///
-    /// [interpolation]: https://w3c.github.io/web-animations/#animation-interpolation
-    fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
-        self.add_weighted(other, 1.0 - progress, progress)
-    }
-
-    /// Returns the [sum][animation-addition] of this value and |other|.
-    ///
-    /// [animation-addition]: https://w3c.github.io/web-animations/#animation-addition
-    fn add(&self, other: &Self) -> Result<Self, ()> {
-        self.add_weighted(other, 1.0, 1.0)
-    }
-
-    /// [Accumulates][animation-accumulation] this value onto itself (|count| - 1) times then
-    /// accumulates |other| onto the result.
-    /// If |count| is zero, the result will be |other|.
-    ///
-    /// [animation-accumulation]: https://w3c.github.io/web-animations/#animation-accumulation
-    fn accumulate(&self, other: &Self, count: u64) -> Result<Self, ()> {
-        self.add_weighted(other, count as f64, 1.0)
-    }
-}
-
 /// https://drafts.csswg.org/css-transitions/#animtype-repeatable-list
-pub trait RepeatableListAnimatable: Animatable {}
+pub trait RepeatableListAnimatable: Animate {}
 
 /// A longhand property whose animation type is not "none".
 ///
 /// NOTE: This includes the 'display' property since it is animatable from SMIL even though it is
 /// not animatable from CSS animations or Web Animations. CSS transitions also does not allow
 /// animating 'display', but for CSS transitions we have the separate TransitionProperty type.
 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@@ -444,17 +415,17 @@ impl AnimatedProperty {
         match *self {
             % for prop in data.longhands:
                 % if prop.animatable:
                     AnimatedProperty::${prop.camel_case}(ref from, ref to) => {
                         // https://w3c.github.io/web-animations/#discrete-animation-type
                         % if prop.animation_value_type == "discrete":
                             let value = if progress < 0.5 { from.clone() } else { to.clone() };
                         % else:
-                            let value = match from.interpolate(to, progress) {
+                            let value = match from.animate(to, Procedure::Interpolate { progress }) {
                                 Ok(value) => value,
                                 Err(()) => return,
                             };
                         % endif
                         % if not prop.is_animatable_with_computed_value:
                             let value: longhands::${prop.ident}::computed_value::T =
                                 ToAnimatedValue::from_animated_value(value);
                         % endif
@@ -655,88 +626,48 @@ impl AnimationValue {
                         )
                     }
                 % endif
             % endfor
         }
     }
 }
 
-impl Animatable for AnimationValue {
-    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64)
-        -> Result<Self, ()> {
-        match (self, other) {
-            % for prop in data.longhands:
-                % if prop.animatable:
-                    (&AnimationValue::${prop.camel_case}(ref from),
-                     &AnimationValue::${prop.camel_case}(ref to)) => {
-                        % if prop.animation_value_type == "discrete":
-                            if self_portion > other_portion {
-                                Ok(AnimationValue::${prop.camel_case}(from.clone()))
-                            } else {
-                                Ok(AnimationValue::${prop.camel_case}(to.clone()))
-                            }
-                        % else:
-                            from.add_weighted(to, self_portion, other_portion)
-                                .map(AnimationValue::${prop.camel_case})
-                        % endif
-                    }
-                % endif
-            % endfor
-            _ => {
-                panic!("Expected weighted addition of computed values of the same \
-                        property, got: {:?}, {:?}", self, other);
-            }
-        }
-    }
-
-    fn add(&self, other: &Self) -> Result<Self, ()> {
+impl Animate for AnimationValue {
+    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
         match (self, other) {
             % for prop in data.longhands:
-                % if prop.animatable:
-                    % if prop.animation_value_type == "discrete":
-                        (&AnimationValue::${prop.camel_case}(_),
-                         &AnimationValue::${prop.camel_case}(_)) => {
-                            Err(())
-                        }
-                    % else:
-                        (&AnimationValue::${prop.camel_case}(ref from),
-                         &AnimationValue::${prop.camel_case}(ref to)) => {
-                            from.add(to).map(AnimationValue::${prop.camel_case})
-                        }
-                    % endif
-                % endif
+            % if prop.animatable:
+            % if prop.animation_value_type != "discrete":
+            (
+                &AnimationValue::${prop.camel_case}(ref this),
+                &AnimationValue::${prop.camel_case}(ref other),
+            ) => {
+                Ok(AnimationValue::${prop.camel_case}(
+                    this.animate(other, procedure)?,
+                ))
+            },
+            % else:
+            (
+                &AnimationValue::${prop.camel_case}(ref this),
+                &AnimationValue::${prop.camel_case}(ref other),
+            ) => {
+                if let Procedure::Interpolate { progress } = procedure {
+                    Ok(AnimationValue::${prop.camel_case}(
+                        if progress < 0.5 { this.clone() } else { other.clone() },
+                    ))
+                } else {
+                    Err(())
+                }
+            },
+            % endif
+            % endif
             % endfor
             _ => {
-                panic!("Expected addition of computed values of the same \
-                        property, got: {:?}, {:?}", self, other);
-            }
-        }
-    }
-
-    fn accumulate(&self, other: &Self, count: u64) -> Result<Self, ()> {
-        match (self, other) {
-            % for prop in data.longhands:
-                % if prop.animatable:
-                    % if prop.animation_value_type == "discrete":
-                        (&AnimationValue::${prop.camel_case}(_),
-                         &AnimationValue::${prop.camel_case}(_)) => {
-                            Err(())
-                        }
-                    % else:
-                        (&AnimationValue::${prop.camel_case}(ref from),
-                         &AnimationValue::${prop.camel_case}(ref to)) => {
-                            from.accumulate(to, count).map(AnimationValue::${prop.camel_case})
-                        }
-                    % endif
-                % endif
-            % endfor
-            _ => {
-                panic!("Expected accumulation of computed values of the same \
-                        property, got: {:?}, {:?}", self, other);
+                panic!("Unexpected AnimationValue::animate call, got: {:?}, {:?}", self, other);
             }
         }
     }
 }
 
 impl ComputeSquaredDistance for AnimationValue {
     fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
         match (self, other) {
@@ -782,27 +713,29 @@ impl ToAnimatedZero for AnimationValue {
 
 impl RepeatableListAnimatable for LengthOrPercentage {}
 impl RepeatableListAnimatable for Either<f32, LengthOrPercentage> {}
 impl RepeatableListAnimatable for Either<NonNegativeNumber, NonNegativeLengthOrPercentage> {}
 impl RepeatableListAnimatable for SvgLengthOrPercentageOrNumber<NonNegativeLengthOrPercentage, NonNegativeNumber> {}
 
 macro_rules! repeated_vec_impl {
     ($($ty:ty),*) => {
-        $(impl<T: RepeatableListAnimatable> Animatable for $ty {
-            fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64)
-                -> Result<Self, ()> {
+        $(impl<T> Animate for $ty
+        where
+            T: RepeatableListAnimatable,
+        {
+            fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
                 // If the length of either list is zero, the least common multiple is undefined.
                 if self.is_empty() || other.is_empty() {
                     return Err(());
                 }
                 use num_integer::lcm;
                 let len = lcm(self.len(), other.len());
-                self.iter().cycle().zip(other.iter().cycle()).take(len).map(|(me, you)| {
-                    me.add_weighted(you, self_portion, other_portion)
+                self.iter().cycle().zip(other.iter().cycle()).take(len).map(|(this, other)| {
+                    this.animate(other, procedure)
                 }).collect()
             }
         }
 
         impl<T> ComputeSquaredDistance for $ty
         where
             T: ComputeSquaredDistance + RepeatableListAnimatable,
         {
@@ -818,73 +751,27 @@ macro_rules! repeated_vec_impl {
                 }).sum()
             }
         })*
     };
 }
 
 repeated_vec_impl!(SmallVec<[T; 1]>, Vec<T>);
 
-/// https://drafts.csswg.org/css-transitions/#animtype-number
-impl Animatable for Au {
-    #[inline]
-    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
-        Ok(Au((self.0 as f64 * self_portion + other.0 as f64 * other_portion).round() as i32))
-    }
-}
-
-impl <T> Animatable for Option<T>
-    where T: Animatable,
-{
-    #[inline]
-    fn add_weighted(&self, other: &Option<T>, self_portion: f64, other_portion: f64) -> Result<Option<T>, ()> {
-        match (self, other) {
-            (&Some(ref this), &Some(ref other)) => {
-                Ok(this.add_weighted(other, self_portion, other_portion).ok())
-            }
-            (&None, &None) => Ok(None),
-            _ => Err(()),
-        }
-    }
-}
-
-/// https://drafts.csswg.org/css-transitions/#animtype-number
-impl Animatable for f32 {
+/// https://drafts.csswg.org/css-transitions/#animtype-visibility
+impl Animate for Visibility {
     #[inline]
-    fn add_weighted(&self, other: &f32, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
-        Ok((*self as f64 * self_portion + *other as f64 * other_portion) as f32)
-    }
-}
-
-/// https://drafts.csswg.org/css-transitions/#animtype-number
-impl Animatable for f64 {
-    #[inline]
-    fn add_weighted(&self, other: &f64, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
-        Ok(*self * self_portion + *other * other_portion)
-    }
-}
-
-/// https://drafts.csswg.org/css-transitions/#animtype-integer
-impl Animatable for i32 {
-    #[inline]
-    fn add_weighted(&self, other: &i32, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
-        Ok((*self as f64 * self_portion + *other as f64 * other_portion + 0.5).floor() as i32)
-    }
-}
-
-/// https://drafts.csswg.org/css-transitions/#animtype-visibility
-impl Animatable for Visibility {
-    #[inline]
-    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
+        let (this_weight, other_weight) = procedure.weights();
         match (*self, *other) {
             (Visibility::visible, _) => {
-                Ok(if self_portion > 0.0 { *self } else { *other })
+                Ok(if this_weight > 0.0 { *self } else { *other })
             },
             (_, Visibility::visible) => {
-                Ok(if other_portion > 0.0 { *other } else { *self })
+                Ok(if other_weight > 0.0 { *other } else { *self })
             },
             _ => Err(()),
         }
     }
 }
 
 impl ComputeSquaredDistance for Visibility {
     #[inline]
@@ -895,47 +782,29 @@ impl ComputeSquaredDistance for Visibili
 
 impl ToAnimatedZero for Visibility {
     #[inline]
     fn to_animated_zero(&self) -> Result<Self, ()> {
         Err(())
     }
 }
 
-impl<T: Animatable + Copy> Animatable for Size2D<T> {
-    #[inline]
-    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
-        let width = self.width.add_weighted(&other.width, self_portion, other_portion)?;
-        let height = self.height.add_weighted(&other.height, self_portion, other_portion)?;
-
-        Ok(Size2D::new(width, height))
-    }
-}
-
-impl<T: Animatable + Copy> Animatable for Point2D<T> {
+/// https://drafts.csswg.org/css-transitions/#animtype-length
+impl Animate for VerticalAlign {
     #[inline]
-    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
-        let x = self.x.add_weighted(&other.x, self_portion, other_portion)?;
-        let y = self.y.add_weighted(&other.y, self_portion, other_portion)?;
-
-        Ok(Point2D::new(x, y))
-    }
-}
-
-/// https://drafts.csswg.org/css-transitions/#animtype-length
-impl Animatable for VerticalAlign {
-    #[inline]
-    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
-        match (*self, *other) {
-            (VerticalAlign::LengthOrPercentage(ref this),
-             VerticalAlign::LengthOrPercentage(ref other)) => {
-                this.add_weighted(other, self_portion, other_portion).map(|value| {
-                    VerticalAlign::LengthOrPercentage(value)
-                })
-            }
+    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
+        match (self, other) {
+            (
+                &VerticalAlign::LengthOrPercentage(ref this),
+                &VerticalAlign::LengthOrPercentage(ref other),
+            ) => {
+                Ok(VerticalAlign::LengthOrPercentage(
+                    this.animate(other, procedure)?
+                ))
+            },
             _ => Err(()),
         }
     }
 }
 
 impl ComputeSquaredDistance for VerticalAlign {
     #[inline]
     fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
@@ -953,112 +822,114 @@ impl ComputeSquaredDistance for Vertical
 }
 
 impl ToAnimatedZero for VerticalAlign {
     #[inline]
     fn to_animated_zero(&self) -> Result<Self, ()> { Err(()) }
 }
 
 /// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
-impl Animatable for CalcLengthOrPercentage {
+impl Animate for CalcLengthOrPercentage {
     #[inline]
-    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
-        fn add_weighted_half<T>(this: Option<T>,
-                                other: Option<T>,
-                                self_portion: f64,
-                                other_portion: f64)
-                                -> Result<Option<T>, ()>
-            where T: Default + Animatable,
-        {
-            match (this, other) {
-                (None, None) => Ok(None),
-                (this, other) => {
-                    let this = this.unwrap_or(T::default());
-                    let other = other.unwrap_or(T::default());
-                    this.add_weighted(&other, self_portion, other_portion).map(Some)
-                }
+    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
+        let animate_percentage_half = |this: Option<Percentage>, other: Option<Percentage>| {
+            if this.is_none() && other.is_none() {
+                return Ok(None);
             }
-        }
+            let this = this.unwrap_or_default();
+            let other = other.unwrap_or_default();
+            Ok(Some(this.animate(&other, procedure)?))
+        };
 
-        let length = self.unclamped_length().add_weighted(&other.unclamped_length(), self_portion, other_portion)?;
-        let percentage = add_weighted_half(self.percentage, other.percentage, self_portion, other_portion)?;
+        let length = self.unclamped_length().animate(&other.unclamped_length(), procedure)?;
+        let percentage = animate_percentage_half(self.percentage, other.percentage)?;
         Ok(CalcLengthOrPercentage::with_clamping_mode(length, percentage, self.clamping_mode))
     }
 }
 
 /// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
-impl Animatable for LengthOrPercentage {
+impl Animate for LengthOrPercentage {
     #[inline]
-    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
-        match (*self, *other) {
-            (LengthOrPercentage::Length(ref this),
-             LengthOrPercentage::Length(ref other)) => {
-                this.add_weighted(other, self_portion, other_portion)
-                    .map(LengthOrPercentage::Length)
-            }
-            (LengthOrPercentage::Percentage(ref this),
-             LengthOrPercentage::Percentage(ref other)) => {
-                this.add_weighted(other, self_portion, other_portion)
-                    .map(LengthOrPercentage::Percentage)
-            }
+    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
+        match (self, other) {
+            (
+                &LengthOrPercentage::Length(ref this),
+                &LengthOrPercentage::Length(ref other),
+            ) => {
+                Ok(LengthOrPercentage::Length(this.animate(other, procedure)?))
+            },
+            (
+                &LengthOrPercentage::Percentage(ref this),
+                &LengthOrPercentage::Percentage(ref other),
+            ) => {
+                Ok(LengthOrPercentage::Percentage(this.animate(other, procedure)?))
+            },
             (this, other) => {
                 // Special handling for zero values since these should not require calc().
                 if this.is_definitely_zero() {
-                    return other.add_weighted(&other, 0., other_portion)
-                } else if other.is_definitely_zero() {
-                    return this.add_weighted(self, self_portion, 0.)
+                    return other.to_animated_zero()?.animate(other, procedure);
+                }
+                if other.is_definitely_zero() {
+                    return this.animate(&this.to_animated_zero()?, procedure);
                 }
 
-                let this: CalcLengthOrPercentage = From::from(this);
-                let other: CalcLengthOrPercentage = From::from(other);
-                this.add_weighted(&other, self_portion, other_portion)
-                    .map(LengthOrPercentage::Calc)
+                let this = CalcLengthOrPercentage::from(*this);
+                let other = CalcLengthOrPercentage::from(*other);
+                Ok(LengthOrPercentage::Calc(this.animate(&other, procedure)?))
             }
         }
     }
 }
 
 impl ToAnimatedZero for LengthOrPercentage {
     #[inline]
     fn to_animated_zero(&self) -> Result<Self, ()> {
-        match self {
-            &LengthOrPercentage::Length(_) | &LengthOrPercentage::Calc(_) =>
-                Ok(LengthOrPercentage::zero()),
-            &LengthOrPercentage::Percentage(_) =>
-                Ok(LengthOrPercentage::Percentage(Percentage::zero())),
-        }
+         match *self {
+             LengthOrPercentage::Length(ref length) => {
+                 Ok(LengthOrPercentage::Length(length.to_animated_zero()?))
+             },
+             LengthOrPercentage::Percentage(ref percentage) => {
+                 Ok(LengthOrPercentage::Percentage(percentage.to_animated_zero()?))
+             },
+             LengthOrPercentage::Calc(ref calc) => {
+                 Ok(LengthOrPercentage::Calc(calc.to_animated_zero()?))
+             },
+         }
     }
 }
 
 /// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
-impl Animatable for LengthOrPercentageOrAuto {
+impl Animate for LengthOrPercentageOrAuto {
     #[inline]
-    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
-        match (*self, *other) {
-            (LengthOrPercentageOrAuto::Length(ref this),
-             LengthOrPercentageOrAuto::Length(ref other)) => {
-                this.add_weighted(other, self_portion, other_portion)
-                    .map(LengthOrPercentageOrAuto::Length)
-            }
-            (LengthOrPercentageOrAuto::Percentage(ref this),
-             LengthOrPercentageOrAuto::Percentage(ref other)) => {
-                this.add_weighted(other, self_portion, other_portion)
-                    .map(LengthOrPercentageOrAuto::Percentage)
-            }
-            (LengthOrPercentageOrAuto::Auto, LengthOrPercentageOrAuto::Auto) => {
+    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
+        match (self, other) {
+            (
+                &LengthOrPercentageOrAuto::Length(ref this),
+                &LengthOrPercentageOrAuto::Length(ref other),
+            ) => {
+                Ok(LengthOrPercentageOrAuto::Length(this.animate(other, procedure)?))
+            },
+            (
+                &LengthOrPercentageOrAuto::Percentage(ref this),
+                &LengthOrPercentageOrAuto::Percentage(ref other),
+            ) => {
+                Ok(LengthOrPercentageOrAuto::Percentage(
+                    this.animate(other, procedure)?,
+                ))
+            },
+            (&LengthOrPercentageOrAuto::Auto, &LengthOrPercentageOrAuto::Auto) => {
                 Ok(LengthOrPercentageOrAuto::Auto)
-            }
+            },
             (this, other) => {
-                let this: Option<CalcLengthOrPercentage> = From::from(this);
-                let other: Option<CalcLengthOrPercentage> = From::from(other);
-                match this.add_weighted(&other, self_portion, other_portion) {
-                    Ok(Some(result)) => Ok(LengthOrPercentageOrAuto::Calc(result)),
-                    _ => Err(()),
-                }
-            }
+                let this: Option<CalcLengthOrPercentage> = From::from(*this);
+                let other: Option<CalcLengthOrPercentage> = From::from(*other);
+                Ok(LengthOrPercentageOrAuto::Calc(
+                    this.animate(&other, procedure)?.ok_or(())?,
+                ))
+            },
         }
     }
 }
 
 impl ToAnimatedZero for LengthOrPercentageOrAuto {
     #[inline]
     fn to_animated_zero(&self) -> Result<Self, ()> {
         match *self {
@@ -1068,40 +939,43 @@ impl ToAnimatedZero for LengthOrPercenta
                 Ok(LengthOrPercentageOrAuto::Length(Au(0)))
             },
             LengthOrPercentageOrAuto::Auto => Err(()),
         }
     }
 }
 
 /// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
-impl Animatable for LengthOrPercentageOrNone {
+impl Animate for LengthOrPercentageOrNone {
     #[inline]
-    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
-        match (*self, *other) {
-            (LengthOrPercentageOrNone::Length(ref this),
-             LengthOrPercentageOrNone::Length(ref other)) => {
-                this.add_weighted(other, self_portion, other_portion)
-                    .map(LengthOrPercentageOrNone::Length)
+    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
+        match (self, other) {
+            (
+                &LengthOrPercentageOrNone::Length(ref this),
+                &LengthOrPercentageOrNone::Length(ref other),
+            ) => {
+                Ok(LengthOrPercentageOrNone::Length(this.animate(other, procedure)?))
+            },
+            (
+                &LengthOrPercentageOrNone::Percentage(ref this),
+                &LengthOrPercentageOrNone::Percentage(ref other),
+            ) => {
+                Ok(LengthOrPercentageOrNone::Percentage(
+                    this.animate(other, procedure)?,
+                ))
             }
-            (LengthOrPercentageOrNone::Percentage(ref this),
-             LengthOrPercentageOrNone::Percentage(ref other)) => {
-                this.add_weighted(other, self_portion, other_portion)
-                    .map(LengthOrPercentageOrNone::Percentage)
-            }
-            (LengthOrPercentageOrNone::None, LengthOrPercentageOrNone::None) => {
+            (&LengthOrPercentageOrNone::None, &LengthOrPercentageOrNone::None) => {
                 Ok(LengthOrPercentageOrNone::None)
-            }
+            },
             (this, other) => {
-                let this = <Option<CalcLengthOrPercentage>>::from(this);
-                let other = <Option<CalcLengthOrPercentage>>::from(other);
-                match this.add_weighted(&other, self_portion, other_portion) {
-                    Ok(Some(result)) => Ok(LengthOrPercentageOrNone::Calc(result)),
-                    _ => Err(()),
-                }
+                let this = <Option<CalcLengthOrPercentage>>::from(*this);
+                let other = <Option<CalcLengthOrPercentage>>::from(*other);
+                Ok(LengthOrPercentageOrNone::Calc(
+                    this.animate(&other, procedure)?.ok_or(())?,
+                ))
             },
         }
     }
 }
 
 impl ToAnimatedZero for LengthOrPercentageOrNone {
     #[inline]
     fn to_animated_zero(&self) -> Result<Self, ()> {
@@ -1112,24 +986,27 @@ impl ToAnimatedZero for LengthOrPercenta
                 Ok(LengthOrPercentageOrNone::Length(Au(0)))
             },
             LengthOrPercentageOrNone::None => Err(()),
         }
     }
 }
 
 /// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
-impl Animatable for MozLength {
+impl Animate for MozLength {
     #[inline]
-    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
-        match (*self, *other) {
-            (MozLength::LengthOrPercentageOrAuto(ref this),
-             MozLength::LengthOrPercentageOrAuto(ref other)) => {
-                this.add_weighted(other, self_portion, other_portion)
-                    .map(MozLength::LengthOrPercentageOrAuto)
+    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
+        match (self, other) {
+            (
+                &MozLength::LengthOrPercentageOrAuto(ref this),
+                &MozLength::LengthOrPercentageOrAuto(ref other),
+            ) => {
+                Ok(MozLength::LengthOrPercentageOrAuto(
+                    this.animate(other, procedure)?,
+                ))
             }
             _ => Err(()),
         }
     }
 }
 
 impl ToAnimatedZero for MozLength {
     #[inline]
@@ -1139,67 +1016,69 @@ impl ToAnimatedZero for MozLength {
                 Ok(MozLength::LengthOrPercentageOrAuto(length.to_animated_zero()?))
             },
             _ => Err(())
         }
     }
 }
 
 /// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
-impl Animatable for MaxLength {
+impl Animate for MaxLength {
     #[inline]
-    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
-        match (*self, *other) {
-            (MaxLength::LengthOrPercentageOrNone(ref this),
-             MaxLength::LengthOrPercentageOrNone(ref other)) => {
-                this.add_weighted(other, self_portion, other_portion)
-                    .map(MaxLength::LengthOrPercentageOrNone)
-            }
+    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
+        match (self, other) {
+            (
+                &MaxLength::LengthOrPercentageOrNone(ref this),
+                &MaxLength::LengthOrPercentageOrNone(ref other),
+            ) => {
+                Ok(MaxLength::LengthOrPercentageOrNone(
+                    this.animate(other, procedure)?,
+                ))
+            },
             _ => Err(()),
         }
     }
 }
 
 impl ToAnimatedZero for MaxLength {
     #[inline]
     fn to_animated_zero(&self) -> Result<Self, ()> { Err(()) }
 }
 
 /// http://dev.w3.org/csswg/css-transitions/#animtype-font-weight
-impl Animatable for FontWeight {
+impl Animate for FontWeight {
     #[inline]
-    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
         let a = self.0 as f64;
         let b = other.0 as f64;
         const NORMAL: f64 = 400.;
-        let weight = (a - NORMAL) * self_portion + (b - NORMAL) * other_portion + NORMAL;
+        let (this_weight, other_weight) = procedure.weights();
+        let weight = (a - NORMAL) * this_weight + (b - NORMAL) * other_weight + NORMAL;
         let weight = (weight.max(100.).min(900.) / 100.).round() * 100.;
         Ok(FontWeight(weight as u16))
     }
 }
 
 impl ToAnimatedZero for FontWeight {
     #[inline]
     fn to_animated_zero(&self) -> Result<Self, ()> {
         Ok(FontWeight::normal())
     }
 }
 
 /// https://drafts.csswg.org/css-fonts/#font-stretch-prop
-impl Animatable for FontStretch {
+impl Animate for FontStretch {
     #[inline]
-    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64)
-        -> Result<Self, ()>
+    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()>
     {
         let from = f64::from(*self);
         let to = f64::from(*other);
-        // FIXME: When `const fn` is available in release rust, make |normal|, below, const.
         let normal = f64::from(FontStretch::normal);
-        let result = (from - normal) * self_portion + (to - normal) * other_portion + normal;
-
+        let (this_weight, other_weight) = procedure.weights();
+        let result = (from - normal) * this_weight + (to - normal) * other_weight + normal;
         Ok(result.into())
     }
 }
 
 impl ComputeSquaredDistance for FontStretch {
     #[inline]
     fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
         f64::from(*self).compute_squared_distance(&(*other).into())
@@ -1236,23 +1115,26 @@ impl Into<FontStretch> for f64 {
         let index = (self + 0.5).floor().min(9.0).max(1.0);
         static FONT_STRETCH_ENUM_MAP: [FontStretch; 9] =
             [ ultra_condensed, extra_condensed, condensed, semi_condensed, normal,
               semi_expanded, expanded, extra_expanded, ultra_expanded ];
         FONT_STRETCH_ENUM_MAP[(index - 1.0) as usize]
     }
 }
 
-/// https://drafts.csswg.org/css-transitions/#animtype-simple-list
-impl<H: Animatable, V: Animatable> Animatable for generic_position::Position<H, V> {
+impl<H, V> Animate for generic_position::Position<H, V>
+where
+    H: Animate,
+    V: Animate,
+{
     #[inline]
-    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
         Ok(generic_position::Position {
-            horizontal: self.horizontal.add_weighted(&other.horizontal, self_portion, other_portion)?,
-            vertical: self.vertical.add_weighted(&other.vertical, self_portion, other_portion)?,
+            horizontal: self.horizontal.animate(&other.horizontal, procedure)?,
+            vertical: self.vertical.animate(&other.vertical, procedure)?,
         })
     }
 }
 
 impl<H, V> ToAnimatedZero for generic_position::Position<H, V>
 where
     H: ToAnimatedZero,
     V: ToAnimatedZero,
@@ -1265,218 +1147,172 @@ where
         })
     }
 }
 
 impl<H, V> RepeatableListAnimatable for generic_position::Position<H, V>
     where H: RepeatableListAnimatable, V: RepeatableListAnimatable {}
 
 /// https://drafts.csswg.org/css-transitions/#animtype-rect
-impl Animatable for ClipRect {
+impl Animate for ClipRect {
     #[inline]
-    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64)
-        -> Result<Self, ()> {
+    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
         Ok(ClipRect {
-            top: self.top.add_weighted(&other.top, self_portion, other_portion)?,
-            right: self.right.add_weighted(&other.right, self_portion, other_portion)?,
-            bottom: self.bottom.add_weighted(&other.bottom, self_portion, other_portion)?,
-            left: self.left.add_weighted(&other.left, self_portion, other_portion)?,
+            top: self.top.animate(&other.top, procedure)?,
+            right: self.right.animate(&other.right, procedure)?,
+            bottom: self.bottom.animate(&other.bottom, procedure)?,
+            left: self.left.animate(&other.left, procedure)?,
         })
     }
 }
 
 impl ToAnimatedZero for ClipRect {
     #[inline]
     fn to_animated_zero(&self) -> Result<Self, ()> { Err(()) }
 }
 
-/// Check if it's possible to do a direct numerical interpolation
-/// between these two transform lists.
-/// http://dev.w3.org/csswg/css-transforms/#transform-transform-animation
-fn can_interpolate_list(from_list: &[TransformOperation],
-                        to_list: &[TransformOperation]) -> bool {
-    // Lists must be equal length
-    if from_list.len() != to_list.len() {
-        return false;
-    }
-
-    // Each transform operation must match primitive type in other list
-    for (from, to) in from_list.iter().zip(to_list) {
-        match (from, to) {
-            (&TransformOperation::Matrix(..), &TransformOperation::Matrix(..)) |
-            (&TransformOperation::Skew(..), &TransformOperation::Skew(..)) |
-            (&TransformOperation::Translate(..), &TransformOperation::Translate(..)) |
-            (&TransformOperation::Scale(..), &TransformOperation::Scale(..)) |
-            (&TransformOperation::Rotate(..), &TransformOperation::Rotate(..)) |
-            (&TransformOperation::Perspective(..), &TransformOperation::Perspective(..)) => {}
-            _ => {
-                return false;
-            }
-        }
-    }
-
-    true
-}
-
 /// Build an equivalent 'identity transform function list' based
 /// on an existing transform list.
 /// http://dev.w3.org/csswg/css-transforms/#none-transform-animation
-fn build_identity_transform_list(list: &[TransformOperation]) -> Vec<TransformOperation> {
-    let mut result = vec!();
-
-    for operation in list {
-        match *operation {
+impl ToAnimatedZero for TransformOperation {
+    fn to_animated_zero(&self) -> Result<Self, ()> {
+        match *self {
             TransformOperation::Matrix(..) => {
-                let identity = ComputedMatrix::identity();
-                result.push(TransformOperation::Matrix(identity));
-            }
-            TransformOperation::MatrixWithPercents(..) => {}
-            TransformOperation::Skew(..) => {
-                result.push(TransformOperation::Skew(Angle::zero(), Angle::zero()))
-            }
-            TransformOperation::Translate(..) => {
-                result.push(TransformOperation::Translate(LengthOrPercentage::zero(),
-                                                          LengthOrPercentage::zero(),
-                                                          Au(0)));
-            }
+                Ok(TransformOperation::Matrix(ComputedMatrix::identity()))
+            },
+            TransformOperation::MatrixWithPercents(..) => {
+                // FIXME(nox): Should be MatrixWithPercents value.
+                Ok(TransformOperation::Matrix(ComputedMatrix::identity()))
+            },
+            TransformOperation::Skew(sx, sy) => {
+                Ok(TransformOperation::Skew(
+                    sx.to_animated_zero()?,
+                    sy.to_animated_zero()?,
+                ))
+            },
+            TransformOperation::Translate(ref tx, ref ty, ref tz) => {
+                Ok(TransformOperation::Translate(
+                    tx.to_animated_zero()?,
+                    ty.to_animated_zero()?,
+                    tz.to_animated_zero()?,
+                ))
+            },
             TransformOperation::Scale(..) => {
-                result.push(TransformOperation::Scale(1.0, 1.0, 1.0));
-            }
+                Ok(TransformOperation::Scale(1.0, 1.0, 1.0))
+            },
             TransformOperation::Rotate(x, y, z, a) => {
                 let (x, y, z, _) = get_normalized_vector_and_angle(x, y, z, a);
-                result.push(TransformOperation::Rotate(x, y, z, Angle::zero()));
-            }
+                Ok(TransformOperation::Rotate(x, y, z, Angle::zero()))
+            },
             TransformOperation::Perspective(..) |
             TransformOperation::AccumulateMatrix { .. } |
             TransformOperation::InterpolateMatrix { .. } => {
                 // Perspective: We convert a perspective function into an equivalent
                 //     ComputedMatrix, and then decompose/interpolate/recompose these matrices.
                 // AccumulateMatrix/InterpolateMatrix: We do interpolation on
                 //     AccumulateMatrix/InterpolateMatrix by reading it as a ComputedMatrix
                 //     (with layout information), and then do matrix interpolation.
                 //
                 // Therefore, we use an identity matrix to represent the identity transform list.
                 // http://dev.w3.org/csswg/css-transforms/#identity-transform-function
-                let identity = ComputedMatrix::identity();
-                result.push(TransformOperation::Matrix(identity));
-            }
+                //
+                // FIXME(nox): This does not actually work, given the impl of
+                // Animate for TransformOperation bails out if the two given
+                // values are dissimilar.
+                Ok(TransformOperation::Matrix(ComputedMatrix::identity()))
+            },
         }
     }
-
-    result
 }
 
-/// A wrapper for calling add_weighted that interpolates the distance of the two values from
-/// an initial_value and uses that to produce an interpolated value.
-/// This is used for values such as 'scale' where the initial value is 1 and where if we interpolate
-/// the absolute values, we will produce odd results for accumulation.
-fn add_weighted_with_initial_val<T: Animatable>(a: &T,
-                                                b: &T,
-                                                a_portion: f64,
-                                                b_portion: f64,
-                                                initial_val: &T) -> Result<T, ()> {
-    let a = a.add_weighted(&initial_val, 1.0, -1.0)?;
-    let b = b.add_weighted(&initial_val, 1.0, -1.0)?;
-    let result = a.add_weighted(&b, a_portion, b_portion)?;
-    result.add_weighted(&initial_val, 1.0, 1.0)
+fn animate_multiplicative_factor(
+    this: CSSFloat,
+    other: CSSFloat,
+    procedure: Procedure,
+) -> Result<CSSFloat, ()> {
+    Ok((this - 1.).animate(&(other - 1.), procedure)? + 1.)
 }
 
-/// Add two transform lists.
 /// http://dev.w3.org/csswg/css-transforms/#interpolation-of-transforms
-fn add_weighted_transform_lists(from_list: &[TransformOperation],
-                                to_list: &[TransformOperation],
-                                self_portion: f64,
-                                other_portion: f64) -> TransformList {
-    let mut result = vec![];
-
-    if can_interpolate_list(from_list, to_list) {
-        for (from, to) in from_list.iter().zip(to_list) {
-            match (from, to) {
-                (&TransformOperation::Matrix(from),
-                 &TransformOperation::Matrix(_to)) => {
-                    let sum = from.add_weighted(&_to, self_portion, other_portion).unwrap();
-                    result.push(TransformOperation::Matrix(sum));
-                }
-                (&TransformOperation::MatrixWithPercents(_),
-                 &TransformOperation::MatrixWithPercents(_)) => {
-                    // We don't add_weighted `-moz-transform` matrices yet.
-                    // They contain percentage values.
-                    {}
-                }
-                (&TransformOperation::Skew(fx, fy),
-                 &TransformOperation::Skew(tx, ty)) => {
-                    let ix = fx.add_weighted(&tx, self_portion, other_portion).unwrap();
-                    let iy = fy.add_weighted(&ty, self_portion, other_portion).unwrap();
-                    result.push(TransformOperation::Skew(ix, iy));
-                }
-                (&TransformOperation::Translate(fx, fy, fz),
-                 &TransformOperation::Translate(tx, ty, tz)) => {
-                    let ix = fx.add_weighted(&tx, self_portion, other_portion).unwrap();
-                    let iy = fy.add_weighted(&ty, self_portion, other_portion).unwrap();
-                    let iz = fz.add_weighted(&tz, self_portion, other_portion).unwrap();
-                    result.push(TransformOperation::Translate(ix, iy, iz));
-                }
-                (&TransformOperation::Scale(fx, fy, fz),
-                 &TransformOperation::Scale(tx, ty, tz)) => {
-                    let ix = add_weighted_with_initial_val(&fx, &tx, self_portion,
-                                                           other_portion, &1.0).unwrap();
-                    let iy = add_weighted_with_initial_val(&fy, &ty, self_portion,
-                                                           other_portion, &1.0).unwrap();
-                    let iz = add_weighted_with_initial_val(&fz, &tz, self_portion,
-                                                           other_portion, &1.0).unwrap();
-                    result.push(TransformOperation::Scale(ix, iy, iz));
+impl Animate for TransformOperation {
+    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
+        match (self, other) {
+            (
+                &TransformOperation::Matrix(ref this),
+                &TransformOperation::Matrix(ref other),
+            ) => {
+                Ok(TransformOperation::Matrix(
+                    this.animate(other, procedure)?,
+                ))
+            },
+            (
+                &TransformOperation::Skew(ref fx, ref fy),
+                &TransformOperation::Skew(ref tx, ref ty),
+            ) => {
+                Ok(TransformOperation::Skew(
+                    fx.animate(tx, procedure)?,
+                    fy.animate(ty, procedure)?,
+                ))
+            },
+            (
+                &TransformOperation::Translate(ref fx, ref fy, ref fz),
+                &TransformOperation::Translate(ref tx, ref ty, ref tz),
+            ) => {
+                Ok(TransformOperation::Translate(
+                    fx.animate(tx, procedure)?,
+                    fy.animate(ty, procedure)?,
+                    fz.animate(tz, procedure)?,
+                ))
+            },
+            (
+                &TransformOperation::Scale(ref fx, ref fy, ref fz),
+                &TransformOperation::Scale(ref tx, ref ty, ref tz),
+            ) => {
+                Ok(TransformOperation::Scale(
+                    animate_multiplicative_factor(*fx, *tx, procedure)?,
+                    animate_multiplicative_factor(*fy, *ty, procedure)?,
+                    animate_multiplicative_factor(*fz, *tz, procedure)?,
+                ))
+            },
+            (
+                &TransformOperation::Rotate(fx, fy, fz, fa),
+                &TransformOperation::Rotate(tx, ty, tz, ta),
+            ) => {
+                let (fx, fy, fz, fa) = get_normalized_vector_and_angle(fx, fy, fz, fa);
+                let (tx, ty, tz, ta) = get_normalized_vector_and_angle(tx, ty, tz, ta);
+                if (fx, fy, fz) == (tx, ty, tz) {
+                    let ia = fa.animate(&ta, procedure)?;
+                    Ok(TransformOperation::Rotate(fx, fy, fz, ia))
+                } else {
+                    let matrix_f = rotate_to_matrix(fx, fy, fz, fa);
+                    let matrix_t = rotate_to_matrix(tx, ty, tz, ta);
+                    Ok(TransformOperation::Matrix(
+                        matrix_f.animate(&matrix_t, procedure)?,
+                    ))
                 }
-                (&TransformOperation::Rotate(fx, fy, fz, fa),
-                 &TransformOperation::Rotate(tx, ty, tz, ta)) => {
-                    let (fx, fy, fz, fa) = get_normalized_vector_and_angle(fx, fy, fz, fa);
-                    let (tx, ty, tz, ta) = get_normalized_vector_and_angle(tx, ty, tz, ta);
-                    if (fx, fy, fz) == (tx, ty, tz) {
-                        let ia = fa.add_weighted(&ta, self_portion, other_portion).unwrap();
-                        result.push(TransformOperation::Rotate(fx, fy, fz, ia));
-                    } else {
-                        let matrix_f = rotate_to_matrix(fx, fy, fz, fa);
-                        let matrix_t = rotate_to_matrix(tx, ty, tz, ta);
-                        let sum = matrix_f.add_weighted(&matrix_t, self_portion, other_portion)
-                                          .unwrap();
-
-                        result.push(TransformOperation::Matrix(sum));
-                    }
+            },
+            (
+                &TransformOperation::Perspective(ref fd),
+                &TransformOperation::Perspective(ref td),
+            ) => {
+                let mut fd_matrix = ComputedMatrix::identity();
+                let mut td_matrix = ComputedMatrix::identity();
+                if fd.0 > 0 {
+                    fd_matrix.m34 = -1. / fd.to_f32_px();
                 }
-                (&TransformOperation::Perspective(fd),
-                 &TransformOperation::Perspective(td)) => {
-                    let mut fd_matrix = ComputedMatrix::identity();
-                    let mut td_matrix = ComputedMatrix::identity();
-                    if fd.0 > 0 {
-                        fd_matrix.m34 = -1. / fd.to_f32_px();
-                    }
-
-                    if td.0 > 0 {
-                        td_matrix.m34 = -1. / td.to_f32_px();
-                    }
-
-                    let sum = fd_matrix.add_weighted(&td_matrix, self_portion, other_portion)
-                                       .unwrap();
-                    result.push(TransformOperation::Matrix(sum));
+                if td.0 > 0 {
+                    td_matrix.m34 = -1. / td.to_f32_px();
                 }
-                _ => {
-                    // This should be unreachable due to the can_interpolate_list() call.
-                    unreachable!();
-                }
-            }
+                Ok(TransformOperation::Matrix(
+                    fd_matrix.animate(&td_matrix, procedure)?,
+                ))
+            },
+            _ => Err(()),
         }
-    } else {
-        let from_transform_list = TransformList(Some(from_list.to_vec()));
-        let to_transform_list = TransformList(Some(to_list.to_vec()));
-        result.push(
-            TransformOperation::InterpolateMatrix { from_list: from_transform_list,
-                                                    to_list: to_transform_list,
-                                                    progress: Percentage(other_portion as f32) });
     }
-
-    TransformList(Some(result))
 }
 
 /// https://www.w3.org/TR/css-transforms-1/#Rotate3dDefined
 fn rotate_to_matrix(x: f32, y: f32, z: f32, a: Angle) -> ComputedMatrix {
     let half_rad = a.radians() / 2.0;
     let sc = (half_rad).sin() * (half_rad).cos();
     let sq = (half_rad).sin().powi(2);
 
@@ -1535,50 +1371,48 @@ pub struct MatrixDecomposed2D {
     /// The scale function.
     pub scale: Scale2D,
     /// The rotation angle.
     pub angle: f32,
     /// The inner matrix.
     pub matrix: InnerMatrix2D,
 }
 
-impl Animatable for InnerMatrix2D {
-    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+impl Animate for InnerMatrix2D {
+    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
         Ok(InnerMatrix2D {
-            m11: add_weighted_with_initial_val(&self.m11, &other.m11,
-                                               self_portion, other_portion, &1.0)?,
-            m12: self.m12.add_weighted(&other.m12, self_portion, other_portion)?,
-            m21: self.m21.add_weighted(&other.m21, self_portion, other_portion)?,
-            m22: add_weighted_with_initial_val(&self.m22, &other.m22,
-                                               self_portion, other_portion, &1.0)?,
+            m11: animate_multiplicative_factor(self.m11, other.m11, procedure)?,
+            m12: self.m12.animate(&other.m12, procedure)?,
+            m21: self.m21.animate(&other.m21, procedure)?,
+            m22: animate_multiplicative_factor(self.m22, other.m22, procedure)?,
         })
     }
 }
 
-impl Animatable for Translate2D {
-    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+impl Animate for Translate2D {
+    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
         Ok(Translate2D(
-            self.0.add_weighted(&other.0, self_portion, other_portion)?,
-            self.1.add_weighted(&other.1, self_portion, other_portion)?,
+            self.0.animate(&other.0, procedure)?,
+            self.1.animate(&other.1, procedure)?,
         ))
     }
 }
 
-impl Animatable for Scale2D {
-    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+impl Animate for Scale2D {
+    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
         Ok(Scale2D(
-            add_weighted_with_initial_val(&self.0, &other.0, self_portion, other_portion, &1.0)?,
-            add_weighted_with_initial_val(&self.1, &other.1, self_portion, other_portion, &1.0)?,
+            animate_multiplicative_factor(self.0, other.0, procedure)?,
+            animate_multiplicative_factor(self.1, other.1, procedure)?,
         ))
     }
 }
 
-impl Animatable for MatrixDecomposed2D {
+impl Animate for MatrixDecomposed2D {
     /// https://drafts.csswg.org/css-transforms/#interpolation-of-decomposed-2d-matrix-values
-    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
         // If x-axis of one is flipped, and y-axis of the other,
         // convert to an unflipped rotation.
         let mut scale = self.scale;
         let mut angle = self.angle;
         let mut other_angle = other.angle;
         if (scale.0 < 0.0 && other.scale.1 < 0.0) || (scale.1 < 0.0 && other.scale.0 < 0.0) {
             scale.0 = -scale.0;
             scale.1 = -scale.1;
@@ -1598,20 +1432,20 @@ impl Animatable for MatrixDecomposed2D {
                 angle -= 360.
             }
             else{
                 other_angle -= 360.
             }
         }
 
         // Interpolate all values.
-        let translate = self.translate.add_weighted(&other.translate, self_portion, other_portion)?;
-        let scale = scale.add_weighted(&other.scale, self_portion, other_portion)?;
-        let angle = angle.add_weighted(&other_angle, self_portion, other_portion)?;
-        let matrix = self.matrix.add_weighted(&other.matrix, self_portion, other_portion)?;
+        let translate = self.translate.animate(&other.translate, procedure)?;
+        let scale = scale.animate(&other.scale, procedure)?;
+        let angle = angle.animate(&other_angle, procedure)?;
+        let matrix = self.matrix.animate(&other.matrix, procedure)?;
 
         Ok(MatrixDecomposed2D {
             translate: translate,
             scale: scale,
             angle: angle,
             matrix: matrix,
         })
     }
@@ -1626,36 +1460,35 @@ impl ComputeSquaredDistance for MatrixDe
         let angle2 = other.angle as f64 * RAD_PER_DEG;
         Ok(self.translate.compute_squared_distance(&other.translate)? +
            self.scale.compute_squared_distance(&other.scale)? +
            angle1.compute_squared_distance(&angle2)? +
            self.matrix.compute_squared_distance(&other.matrix)?)
     }
 }
 
-impl Animatable for ComputedMatrix {
-    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+impl Animate for ComputedMatrix {
+    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
         if self.is_3d() || other.is_3d() {
             let decomposed_from = decompose_3d_matrix(*self);
             let decomposed_to = decompose_3d_matrix(*other);
             match (decomposed_from, decomposed_to) {
-                (Ok(from), Ok(to)) => {
-                    let sum = from.add_weighted(&to, self_portion, other_portion)?;
-                    Ok(ComputedMatrix::from(sum))
+                (Ok(this), Ok(other)) => {
+                    Ok(ComputedMatrix::from(this.animate(&other, procedure)?))
                 },
                 _ => {
-                    let result = if self_portion > other_portion {*self} else {*other};
+                    let (this_weight, other_weight) = procedure.weights();
+                    let result = if this_weight > other_weight { *self } else { *other };
                     Ok(result)
-                }
+                },
             }
         } else {
-            let decomposed_from = MatrixDecomposed2D::from(*self);
-            let decomposed_to = MatrixDecomposed2D::from(*other);
-            let sum = decomposed_from.add_weighted(&decomposed_to, self_portion, other_portion)?;
-            Ok(ComputedMatrix::from(sum))
+            let this = MatrixDecomposed2D::from(*self);
+            let other = MatrixDecomposed2D::from(*other);
+            Ok(ComputedMatrix::from(this.animate(&other, procedure)?))
         }
     }
 }
 
 impl ComputeSquaredDistance for ComputedMatrix {
     #[inline]
     fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
         if self.is_3d() || other.is_3d() {
@@ -2082,103 +1915,104 @@ fn dot(a: [f32; 3], b: [f32; 3]) -> f32 
 fn cross(row1: [f32; 3], row2: [f32; 3]) -> [f32; 3] {
     [
         row1[1] * row2[2] - row1[2] * row2[1],
         row1[2] * row2[0] - row1[0] * row2[2],
         row1[0] * row2[1] - row1[1] * row2[0]
     ]
 }
 
-impl Animatable for Translate3D {
-    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+impl Animate for Translate3D {
+    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
         Ok(Translate3D(
-            self.0.add_weighted(&other.0, self_portion, other_portion)?,
-            self.1.add_weighted(&other.1, self_portion, other_portion)?,
-            self.2.add_weighted(&other.2, self_portion, other_portion)?,
+            self.0.animate(&other.0, procedure)?,
+            self.1.animate(&other.1, procedure)?,
+            self.2.animate(&other.2, procedure)?,
         ))
     }
 }
 
-impl Animatable for Scale3D {
-    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+impl Animate for Scale3D {
+    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
         Ok(Scale3D(
-            add_weighted_with_initial_val(&self.0, &other.0, self_portion, other_portion, &1.0)?,
-            add_weighted_with_initial_val(&self.1, &other.1, self_portion, other_portion, &1.0)?,
-            add_weighted_with_initial_val(&self.2, &other.2, self_portion, other_portion, &1.0)?,
+            animate_multiplicative_factor(self.0, other.0, procedure)?,
+            animate_multiplicative_factor(self.1, other.1, procedure)?,
+            animate_multiplicative_factor(self.2, other.2, procedure)?,
         ))
     }
 }
 
-impl Animatable for Skew {
-    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+impl Animate for Skew {
+    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
         Ok(Skew(
-            self.0.add_weighted(&other.0, self_portion, other_portion)?,
-            self.1.add_weighted(&other.1, self_portion, other_portion)?,
-            self.2.add_weighted(&other.2, self_portion, other_portion)?,
+            self.0.animate(&other.0, procedure)?,
+            self.1.animate(&other.1, procedure)?,
+            self.2.animate(&other.2, procedure)?,
         ))
     }
 }
 
 impl ComputeSquaredDistance for Skew {
     // We have to use atan() to convert the skew factors into skew angles, so implement
     // ComputeSquaredDistance manually.
     #[inline]
     fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
         Ok(self.0.atan().compute_squared_distance(&other.0.atan())? +
            self.1.atan().compute_squared_distance(&other.1.atan())? +
            self.2.atan().compute_squared_distance(&other.2.atan())?)
     }
 }
 
-impl Animatable for Perspective {
-    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+impl Animate for Perspective {
+    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
         Ok(Perspective(
-            self.0.add_weighted(&other.0, self_portion, other_portion)?,
-            self.1.add_weighted(&other.1, self_portion, other_portion)?,
-            self.2.add_weighted(&other.2, self_portion, other_portion)?,
-            add_weighted_with_initial_val(&self.3, &other.3, self_portion, other_portion, &1.0)?,
+            self.0.animate(&other.0, procedure)?,
+            self.1.animate(&other.1, procedure)?,
+            self.2.animate(&other.2, procedure)?,
+            animate_multiplicative_factor(self.3, other.3, procedure)?,
         ))
     }
 }
 
-impl Animatable for MatrixDecomposed3D {
+impl Animate for MatrixDecomposed3D {
     /// https://drafts.csswg.org/css-transforms/#interpolation-of-decomposed-3d-matrix-values
-    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64)
-        -> Result<Self, ()> {
+    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
         use std::f64;
 
-        debug_assert!((self_portion + other_portion - 1.0f64).abs() <= f64::EPSILON ||
-                      other_portion == 1.0f64 || other_portion == 0.0f64,
-                      "add_weighted should only be used for interpolating or accumulating transforms");
+        let (this_weight, other_weight) = procedure.weights();
+
+        debug_assert!((this_weight + other_weight - 1.0f64).abs() <= f64::EPSILON ||
+                      other_weight == 1.0f64 || other_weight == 0.0f64,
+                      "animate should only be used for interpolating or accumulating transforms");
 
         let mut sum = *self;
 
         // Add translate, scale, skew and perspective components.
-        sum.translate = self.translate.add_weighted(&other.translate, self_portion, other_portion)?;
-        sum.scale = self.scale.add_weighted(&other.scale, self_portion, other_portion)?;
-        sum.skew = self.skew.add_weighted(&other.skew, self_portion, other_portion)?;
-        sum.perspective = self.perspective.add_weighted(&other.perspective, self_portion, other_portion)?;
+        sum.translate = self.translate.animate(&other.translate, procedure)?;
+        sum.scale = self.scale.animate(&other.scale, procedure)?;
+        sum.skew = self.skew.animate(&other.skew, procedure)?;
+        sum.perspective = self.perspective.animate(&other.perspective, procedure)?;
 
         // Add quaternions using spherical linear interpolation (Slerp).
         //
-        // We take a specialized code path for accumulation (where other_portion is 1)
-        if other_portion == 1.0 {
-            if self_portion == 0.0 {
+        // We take a specialized code path for accumulation (where other_weight is 1)
+        if other_weight == 1.0 {
+            if this_weight == 0.0 {
                 return Ok(*other)
             }
 
             let clamped_w = self.quaternion.3.min(1.0).max(-1.0);
 
             // Determine the scale factor.
             let mut theta = clamped_w.acos();
             let mut scale = if theta == 0.0 { 0.0 } else { 1.0 / theta.sin() };
-            theta *= self_portion;
+            theta *= this_weight;
             scale *= theta.sin();
 
-            // Scale the self matrix by self_portion.
+            // Scale the self matrix by this_weight.
             let mut scaled_self = *self;
             % for i in range(3):
                 scaled_self.quaternion.${i} *= scale;
             % endfor
             scaled_self.quaternion.3 = theta.cos();
 
             // Multiply scaled-self by other.
             let a = &scaled_self.quaternion;
@@ -2199,22 +2033,22 @@ impl Animatable for MatrixDecomposed3D {
             product = product.min(1.0);
             product = product.max(-1.0);
 
             if product == 1.0 {
                 return Ok(sum);
             }
 
             let theta = product.acos();
-            let w = (other_portion * theta).sin() * 1.0 / (1.0 - product * product).sqrt();
+            let w = (other_weight * theta).sin() * 1.0 / (1.0 - product * product).sqrt();
 
             let mut a = *self;
             let mut b = *other;
             % for i in range(4):
-                a.quaternion.${i} *= (other_portion * theta).cos() - product * w;
+                a.quaternion.${i} *= (other_weight * theta).cos() - product * w;
                 b.quaternion.${i} *= w;
                 sum.quaternion.${i} = a.quaternion.${i} + b.quaternion.${i};
             % endfor
         }
 
         Ok(sum)
     }
 }
@@ -2412,233 +2246,242 @@ impl ComputedMatrix {
              self.m12*self.m21*self.m33 + self.m11*self.m22*self.m33),
         };
 
         Some(x)
     }
 }
 
 /// https://drafts.csswg.org/css-transforms/#interpolation-of-transforms
-impl Animatable for TransformList {
+impl Animate for TransformList {
     #[inline]
-    fn add_weighted(&self, other: &TransformList, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
-        // http://dev.w3.org/csswg/css-transforms/#interpolation-of-transforms
-        let result = match (&self.0, &other.0) {
-            (&Some(ref from_list), &Some(ref to_list)) => {
-                // Two lists of transforms
-                add_weighted_transform_lists(from_list, &to_list, self_portion, other_portion)
-            }
-            (&Some(ref from_list), &None) => {
-                // http://dev.w3.org/csswg/css-transforms/#none-transform-animation
-                let to_list = build_identity_transform_list(from_list);
-                add_weighted_transform_lists(from_list, &to_list, self_portion, other_portion)
-            }
-            (&None, &Some(ref to_list)) => {
-                // http://dev.w3.org/csswg/css-transforms/#none-transform-animation
-                let from_list = build_identity_transform_list(to_list);
-                add_weighted_transform_lists(&from_list, to_list, self_portion, other_portion)
-            }
-            _ => {
-                // http://dev.w3.org/csswg/css-transforms/#none-none-animation
-                TransformList(None)
-            }
+    fn animate(
+        &self,
+        other: &Self,
+        procedure: Procedure,
+    ) -> Result<Self, ()> {
+        if self.0.is_none() && other.0.is_none() {
+            return Ok(TransformList(None));
+        }
+
+        if procedure == Procedure::Add {
+            let this = self.0.as_ref().map_or(&[][..], |l| l);
+            let other = other.0.as_ref().map_or(&[][..], |l| l);
+            let result = this.iter().chain(other).cloned().collect::<Vec<_>>();
+            return Ok(TransformList(if result.is_empty() {
+                None
+            } else {
+                Some(result)
+            }));
+        }
+
+        let this = if self.0.is_some() {
+            Cow::Borrowed(self)
+        } else {
+            Cow::Owned(other.to_animated_zero()?)
+        };
+        let other = if other.0.is_some() {
+            Cow::Borrowed(other)
+        } else {
+            Cow::Owned(self.to_animated_zero()?)
         };
 
-        Ok(result)
-    }
-
-    fn add(&self, other: &Self) -> Result<Self, ()> {
-        match (&self.0, &other.0) {
-            (&Some(ref from_list), &Some(ref to_list)) => {
-                Ok(TransformList(Some([&from_list[..], &to_list[..]].concat())))
-            }
-            (&Some(_), &None) => {
-                Ok(self.clone())
-            }
-            (&None, &Some(_)) => {
-                Ok(other.clone())
-            }
-            _ => {
-                Ok(TransformList(None))
+        {
+            let this = (*this).0.as_ref().map_or(&[][..], |l| l);
+            let other = (*other).0.as_ref().map_or(&[][..], |l| l);
+            if this.len() == other.len() {
+                let result = this.iter().zip(other).map(|(this, other)| {
+                    this.animate(other, procedure)
+                }).collect::<Result<Vec<_>, _>>();
+                if let Ok(list) = result {
+                    return Ok(TransformList(if list.is_empty() {
+                        None
+                    } else {
+                        Some(list)
+                    }));
+                }
             }
         }
-    }
 
-    #[inline]
-    fn accumulate(&self, other: &Self, count: u64) -> Result<Self, ()> {
-        match (&self.0, &other.0) {
-            (&Some(ref from_list), &Some(ref to_list)) => {
-                if can_interpolate_list(from_list, to_list) {
-                    Ok(add_weighted_transform_lists(from_list, &to_list, count as f64, 1.0))
-                } else {
-                    use std::i32;
-                    let result = vec![TransformOperation::AccumulateMatrix {
-                        from_list: self.clone(),
-                        to_list: other.clone(),
-                        count: cmp::min(count, i32::MAX as u64) as i32
-                    }];
-                    Ok(TransformList(Some(result)))
-                }
-            }
-            (&Some(ref from_list), &None) => {
-                Ok(add_weighted_transform_lists(from_list, from_list, count as f64, 0.0))
-            }
-            (&None, &Some(_)) => {
-                // If |self| is 'none' then we are calculating:
-                //
-                //    none * |count| + |other|
-                //    = none + |other|
-                //    = |other|
-                //
-                // Hence the result is just |other|.
-                Ok(other.clone())
-            }
-            _ => {
-                Ok(TransformList(None))
-            }
+        match procedure {
+            Procedure::Add => Err(()),
+            Procedure::Interpolate { progress } => {
+                Ok(TransformList(Some(vec![TransformOperation::InterpolateMatrix {
+                    from_list: this.into_owned(),
+                    to_list: other.into_owned(),
+                    progress: Percentage(progress as f32),
+                }])))
+            },
+            Procedure::Accumulate { count } => {
+                Ok(TransformList(Some(vec![TransformOperation::AccumulateMatrix {
+                    from_list: this.into_owned(),
+                    to_list: other.into_owned(),
+                    count: cmp::min(count, i32::max_value() as u64) as i32,
+                }])))
+            },
         }
     }
 }
 
-/// A helper function to retrieve the pixel length and percentage value.
-fn extract_pixel_calc_value(lop: &LengthOrPercentage) -> (f64, CSSFloat) {
-    match lop {
-        &LengthOrPercentage::Length(au) => (au.to_f64_px(), 0.),
-        &LengthOrPercentage::Percentage(percent) => (0., percent.0),
-        &LengthOrPercentage::Calc(calc) => (calc.length().to_f64_px(), calc.percentage())
-    }
-}
-
-/// Compute the squared distance of two transform lists.
 // This might not be the most useful definition of distance. It might be better, for example,
 // to trace the distance travelled by a point as its transform is interpolated between the two
 // lists. That, however, proves to be quite complicated so we take a simple approach for now.
 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1318591#c0.
-fn compute_transform_lists_squared_distance(from_list: &[TransformOperation],
-                                            to_list: &[TransformOperation])
-                                            -> Result<SquaredDistance, ()> {
-    let zero_distance = SquaredDistance::Value(0.);
-    let squared_distance = from_list.iter().zip(to_list.iter()).map(|(from, to)| {
-        match (from, to) {
-            (&TransformOperation::Matrix(from),
-             &TransformOperation::Matrix(to)) => {
-                from.compute_squared_distance(&to).unwrap_or(zero_distance)
-            }
-            (&TransformOperation::Skew(fx, fy),
-             &TransformOperation::Skew(tx, ty)) => {
-                fx.compute_squared_distance(&tx).unwrap_or(zero_distance) +
-                    fy.compute_squared_distance(&ty).unwrap_or(zero_distance)
-            }
-            (&TransformOperation::Translate(fx, fy, fz),
-             &TransformOperation::Translate(tx, ty, tz)) => {
+impl ComputeSquaredDistance for TransformOperation {
+    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
+        match (self, other) {
+            (
+                &TransformOperation::Matrix(ref this),
+                &TransformOperation::Matrix(ref other),
+            ) => {
+                this.compute_squared_distance(other)
+            },
+            (
+                &TransformOperation::Skew(ref fx, ref fy),
+                &TransformOperation::Skew(ref tx, ref ty),
+            ) => {
+                Ok(
+                    fx.compute_squared_distance(&tx)? +
+                    fy.compute_squared_distance(&ty)?,
+                )
+            },
+            (
+                &TransformOperation::Translate(ref fx, ref fy, ref fz),
+                &TransformOperation::Translate(ref tx, ref ty, ref tz),
+            ) => {
                 // We don't want to require doing layout in order to calculate the result, so
                 // drop the percentage part. However, dropping percentage makes us impossible to
                 // compute the distance for the percentage-percentage case, but Gecko uses the
                 // same formula, so it's fine for now.
                 // Note: We use pixel value to compute the distance for translate, so we have to
                 // convert Au into px.
-                let diff_x = fx.add_weighted(&tx, 1., -1.).unwrap_or(LengthOrPercentage::zero());
-                let diff_y = fy.add_weighted(&ty, 1., -1.).unwrap_or(LengthOrPercentage::zero());
-                let (diff_x_length, _) = extract_pixel_calc_value(&diff_x);
-                let (diff_y_length, _) = extract_pixel_calc_value(&diff_y);
-                SquaredDistance::Value(diff_x_length * diff_x_length) +
-                    SquaredDistance::Value(diff_y_length * diff_y_length) +
-                    fz.to_f64_px().compute_squared_distance(&tz.to_f64_px()).unwrap_or(zero_distance)
-            }
-            (&TransformOperation::Scale(fx, fy, fz),
-             &TransformOperation::Scale(tx, ty, tz)) => {
-                fx.compute_squared_distance(&tx).unwrap_or(zero_distance) +
-                    fy.compute_squared_distance(&ty).unwrap_or(zero_distance) +
-                    fz.compute_squared_distance(&tz).unwrap_or(zero_distance)
-            }
-            (&TransformOperation::Rotate(fx, fy, fz, fa),
-             &TransformOperation::Rotate(tx, ty, tz, ta)) => {
+                let extract_pixel_length = |lop: &LengthOrPercentage| {
+                    match *lop {
+                        LengthOrPercentage::Length(au) => au.to_f64_px(),
+                        LengthOrPercentage::Percentage(_) => 0.,
+                        LengthOrPercentage::Calc(calc) => calc.length().to_f64_px(),
+                    }
+                };
+
+                let fx = extract_pixel_length(&fx);
+                let fy = extract_pixel_length(&fy);
+                let tx = extract_pixel_length(&tx);
+                let ty = extract_pixel_length(&ty);
+
+                Ok(
+                    fx.compute_squared_distance(&tx)? +
+                    fy.compute_squared_distance(&ty)? +
+                    fz.to_f64_px().compute_squared_distance(&tz.to_f64_px())?,
+                )
+            },
+            (
+                &TransformOperation::Scale(ref fx, ref fy, ref fz),
+                &TransformOperation::Scale(ref tx, ref ty, ref tz),
+            ) => {
+                Ok(
+                    fx.compute_squared_distance(&tx)? +
+                    fy.compute_squared_distance(&ty)? +
+                    fz.compute_squared_distance(&tz)?,
+                )
+            },
+            (
+                &TransformOperation::Rotate(fx, fy, fz, fa),
+                &TransformOperation::Rotate(tx, ty, tz, ta),
+            ) => {
                 let (fx, fy, fz, angle1) = get_normalized_vector