merge mozilla-central to mozilla-inbound. r=merge a=merge
authorSebastian Hengst <archaeopteryx@coole-files.de>
Thu, 10 Aug 2017 17:41:11 +0200
changeset 373974 17a9aa5732e58048b1044b3cc12873859d4df3ce
parent 373973 93151fa47fcf9a2cc9b12a7d3ba5659e2bb89df4 (current diff)
parent 373802 5322c03f4c8587fe526172d3f87160031faa6d75 (diff)
child 373975 5057eb5a60e7f2473af9ddbfafd81b6556d367f6
push id32311
push userkwierso@gmail.com
push dateFri, 11 Aug 2017 01:14:57 +0000
treeherdermozilla-central@253a8560dc34 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone57.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-central to mozilla-inbound. r=merge a=merge
browser/base/content/browser.js
browser/base/content/browser.xul
browser/base/content/tabbrowser.xml
browser/components/nsBrowserGlue.js
browser/components/sessionstore/SessionStore.jsm
browser/installer/package-manifest.in
browser/themes/shared/browser.inc
devtools/server/actors/utils/webconsole-listeners.js
devtools/server/actors/utils/webconsole-utils.js
devtools/server/actors/utils/webconsole-worker-listeners.js
dom/ipc/ContentChild.cpp
dom/media/MediaDecoder.cpp
dom/media/MediaDecoder.h
dom/media/MediaDecoderStateMachine.cpp
dom/media/webaudio/AudioContext.cpp
dom/media/webaudio/AudioContext.h
editor/libeditor/EditorBase.cpp
gfx/layers/wr/WebRenderLayerManager.cpp
js/src/moz.build
layout/base/ServoRestyleManager.cpp
layout/painting/nsDisplayList.cpp
layout/style/test/stylo-failures.md
modules/libpref/init/all.js
third_party/rust/heapsize-0.3.8/.cargo-checksum.json
third_party/rust/heapsize-0.3.8/.cargo-ok
third_party/rust/heapsize-0.3.8/.travis.yml
third_party/rust/heapsize-0.3.8/Cargo.toml
third_party/rust/heapsize-0.3.8/README.md
third_party/rust/heapsize-0.3.8/appveyor.yml
third_party/rust/heapsize-0.3.8/build.rs
third_party/rust/heapsize-0.3.8/src/lib.rs
third_party/rust/heapsize-0.3.8/tests/tests.rs
toolkit/themes/osx/global/icons/chevron-inverted.png
toolkit/themes/osx/global/icons/chevron-inverted@2x.png
toolkit/themes/osx/global/icons/chevron.png
toolkit/themes/osx/global/icons/chevron@2x.png
toolkit/themes/windows/global/toolbar/chevron-inverted.png
toolkit/themes/windows/global/toolbar/chevron.gif
widget/windows/WinUtils.cpp
--- a/.cron.yml
+++ b/.cron.yml
@@ -33,21 +33,31 @@ jobs:
           type: decision-task
           treeherder-symbol: Nd-OSX
           target-tasks-method: nightly_macosx
       run-on-projects:
           - mozilla-central
           - date
       when: [] # never (hook only)
 
-    - name: nightly-desktop-win
+    - name: nightly-desktop-win32
       job:
           type: decision-task
-          treeherder-symbol: Nd-Win
-          target-tasks-method: nightly_win
+          treeherder-symbol: Nd-win32
+          target-tasks-method: nightly_win32
+      run-on-projects:
+          - mozilla-central
+          - date
+      when: [] # never (hook only)
+
+    - name: nightly-desktop-win64
+      job:
+          type: decision-task
+          treeherder-symbol: Nd-win64
+          target-tasks-method: nightly_win64
       run-on-projects:
           - mozilla-central
           - date
       when: [] # never (hook only)
 
     - name: nightly-android
       job:
           type: decision-task
--- a/accessible/interfaces/nsIAccessibilityService.idl
+++ b/accessible/interfaces/nsIAccessibilityService.idl
@@ -93,16 +93,8 @@ interface nsIAccessibilityService : nsIS
    */
   void setLogging(in ACString aModules);
 
   /**
    * Return true if the given module is logged.
    */
   boolean isLogged(in AString aModule);
 };
-
-/**
- * @deprecated, use nsIAccessibilityService instead.
- */
-[scriptable, builtinclass, uuid(d85e0cbe-47ce-490c-8488-f821dd2be0c2)]
-interface nsIAccessibleRetrieval : nsIAccessibilityService
-{
-};
--- a/accessible/xpcom/xpcAccessibilityService.cpp
+++ b/accessible/xpcom/xpcAccessibilityService.cpp
@@ -86,18 +86,17 @@ xpcAccessibilityService::Release(void)
         nsITimer::TYPE_ONE_SHOT,
         "xpcAccessibilityService::Release");
     }
   }
 
   return count;
 }
 
-NS_IMPL_QUERY_INTERFACE(xpcAccessibilityService, nsIAccessibilityService,
-                                                 nsIAccessibleRetrieval)
+NS_IMPL_QUERY_INTERFACE(xpcAccessibilityService, nsIAccessibilityService)
 
 NS_IMETHODIMP
 xpcAccessibilityService::GetApplicationAccessible(nsIAccessible** aAccessibleApplication)
 {
   NS_ENSURE_ARG_POINTER(aAccessibleApplication);
 
   NS_IF_ADDREF(*aAccessibleApplication = XPCApplicationAcc());
   return NS_OK;
--- a/accessible/xpcom/xpcAccessibilityService.h
+++ b/accessible/xpcom/xpcAccessibilityService.h
@@ -2,23 +2,22 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_a11y_xpcAccessibilityService_h_
 #define mozilla_a11y_xpcAccessibilityService_h_
 
 #include "nsIAccessibilityService.h"
 
-class xpcAccessibilityService : public nsIAccessibleRetrieval
+class xpcAccessibilityService : public nsIAccessibilityService
 {
 
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIACCESSIBILITYSERVICE
-  NS_DECL_NSIACCESSIBLERETRIEVAL
 
   /**
    * Return true if xpc accessibility service is in use.
    */
   static bool IsInUse() {
     // When ref count goes down to 1 (held internally as a static reference),
     // it means that there are no more external references and thus it is not in
     // use.
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1091,17 +1091,21 @@ pref("security.sandbox.content.level", 3
 // to whitelist more system calls.
 //
 // So the purpose of this setting is to allow nightly users to disable the
 // sandbox while we fix their problems. This way, they won't have to wait for
 // another nightly release which disables seccomp-bpf again.
 //
 // This setting may not be required anymore once we decide to permanently
 // enable the content sandbox.
+#ifdef NIGHTLY_BUILD
 pref("security.sandbox.content.level", 3);
+#else
+pref("security.sandbox.content.level", 2);
+#endif
 pref("security.sandbox.content.write_path_whitelist", "");
 pref("security.sandbox.content.read_path_whitelist", "");
 pref("security.sandbox.content.syscall_whitelist", "");
 #endif
 
 #if defined(XP_MACOSX) || defined(XP_WIN)
 #if defined(MOZ_SANDBOX) && defined(MOZ_CONTENT_SANDBOX)
 // ID (a UUID when set by gecko) that is used to form the name of a
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -17,64 +17,63 @@ Cu.import("resource://gre/modules/Notifi
 
 XPCOMUtils.defineLazyGetter(this, "extensionNameFromURI", () => {
   return Cu.import("resource://gre/modules/ExtensionParent.jsm", {}).extensionNameFromURI;
 });
 
 // lazy module getters
 
 XPCOMUtils.defineLazyModuleGetters(this, {
-  AboutHome: "resource:///modules/AboutHome.jsm",
-  BrowserUITelemetry: "resource:///modules/BrowserUITelemetry.jsm",
-  BrowserUsageTelemetry: "resource:///modules/BrowserUsageTelemetry.jsm",
-  BrowserUtils: "resource://gre/modules/BrowserUtils.jsm",
-  CastingApps: "resource:///modules/CastingApps.jsm",
-  CharsetMenu: "resource://gre/modules/CharsetMenu.jsm",
-  Color: "resource://gre/modules/Color.jsm",
-  ContentSearch: "resource:///modules/ContentSearch.jsm",
-  ContextualIdentityService: "resource://gre/modules/ContextualIdentityService.jsm",
-  CustomizableUI: "resource:///modules/CustomizableUI.jsm",
-  Deprecated: "resource://gre/modules/Deprecated.jsm",
-  DownloadsCommon: "resource:///modules/DownloadsCommon.jsm",
-  E10SUtils: "resource:///modules/E10SUtils.jsm",
-  ExtensionsUI: "resource:///modules/ExtensionsUI.jsm",
-  FormValidationHandler: "resource:///modules/FormValidationHandler.jsm",
-  GMPInstallManager: "resource://gre/modules/GMPInstallManager.jsm",
-  LightweightThemeManager: "resource://gre/modules/LightweightThemeManager.jsm",
-  Log: "resource://gre/modules/Log.jsm",
-  LoginManagerParent: "resource://gre/modules/LoginManagerParent.jsm",
-  NewTabUtils: "resource://gre/modules/NewTabUtils.jsm",
-  PageActions: "resource:///modules/PageActions.jsm",
-  PageThumbs: "resource://gre/modules/PageThumbs.jsm",
-  PluralForm: "resource://gre/modules/PluralForm.jsm",
-  PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
-  ProcessHangMonitor: "resource:///modules/ProcessHangMonitor.jsm",
-  PromiseUtils: "resource://gre/modules/PromiseUtils.jsm",
-  ReaderMode: "resource://gre/modules/ReaderMode.jsm",
-  ReaderParent: "resource:///modules/ReaderParent.jsm",
-  RecentWindow: "resource:///modules/RecentWindow.jsm",
-  SafeBrowsing: "resource://gre/modules/SafeBrowsing.jsm",
-  SessionStore: "resource:///modules/sessionstore/SessionStore.jsm",
-  ShortcutUtils: "resource://gre/modules/ShortcutUtils.jsm",
-  SimpleServiceDiscovery: "resource://gre/modules/SimpleServiceDiscovery.jsm",
-  SitePermissions: "resource:///modules/SitePermissions.jsm",
-  Social: "resource:///modules/Social.jsm",
-  TabCrashHandler: "resource:///modules/ContentCrashHandlers.jsm",
-  TelemetryStopwatch: "resource://gre/modules/TelemetryStopwatch.jsm",
-  Translation: "resource:///modules/translation/Translation.jsm",
-  UITour: "resource:///modules/UITour.jsm",
-  UpdateUtils: "resource://gre/modules/UpdateUtils.jsm",
-  Utils: "resource://gre/modules/sessionstore/Utils.jsm",
-  Weave: "resource://services-sync/main.js",
-  WebNavigationFrames: "resource://gre/modules/WebNavigationFrames.jsm",
-  fxAccounts: "resource://gre/modules/FxAccounts.jsm",
-  gDevTools: "resource://devtools/client/framework/gDevTools.jsm",
-  gDevToolsBrowser: "resource://devtools/client/framework/gDevTools.jsm",
-  webrtcUI: "resource:///modules/webrtcUI.jsm",
-  ZoomUI: "resource:///modules/ZoomUI.jsm",
+  ["AboutHome", "resource:///modules/AboutHome.jsm"],
+  ["BrowserUITelemetry", "resource:///modules/BrowserUITelemetry.jsm"],
+  ["BrowserUsageTelemetry", "resource:///modules/BrowserUsageTelemetry.jsm"],
+  ["BrowserUtils", "resource://gre/modules/BrowserUtils.jsm"],
+  ["CastingApps", "resource:///modules/CastingApps.jsm"],
+  ["CharsetMenu", "resource://gre/modules/CharsetMenu.jsm"],
+  ["Color", "resource://gre/modules/Color.jsm"],
+  ["ContentSearch", "resource:///modules/ContentSearch.jsm"],
+  ["ContextualIdentityService", "resource://gre/modules/ContextualIdentityService.jsm"],
+  ["CustomizableUI", "resource:///modules/CustomizableUI.jsm"],
+  ["Deprecated", "resource://gre/modules/Deprecated.jsm"],
+  ["DownloadsCommon", "resource:///modules/DownloadsCommon.jsm"],
+  ["E10SUtils", "resource:///modules/E10SUtils.jsm"],
+  ["ExtensionsUI", "resource:///modules/ExtensionsUI.jsm"],
+  ["FormValidationHandler", "resource:///modules/FormValidationHandler.jsm"],
+  ["LightweightThemeManager", "resource://gre/modules/LightweightThemeManager.jsm"],
+  ["Log", "resource://gre/modules/Log.jsm"],
+  ["LoginManagerParent", "resource://gre/modules/LoginManagerParent.jsm"],
+  ["NewTabUtils", "resource://gre/modules/NewTabUtils.jsm"],
+  ["PageActions", "resource:///modules/PageActions.jsm"],
+  ["PageThumbs", "resource://gre/modules/PageThumbs.jsm"],
+  ["PluralForm", "resource://gre/modules/PluralForm.jsm"],
+  ["PrivateBrowsingUtils", "resource://gre/modules/PrivateBrowsingUtils.jsm"],
+  ["ProcessHangMonitor", "resource:///modules/ProcessHangMonitor.jsm"],
+  ["PromiseUtils", "resource://gre/modules/PromiseUtils.jsm"],
+  ["ReaderMode", "resource://gre/modules/ReaderMode.jsm"],
+  ["ReaderParent", "resource:///modules/ReaderParent.jsm"],
+  ["RecentWindow", "resource:///modules/RecentWindow.jsm"],
+  ["SafeBrowsing", "resource://gre/modules/SafeBrowsing.jsm"],
+  ["SessionStore", "resource:///modules/sessionstore/SessionStore.jsm"],
+  ["ShortcutUtils", "resource://gre/modules/ShortcutUtils.jsm"],
+  ["SimpleServiceDiscovery", "resource://gre/modules/SimpleServiceDiscovery.jsm"],
+  ["SitePermissions", "resource:///modules/SitePermissions.jsm"],
+  ["Social", "resource:///modules/Social.jsm"],
+  ["TabCrashHandler", "resource:///modules/ContentCrashHandlers.jsm"],
+  ["TelemetryStopwatch", "resource://gre/modules/TelemetryStopwatch.jsm"],
+  ["Translation", "resource:///modules/translation/Translation.jsm"],
+  ["UITour", "resource:///modules/UITour.jsm"],
+  ["UpdateUtils", "resource://gre/modules/UpdateUtils.jsm"],
+  ["Utils", "resource://gre/modules/sessionstore/Utils.jsm"],
+  ["Weave", "resource://services-sync/main.js"],
+  ["WebNavigationFrames", "resource://gre/modules/WebNavigationFrames.jsm"],
+  ["fxAccounts", "resource://gre/modules/FxAccounts.jsm"],
+  ["gDevTools", "resource://devtools/client/framework/gDevTools.jsm"],
+  ["gDevToolsBrowser", "resource://devtools/client/framework/gDevTools.jsm"],
+  ["webrtcUI", "resource:///modules/webrtcUI.jsm"],
+  ["ZoomUI", "resource:///modules/ZoomUI.jsm"],
 });
 
 if (AppConstants.MOZ_CRASHREPORTER) {
   XPCOMUtils.defineLazyModuleGetter(this, "PluginCrashReporter",
     "resource:///modules/ContentCrashHandlers.jsm");
 }
 
 XPCOMUtils.defineLazyScriptGetter(this, "PrintUtils",
@@ -1604,44 +1603,17 @@ var gBrowserInit = {
     Cu.import("resource:///modules/UpdateTopLevelContentWindowIDHelper.jsm", {})
       .trackBrowserWindow(window);
 
     PlacesToolbarHelper.init();
 
     ctrlTab.readPref();
     gPrefService.addObserver(ctrlTab.prefName, ctrlTab);
 
-    // Initialize the download manager some time after the app starts so that
-    // auto-resume downloads begin (such as after crashing or quitting with
-    // active downloads) and speeds up the first-load of the download manager UI.
-    // If the user manually opens the download manager before the timeout, the
-    // downloads will start right away, and initializing again won't hurt.
-    setTimeout(function() {
-      try {
-        DownloadsCommon.initializeAllDataLinks();
-        Cu.import("resource:///modules/DownloadsTaskbar.jsm", {})
-          .DownloadsTaskbar.registerIndicator(window);
-      } catch (ex) {
-        Cu.reportError(ex);
-      }
-    }, 10000);
-
-    // Load the Login Manager data from disk off the main thread, some time
-    // after startup.  If the data is required before the timeout, for example
-    // because a restored page contains a password field, it will be loaded on
-    // the main thread, and this initialization request will be ignored.
-    setTimeout(function() {
-      try {
-        Services.logins;
-      } catch (ex) {
-        Cu.reportError(ex);
-      }
-    }, 3000);
-
-    // The object handling the downloads indicator is also initialized here in the
+    // The object handling the downloads indicator is initialized here in the
     // delayed startup function, but the actual indicator element is not loaded
     // unless there are downloads to be displayed.
     DownloadsButton.initializeIndicator();
 
     if (AppConstants.platform != "macosx") {
       updateEditUIVisibility();
       let placesContext = document.getElementById("placesContext");
       placesContext.addEventListener("popupshowing", updateEditUIVisibility);
@@ -1655,37 +1627,22 @@ var gBrowserInit = {
 
     FullScreen.init();
     PointerLock.init();
 
     if (AppConstants.isPlatformAndVersionAtLeast("win", "10")) {
       MenuTouchModeObserver.init();
     }
 
-    // initialize the sync UI
-    requestIdleCallback(() => {
-      gSync.init();
-    }, {timeout: 1000 * 5});
-
     if (AppConstants.MOZ_DATA_REPORTING)
       gDataNotificationInfoBar.init();
 
     if (!AppConstants.MOZILLA_OFFICIAL)
       DevelopmentHelpers.init();
 
-    requestIdleCallback(() => {
-      // setup simple gestures support
-      gGestureSupport.init(true);
-
-      // setup history swipe animation
-      gHistorySwipeAnimation.init();
-    });
-
-    requestIdleCallback(() => { gBrowserThumbnails.init(); });
-
     gExtensionsNotifications.init();
 
     let wasMinimized = window.windowState == window.STATE_MINIMIZED;
     window.addEventListener("sizemodechange", () => {
       let isMinimized = window.windowState == window.STATE_MINIMIZED;
       if (wasMinimized != isMinimized) {
         wasMinimized = isMinimized;
         UpdatePopupNotificationsVisibility();
@@ -1693,78 +1650,112 @@ var gBrowserInit = {
     });
 
     window.addEventListener("mousemove", MousePosTracker);
     window.addEventListener("dragover", MousePosTracker);
 
     gNavToolbox.addEventListener("customizationstarting", CustomizationHandler);
     gNavToolbox.addEventListener("customizationending", CustomizationHandler);
 
-    // End startup crash tracking after a delay to catch crashes while restoring
-    // tabs and to postpone saving the pref to disk.
-    try {
-      const startupCrashEndDelay = 30 * 1000;
-      setTimeout(Services.startup.trackStartupCrashEnd, startupCrashEndDelay);
-    } catch (ex) {
-      Cu.reportError("Could not end startup crash tracking: " + ex);
-    }
-
-    // Delay this a minute into the idle time because there's no rush.
-    requestIdleCallback(() => {
-      this.gmpInstallManager = new GMPInstallManager();
-      // We don't really care about the results, if someone is interested they
-      // can check the log.
-      this.gmpInstallManager.simpleCheckAndInstall().catch(() => {});
-    }, {timeout: 1000 * 60});
-
     SessionStore.promiseInitialized.then(() => {
       // Bail out if the window has been closed in the meantime.
       if (window.closed) {
         return;
       }
 
       // Enable the Restore Last Session command if needed
       RestoreLastSessionObserver.init();
 
       SidebarUI.startDelayedLoad();
       SocialUI.init();
 
-      // Telemetry for master-password - we do this after 5 seconds as it
-      // can cause IO if NSS/PSM has not already initialized.
-      setTimeout(() => {
-        if (window.closed) {
-          return;
-        }
-        let tokenDB = Cc["@mozilla.org/security/pk11tokendb;1"]
-                        .getService(Ci.nsIPK11TokenDB);
-        let token = tokenDB.getInternalKeyToken();
-        let mpEnabled = token.hasPassword;
-        if (mpEnabled) {
-          Services.telemetry.getHistogramById("MASTER_PASSWORD_ENABLED").add(mpEnabled);
-        }
-      }, 5000);
-
       PanicButtonNotifier.init();
     });
 
     gBrowser.tabContainer.addEventListener("TabSelect", function() {
       for (let panel of document.querySelectorAll("panel[tabspecific='true']")) {
         if (panel.state == "open") {
           panel.hidePopup();
         }
       }
     });
 
     this.delayedStartupFinished = true;
 
     _resolveDelayedStartup();
+
+    SessionStore.promiseAllWindowsRestored.then(() => {
+      this._schedulePerWindowIdleTasks();
+    });
+
     Services.obs.notifyObservers(window, "browser-delayed-startup-finished");
     TelemetryTimestamps.add("delayedStartupFinished");
   },
 
+  /**
+   * Use this function as an entry point to schedule tasks that
+   * need to run once per window after startup, and can be scheduled
+   * by using an idle callback.
+   *
+   * The functions scheduled here will fire from idle callbacks
+   * once every window has finished being restored by session
+   * restore, and after the equivalent only-once tasks
+   * have run (from _scheduleStartupIdleTasks in nsBrowserGlue.js).
+   */
+  _schedulePerWindowIdleTasks() {
+    // Bail out if the window has been closed in the meantime.
+    if (window.closed) {
+      return;
+    }
+
+    function scheduleIdleTask(func, options) {
+      requestIdleCallback(function idleTaskRunner() {
+        if (!window.closed) {
+          func();
+        }
+      }, options);
+    }
+
+    scheduleIdleTask(() => {
+      // Initialize the Sync UI
+      gSync.init();
+    });
+
+    scheduleIdleTask(() => {
+      CombinedStopReload.startAnimationPrefMonitoring();
+    });
+
+    scheduleIdleTask(() => {
+      // setup simple gestures support
+      gGestureSupport.init(true);
+
+      // setup history swipe animation
+      gHistorySwipeAnimation.init();
+    });
+
+    scheduleIdleTask(() => {
+      gBrowserThumbnails.init();
+    });
+
+    scheduleIdleTask(() => {
+      // Initialize the download manager some time after the app starts so that
+      // auto-resume downloads begin (such as after crashing or quitting with
+      // active downloads) and speeds up the first-load of the download manager UI.
+      // If the user manually opens the download manager before the timeout, the
+      // downloads will start right away, and initializing again won't hurt.
+      try {
+        DownloadsCommon.initializeAllDataLinks();
+        Cu.import("resource:///modules/DownloadsTaskbar.jsm", {})
+          .DownloadsTaskbar.registerIndicator(window);
+      } catch (ex) {
+        Cu.reportError(ex);
+      }
+    }, {timeout: 10000});
+  },
+
   // Returns the URI(s) to load at startup.
   _getUriToLoad() {
     // window.arguments[0]: URI to load (string), or an nsIArray of
     //                      nsISupportsStrings to load, or a xul:tab of
     //                      a tabbrowser, which will be replaced by this
     //                      window (for this case, all other arguments are
     //                      ignored).
     if (!window.arguments || !window.arguments[0])
@@ -1870,20 +1861,16 @@ var gBrowserInit = {
       window.messageManager.removeMessageListener("Browser:LoadURI", RedirectLoad);
 
       try {
         gPrefService.removeObserver(gHomeButton.prefDomain, gHomeButton);
       } catch (ex) {
         Cu.reportError(ex);
       }
 
-      if (this.gmpInstallManager) {
-        this.gmpInstallManager.uninit();
-      }
-
       if (AppConstants.isPlatformAndVersionAtLeast("win", "10")) {
         MenuTouchModeObserver.uninit();
       }
       BrowserOffline.uninit();
       IndexedDBPromptHelper.uninit();
       PanelUI.uninit();
       AutoShowBookmarksToolbar.uninit();
     }
@@ -4961,24 +4948,16 @@ var CombinedStopReload = {
     stop.addEventListener("click", this);
     this.reload = reload;
     this.stop = stop;
     this.stopReloadContainer = this.reload.parentNode;
     this.timeWhenSwitchedToStop = 0;
 
     // Disable animations until the browser has fully loaded.
     this.animate = false;
-    let startupInfo = Cc["@mozilla.org/toolkit/app-startup;1"]
-                        .getService(Ci.nsIAppStartup)
-                        .getStartupInfo();
-    if (startupInfo.sessionRestored) {
-      this.startAnimationPrefMonitoring();
-    } else {
-      Services.obs.addObserver(this, "sessionstore-windows-restored");
-    }
   },
 
   uninit() {
     if (!this._initialized)
       return;
 
     Services.prefs.removeObserver("toolkit.cosmeticAnimations.enabled", this);
     this._cancelTransition();
@@ -5005,34 +4984,29 @@ var CombinedStopReload = {
              event.animationName == "stop-to-reload-rtl")) {
           this.stopReloadContainer.removeAttribute("animate");
         }
       }
     }
   },
 
   observe(subject, topic, data) {
-    if (topic == "sessionstore-windows-restored") {
-      Services.obs.removeObserver(this, "sessionstore-windows-restored");
-      this.startAnimationPrefMonitoring();
-    } else if (topic == "nsPref:changed") {
+    if (topic == "nsPref:changed") {
       this.animate = Services.prefs.getBoolPref("toolkit.cosmeticAnimations.enabled");
     }
   },
 
   startAnimationPrefMonitoring() {
-    requestIdleCallback(() => {
-      // CombinedStopReload may have been uninitialized before the idleCallback is executed.
-      if (!this._initialized)
-        return;
-      this.animate = Services.prefs.getBoolPref("toolkit.cosmeticAnimations.enabled") &&
-                     Services.prefs.getBoolPref("browser.stopReloadAnimation.enabled");
-      Services.prefs.addObserver("toolkit.cosmeticAnimations.enabled", this);
-      this.stopReloadContainer.addEventListener("animationend", this);
-    });
+    // CombinedStopReload may have been uninitialized before the idleCallback is executed.
+    if (!this._initialized)
+      return;
+    this.animate = Services.prefs.getBoolPref("toolkit.cosmeticAnimations.enabled") &&
+                   Services.prefs.getBoolPref("browser.stopReloadAnimation.enabled");
+    Services.prefs.addObserver("toolkit.cosmeticAnimations.enabled", this);
+    this.stopReloadContainer.addEventListener("animationend", this);
   },
 
   onTabSwitch() {
     // Reset the time in the event of a tabswitch since the stored time
     // would have been associated with the previous tab.
     this.timeWhenSwitchedToStop = 0;
   },
 
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -1110,17 +1110,17 @@
                      mousethrough="always"
                      collapsed="true"/>
             </hbox>
             <scrollbox orient="horizontal"
                        id="PlacesToolbarItems"
                        flex="1"/>
             <toolbarbutton type="menu"
                            id="PlacesChevron"
-                           class="chevron"
+                           class="toolbarbutton-1"
                            mousethrough="never"
                            collapsed="true"
                            tooltiptext="&bookmarksToolbarChevron.tooltip;"
                            onpopupshowing="document.getElementById('PlacesToolbar')
                                                    ._placesView._onChevronPopupShowing(event);">
               <menupopup id="PlacesChevronPopup"
                          placespopup="true"
                          tooltip="bhTooltip" popupsinherittooltip="true"
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -5943,16 +5943,18 @@
           tab.label = this.tabbrowser.mStringBundle.getString("tabs.emptyTabTitle");
           tab.setAttribute("onerror", "this.removeAttribute('image');");
 
           window.addEventListener("resize", this);
           window.addEventListener("load", this);
 
           Services.prefs.addObserver("privacy.userContext", this);
           this.observe(null, "nsPref:changed", "privacy.userContext.enabled");
+
+          this._setPositionalAttributes();
         ]]>
       </constructor>
 
       <destructor>
         <![CDATA[
           Services.prefs.removeObserver("privacy.userContext", this);
         ]]>
       </destructor>
--- a/browser/base/content/test/performance/browser_startup_images.js
+++ b/browser/base/content/test/performance/browser_startup_images.js
@@ -82,24 +82,18 @@ const whitelist = [
   },
   {
     file: "resource://gre-resources/broken-image.png",
     platforms: ["win", "macosx"],
     intermittentNotLoaded: ["win", "macosx"],
   },
 
   {
-    file: "chrome://global/skin/icons/chevron.png",
-    hidpi: "chrome://global/skin/icons/chevron@2x.png",
-    platforms: ["macosx"],
-  },
-
-  {
-    file: "chrome://global/skin/toolbar/chevron.gif",
-    platforms: ["win", "linux"],
+    file: "chrome://browser/skin/chevron.svg",
+    platforms: ["win", "linux", "macosx"],
   },
 
   {
     file: "chrome://global/skin/icons/resizer.png",
     platforms: ["win"],
   },
 ];
 
--- a/browser/base/content/test/performance/browser_tabstrip_overflow_underflow_reflows.js
+++ b/browser/base/content/test/performance/browser_tabstrip_overflow_underflow_reflows.js
@@ -10,27 +10,16 @@
  * for tips on how to do that.
  */
 const EXPECTED_OVERFLOW_REFLOWS = [
   {
     stack: [
       "select@chrome://global/content/bindings/textbox.xml",
       "focusAndSelectUrlBar@chrome://browser/content/browser.js",
       "_adjustFocusAfterTabSwitch@chrome://browser/content/tabbrowser.xml",
-      "updateDisplay/<@chrome://browser/content/tabbrowser.xml",
-      "set_selectedIndex@chrome://browser/content/tabbrowser.xml",
-      "set_selectedPanel@chrome://global/content/bindings/tabbox.xml",
-      "set_selectedIndex@chrome://global/content/bindings/tabbox.xml",
-      "set_selectedItem@chrome://global/content/bindings/tabbox.xml",
-      "set_selectedTab@chrome://global/content/bindings/tabbox.xml",
-      "set_selectedTab@chrome://browser/content/tabbrowser.xml",
-      "loadOneTab@chrome://browser/content/tabbrowser.xml",
-      "openLinkIn@chrome://browser/content/utilityOverlay.js",
-      "openUILinkIn@chrome://browser/content/utilityOverlay.js",
-      "BrowserOpenTab@chrome://browser/content/browser.js",
     ]
   },
 ];
 
 const EXPECTED_UNDERFLOW_REFLOWS = [
   /**
    * Nothing here! Please don't add anything new!
    */
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -207,18 +207,20 @@ const listeners = {
 // Seconds of idle before trying to create a bookmarks backup.
 const BOOKMARKS_BACKUP_IDLE_TIME_SEC = 8 * 60;
 // Minimum interval between backups.  We try to not create more than one backup
 // per interval.
 const BOOKMARKS_BACKUP_MIN_INTERVAL_DAYS = 1;
 // Maximum interval between backups.  If the last backup is older than these
 // days we will try to create a new one more aggressively.
 const BOOKMARKS_BACKUP_MAX_INTERVAL_DAYS = 3;
-// Seconds of idle time before reporting media telemetry.
-const MEDIA_TELEMETRY_IDLE_TIME_SEC = 20;
+// Seconds of idle time before the late idle tasks will be scheduled.
+const LATE_TASKS_IDLE_TIME_SEC = 20;
+// Time after we stop tracking startup crashes.
+const STARTUP_CRASHES_END_DELAY_MS = 30 * 1000;
 
 // Factory object
 const BrowserGlueServiceFactory = {
   _instance: null,
   createInstance: function BGSF_createInstance(outer, iid) {
     if (outer != null)
       throw Components.results.NS_ERROR_NO_AGGREGATION;
     return this._instance == null ?
@@ -549,19 +551,23 @@ BrowserGlue.prototype = {
     os.removeObserver(this, "fxaccounts:verify_login");
     os.removeObserver(this, "fxaccounts:device_disconnected");
     os.removeObserver(this, "weave:engine:clients:display-uris");
     os.removeObserver(this, "session-save");
     if (this._bookmarksBackupIdleTime) {
       this._idleService.removeIdleObserver(this, this._bookmarksBackupIdleTime);
       delete this._bookmarksBackupIdleTime;
     }
-    if (this._mediaTelemetryIdleObserver) {
-      this._idleService.removeIdleObserver(this._mediaTelemetryIdleObserver, MEDIA_TELEMETRY_IDLE_TIME_SEC);
-      delete this._mediaTelemetryIdleObserver;
+    if (this._lateTasksIdleObserver) {
+      this._idleService.removeIdleObserver(this._lateTasksIdleObserver, LATE_TASKS_IDLE_TIME_SEC);
+      delete this._lateTasksIdleObserver;
+    }
+    if (this._gmpInstallManager) {
+      this._gmpInstallManager.uninit();
+      delete this._gmpInstallManager;
     }
     try {
       os.removeObserver(this, "places-init-complete");
     } catch (ex) { /* Could have been removed already */ }
     os.removeObserver(this, "handle-xul-text-link");
     os.removeObserver(this, "profile-before-change");
     os.removeObserver(this, "keyword-search");
     os.removeObserver(this, "browser-search-engine-modified");
@@ -863,27 +869,16 @@ BrowserGlue.prototype = {
     // handler and init message manager child shim for privileged api access.
     // With older versions of the extension installed, this load will fail
     // passively.
     Services.ppmm.loadProcessScript("resource://pdf.js/pdfjschildbootstrap.js", true);
     if (PdfJs.enabled) {
       Services.ppmm.loadProcessScript("resource://pdf.js/pdfjschildbootstrap-enabled.js", true);
     }
 
-    if (AppConstants.platform == "win") {
-      // For Windows 7, initialize the jump list module.
-      const WINTASKBAR_CONTRACTID = "@mozilla.org/windows-taskbar;1";
-      if (WINTASKBAR_CONTRACTID in Cc &&
-          Cc[WINTASKBAR_CONTRACTID].getService(Ci.nsIWinTaskbar).available) {
-        let temp = {};
-        Cu.import("resource:///modules/WindowsJumpLists.jsm", temp);
-        temp.WinTaskbarJumpList.startup();
-      }
-    }
-
     TabCrashHandler.init();
     if (AppConstants.MOZ_CRASHREPORTER) {
       PluginCrashReporter.init();
     }
 
     ProcessHangMonitor.init();
 
     // A channel for "remote troubleshooting" code...
@@ -946,37 +941,22 @@ BrowserGlue.prototype = {
     DirectoryLinksProvider.init();
     NewTabUtils.init();
     NewTabUtils.links.addProvider(DirectoryLinksProvider);
 
     PageActions.init();
 
     this._firstWindowTelemetry(aWindow);
     this._firstWindowLoaded();
-
-    this._mediaTelemetryIdleObserver = {
-      browserGlue: this,
-      observe(aSubject, aTopic, aData) {
-        if (aTopic != "idle") {
-          return;
-        }
-        this.browserGlue._sendMediaTelemetry();
-      }
-    };
-    this._idleService.addIdleObserver(this._mediaTelemetryIdleObserver,
-                                      MEDIA_TELEMETRY_IDLE_TIME_SEC);
   },
 
   _sendMediaTelemetry() {
     let win = RecentWindow.getMostRecentBrowserWindow();
     let v = win.document.createElementNS("http://www.w3.org/1999/xhtml", "video");
     v.reportCanPlayTelemetry();
-    this._idleService.removeIdleObserver(this._mediaTelemetryIdleObserver,
-                                         MEDIA_TELEMETRY_IDLE_TIME_SEC);
-    delete this._mediaTelemetryIdleObserver;
   },
 
   /**
    * Application shutdown handler.
    */
   _onQuitApplicationGranted() {
     // This pref must be set here because SessionStore will use its value
     // on quit-application.
@@ -1033,20 +1013,16 @@ BrowserGlue.prototype = {
     SimpleServiceDiscovery.search(120 * 1000);
   },
 
   // All initial windows have opened.
   _onWindowsRestored: function BG__onWindowsRestored() {
     BrowserUsageTelemetry.init();
     BrowserUITelemetry.init();
 
-    if (AppConstants.MOZ_DEV_EDITION) {
-      this._createExtraDefaultProfile();
-    }
-
     this._initServiceDiscovery();
 
     // Show update notification, if needed.
     if (Services.prefs.prefHasUserValue("app.update.postupdate"))
       this._showUpdateNotification();
 
     ExtensionsUI.init();
 
@@ -1071,109 +1047,153 @@ BrowserGlue.prototype = {
         }
       });
     }
 
     if (AddonManager.nonMpcDisabled) {
       this._notifyDisabledNonMpc();
     }
 
-    // Perform default browser checking.
-    if (ShellService) {
-      let shouldCheck = AppConstants.DEBUG ? false :
-                                             ShellService.shouldCheckDefaultBrowser;
-
-      const skipDefaultBrowserCheck =
-        Services.prefs.getBoolPref("browser.shell.skipDefaultBrowserCheckOnFirstRun") &&
-        !Services.prefs.getBoolPref("browser.shell.didSkipDefaultBrowserCheckOnFirstRun");
-
-      const usePromptLimit = !AppConstants.RELEASE_OR_BETA;
-      let promptCount =
-        usePromptLimit ? Services.prefs.getIntPref("browser.shell.defaultBrowserCheckCount") : 0;
-
-      let willRecoverSession = false;
-      try {
-        let ss = Cc["@mozilla.org/browser/sessionstartup;1"].
-                 getService(Ci.nsISessionStartup);
-        willRecoverSession =
-          (ss.sessionType == Ci.nsISessionStartup.RECOVER_SESSION);
-      } catch (ex) { /* never mind; suppose SessionStore is broken */ }
-
-      // startup check, check all assoc
-      let isDefault = false;
-      let isDefaultError = false;
-      try {
-        isDefault = ShellService.isDefaultBrowser(true, false);
-      } catch (ex) {
-        isDefaultError = true;
-      }
-
-      if (isDefault) {
-        let now = (Math.floor(Date.now() / 1000)).toString();
-        Services.prefs.setCharPref("browser.shell.mostRecentDateSetAsDefault", now);
-      }
-
-      let willPrompt = shouldCheck && !isDefault && !willRecoverSession;
-
-      // Skip the "Set Default Browser" check during first-run or after the
-      // browser has been run a few times.
-      if (willPrompt) {
-        if (skipDefaultBrowserCheck) {
-          Services.prefs.setBoolPref("browser.shell.didSkipDefaultBrowserCheckOnFirstRun", true);
-          willPrompt = false;
-        } else {
-          promptCount++;
-        }
-        if (usePromptLimit && promptCount > 3) {
-          willPrompt = false;
-        }
-      }
-
-      if (usePromptLimit && willPrompt) {
-        Services.prefs.setIntPref("browser.shell.defaultBrowserCheckCount", promptCount);
-      }
-
-      try {
-        // Report default browser status on startup to telemetry
-        // so we can track whether we are the default.
-        Services.telemetry.getHistogramById("BROWSER_IS_USER_DEFAULT")
-                          .add(isDefault);
-        Services.telemetry.getHistogramById("BROWSER_IS_USER_DEFAULT_ERROR")
-                          .add(isDefaultError);
-        Services.telemetry.getHistogramById("BROWSER_SET_DEFAULT_ALWAYS_CHECK")
-                          .add(shouldCheck);
-        Services.telemetry.getHistogramById("BROWSER_SET_DEFAULT_DIALOG_PROMPT_RAWCOUNT")
-                          .add(promptCount);
-      } catch (ex) { /* Don't break the default prompt if telemetry is broken. */ }
-
-      if (willPrompt) {
-        Services.tm.dispatchToMainThread(function() {
-          DefaultBrowserCheck.prompt(RecentWindow.getMostRecentBrowserWindow());
-        });
-      }
+    if (AppConstants.MOZ_CRASHREPORTER) {
+      UnsubmittedCrashHandler.init();
     }
 
+    this._sanitizer.onStartup();
+    E10SAccessibilityCheck.onWindowsRestored();
+
+    this._scheduleStartupIdleTasks();
+
+    this._lateTasksIdleObserver = (idleService, topic, data) => {
+      if (topic == "idle") {
+        idleService.removeIdleObserver(this._lateTasksIdleObserver,
+                                       LATE_TASKS_IDLE_TIME_SEC);
+        delete this._lateTasksIdleObserver;
+        this._scheduleArbitrarilyLateIdleTasks();
+      }
+    };
+    this._idleService.addIdleObserver(
+      this._lateTasksIdleObserver, LATE_TASKS_IDLE_TIME_SEC);
+  },
+
+  /**
+   * Use this function as an entry point to schedule tasks that
+   * need to run only once after startup, and can be scheduled
+   * by using an idle callback.
+   *
+   * The functions scheduled here will fire from idle callbacks
+   * once every window has finished being restored by session
+   * restore, and it's guaranteed that they will run before
+   * the equivalent per-window idle tasks
+   * (from _schedulePerWindowIdleTasks in browser.js).
+   *
+   * If you have something that can wait even further than the
+   * per-window initialization, please schedule them using
+   * _scheduleArbitrarilyLateIdleTasks.
+   * Don't be fooled by thinking that the use of the timeout parameter
+   * will delay your function: it will just ensure that it potentially
+   * happens _earlier_ than expected (when the timeout limit has been reached),
+   * but it will not make it happen later (and out of order) compared
+   * to the other ones scheduled together.
+   */
+  _scheduleStartupIdleTasks() {
+    Services.tm.idleDispatchToMainThread(() => {
+      ContextualIdentityService.load();
+    });
+
+    // Load the Login Manager data from disk off the main thread, some time
+    // after startup.  If the data is required before this runs, for example
+    // because a restored page contains a password field, it will be loaded on
+    // the main thread, and this initialization request will be ignored.
+    Services.tm.idleDispatchToMainThread(() => {
+      try {
+        Services.logins;
+      } catch (ex) {
+        Cu.reportError(ex);
+      }
+    }, 3000);
+
+    // It's important that SafeBrowsing is initialized reasonably
+    // early, so we use a maximum timeout for it.
+    Services.tm.idleDispatchToMainThread(() => {
+      SafeBrowsing.init();
+    }, 5000);
+
     if (AppConstants.MOZ_CRASHREPORTER) {
-      UnsubmittedCrashHandler.init();
-      Services.tm.idleDispatchToMainThread(function() {
+      Services.tm.idleDispatchToMainThread(() => {
         UnsubmittedCrashHandler.checkForUnsubmittedCrashReports();
       });
     }
 
-    // Let's load the contextual identities.
+    if (AppConstants.platform == "win") {
+      Services.tm.idleDispatchToMainThread(() => {
+        // For Windows 7, initialize the jump list module.
+        const WINTASKBAR_CONTRACTID = "@mozilla.org/windows-taskbar;1";
+        if (WINTASKBAR_CONTRACTID in Cc &&
+            Cc[WINTASKBAR_CONTRACTID].getService(Ci.nsIWinTaskbar).available) {
+          let temp = {};
+          Cu.import("resource:///modules/WindowsJumpLists.jsm", temp);
+          temp.WinTaskbarJumpList.startup();
+        }
+      });
+    }
+
+    if (AppConstants.MOZ_DEV_EDITION) {
+      Services.tm.idleDispatchToMainThread(() => {
+        this._createExtraDefaultProfile();
+      });
+    }
+
     Services.tm.idleDispatchToMainThread(() => {
-      ContextualIdentityService.load();
+      this._checkForDefaultBrowser();
     });
 
     Services.tm.idleDispatchToMainThread(() => {
-      SafeBrowsing.init();
-    }, 5000);
+      let {setTimeout} = Cu.import("resource://gre/modules/Timer.jsm", {});
+      setTimeout(function() {
+        Services.tm.idleDispatchToMainThread(Services.startup.trackStartupCrashEnd);
+      }, STARTUP_CRASHES_END_DELAY_MS);
+    });
+  },
 
-    this._sanitizer.onStartup();
-    E10SAccessibilityCheck.onWindowsRestored();
+  /**
+   * Use this function as an entry point to schedule tasks that need
+   * to run once per session, at any arbitrary point in time.
+   * This function will be called from an idle observer. Check the value of
+   * LATE_TASKS_IDLE_TIME_SEC to see the current value for this idle
+   * observer.
+   *
+   * Note: this function may never be called if the user is never idle for the
+   * full length of the period of time specified. But given a reasonably low
+   * value, this is unlikely.
+   */
+  _scheduleArbitrarilyLateIdleTasks() {
+    Services.tm.idleDispatchToMainThread(() => {
+      this._sendMediaTelemetry();
+    });
+
+    Services.tm.idleDispatchToMainThread(() => {
+      // Telemetry for master-password - we do this after a delay as it
+      // can cause IO if NSS/PSM has not already initialized.
+      let tokenDB = Cc["@mozilla.org/security/pk11tokendb;1"]
+                .getService(Ci.nsIPK11TokenDB);
+      let token = tokenDB.getInternalKeyToken();
+      let mpEnabled = token.hasPassword;
+      if (mpEnabled) {
+        Services.telemetry.getHistogramById("MASTER_PASSWORD_ENABLED").add(mpEnabled);
+      }
+    });
+
+    Services.tm.idleDispatchToMainThread(() => {
+      let obj = {};
+      Cu.import("resource://gre/modules/GMPInstallManager.jsm", obj);
+      this._gmpInstallManager = new obj.GMPInstallManager();
+      // We don't really care about the results, if someone is interested they
+      // can check the log.
+      this._gmpInstallManager.simpleCheckAndInstall().catch(() => {});
+    });
   },
 
   _createExtraDefaultProfile() {
     if (!AppConstants.MOZ_DEV_EDITION) {
       return;
     }
     // If Developer Edition is the only installed Firefox version and no other
     // profiles are present, create a second one for use by other versions.
@@ -2079,16 +2099,93 @@ BrowserGlue.prototype = {
         Services.prefs.setIntPref("browser.uidensity", 1);
       }
     }
 
     // Update the migration version.
     Services.prefs.setIntPref("browser.migration.version", UI_VERSION);
   },
 
+  _checkForDefaultBrowser() {
+    // Perform default browser checking.
+    if (!ShellService) {
+      return;
+    }
+
+    let shouldCheck = AppConstants.DEBUG ? false :
+                                           ShellService.shouldCheckDefaultBrowser;
+
+    const skipDefaultBrowserCheck =
+      Services.prefs.getBoolPref("browser.shell.skipDefaultBrowserCheckOnFirstRun") &&
+      !Services.prefs.getBoolPref("browser.shell.didSkipDefaultBrowserCheckOnFirstRun");
+
+    const usePromptLimit = !AppConstants.RELEASE_OR_BETA;
+    let promptCount =
+      usePromptLimit ? Services.prefs.getIntPref("browser.shell.defaultBrowserCheckCount") : 0;
+
+    let willRecoverSession = false;
+    try {
+      let ss = Cc["@mozilla.org/browser/sessionstartup;1"].
+               getService(Ci.nsISessionStartup);
+      willRecoverSession =
+        (ss.sessionType == Ci.nsISessionStartup.RECOVER_SESSION);
+    } catch (ex) { /* never mind; suppose SessionStore is broken */ }
+
+    // startup check, check all assoc
+    let isDefault = false;
+    let isDefaultError = false;
+    try {
+      isDefault = ShellService.isDefaultBrowser(true, false);
+    } catch (ex) {
+      isDefaultError = true;
+    }
+
+    if (isDefault) {
+      let now = (Math.floor(Date.now() / 1000)).toString();
+      Services.prefs.setCharPref("browser.shell.mostRecentDateSetAsDefault", now);
+    }
+
+    let willPrompt = shouldCheck && !isDefault && !willRecoverSession;
+
+    // Skip the "Set Default Browser" check during first-run or after the
+    // browser has been run a few times.
+    if (willPrompt) {
+      if (skipDefaultBrowserCheck) {
+        Services.prefs.setBoolPref("browser.shell.didSkipDefaultBrowserCheckOnFirstRun", true);
+        willPrompt = false;
+      } else {
+        promptCount++;
+      }
+      if (usePromptLimit && promptCount > 3) {
+        willPrompt = false;
+      }
+    }
+
+    if (usePromptLimit && willPrompt) {
+      Services.prefs.setIntPref("browser.shell.defaultBrowserCheckCount", promptCount);
+    }
+
+    try {
+      // Report default browser status on startup to telemetry
+      // so we can track whether we are the default.
+      Services.telemetry.getHistogramById("BROWSER_IS_USER_DEFAULT")
+                        .add(isDefault);
+      Services.telemetry.getHistogramById("BROWSER_IS_USER_DEFAULT_ERROR")
+                        .add(isDefaultError);
+      Services.telemetry.getHistogramById("BROWSER_SET_DEFAULT_ALWAYS_CHECK")
+                        .add(shouldCheck);
+      Services.telemetry.getHistogramById("BROWSER_SET_DEFAULT_DIALOG_PROMPT_RAWCOUNT")
+                        .add(promptCount);
+    } catch (ex) { /* Don't break the default prompt if telemetry is broken. */ }
+
+    if (willPrompt) {
+      DefaultBrowserCheck.prompt(RecentWindow.getMostRecentBrowserWindow());
+    }
+  },
+
   // ------------------------------
   // public nsIBrowserGlue members
   // ------------------------------
 
   sanitize: function BG_sanitize(aParentWindow) {
     this._sanitizer.sanitize(aParentWindow);
   },
 
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -209,16 +209,20 @@ function debug(aMsg) {
  */
 var gResistFingerprintingEnabled = false;
 
 this.SessionStore = {
   get promiseInitialized() {
     return SessionStoreInternal.promiseInitialized;
   },
 
+  get promiseAllWindowsRestored() {
+    return SessionStoreInternal.promiseAllWindowsRestored;
+  },
+
   get canRestoreLastSession() {
     return SessionStoreInternal.canRestoreLastSession;
   },
 
   set canRestoreLastSession(val) {
     SessionStoreInternal.canRestoreLastSession = val;
   },
 
@@ -527,16 +531,32 @@ var SessionStoreInternal = {
     });
 
     return deferred;
   })(),
 
   // Whether session has been initialized
   _sessionInitialized: false,
 
+  // A promise resolved once all windows are restored.
+  _deferredAllWindowsRestored: (function() {
+    let deferred = {};
+
+    deferred.promise = new Promise((resolve, reject) => {
+      deferred.resolve = resolve;
+      deferred.reject = reject;
+    });
+
+    return deferred;
+  })(),
+
+  get promiseAllWindowsRestored() {
+    return this._deferredAllWindowsRestored.promise;
+  },
+
   // Promise that is resolved when we're ready to initialize
   // and restore the session.
   _promiseReadyForInitialization: null,
 
   // Keep busy state counters per window.
   _windowBusyStates: new WeakMap(),
 
   /**
@@ -1124,16 +1144,17 @@ var SessionStoreInternal = {
         if (isPrivateWindow) {
           // We're starting with a single private window. Save the state we
           // actually wanted to restore so that we can do it later in case
           // the user opens another, non-private window.
           this._deferredInitialState = gSessionStartup.state;
 
           // Nothing to restore now, notify observers things are complete.
           Services.obs.notifyObservers(null, NOTIFY_WINDOWS_RESTORED);
+          this._deferredAllWindowsRestored.resolve();
         } else {
           TelemetryTimestamps.add("sessionRestoreRestoring");
           this._restoreCount = aInitialState.windows ? aInitialState.windows.length : 0;
 
           // global data must be restored before restoreWindow is called so that
           // it happens before observers are notified
           this._globalState.setFromState(aInitialState);
 
@@ -1142,16 +1163,17 @@ var SessionStoreInternal = {
 
           let overwrite = this._isCmdLineEmpty(aWindow, aInitialState);
           let options = {firstWindow: true, overwriteTabs: overwrite};
           this.restoreWindows(aWindow, aInitialState, options);
         }
       } else {
         // Nothing to restore, notify observers things are complete.
         Services.obs.notifyObservers(null, NOTIFY_WINDOWS_RESTORED);
+        this._deferredAllWindowsRestored.resolve();
       }
     // this window was opened by _openWindowWithState
     } else if (!this._isWindowLoaded(aWindow)) {
       let state = this._statesToRestore[aWindow.__SS_restoreID];
       let options = {overwriteTabs: true, isFollowUp: state.windows.length == 1};
       this.restoreWindow(aWindow, state.windows[0], options);
     // The user opened another, non-private window after starting up with
     // a single private one. Let's restore the session we actually wanted to
@@ -4445,18 +4467,25 @@ var SessionStoreInternal = {
       return;
     }
 
     // observers were already notified
     if (this._restoreCount == -1)
       return;
 
     // This was the last window restored at startup, notify observers.
-    Services.obs.notifyObservers(null,
-      this._browserSetState ? NOTIFY_BROWSER_STATE_RESTORED : NOTIFY_WINDOWS_RESTORED);
+    if (!this._browserSetState) {
+      Services.obs.notifyObservers(null, NOTIFY_WINDOWS_RESTORED);
+      this._deferredAllWindowsRestored.resolve();
+    } else {
+      // _browserSetState is used only by tests, and it uses an alternate
+      // notification in order not to retrigger startup observers that
+      // are listening for NOTIFY_WINDOWS_RESTORED.
+      Services.obs.notifyObservers(null, NOTIFY_BROWSER_STATE_RESTORED);
+    }
 
     this._browserSetState = false;
     this._restoreCount = -1;
   },
 
    /**
    * Set the given window's busy state
    * @param aWindow the window
--- a/browser/extensions/formautofill/FormAutofillContent.jsm
+++ b/browser/extensions/formautofill/FormAutofillContent.jsm
@@ -380,39 +380,22 @@ var FormAutofillContent = {
     }
 
     let handler = this._formsDetails.get(formElement);
     if (!handler) {
       this.log.debug("Form element could not map to an existing handler");
       return true;
     }
 
-    let {addressRecord, creditCardRecord} = handler.createRecords();
-
-    if (!addressRecord && !creditCardRecord) {
+    let records = handler.createRecords();
+    if (!Object.keys(records).length) {
       return true;
     }
 
-    let data = {};
-    if (addressRecord) {
-      data.address = {
-        guid: handler.address.filledRecordGUID,
-        record: addressRecord,
-      };
-    }
-
-    if (creditCardRecord) {
-      data.creditCard = {
-        guid: handler.creditCard.filledRecordGUID,
-        record: creditCardRecord,
-      };
-    }
-
-    this._onFormSubmit(data, domWin);
-
+    this._onFormSubmit(records, domWin);
     return true;
   },
 
   receiveMessage({name, data}) {
     switch (name) {
       case "FormAutofill:enabledStatus": {
         if (data) {
           ProfileAutocomplete.ensureRegistered();
--- a/browser/extensions/formautofill/FormAutofillHandler.jsm
+++ b/browser/extensions/formautofill/FormAutofillHandler.jsm
@@ -446,49 +446,63 @@ FormAutofillHandler.prototype = {
 
     fieldDetail.state = nextState;
   },
 
   /**
    * Return the records that is converted from address/creditCard fieldDetails and
    * only valid form records are included.
    *
-   * @returns {Object} The new profile that convert from details with trimmed result.
+   * @returns {Object}
+   *          Consists of two record objects: address, creditCard. Each one can
+   *          be omitted if there's no valid fields. A record object consists of
+   *          three properties:
+   *            - guid: The id of the previously-filled profile or null if omitted.
+   *            - record: A valid record converted from details with trimmed result.
+   *            - untouchedFields: Fields that aren't touched after autofilling.
    */
   createRecords() {
-    let records = {};
+    let data = {};
 
     ["address", "creditCard"].forEach(type => {
       let details = this[type].fieldDetails;
       if (!details || details.length == 0) {
         return;
       }
 
-      let recordName = `${type}Record`;
-      records[recordName] = {};
+      data[type] = {
+        guid: this[type].filledRecordGUID,
+        record: {},
+        untouchedFields: [],
+      };
+
       details.forEach(detail => {
         let element = detail.elementWeakRef.get();
         // Remove the unnecessary spaces
         let value = element && element.value.trim();
         if (!value) {
           return;
         }
 
-        records[recordName][detail.fieldName] = value;
+        data[type].record[detail.fieldName] = value;
+
+        if (detail.state == "AUTO_FILLED") {
+          data[type].untouchedFields.push(detail.fieldName);
+        }
       });
     });
 
-    if (records.addressRecord &&
-        Object.keys(records.addressRecord).length < FormAutofillUtils.AUTOFILL_FIELDS_THRESHOLD) {
+    if (data.address &&
+        Object.keys(data.address.record).length < FormAutofillUtils.AUTOFILL_FIELDS_THRESHOLD) {
       log.debug("No address record saving since there are only",
-                     Object.keys(records.addressRecord).length,
+                     Object.keys(data.address.record).length,
                      "usable fields");
-      delete records.addressRecord;
+      delete data.address;
     }
 
-    if (records.creditCardRecord && !records.creditCardRecord["cc-number"]) {
+    if (data.creditCard && !data.creditCard.record["cc-number"]) {
       log.debug("No credit card record saving since card number is empty");
-      delete records.creditCardRecord;
+      delete data.creditCard;
     }
 
-    return records;
+    return data;
   },
 };
--- a/browser/extensions/formautofill/FormAutofillParent.jsm
+++ b/browser/extensions/formautofill/FormAutofillParent.jsm
@@ -281,16 +281,24 @@ FormAutofillParent.prototype = {
                                         Services.ppmm.initialProcessData.autofillSavedFieldNames);
     this._updateStatus();
   },
 
   _onFormSubmit(data, target) {
     let {address} = data;
 
     if (address.guid) {
+      // Avoid updating the fields that users don't modify.
+      let originalAddress = this.profileStorage.addresses.get(address.guid);
+      for (let field in address.record) {
+        if (address.untouchedFields.includes(field) && originalAddress[field]) {
+          address.record[field] = originalAddress[field];
+        }
+      }
+
       if (!this.profileStorage.addresses.mergeIfPossible(address.guid, address.record)) {
         FormAutofillDoorhanger.show(target, "update").then((state) => {
           let changedGUIDs = this.profileStorage.addresses.mergeToStorage(address.record);
           switch (state) {
             case "create":
               if (!changedGUIDs.length) {
                 changedGUIDs.push(this.profileStorage.addresses.add(address.record));
               }
--- a/browser/extensions/formautofill/ProfileStorage.jsm
+++ b/browser/extensions/formautofill/ProfileStorage.jsm
@@ -1370,20 +1370,35 @@ class Addresses extends AutofillRecords 
       throw new Error("No matching address.");
     }
 
     let addressToMerge = this._clone(address);
     this._normalizeRecord(addressToMerge);
     let hasMatchingField = false;
 
     for (let field of this.VALID_FIELDS) {
-      if (addressToMerge[field] !== undefined && addressFound[field] !== undefined) {
-        if (addressToMerge[field] != addressFound[field]) {
-          this.log.debug("Conflicts: field", field, "has different value.");
-          return false;
+      let existingField = addressFound[field];
+      let incomingField = addressToMerge[field];
+      if (incomingField !== undefined && existingField !== undefined) {
+        if (incomingField != existingField) {
+          // Treat "street-address" as mergeable if their single-line versions
+          // match each other.
+          if (field == "street-address" &&
+              FormAutofillUtils.toOneLineAddress(existingField) == FormAutofillUtils.toOneLineAddress(incomingField)) {
+            // Keep the value in storage if its amount of lines is greater than
+            // or equal to the incoming one.
+            if (existingField.split("\n").length >= incomingField.split("\n").length) {
+              // Replace the incoming field with the one in storage so it will
+              // be further merged back to storage.
+              addressToMerge[field] = existingField;
+            }
+          } else {
+            this.log.debug("Conflicts: field", field, "has different value.");
+            return false;
+          }
         }
         hasMatchingField = true;
       }
     }
 
     // We merge the address only when at least one field has the same value.
     if (!hasMatchingField) {
       this.log.debug("Unable to merge because no field has the same value");
--- a/browser/extensions/formautofill/test/browser/browser_update_doorhanger.js
+++ b/browser/extensions/formautofill/test/browser/browser_update_doorhanger.js
@@ -14,17 +14,17 @@ add_task(async function test_update_addr
       await openPopupOn(browser, "form #organization");
       await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
       await BrowserTestUtils.synthesizeKey("VK_RETURN", {}, browser);
 
       await ContentTask.spawn(browser, null, async function() {
         let form = content.document.getElementById("form");
         let org = form.querySelector("#organization");
         await new Promise(resolve => setTimeout(resolve, 1000));
-        org.value = "Mozilla";
+        org.setUserInput("Mozilla");
 
         // Wait 1000ms before submission to make sure the input value applied
         await new Promise(resolve => setTimeout(resolve, 1000));
         form.querySelector("input[type=submit]").click();
       });
 
       await promiseShown;
       await clickDoorhangerButton(MAIN_BUTTON_INDEX);
@@ -47,31 +47,31 @@ add_task(async function test_create_new_
       await openPopupOn(browser, "form #tel");
       await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
       await BrowserTestUtils.synthesizeKey("VK_RETURN", {}, browser);
 
       await ContentTask.spawn(browser, null, async function() {
         let form = content.document.getElementById("form");
         let tel = form.querySelector("#tel");
         await new Promise(resolve => setTimeout(resolve, 1000));
-        tel.value = "+1-234-567-890";
+        tel.setUserInput("+1234567890");
 
         // Wait 1000ms before submission to make sure the input value applied
         await new Promise(resolve => setTimeout(resolve, 1000));
         form.querySelector("input[type=submit]").click();
       });
 
       await promiseShown;
       await clickDoorhangerButton(SECONDARY_BUTTON_INDEX);
     }
   );
 
   addresses = await getAddresses();
   is(addresses.length, 2, "2 addresses in storage");
-  is(addresses[1].tel, "+1-234-567-890", "Verify the tel field");
+  is(addresses[1].tel, "+1234567890", "Verify the tel field");
 });
 
 add_task(async function test_create_new_address_merge() {
   let addresses = await getAddresses();
   is(addresses.length, 2, "2 addresses in storage");
 
   await BrowserTestUtils.withNewTab({gBrowser, url: FORM_URL},
     async function(browser) {
@@ -80,23 +80,61 @@ add_task(async function test_create_new_
       await openPopupOn(browser, "form #tel");
       await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
       await BrowserTestUtils.synthesizeKey("VK_RETURN", {}, browser);
 
       // Choose the latest address and revert to the original phone number
       await ContentTask.spawn(browser, null, async function() {
         let form = content.document.getElementById("form");
         let tel = form.querySelector("#tel");
-        tel.value = "+1 617 253 5702";
+        tel.setUserInput("+16172535702");
 
         // Wait 1000ms before submission to make sure the input value applied
         await new Promise(resolve => setTimeout(resolve, 1000));
         form.querySelector("input[type=submit]").click();
       });
 
       await promiseShown;
       await clickDoorhangerButton(SECONDARY_BUTTON_INDEX);
     }
   );
 
   addresses = await getAddresses();
   is(addresses.length, 2, "Still 2 addresses in storage");
 });
+
+add_task(async function test_submit_untouched_fields() {
+  let addresses = await getAddresses();
+  is(addresses.length, 2, "2 addresses in storage");
+
+  await BrowserTestUtils.withNewTab({gBrowser, url: FORM_URL},
+    async function(browser) {
+      let promiseShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
+                                                       "popupshown");
+      await openPopupOn(browser, "form #organization");
+      await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
+      await BrowserTestUtils.synthesizeKey("VK_RETURN", {}, browser);
+
+      await ContentTask.spawn(browser, null, async function() {
+        let form = content.document.getElementById("form");
+        let org = form.querySelector("#organization");
+        await new Promise(resolve => setTimeout(resolve, 1000));
+        org.setUserInput("Organization");
+
+        let tel = form.querySelector("#tel");
+        await new Promise(resolve => setTimeout(resolve, 1000));
+        tel.value = "12345"; // ".value" won't change the highlight status.
+
+        // Wait 1000ms before submission to make sure the input value applied
+        await new Promise(resolve => setTimeout(resolve, 1000));
+        form.querySelector("input[type=submit]").click();
+      });
+
+      await promiseShown;
+      await clickDoorhangerButton(MAIN_BUTTON_INDEX);
+    }
+  );
+
+  addresses = await getAddresses();
+  is(addresses.length, 2, "Still 2 addresses in storage");
+  is(addresses[0].organization, "Organization", "organization should change");
+  is(addresses[0].tel, "+16172535702", "tel should remain unchanged");
+});
--- a/browser/extensions/formautofill/test/unit/test_addressRecords.js
+++ b/browser/extensions/formautofill/test/unit/test_addressRecords.js
@@ -97,16 +97,111 @@ const MERGE_TESTCASES = [
     },
     expectedAddress: {
       "given-name": "Timothy",
       "street-address": "331 E. Evelyn Avenue",
       "tel": "+16509030800",
       country: "US",
     },
   },
+  {
+    description: "Merge an address with multi-line street-address in storage and single-line incoming one",
+    addressInStorage: {
+      "given-name": "Timothy",
+      "street-address": "331 E. Evelyn Avenue\nLine2",
+      "tel": "+16509030800",
+    },
+    addressToMerge: {
+      "street-address": "331 E. Evelyn Avenue Line2",
+      "tel": "+16509030800",
+      country: "US",
+    },
+    expectedAddress: {
+      "given-name": "Timothy",
+      "street-address": "331 E. Evelyn Avenue\nLine2",
+      "tel": "+16509030800",
+      country: "US",
+    },
+  },
+  {
+    description: "Merge an address with 3-line street-address in storage and 2-line incoming one",
+    addressInStorage: {
+      "given-name": "Timothy",
+      "street-address": "331 E. Evelyn Avenue\nLine2\nLine3",
+      "tel": "+16509030800",
+    },
+    addressToMerge: {
+      "street-address": "331 E. Evelyn Avenue\nLine2 Line3",
+      "tel": "+16509030800",
+      country: "US",
+    },
+    expectedAddress: {
+      "given-name": "Timothy",
+      "street-address": "331 E. Evelyn Avenue\nLine2\nLine3",
+      "tel": "+16509030800",
+      country: "US",
+    },
+  },
+  {
+    description: "Merge an address with single-line street-address in storage and multi-line incoming one",
+    addressInStorage: {
+      "given-name": "Timothy",
+      "street-address": "331 E. Evelyn Avenue Line2",
+      "tel": "+16509030800",
+    },
+    addressToMerge: {
+      "street-address": "331 E. Evelyn Avenue\nLine2",
+      "tel": "+16509030800",
+      country: "US",
+    },
+    expectedAddress: {
+      "given-name": "Timothy",
+      "street-address": "331 E. Evelyn Avenue\nLine2",
+      "tel": "+16509030800",
+      country: "US",
+    },
+  },
+  {
+    description: "Merge an address with 2-line street-address in storage and 3-line incoming one",
+    addressInStorage: {
+      "given-name": "Timothy",
+      "street-address": "331 E. Evelyn Avenue\nLine2 Line3",
+      "tel": "+16509030800",
+    },
+    addressToMerge: {
+      "street-address": "331 E. Evelyn Avenue\nLine2\nLine3",
+      "tel": "+16509030800",
+      country: "US",
+    },
+    expectedAddress: {
+      "given-name": "Timothy",
+      "street-address": "331 E. Evelyn Avenue\nLine2\nLine3",
+      "tel": "+16509030800",
+      country: "US",
+    },
+  },
+  {
+    description: "Merge an address with the same amount of lines",
+    addressInStorage: {
+      "given-name": "Timothy",
+      "street-address": "331 E. Evelyn Avenue\nLine2\nLine3",
+      "tel": "+16509030800",
+    },
+    addressToMerge: {
+      "street-address": "331 E. Evelyn\nAvenue Line2\nLine3",
+      "tel": "+16509030800",
+      country: "US",
+    },
+    expectedAddress: {
+      "given-name": "Timothy",
+      "street-address": "331 E. Evelyn Avenue\nLine2\nLine3",
+      "tel": "+16509030800",
+      country: "US",
+    },
+  },
 ];
 
 let do_check_record_matches = (recordWithMeta, record) => {
   for (let key in record) {
     do_check_eq(recordWithMeta[key], record[key]);
   }
 };
 
--- a/browser/extensions/formautofill/test/unit/test_onFormSubmitted.js
+++ b/browser/extensions/formautofill/test/unit/test_onFormSubmitted.js
@@ -51,16 +51,17 @@ const TESTCASES = [
       records: {
         address: {
           guid: null,
           record: {
             "street-address": "331 E. Evelyn Avenue",
             "country": "USA",
             "tel": "1-650-903-0800",
           },
+          untouchedFields: [],
         },
       },
     },
   },
   {
     description: "Trigger credit card saving",
     formValue: {
       "cc-name": "John Doe",
@@ -74,16 +75,17 @@ const TESTCASES = [
         creditCard: {
           guid: null,
           record: {
             "cc-name": "John Doe",
             "cc-number": "1234567812345678",
             "cc-exp-month": 12,
             "cc-exp-year": 2000,
           },
+          untouchedFields: [],
         },
       },
     },
   },
   {
     description: "Trigger address and credit card saving",
     formValue: {
       "street-addr": "331 E. Evelyn Avenue",
@@ -99,25 +101,27 @@ const TESTCASES = [
       records: {
         address: {
           guid: null,
           record: {
             "street-address": "331 E. Evelyn Avenue",
             "country": "USA",
             "tel": "1-650-903-0800",
           },
+          untouchedFields: [],
         },
         creditCard: {
           guid: null,
           record: {
             "cc-name": "John Doe",
             "cc-number": "1234567812345678",
             "cc-exp-month": 12,
             "cc-exp-year": 2000,
           },
+          untouchedFields: [],
         },
       },
     },
   },
   {
     description: "Profile saved with trimmed string",
     formValue: {
       "street-addr": "331 E. Evelyn Avenue  ",
@@ -129,16 +133,17 @@ const TESTCASES = [
       records: {
         address: {
           guid: null,
           record: {
             "street-address": "331 E. Evelyn Avenue",
             "country": "USA",
             "tel": "1-650-903-0800",
           },
+          untouchedFields: [],
         },
       },
     },
   },
   {
     description: "Eliminate the field that is empty after trimmed",
     formValue: {
       "street-addr": "331 E. Evelyn Avenue",
@@ -151,16 +156,17 @@ const TESTCASES = [
       records: {
         address: {
           guid: null,
           record: {
             "street-address": "331 E. Evelyn Avenue",
             "country": "USA",
             "tel": "1-650-903-0800",
           },
+          untouchedFields: [],
         },
       },
     },
   },
 ];
 
 add_task(async function handle_earlyformsubmit_event() {
   do_print("Starting testcase: Test an invalid form element");
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -391,18 +391,20 @@
 @RESPATH@/components/DownloadLegacy.js
 @RESPATH@/components/BrowserPageThumbs.manifest
 @RESPATH@/components/crashmonitor.manifest
 @RESPATH@/components/nsCrashMonitor.js
 @RESPATH@/components/toolkitsearch.manifest
 @RESPATH@/components/nsSearchService.js
 @RESPATH@/components/nsSearchSuggestions.js
 @RESPATH@/components/nsSidebar.js
+#ifdef NIGHTLY_BUILD
 @RESPATH@/components/payments.manifest
 @RESPATH@/components/paymentUIService.js
+#endif
 @RESPATH@/components/passwordmgr.manifest
 @RESPATH@/components/nsLoginInfo.js
 @RESPATH@/components/nsLoginManager.js
 @RESPATH@/components/nsLoginManagerPrompter.js
 @RESPATH@/components/storage-json.js
 @RESPATH@/components/crypto-SDR.js
 @RESPATH@/components/TooltipTextProvider.js
 @RESPATH@/components/TooltipTextProvider.manifest
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -5,18 +5,16 @@
 %endif
 
 @import url("chrome://global/skin/");
 
 @namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
 @namespace html url("http://www.w3.org/1999/xhtml");
 @namespace svg url("http://www.w3.org/2000/svg");
 
-%include ../shared/browser.inc
-
 %include ../shared/browser.inc.css
 
 :root {
   --tabs-border: rgba(0,0,0,.3);
 
   --toolbar-non-lwt-bgcolor: -moz-dialog;
   --toolbar-non-lwt-textcolor: -moz-dialogtext;
   --toolbar-non-lwt-bgimage: linear-gradient(rgba(255,255,255,.15), rgba(255,255,255,.15));
@@ -807,38 +805,16 @@ html|span.ac-emphasize-text-url {
 }
 
 /* All tabs menupopup */
 
 .alltabs-item[selected="true"] {
   font-weight: bold;
 }
 
-
-toolbarbutton.chevron {
-  list-style-image: url("chrome://global/skin/toolbar/chevron.gif") !important;
-}
-
-toolbar[brighttext] toolbarbutton.chevron {
-  list-style-image: url("chrome://global/skin/toolbar/chevron-inverted.png") !important;
-}
-
-toolbarbutton.chevron:-moz-locale-dir(rtl) > .toolbarbutton-icon {
-  transform: scaleX(-1);
-}
-
-toolbarbutton.chevron > .toolbarbutton-text,
-toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
-  display: none;
-}
-
-toolbarbutton.chevron > .toolbarbutton-icon {
-  margin: 0;
-}
-
 /* Status panel */
 
 .statuspanel-label {
   margin: 0;
   padding: 2px 4px;
   background-color: -moz-dialog;
   border: 1px none ThreeDShadow;
   border-top-style: solid;
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -190,48 +190,16 @@
 
 /* ----- BOOKMARK TOOLBAR ----- */
 
 #nav-bar-customization-target > #wrapper-personal-bookmarks > #personal-bookmarks {
   min-height: 32px;
   -moz-box-align: center;
 }
 
-toolbarbutton.chevron {
-  list-style-image: url("chrome://global/skin/icons/chevron.png");
-  margin: 1px 0 0;
-  padding: 0;
-}
-
-toolbar[brighttext] toolbarbutton.chevron {
-  list-style-image: url("chrome://global/skin/icons/chevron-inverted.png");
-}
-
-toolbarbutton.chevron > .toolbarbutton-text {
-  display: none;
-}
-
-toolbarbutton.chevron:-moz-locale-dir(rtl) > .toolbarbutton-icon {
-  transform: scaleX(-1);
-}
-
-@media (min-resolution: 2dppx) {
-  toolbarbutton.chevron {
-    list-style-image: url("chrome://global/skin/icons/chevron@2x.png");
-  }
-
-  toolbar[brighttext] toolbarbutton.chevron {
-    list-style-image: url("chrome://global/skin/icons/chevron-inverted@2x.png");
-  }
-
-  toolbarbutton.chevron > .toolbarbutton-icon {
-    width: 13px;
-  }
-}
-
 /* ----- BOOKMARK BUTTONS ----- */
 
 .bookmark-item[container] {
   list-style-image: url("chrome://global/skin/tree/folder.png");
 }
 
 .bookmark-item[container][livemark] {
   list-style-image: url("chrome://browser/skin/page-livemarks.png");
@@ -1067,20 +1035,16 @@ html|span.ac-emphasize-text-url {
 
 .browserContainer > findbar {
   background: @scopeBarBackground@;
   border-top: @scopeBarSeparatorBorder@;
   color: -moz-DialogText;
   text-shadow: none;
 }
 
-toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
-  display: none;
-}
-
 .bookmark-item {
   list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.svg");
 }
 
 .openintabs-menuitem {
   list-style-image: none;
 }
 
--- a/browser/themes/osx/shared.inc
+++ b/browser/themes/osx/shared.inc
@@ -1,7 +1,6 @@
 %include ../../../toolkit/themes/osx/global/shared.inc
-%include ../shared/browser.inc
 
 %filter substitution
 
 %define hudButton -moz-appearance: none; color: #434343; border-radius: 4px; border: 1px solid #b5b5b5; background: linear-gradient(#fff, #f2f2f2); box-shadow: inset 0 1px rgba(255,255,255,.8), inset 0 0 1px rgba(255,255, 255,.25), 0 1px rgba(255,255,255,.3); background-clip: padding-box; background-origin: padding-box; padding: 2px 6px;
 %define hudButtonPressed box-shadow: inset 0 1px 4px -3px #000, 0 1px rgba(255,255,255,.3);
deleted file mode 100644
--- a/browser/themes/shared/browser.inc
+++ /dev/null
@@ -1,8 +0,0 @@
-%filter substitution
-
-% Note that zoom-reset-button is a bit different since it doesn't use an image and thus has the image with display: none.
-%define nestedButtons #zoom-out-button, #zoom-reset-button, #zoom-in-button, #cut-button, #copy-button, #paste-button
-
-%define inAnyPanel :-moz-any(:not([cui-areatype="toolbar"]), [overflowedItem=true])
-
-%define panelPaletteIconSize var(--panel-palette-icon-size)
\ No newline at end of file
--- a/browser/themes/shared/customizableui/panelUI.inc.css
+++ b/browser/themes/shared/customizableui/panelUI.inc.css
@@ -12,26 +12,25 @@
 % There's no calc() here (and therefore lots of calc() where this is used) because
 % we don't support nested calc(): https://bugzilla.mozilla.org/show_bug.cgi?id=968761
 %define menuPanelButtonWidth (@menuPanelWidth@ / 3 - 0.1px)
 %define buttonStateHover :not(:-moz-any([disabled],[open],:active)):-moz-any(:hover,:focus)
 %define menuStateHover :not(:-moz-any([disabled],:active))[_moz-menuactive]
 %define buttonStateActive :not([disabled]):-moz-any([open],:hover:active)
 %define menuStateActive :not([disabled])[_moz-menuactive]:active
 %define menuStateMenuActive :not([disabled])[_moz-menuactive]
-
-%include ../browser.inc
+%define inAnyPanel :-moz-any(:not([cui-areatype="toolbar"]), [overflowedItem=true])
+%define panelPaletteIconSize 16px
 
 :root {
   --panel-ui-exit-subview-gutter-width: 38px;
   --appmenu-yellow-warning-border-color: hsl(45, 100%, 77%);
   --appmenu-yellow-warning-color: #FFEFBF;
   --appmenu-yellow-warning-hover-color: #FFE8A2;
   --appmenu-yellow-warning-active-color: #FFE38F;
-  --panel-palette-icon-size: 16px;
 }
 
 #PanelUI-popup #PanelUI-contents:empty {
   height: 128px;
 }
 
 #PanelUI-popup #PanelUI-contents:empty::before {
   content: "";
--- a/browser/themes/shared/icons/chevron-animation.svg
+++ b/browser/themes/shared/icons/chevron-animation.svg
@@ -1,218 +1,221 @@
-<!-- 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
+<!-- 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="1260" height="36" fill="context-fill">
+<svg xmlns="http://www.w3.org/2000/svg" width="1278" height="36" fill="context-fill">
   <svg>
-    <path d="M9.714 17.335l-4.948 -5.052a1 1 0 1 0 -1.429 1.4l4.25 4.337 -4.338 4.248a1 1 0 0 0 -0.04 1.414 1 1 0 0 0 1.414 0.04l0.025 -0.025 5.052 -4.948a1 1 0 0 0 0.014 -1.414zm6 0.062l-4.948 -5.051a1 1 0 1 0 -1.429 1.4l4.249 4.336 -4.338 4.248a1 1 0 0 0 -0.04 1.414 1 1 0 0 0 1.415 0.04l0.025 -0.025 5.051 -4.948a1 1 0 0 0 0.015 -1.414z"/>
+    <path d="M9.707 17.293l-5 -5a1 1 0 1 0 -1.414 1.414l4.293 4.293 -4.293 4.293a1 1 0 0 0 -0.025 1.414 1 1 0 0 0 1.414 0.025l0.025 -0.025 5 -5a1 1 0 0 0 0 -1.414zm6 0l-5 -5a1 1 0 1 0 -1.414 1.414l4.293 4.293 -4.293 4.293a1 1 0 0 0 -0.025 1.414 1 1 0 0 0 1.414 0.025l0.025 -0.025 5 -5a1 1 0 0 0 0 -1.414z"/>
   </svg>
   <svg x="18">
-    <path d="M9.735 17.456l-4.796 -5.196a1 1 0 1 0 -1.47 1.356l4.118 4.461 -4.461 4.118a1 1 0 0 0 -0.082 1.412 1 1 0 0 0 1.412 0.081c0.01 -0.007 0.017 -0.016 0.026 -0.024l5.196 -4.795a1 1 0 0 0 0.057 -1.413zm5.995 0.24l-4.796 -5.196a1 1 0 1 0 -1.47 1.356l4.118 4.462 -4.461 4.117a1 1 0 0 0 -0.082 1.412 1 1 0 0 0 1.412 0.082l0.026 -0.024 5.196 -4.796a1 1 0 0 0 0.057 -1.413z"/>
+    <path d="M9.714 17.3l-4.948 -5.051a1 1 0 1 0 -1.429 1.4l4.25 4.336 -4.338 4.249a1 1 0 0 0 -0.04 1.413 1 1 0 0 0 1.414 0.04l0.025 -0.025 5.052 -4.948a1 1 0 0 0 0.014 -1.414zm6 0.063l-4.948 -5.052a1 1 0 1 0 -1.429 1.4l4.249 4.337 -4.338 4.248a1 1 0 0 0 -0.04 1.413 1 1 0 0 0 1.415 0.04l0.025 -0.025 5.051 -4.948a1 1 0 0 0 0.015 -1.413z"/>
   </svg>
   <svg x="36">
-    <path d="M9.766 17.648l-4.546 -5.416a1 1 0 1 0 -1.532 1.286l3.903 4.65 -4.65 3.903a1 1 0 0 0 -0.148 1.407 1 1 0 0 0 1.407 0.148c0.01 -0.007 0.018 -0.016 0.027 -0.023l5.416 -4.546a1 1 0 0 0 0.123 -1.409zm5.977 0.522l-4.546 -5.416a1 1 0 1 0 -1.532 1.286l3.904 4.65 -4.65 3.903a1 1 0 0 0 -0.149 1.407 1 1 0 0 0 1.407 0.148c0.01 -0.007 0.018 -0.016 0.027 -0.023l5.416 -4.546a1 1 0 0 0 0.123 -1.409z"/>
+    <path d="M9.735 17.322l-4.796 -5.196a1 1 0 1 0 -1.47 1.356l4.118 4.461 -4.461 4.118a1 1 0 0 0 -0.082 1.412 1 1 0 0 0 1.412 0.081c0.01 -0.007 0.017 -0.016 0.026 -0.024l5.196 -4.795a1 1 0 0 0 0.057 -1.413zm5.995 0.24l-4.796 -5.196a1 1 0 1 0 -1.47 1.356l4.118 4.462 -4.461 4.117a1 1 0 0 0 -0.082 1.412 1 1 0 0 0 1.412 0.082l0.026 -0.024 5.196 -4.796a1 1 0 0 0 0.057 -1.413z"/>
   </svg>
   <svg x="54">
-    <path d="M9.804 17.905l-4.2 -5.688a1 1 0 1 0 -1.609 1.188l3.607 4.884 -4.884 3.606a1 1 0 0 0 -0.236 1.395 1 1 0 0 0 1.395 0.235l0.028 -0.021 5.689 -4.2a1 1 0 0 0 0.21 -1.399zm5.934 0.894l-4.2 -5.689a1 1 0 1 0 -1.609 1.188l3.606 4.884 -4.884 3.606a1 1 0 0 0 -0.235 1.395 1 1 0 0 0 1.394 0.235l0.029 -0.021 5.688 -4.2a1 1 0 0 0 0.21 -1.398z"/>
+    <path d="M9.766 17.357l-4.546 -5.416a1 1 0 1 0 -1.532 1.286l3.903 4.65 -4.65 3.903a1 1 0 0 0 -0.148 1.407 1 1 0 0 0 1.407 0.148l0.027 -0.023 5.416 -4.546a1 1 0 0 0 0.123 -1.409zm5.977 0.522l-4.546 -5.416a1 1 0 1 0 -1.532 1.286l3.904 4.65 -4.65 3.903a1 1 0 0 0 -0.149 1.407 1 1 0 0 0 1.407 0.148l0.027 -0.023 5.416 -4.546a1 1 0 0 0 0.123 -1.409z"/>
   </svg>
   <svg x="72">
-    <path d="M9.847 18.22l-3.76 -5.989a1 1 0 1 0 -1.694 1.063l3.229 5.142 -5.143 3.228a1 1 0 0 0 -0.34 1.373 1 1 0 0 0 1.373 0.34c0.011 -0.006 0.02 -0.013 0.03 -0.02l5.99 -3.758a1 1 0 0 0 0.315 -1.379zm5.849 1.338l-3.76 -5.989a1 1 0 1 0 -1.694 1.063l3.229 5.142 -5.143 3.228a1 1 0 0 0 -0.34 1.373 1 1 0 0 0 1.373 0.34l0.03 -0.02 5.99 -3.758a1 1 0 0 0 0.315 -1.379z"/>
+    <path d="M9.804 17.406l-4.2 -5.688a1 1 0 1 0 -1.609 1.188l3.607 4.884 -4.884 3.606a1 1 0 0 0 -0.236 1.394 1 1 0 0 0 1.395 0.235l0.028 -0.02 5.689 -4.2a1 1 0 0 0 0.21 -1.399zm5.934 0.893l-4.2 -5.688a1 1 0 1 0 -1.609 1.188l3.606 4.884 -4.884 3.606a1 1 0 0 0 -0.235 1.394 1 1 0 0 0 1.394 0.236l0.029 -0.021 5.688 -4.2a1 1 0 0 0 0.21 -1.399z"/>
   </svg>
   <svg x="90">
-    <path d="M9.89 18.585l-3.227 -6.292a1 1 0 1 0 -1.78 0.912l2.771 5.403 -5.402 2.77a1 1 0 0 0 -0.458 1.338 1 1 0 0 0 1.339 0.458l0.031 -0.016 6.292 -3.227a1 1 0 0 0 0.434 -1.346zm5.71 1.84l-3.226 -6.293a1 1 0 1 0 -1.78 0.912l2.771 5.403 -5.402 2.77a1 1 0 0 0 -0.457 1.339 1 1 0 0 0 1.338 0.457l0.031 -0.016 6.292 -3.227a1 1 0 0 0 0.434 -1.345z"/>
+    <path d="M9.847 17.468l-3.76 -5.989a1 1 0 1 0 -1.694 1.063l3.229 5.143 -5.143 3.227a1 1 0 0 0 -0.34 1.373 1 1 0 0 0 1.373 0.34c0.011 -0.006 0.02 -0.013 0.03 -0.02l5.99 -3.758a1 1 0 0 0 0.315 -1.379zm5.849 1.339l-3.76 -5.99a1 1 0 1 0 -1.694 1.063l3.229 5.143 -5.143 3.227a1 1 0 0 0 -0.34 1.373 1 1 0 0 0 1.373 0.34l0.03 -0.019 5.99 -3.76a1 1 0 0 0 0.315 -1.377z"/>
   </svg>
   <svg x="108">
-    <path d="M9.93 18.993l-2.61 -6.572a1 1 0 1 0 -1.859 0.738l2.24 5.642 -5.642 2.241a1 1 0 0 0 -0.583 1.288 1 1 0 0 0 1.288 0.584l0.033 -0.013 6.572 -2.61a1 1 0 0 0 0.56 -1.298zm5.508 2.377l-2.61 -6.572a1 1 0 1 0 -1.859 0.738l2.242 5.643 -5.643 2.24a1 1 0 0 0 -0.583 1.289 1 1 0 0 0 1.288 0.583l0.033 -0.013 6.572 -2.61a1 1 0 0 0 0.56 -1.298z"/>
+    <path d="M9.89 17.544l-3.227 -6.292a1 1 0 1 0 -1.78 0.912l2.771 5.403 -5.402 2.77a1 1 0 0 0 -0.458 1.338 1 1 0 0 0 1.339 0.457l0.031 -0.016 6.292 -3.226a1 1 0 0 0 0.434 -1.346zm5.71 1.84l-3.226 -6.293a1 1 0 1 0 -1.78 0.912l2.771 5.403 -5.402 2.77a1 1 0 0 0 -0.457 1.338 1 1 0 0 0 1.338 0.458l0.031 -0.016 6.292 -3.227a1 1 0 0 0 0.434 -1.346z"/>
   </svg>
   <svg x="126">
-    <path d="M9.962 19.434l-1.92 -6.805a1 1 0 1 0 -1.925 0.543l1.65 5.843 -5.844 1.649a1 1 0 0 0 -0.712 1.222 1 1 0 0 0 1.221 0.712c0.012 -0.002 0.023 -0.006 0.034 -0.01l6.806 -1.92a1 1 0 0 0 0.69 -1.234zm5.236 2.93l-1.92 -6.804a1 1 0 1 0 -1.925 0.543l1.649 5.843 -5.843 1.649a1 1 0 0 0 -0.713 1.221 1 1 0 0 0 1.222 0.713l0.034 -0.01 6.805 -1.92a1 1 0 0 0 0.69 -1.234z"/>
+    <path d="M9.93 17.631l-2.61 -6.572a1 1 0 1 0 -1.859 0.738l2.24 5.643 -5.642 2.24a1 1 0 0 0 -0.583 1.289 1 1 0 0 0 1.288 0.583l0.033 -0.013 6.572 -2.61a1 1 0 0 0 0.56 -1.298zm5.508 2.377l-2.61 -6.572a1 1 0 1 0 -1.859 0.738l2.242 5.643 -5.643 2.24a1 1 0 0 0 -0.583 1.29 1 1 0 0 0 1.288 0.582c0.012 -0.003 0.023 -0.009 0.033 -0.013l6.572 -2.61a1 1 0 0 0 0.56 -1.298z"/>
   </svg>
   <svg x="144">
-    <path d="M9.986 19.9l-1.176 -6.972a1 1 0 1 0 -1.972 0.333l1.01 5.986 -5.987 1.01a1 1 0 0 0 -0.84 1.137 1 1 0 0 0 1.138 0.84c0.012 -0.001 0.024 -0.004 0.035 -0.006l6.972 -1.175a1 1 0 0 0 0.82 -1.153zm4.889 3.478l-1.176 -6.972a1 1 0 1 0 -1.972 0.333l1.01 5.986 -5.987 1.01a1 1 0 0 0 -0.84 1.137 1 1 0 0 0 1.138 0.84c0.012 -0.001 0.024 -0.004 0.035 -0.006l6.972 -1.175a1 1 0 0 0 0.82 -1.153z"/>
+    <path d="M9.962 17.728l-1.92 -6.805a1 1 0 1 0 -1.925 0.543l1.65 5.843 -5.844 1.65a1 1 0 0 0 -0.712 1.221 1 1 0 0 0 1.221 0.712c0.012 -0.002 0.023 -0.006 0.034 -0.01l6.806 -1.92a1 1 0 0 0 0.69 -1.234zm5.236 2.931l-1.92 -6.805a1 1 0 1 0 -1.925 0.543l1.649 5.843 -5.843 1.65a1 1 0 0 0 -0.713 1.22 1 1 0 0 0 1.222 0.713c0.012 -0.002 0.023 -0.006 0.034 -0.01l6.805 -1.92a1 1 0 0 0 0.69 -1.234z"/>
   </svg>
   <svg x="162">
-    <path d="M9.998 20.382l-0.396 -7.06a1 1 0 1 0 -1.997 0.112l0.34 6.061 -6.061 0.34a1 1 0 0 0 -0.96 1.038 1 1 0 0 0 1.037 0.961l0.035 -0.002 7.06 -0.396a1 1 0 0 0 0.942 -1.054zm4.474 3.998l-0.396 -7.06a1 1 0 1 0 -1.997 0.112l0.34 6.062 -6.061 0.34a1 1 0 0 0 -0.961 1.037 1 1 0 0 0 1.037 0.961l0.036 -0.002 7.06 -0.396a1 1 0 0 0 0.942 -1.054z"/>
+    <path d="M9.986 17.834l-1.176 -6.973a1 1 0 1 0 -1.972 0.333l1.01 5.986 -5.987 1.01a1 1 0 0 0 -0.84 1.138 1 1 0 0 0 1.138 0.84l0.035 -0.006 6.972 -1.176a1 1 0 0 0 0.82 -1.152zm4.889 3.478l-1.176 -6.973a1 1 0 1 0 -1.972 0.333l1.01 5.986 -5.987 1.01a1 1 0 0 0 -0.84 1.138 1 1 0 0 0 1.138 0.84l0.035 -0.006 6.972 -1.176a1 1 0 0 0 0.82 -1.152z"/>
   </svg>
   <svg x="180">
-    <path d="M9.998 20.868l0.396 -7.06a1 1 0 1 0 -1.997 -0.112l-0.34 6.062 -6.061 -0.34a1 1 0 0 0 -1.07 0.924 1 1 0 0 0 0.923 1.07l0.035 0.003 7.06 0.396a1 1 0 0 0 1.054 -0.943zm3.999 4.474l0.396 -7.06a1 1 0 1 0 -1.997 -0.112l-0.34 6.062 -6.062 -0.34a1 1 0 0 0 -1.07 0.923 1 1 0 0 0 0.923 1.071 0.211 0.211 0 0 0 0.035 0.002l7.06 0.396a1 1 0 0 0 1.055 -0.942z"/>
+    <path d="M9.998 17.944l-0.396 -7.06a1 1 0 1 0 -1.997 0.112l0.34 6.062 -6.061 0.34a1 1 0 0 0 -0.96 1.037 1 1 0 0 0 1.037 0.961l0.035 -0.002 7.06 -0.396a1 1 0 0 0 0.942 -1.054zm4.474 3.998l-0.396 -7.06a1 1 0 1 0 -1.997 0.112l0.34 6.062 -6.061 0.34a1 1 0 0 0 -0.961 1.038 1 1 0 0 0 1.037 0.96l0.036 -0.001 7.06 -0.396a1 1 0 0 0 0.942 -1.055z"/>
   </svg>
   <svg x="198">
-    <path d="M9.986 21.35l1.176 -6.973a1 1 0 1 0 -1.972 -0.333l-1.01 5.987 -5.986 -1.01a1 1 0 0 0 -1.167 0.8 1 1 0 0 0 0.8 1.167l0.034 0.005 6.973 1.176a1 1 0 0 0 1.152 -0.82zm3.478 4.889l1.176 -6.973a1 1 0 1 0 -1.972 -0.333l-1.01 5.987 -5.986 -1.01a1 1 0 0 0 -1.167 0.8 1 1 0 0 0 0.8 1.167l0.034 0.006 6.973 1.175a1 1 0 0 0 1.152 -0.82z"/>
+    <path d="M9.998 18.056l0.396 -7.06a1 1 0 1 0 -1.997 -0.112l-0.34 6.062 -6.061 -0.34a1 1 0 0 0 -1.07 0.923 1 1 0 0 0 0.923 1.071 0.211 0.211 0 0 0 0.035 0.002l7.06 0.396a1 1 0 0 0 1.054 -0.942zm3.999 4.474l0.396 -7.06a1 1 0 1 0 -1.997 -0.112l-0.34 6.061 -6.062 -0.34a1 1 0 0 0 -1.07 0.924 1 1 0 0 0 0.923 1.07l0.035 0.003 7.06 0.396a1 1 0 0 0 1.055 -0.942z"/>
   </svg>
   <svg x="216">
-    <path d="M9.962 21.816l1.92 -6.805a1 1 0 1 0 -1.925 -0.543l-1.648 5.843 -5.843 -1.65a1 1 0 0 0 -1.246 0.67 1 1 0 0 0 0.67 1.245l0.033 0.01 6.805 1.92a1 1 0 0 0 1.234 -0.69zm2.931 5.235l1.92 -6.805a1 1 0 1 0 -1.925 -0.543l-1.648 5.843 -5.843 -1.649a1 1 0 0 0 -1.246 0.669 1 1 0 0 0 0.669 1.246l0.034 0.01 6.805 1.92a1 1 0 0 0 1.234 -0.69z"/>
+    <path d="M9.986 18.166l1.176 -6.972a1 1 0 1 0 -1.972 -0.333l-1.01 5.987 -5.986 -1.01a1 1 0 0 0 -1.167 0.8 1 1 0 0 0 0.8 1.166c0.011 0.003 0.023 0.004 0.034 0.006l6.973 1.176a1 1 0 0 0 1.152 -0.82zm3.478 4.89l1.176 -6.973a1 1 0 1 0 -1.972 -0.333l-1.01 5.987 -5.986 -1.01a1 1 0 0 0 -1.167 0.8 1 1 0 0 0 0.8 1.166c0.011 0.003 0.023 0.004 0.034 0.006l6.973 1.176a1 1 0 0 0 1.152 -0.82z"/>
   </svg>
   <svg x="234">
-    <path d="M9.93 22.257l2.609 -6.572a1 1 0 1 0 -1.859 -0.738l-2.24 5.643 -5.643 -2.24a1 1 0 0 0 -1.308 0.537 1 1 0 0 0 0.537 1.308l0.033 0.013 6.572 2.61a1 1 0 0 0 1.298 -0.56zm2.377 5.51l2.61 -6.573a1 1 0 1 0 -1.859 -0.738l-2.24 5.643 -5.644 -2.24a1 1 0 0 0 -1.308 0.537 1 1 0 0 0 0.538 1.308l0.032 0.013 6.572 2.61a1 1 0 0 0 1.299 -0.56z"/>
+    <path d="M9.962 18.272l1.92 -6.806a1 1 0 1 0 -1.925 -0.543l-1.648 5.843 -5.843 -1.649a1 1 0 0 0 -1.246 0.669 1 1 0 0 0 0.67 1.246l0.033 0.01 6.805 1.92a1 1 0 0 0 1.234 -0.69zm2.931 5.235l1.92 -6.805a1 1 0 1 0 -1.925 -0.543l-1.648 5.843 -5.843 -1.65a1 1 0 0 0 -1.246 0.67 1 1 0 0 0 0.669 1.246l0.034 0.01 6.805 1.92a1 1 0 0 0 1.234 -0.69z"/>
   </svg>
   <svg x="252">
-    <path d="M9.89 22.665l3.226 -6.292a1 1 0 1 0 -1.78 -0.912l-2.77 5.402 -5.402 -2.77a1 1 0 0 0 -1.353 0.41 1 1 0 0 0 0.41 1.353l0.03 0.016 6.293 3.226a1 1 0 0 0 1.346 -0.433zm1.84 5.71l3.226 -6.291a1 1 0 1 0 -1.78 -0.912l-2.77 5.402 -5.402 -2.77a1 1 0 0 0 -1.354 0.41 1 1 0 0 0 0.41 1.353l0.031 0.016 6.292 3.226a1 1 0 0 0 1.346 -0.433z"/>
+    <path d="M9.93 18.369l2.609 -6.572a1 1 0 1 0 -1.859 -0.738l-2.24 5.643 -5.643 -2.24a1 1 0 0 0 -1.308 0.536 1 1 0 0 0 0.537 1.309l0.033 0.013 6.572 2.61a1 1 0 0 0 1.298 -0.561zm2.377 5.509l2.61 -6.572a1 1 0 1 0 -1.859 -0.738l-2.24 5.643 -5.644 -2.241a1 1 0 0 0 -1.308 0.537 1 1 0 0 0 0.538 1.308c0.01 0.006 0.022 0.01 0.032 0.014l6.572 2.61a1 1 0 0 0 1.299 -0.561z"/>
   </svg>
   <svg x="270">
-    <path d="M9.847 23.03l3.759 -5.99a1 1 0 1 0 -1.694 -1.063l-3.227 5.143 -5.143 -3.228a1 1 0 0 0 -1.384 0.291 1 1 0 0 0 0.291 1.384l0.03 0.02 5.99 3.758a1 1 0 0 0 1.378 -0.315zm1.338 5.849l3.759 -5.99a1 1 0 1 0 -1.694 -1.063l-3.227 5.143 -5.143 -3.228a1 1 0 0 0 -1.384 0.291 1 1 0 0 0 0.291 1.384l0.03 0.02 5.99 3.758a1 1 0 0 0 1.378 -0.315z"/>
+    <path d="M9.89 18.456l3.226 -6.292a1 1 0 1 0 -1.78 -0.912l-2.77 5.402 -5.402 -2.77a1 1 0 0 0 -1.353 0.41 1 1 0 0 0 0.41 1.353l0.03 0.016 6.293 3.227a1 1 0 0 0 1.346 -0.434zm1.84 5.711l3.226 -6.292a1 1 0 1 0 -1.78 -0.912l-2.77 5.402 -5.402 -2.77a1 1 0 0 0 -1.354 0.41 1 1 0 0 0 0.41 1.353l0.031 0.016 6.292 3.227a1 1 0 0 0 1.346 -0.434z"/>
   </svg>
   <svg x="288">
-    <path d="M9.804 23.345l4.2 -5.689a1 1 0 1 0 -1.609 -1.188l-3.605 4.884 -4.885 -3.606a1 1 0 0 0 -1.402 0.186 1 1 0 0 0 0.186 1.402l0.029 0.02 5.688 4.201a1 1 0 0 0 1.398 -0.21zm0.893 5.933l4.2 -5.689a1 1 0 1 0 -1.609 -1.188l-3.605 4.885 -4.885 -3.607a1 1 0 0 0 -1.402 0.186 1 1 0 0 0 0.186 1.402l0.029 0.021 5.688 4.2a1 1 0 0 0 1.398 -0.21z"/>
+    <path d="M9.847 18.532l3.759 -5.99a1 1 0 1 0 -1.694 -1.063l-3.227 5.143 -5.143 -3.228a1 1 0 0 0 -1.384 0.291 1 1 0 0 0 0.291 1.384l0.03 0.019 5.99 3.759a1 1 0 0 0 1.378 -0.315zm1.338 5.848l3.759 -5.989a1 1 0 1 0 -1.694 -1.063l-3.227 5.143 -5.143 -3.228a1 1 0 0 0 -1.384 0.291 1 1 0 0 0 0.291 1.384l0.03 0.019 5.99 3.759a1 1 0 0 0 1.378 -0.316z"/>
   </svg>
   <svg x="306">
-    <path d="M9.766 23.602l4.546 -5.416a1 1 0 1 0 -1.532 -1.286l-3.903 4.65 -4.65 -3.903a1 1 0 0 0 -1.411 0.098 1 1 0 0 0 0.098 1.41c0.009 0.01 0.018 0.016 0.027 0.024l5.416 4.546a1 1 0 0 0 1.409 -0.123zm0.522 5.977l4.546 -5.416a1 1 0 1 0 -1.532 -1.286l-3.903 4.65 -4.65 -3.903a1 1 0 0 0 -1.411 0.098 1 1 0 0 0 0.098 1.411l0.027 0.023 5.416 4.546a1 1 0 0 0 1.409 -0.123z"/>
+    <path d="M9.804 18.594l4.2 -5.689a1 1 0 1 0 -1.609 -1.188l-3.605 4.885 -4.885 -3.606a1 1 0 0 0 -1.402 0.185 1 1 0 0 0 0.186 1.402l0.029 0.021 5.688 4.2a1 1 0 0 0 1.398 -0.21zm0.893 5.933l4.2 -5.688a1 1 0 1 0 -1.609 -1.188l-3.605 4.884 -4.885 -3.606a1 1 0 0 0 -1.402 0.185 1 1 0 0 0 0.186 1.402l0.029 0.021 5.688 4.2a1 1 0 0 0 1.398 -0.21z"/>
   </svg>
   <svg x="324">
-    <path d="M9.735 23.794l4.795 -5.196a1 1 0 1 0 -1.47 -1.356l-4.117 4.461 -4.461 -4.117a1 1 0 0 0 -1.414 0.031 1 1 0 0 0 0.032 1.414 0.195 0.195 0 0 0 0.026 0.024l5.196 4.796a1 1 0 0 0 1.413 -0.057zm0.24 5.995l4.796 -5.196a1 1 0 1 0 -1.47 -1.356l-4.117 4.461 -4.462 -4.117a1 1 0 0 0 -1.414 0.032 1 1 0 0 0 0.032 1.413c0.008 0.01 0.018 0.017 0.026 0.024l5.196 4.796a1 1 0 0 0 1.413 -0.057z"/>
+    <path d="M9.766 18.643l4.546 -5.416a1 1 0 1 0 -1.532 -1.286l-3.903 4.65 -4.65 -3.903a1 1 0 0 0 -1.411 0.098 1 1 0 0 0 0.098 1.411l0.027 0.023 5.416 4.546a1 1 0 0 0 1.409 -0.123zm0.522 5.977l4.546 -5.416a1 1 0 1 0 -1.532 -1.286l-3.903 4.65 -4.65 -3.903a1 1 0 0 0 -1.411 0.099 1 1 0 0 0 0.098 1.41 0.276 0.276 0 0 0 0.027 0.023l5.416 4.546a1 1 0 0 0 1.409 -0.123z"/>
   </svg>
   <svg x="342">
-    <path d="M9.714 23.915l4.948 -5.052a1 1 0 1 0 -1.429 -1.4l-4.248 4.338 -4.337 -4.248a1 1 0 0 0 -1.414 -0.01 1 1 0 0 0 -0.01 1.414l0.025 0.025 5.051 4.948a1 1 0 0 0 1.414 -0.015zm0.062 6l4.948 -5.052a1 1 0 1 0 -1.429 -1.4l-4.247 4.338 -4.338 -4.248a1 1 0 0 0 -1.414 -0.01 1 1 0 0 0 -0.01 1.414l0.025 0.024 5.052 4.948a1 1 0 0 0 1.413 -0.014z"/>
+    <path d="M9.735 18.678l4.795 -5.196a1 1 0 1 0 -1.47 -1.356l-4.117 4.461 -4.461 -4.117a1 1 0 0 0 -1.414 0.031 1 1 0 0 0 0.032 1.414c0.008 0.009 0.017 0.016 0.026 0.024l5.196 4.796a1 1 0 0 0 1.413 -0.057zm0.24 5.995l4.796 -5.196a1 1 0 1 0 -1.47 -1.356l-4.117 4.461 -4.462 -4.117a1 1 0 0 0 -1.414 0.031 1 1 0 0 0 0.032 1.414c0.008 0.009 0.018 0.017 0.026 0.024l5.196 4.796a1 1 0 0 0 1.413 -0.057z"/>
   </svg>
   <svg x="360">
-    <path d="M9.707 23.957l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0zm0 6l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0z"/>
+    <path d="M9.714 18.7l4.948 -5.052a1 1 0 1 0 -1.429 -1.4l-4.248 4.338 -4.337 -4.248a1 1 0 0 0 -1.414 -0.01 1 1 0 0 0 -0.01 1.414l0.025 0.024 5.051 4.948a1 1 0 0 0 1.414 -0.014zm0.062 6l4.948 -5.052a1 1 0 1 0 -1.429 -1.4l-4.247 4.338 -4.338 -4.249a1 1 0 0 0 -1.414 -0.01 1 1 0 0 0 -0.01 1.414c0.008 0.01 0.017 0.017 0.025 0.025l5.052 4.948a1 1 0 0 0 1.413 -0.015z"/>
   </svg>
   <svg x="378">
-    <path d="M9.707 23.957l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0zm0 6l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0z"/>
+    <path d="M9.707 18.707l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0zm0 6l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0z"/>
   </svg>
   <svg x="396">
-    <path d="M9.707 23.957l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0zm0 6l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0z"/>
+    <path d="M9.707 18.793l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0zm0 6l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0z"/>
   </svg>
   <svg x="414">
-    <path d="M9.707 23.957l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0zm0 6l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0z"/>
+    <path d="M9.707 19.02l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0zm0 6l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0z"/>
   </svg>
   <svg x="432">
-    <path d="M9.707 23.957l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0zm0 6l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0z"/>
+    <path d="M9.707 19.34l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0zm0 6l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0z"/>
   </svg>
   <svg x="450">
-    <path d="M9.707 23.957l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0zm0 6l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0z"/>
+    <path d="M9.707 19.707l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0zm0 6l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0z"/>
   </svg>
   <svg x="468">
-    <path d="M9.707 23.957l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0zm0 6l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0z"/>
+    <path d="M9.707 20.074l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0zm0 6l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0z"/>
   </svg>
   <svg x="486">
-    <path d="M9.722 23.957l5.103 -5a0.985 0.985 0 0 0 -0.026 -1.414 1.036 1.036 0 0 0 -1.418 0l-4.381 4.293 -4.381 -4.293a1.036 1.036 0 0 0 -1.444 -0.025 0.987 0.987 0 0 0 -0.025 1.414l0.025 0.025 5.103 5a1.036 1.036 0 0 0 1.444 0zm0 6l5.103 -5a0.985 0.985 0 0 0 -0.026 -1.414 1.036 1.036 0 0 0 -1.418 0l-4.381 4.293 -4.381 -4.293a1.036 1.036 0 0 0 -1.444 -0.025 0.987 0.987 0 0 0 -0.025 1.414l0.025 0.025 5.103 5a1.036 1.036 0 0 0 1.444 0z"/>
+    <path d="M9.707 20.394l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414c0.008 0.01 0.017 0.017 0.025 0.025l5 5a1 1 0 0 0 1.414 0zm0 6l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414c0.008 0.01 0.017 0.017 0.025 0.025l5 5a1 1 0 0 0 1.414 0z"/>
   </svg>
   <svg x="504">
-    <path d="M9.738 23.957l5.217 -5a0.97 0.97 0 0 0 -0.026 -1.414 1.076 1.076 0 0 0 -1.45 0l-4.479 4.293 -4.48 -4.293a1.077 1.077 0 0 0 -1.475 -0.025 0.972 0.972 0 0 0 -0.026 1.414l0.026 0.025 5.217 5a1.077 1.077 0 0 0 1.476 0zm0 6l5.217 -5a0.97 0.97 0 0 0 -0.026 -1.414 1.076 1.076 0 0 0 -1.45 0l-4.479 4.293 -4.48 -4.293a1.077 1.077 0 0 0 -1.475 -0.025 0.972 0.972 0 0 0 -0.026 1.414l0.026 0.025 5.217 5a1.077 1.077 0 0 0 1.476 0z"/>
+    <path d="M9.722 20.621l5.103 -5a0.985 0.985 0 0 0 -0.026 -1.414 1.036 1.036 0 0 0 -1.418 0l-4.381 4.293 -4.381 -4.293a1.036 1.036 0 0 0 -1.444 -0.025 0.987 0.987 0 0 0 -0.025 1.414l0.025 0.025 5.103 5a1.036 1.036 0 0 0 1.444 0zm0 6l5.103 -5a0.985 0.985 0 0 0 -0.026 -1.414 1.036 1.036 0 0 0 -1.418 0l-4.381 4.293 -4.381 -4.293a1.036 1.036 0 0 0 -1.444 -0.025 0.987 0.987 0 0 0 -0.025 1.414l0.025 0.025 5.103 5a1.036 1.036 0 0 0 1.444 0z"/>
   </svg>
   <svg x="522">
-    <path d="M9.755 24.235l5.34 -5c0.41 -0.397 0.398 -1.03 -0.027 -1.414 -0.413 -0.374 -1.069 -0.374 -1.483 0l-4.585 4.293 -4.585 -4.293c-0.41 -0.397 -1.086 -0.408 -1.51 -0.025 -0.424 0.384 -0.435 1.017 -0.026 1.414 0.008 0.01 0.018 0.017 0.026 0.025l5.34 5c0.418 0.39 1.092 0.39 1.51 0zm0 6l5.34 -5c0.41 -0.397 0.398 -1.03 -0.027 -1.414 -0.413 -0.374 -1.069 -0.374 -1.483 0l-4.585 4.293 -4.585 -4.293c-0.41 -0.397 -1.086 -0.408 -1.51 -0.025 -0.424 0.384 -0.435 1.017 -0.026 1.414 0.008 0.01 0.018 0.017 0.026 0.025l5.34 5c0.418 0.39 1.092 0.39 1.51 0z"/>
+    <path d="M9.738 20.707l5.217 -5a0.97 0.97 0 0 0 -0.026 -1.414 1.076 1.076 0 0 0 -1.45 0l-4.479 4.293 -4.48 -4.293a1.077 1.077 0 0 0 -1.475 -0.025 0.972 0.972 0 0 0 -0.026 1.414l0.026 0.025 5.217 5a1.077 1.077 0 0 0 1.476 0zm0 6l5.217 -5a0.97 0.97 0 0 0 -0.026 -1.414 1.076 1.076 0 0 0 -1.45 0l-4.479 4.293 -4.48 -4.293a1.077 1.077 0 0 0 -1.475 -0.025 0.972 0.972 0 0 0 -0.026 1.414l0.026 0.025 5.217 5a1.077 1.077 0 0 0 1.476 0z"/>
   </svg>
   <svg x="540">
-    <path d="M9.773 24.93l5.466 -5c0.42 -0.397 0.408 -1.03 -0.027 -1.414 -0.423 -0.374 -1.094 -0.374 -1.518 0l-4.694 4.293 -4.694 -4.293c-0.42 -0.397 -1.111 -0.408 -1.545 -0.025 -0.434 0.384 -0.447 1.017 -0.028 1.414l0.028 0.025 5.466 5c0.428 0.39 1.118 0.39 1.546 0zm0 6l5.466 -5c0.42 -0.397 0.408 -1.03 -0.027 -1.414 -0.423 -0.374 -1.094 -0.374 -1.518 0l-4.694 4.293 -4.694 -4.293c-0.42 -0.397 -1.111 -0.408 -1.545 -0.025 -0.434 0.384 -0.447 1.017 -0.028 1.414l0.028 0.025 5.466 5c0.428 0.39 1.118 0.39 1.546 0z"/>
+    <path d="M9.755 20.855l5.34 -5c0.41 -0.397 0.398 -1.03 -0.027 -1.414 -0.413 -0.374 -1.069 -0.374 -1.483 0l-4.585 4.293 -4.585 -4.293c-0.41 -0.397 -1.086 -0.408 -1.51 -0.025 -0.424 0.384 -0.435 1.017 -0.026 1.414 0.008 0.01 0.018 0.017 0.026 0.025l5.34 5c0.418 0.39 1.092 0.39 1.51 0zm0 6l5.34 -5c0.41 -0.397 0.398 -1.03 -0.027 -1.414 -0.413 -0.374 -1.069 -0.374 -1.483 0l-4.585 4.293 -4.585 -4.293c-0.41 -0.397 -1.086 -0.408 -1.51 -0.025 -0.424 0.384 -0.435 1.017 -0.026 1.414 0.008 0.01 0.018 0.017 0.026 0.025l5.34 5c0.418 0.39 1.092 0.39 1.51 0z"/>
   </svg>
   <svg x="558">
-    <path d="M9.791 25.832l5.595 -5c0.43 -0.397 0.417 -1.03 -0.028 -1.414 -0.433 -0.374 -1.12 -0.374 -1.555 0l-4.803 4.293 -4.803 -4.293c-0.43 -0.397 -1.138 -0.408 -1.583 -0.025 -0.444 0.384 -0.456 1.017 -0.027 1.414 0.008 0.009 0.019 0.017 0.027 0.025l5.595 5c0.437 0.39 1.145 0.39 1.582 0zm0 6l5.595 -5c0.43 -0.397 0.417 -1.03 -0.028 -1.414 -0.433 -0.374 -1.12 -0.374 -1.555 0l-4.803 4.293 -4.803 -4.293c-0.43 -0.397 -1.138 -0.408 -1.583 -0.025 -0.444 0.384 -0.456 1.017 -0.027 1.414 0.008 0.009 0.019 0.017 0.027 0.025l5.595 5c0.437 0.39 1.145 0.39 1.582 0z"/>
+    <path d="M9.773 21.226l5.466 -5c0.42 -0.397 0.408 -1.03 -0.027 -1.414 -0.423 -0.374 -1.094 -0.374 -1.518 0l-4.694 4.293 -4.694 -4.293c-0.42 -0.397 -1.111 -0.408 -1.545 -0.025 -0.434 0.384 -0.447 1.017 -0.028 1.414l0.028 0.025 5.466 5c0.428 0.39 1.118 0.39 1.546 0zm0 6l5.466 -5c0.42 -0.397 0.408 -1.03 -0.027 -1.414 -0.423 -0.374 -1.094 -0.374 -1.518 0l-4.694 4.293 -4.694 -4.293c-0.42 -0.397 -1.111 -0.408 -1.545 -0.025 -0.434 0.384 -0.447 1.017 -0.028 1.414l0.028 0.025 5.466 5c0.428 0.39 1.118 0.39 1.546 0z"/>
   </svg>
   <svg x="576">
-    <path d="M9.809 26.734l5.72 -5c0.44 -0.397 0.427 -1.03 -0.029 -1.414 -0.442 -0.374 -1.145 -0.374 -1.589 0l-4.911 4.293 -4.911 -4.293c-0.44 -0.397 -1.164 -0.408 -1.618 -0.025 -0.454 0.384 -0.467 1.017 -0.029 1.414 0.01 0.01 0.02 0.017 0.029 0.025l5.72 5c0.447 0.39 1.17 0.39 1.618 0zm0 6l5.72 -5c0.44 -0.397 0.427 -1.03 -0.029 -1.414 -0.442 -0.374 -1.145 -0.374 -1.589 0l-4.911 4.293 -4.911 -4.293c-0.44 -0.397 -1.164 -0.408 -1.618 -0.025 -0.454 0.384 -0.467 1.017 -0.029 1.414 0.01 0.01 0.02 0.017 0.029 0.025l5.72 5c0.447 0.39 1.17 0.39 1.618 0z"/>
+    <path d="M9.791 21.707l5.595 -5c0.43 -0.397 0.417 -1.03 -0.028 -1.414 -0.433 -0.374 -1.12 -0.374 -1.555 0l-4.803 4.293 -4.803 -4.293c-0.43 -0.397 -1.138 -0.408 -1.583 -0.025 -0.444 0.384 -0.456 1.017 -0.027 1.414 0.008 0.009 0.019 0.017 0.027 0.025l5.595 5c0.437 0.39 1.145 0.39 1.582 0zm0 6l5.595 -5c0.43 -0.397 0.417 -1.03 -0.028 -1.414 -0.433 -0.374 -1.12 -0.374 -1.555 0l-4.803 4.293 -4.803 -4.293c-0.43 -0.397 -1.138 -0.408 -1.583 -0.025 -0.444 0.384 -0.456 1.017 -0.027 1.414 0.008 0.009 0.019 0.017 0.027 0.025l5.595 5c0.437 0.39 1.145 0.39 1.582 0z"/>
   </svg>
   <svg x="594">
-    <path d="M9.826 27.429l5.84 -5c0.449 -0.397 0.436 -1.03 -0.03 -1.414 -0.451 -0.374 -1.168 -0.374 -1.622 0l-5.014 4.293 -5.014 -4.293c-0.449 -0.397 -1.188 -0.408 -1.652 -0.025 -0.464 0.384 -0.477 1.017 -0.03 1.414l0.03 0.025 5.84 5c0.457 0.39 1.195 0.39 1.652 0zm0 6l5.84 -5c0.449 -0.397 0.436 -1.03 -0.03 -1.414 -0.451 -0.374 -1.168 -0.374 -1.622 0l-5.014 4.293 -5.014 -4.293c-0.449 -0.397 -1.188 -0.408 -1.652 -0.025 -0.464 0.384 -0.477 1.017 -0.03 1.414l0.03 0.025 5.84 5c0.457 0.39 1.195 0.39 1.652 0z"/>
+    <path d="M9.809 22.188l5.72 -5c0.44 -0.397 0.427 -1.03 -0.029 -1.414 -0.442 -0.374 -1.145 -0.374 -1.589 0l-4.911 4.293 -4.911 -4.293c-0.44 -0.397 -1.164 -0.408 -1.618 -0.025 -0.454 0.384 -0.467 1.017 -0.029 1.414 0.01 0.01 0.02 0.017 0.029 0.025l5.72 5c0.447 0.39 1.17 0.39 1.618 0zm0 6l5.72 -5c0.44 -0.397 0.427 -1.03 -0.029 -1.414 -0.442 -0.374 -1.145 -0.374 -1.589 0l-4.911 4.293 -4.911 -4.293c-0.44 -0.397 -1.164 -0.408 -1.618 -0.025 -0.454 0.384 -0.467 1.017 -0.029 1.414 0.01 0.01 0.02 0.017 0.029 0.025l5.72 5c0.447 0.39 1.17 0.39 1.618 0z"/>
   </svg>
   <svg x="612">
-    <path d="M9.841 27.707l5.952 -5c0.457 -0.397 0.444 -1.03 -0.03 -1.414 -0.46 -0.374 -1.192 -0.374 -1.653 0l-5.11 4.293 -5.11 -4.293c-0.457 -0.397 -1.21 -0.408 -1.683 -0.025 -0.472 0.384 -0.485 1.017 -0.03 1.414l0.03 0.025 5.952 5c0.465 0.39 1.217 0.39 1.682 0zm0 6l5.952 -5c0.457 -0.397 0.444 -1.03 -0.03 -1.414 -0.46 -0.374 -1.192 -0.374 -1.653 0l-5.11 4.293 -5.11 -4.293c-0.457 -0.397 -1.21 -0.408 -1.683 -0.025 -0.472 0.384 -0.485 1.017 -0.03 1.414l0.03 0.025 5.952 5c0.465 0.39 1.217 0.39 1.682 0z"/>
+    <path d="M9.826 22.559l5.84 -5c0.449 -0.397 0.436 -1.03 -0.03 -1.414 -0.451 -0.374 -1.168 -0.374 -1.622 0l-5.014 4.293 -5.014 -4.293c-0.449 -0.397 -1.188 -0.408 -1.652 -0.025 -0.464 0.384 -0.477 1.017 -0.03 1.414l0.03 0.025 5.84 5c0.457 0.39 1.195 0.39 1.652 0zm0 6l5.84 -5c0.449 -0.397 0.436 -1.03 -0.03 -1.414 -0.451 -0.374 -1.168 -0.374 -1.622 0l-5.014 4.293 -5.014 -4.293c-0.449 -0.397 -1.188 -0.408 -1.652 -0.025 -0.464 0.384 -0.477 1.017 -0.03 1.414l0.03 0.025 5.84 5c0.457 0.39 1.195 0.39 1.652 0z"/>
   </svg>
   <svg x="630">
-    <path d="M9.855 27.317l6.05 -5c0.465 -0.397 0.451 -1.03 -0.03 -1.414 -0.469 -0.374 -1.211 -0.374 -1.68 0l-5.195 4.293 -5.194 -4.293c-0.465 -0.397 -1.23 -0.408 -1.711 -0.025 -0.48 0.384 -0.494 1.017 -0.03 1.414l0.03 0.025 6.05 5c0.473 0.39 1.237 0.39 1.71 0zm0 6l6.05 -5c0.465 -0.397 0.451 -1.03 -0.03 -1.414 -0.469 -0.374 -1.211 -0.374 -1.68 0l-5.195 4.293 -5.194 -4.293c-0.465 -0.397 -1.23 -0.408 -1.711 -0.025 -0.48 0.384 -0.494 1.017 -0.03 1.414l0.03 0.025 6.05 5c0.473 0.39 1.237 0.39 1.71 0z"/>
+    <path d="M9.841 22.707l5.952 -5c0.457 -0.397 0.444 -1.03 -0.03 -1.414 -0.46 -0.374 -1.192 -0.374 -1.653 0l-5.11 4.293 -5.11 -4.293c-0.457 -0.397 -1.21 -0.408 -1.683 -0.025 -0.472 0.384 -0.485 1.017 -0.03 1.414l0.03 0.025 5.952 5c0.465 0.39 1.217 0.39 1.682 0zm0 6l5.952 -5c0.457 -0.397 0.444 -1.03 -0.03 -1.414 -0.46 -0.374 -1.192 -0.374 -1.653 0l-5.11 4.293 -5.11 -4.293c-0.457 -0.397 -1.21 -0.408 -1.683 -0.025 -0.472 0.384 -0.485 1.017 -0.03 1.414l0.03 0.025 5.952 5c0.465 0.39 1.217 0.39 1.682 0z"/>
   </svg>
   <svg x="648">
-    <path d="M9.867 26.387l6.132 -5c0.471 -0.397 0.458 -1.03 -0.03 -1.414 -0.475 -0.374 -1.228 -0.374 -1.704 0l-5.265 4.293 -5.265 -4.293c-0.471 -0.397 -1.247 -0.408 -1.734 -0.025 -0.487 0.384 -0.5 1.017 -0.03 1.414 0.009 0.009 0.02 0.017 0.03 0.025l6.132 5c0.48 0.39 1.255 0.39 1.734 0zm0 6l6.132 -5c0.471 -0.397 0.458 -1.03 -0.03 -1.414 -0.475 -0.374 -1.228 -0.374 -1.704 0l-5.265 4.293 -5.265 -4.293c-0.471 -0.397 -1.247 -0.408 -1.734 -0.025 -0.487 0.384 -0.5 1.017 -0.03 1.414 0.009 0.009 0.02 0.017 0.03 0.025l6.132 5c0.48 0.39 1.255 0.39 1.734 0z"/>
+    <path d="M9.855 22.29l6.05 -5c0.465 -0.396 0.451 -1.03 -0.03 -1.413 -0.469 -0.374 -1.211 -0.374 -1.68 0l-5.195 4.293 -5.194 -4.293c-0.465 -0.397 -1.23 -0.408 -1.711 -0.025 -0.48 0.384 -0.494 1.017 -0.03 1.414l0.03 0.025 6.05 5c0.473 0.39 1.237 0.39 1.71 0zm0 6l6.05 -5c0.465 -0.396 0.451 -1.03 -0.03 -1.413 -0.469 -0.374 -1.211 -0.374 -1.68 0l-5.195 4.293 -5.194 -4.293c-0.465 -0.397 -1.23 -0.408 -1.711 -0.025 -0.48 0.384 -0.494 1.017 -0.03 1.414l0.03 0.025 6.05 5c0.473 0.39 1.237 0.39 1.71 0z"/>
   </svg>
   <svg x="666">
-    <path d="M9.876 25.277l6.195 -5c0.476 -0.397 0.462 -1.03 -0.03 -1.414 -0.48 -0.374 -1.241 -0.374 -1.722 0l-5.319 4.293 -5.32 -4.293c-0.475 -0.397 -1.26 -0.408 -1.751 -0.025 -0.492 0.384 -0.506 1.017 -0.031 1.414 0.01 0.01 0.02 0.017 0.03 0.025l6.196 5c0.484 0.39 1.268 0.39 1.752 0zm0 6l6.195 -5c0.476 -0.397 0.462 -1.03 -0.03 -1.414 -0.48 -0.374 -1.241 -0.374 -1.722 0l-5.319 4.293 -5.32 -4.293c-0.475 -0.397 -1.26 -0.408 -1.751 -0.025 -0.492 0.384 -0.506 1.017 -0.031 1.414 0.01 0.01 0.02 0.017 0.03 0.025l6.196 5c0.484 0.39 1.268 0.39 1.752 0z"/>
+    <path d="M9.867 21.299l6.132 -5c0.471 -0.397 0.458 -1.03 -0.03 -1.414 -0.475 -0.374 -1.228 -0.374 -1.704 0l-5.265 4.293 -5.265 -4.293c-0.471 -0.397 -1.247 -0.408 -1.734 -0.025 -0.487 0.384 -0.5 1.017 -0.03 1.414 0.009 0.009 0.02 0.017 0.03 0.025l6.132 5c0.48 0.39 1.255 0.39 1.734 0zm0 6l6.132 -5c0.471 -0.397 0.458 -1.03 -0.03 -1.414 -0.475 -0.374 -1.228 -0.374 -1.704 0l-5.265 4.293 -5.265 -4.293c-0.471 -0.397 -1.247 -0.408 -1.734 -0.025 -0.487 0.384 -0.5 1.017 -0.03 1.414 0.009 0.009 0.02 0.017 0.03 0.025l6.132 5c0.48 0.39 1.255 0.39 1.734 0z"/>
   </svg>
   <svg x="684">
-    <path d="M9.882 24.347l6.235 -5c0.48 -0.397 0.466 -1.03 -0.03 -1.414 -0.483 -0.374 -1.25 -0.374 -1.733 0l-5.354 4.293 -5.354 -4.293c-0.479 -0.397 -1.268 -0.408 -1.763 -0.025 -0.496 0.384 -0.51 1.017 -0.032 1.414 0.01 0.01 0.022 0.017 0.032 0.025l6.235 5c0.488 0.39 1.276 0.39 1.764 0zm0 6l6.235 -5c0.48 -0.397 0.466 -1.03 -0.03 -1.414 -0.483 -0.374 -1.25 -0.374 -1.733 0l-5.354 4.293 -5.354 -4.293c-0.479 -0.397 -1.268 -0.408 -1.763 -0.025 -0.496 0.384 -0.51 1.017 -0.032 1.414 0.01 0.01 0.022 0.017 0.032 0.025l6.235 5c0.488 0.39 1.276 0.39 1.764 0z"/>
+    <path d="M9.876 20.115l6.195 -5c0.476 -0.397 0.462 -1.03 -0.03 -1.414 -0.48 -0.374 -1.241 -0.374 -1.722 0l-5.319 4.293 -5.32 -4.293c-0.475 -0.397 -1.26 -0.408 -1.751 -0.025 -0.492 0.384 -0.506 1.017 -0.031 1.414 0.01 0.01 0.02 0.017 0.03 0.025l6.196 5c0.484 0.39 1.268 0.39 1.752 0zm0 6l6.195 -5c0.476 -0.397 0.462 -1.03 -0.03 -1.414 -0.48 -0.374 -1.241 -0.374 -1.722 0l-5.319 4.293 -5.32 -4.293c-0.475 -0.397 -1.26 -0.408 -1.751 -0.025 -0.492 0.384 -0.506 1.017 -0.031 1.414 0.01 0.01 0.02 0.017 0.03 0.025l6.196 5c0.484 0.39 1.268 0.39 1.752 0z"/>
   </svg>
   <svg x="702">
-    <path d="M9.884 23.957l6.25 -5c0.48 -0.397 0.466 -1.03 -0.032 -1.414 -0.483 -0.374 -1.25 -0.374 -1.736 0l-5.366 4.293 -5.366 -4.293c-0.48 -0.397 -1.272 -0.408 -1.768 -0.025 -0.496 0.384 -0.51 1.017 -0.031 1.414l0.031 0.025 6.25 5c0.489 0.39 1.279 0.39 1.768 0zm0 6l6.25 -5c0.48 -0.397 0.466 -1.03 -0.032 -1.414 -0.483 -0.374 -1.25 -0.374 -1.736 0l-5.366 4.293 -5.366 -4.293c-0.48 -0.397 -1.272 -0.408 -1.768 -0.025 -0.496 0.384 -0.51 1.017 -0.031 1.414l0.031 0.025 6.25 5c0.489 0.39 1.279 0.39 1.768 0z"/>
+    <path d="M9.882 19.123l6.235 -5c0.48 -0.397 0.466 -1.03 -0.03 -1.414 -0.483 -0.374 -1.25 -0.374 -1.733 0l-5.354 4.293 -5.354 -4.293c-0.479 -0.397 -1.268 -0.408 -1.763 -0.025 -0.496 0.384 -0.51 1.017 -0.032 1.414 0.01 0.01 0.022 0.017 0.032 0.025l6.235 5c0.488 0.39 1.276 0.39 1.764 0zm0 6l6.235 -5c0.48 -0.397 0.466 -1.03 -0.03 -1.414 -0.483 -0.374 -1.25 -0.374 -1.733 0l-5.354 4.293 -5.354 -4.293c-0.479 -0.397 -1.268 -0.408 -1.763 -0.025 -0.496 0.384 -0.51 1.017 -0.032 1.414 0.01 0.01 0.022 0.017 0.032 0.025l6.235 5c0.488 0.39 1.276 0.39 1.764 0z"/>
   </svg>
   <svg x="720">
-    <path d="M9.862 23.957l6.098 -5c0.468 -0.397 0.454 -1.03 -0.03 -1.414 -0.473 -0.374 -1.222 -0.374 -1.695 0l-5.235 4.293 -5.235 -4.293c-0.468 -0.397 -1.24 -0.408 -1.725 -0.025 -0.484 0.384 -0.497 1.017 -0.03 1.414l0.03 0.025 6.098 5c0.477 0.39 1.247 0.39 1.724 0zm0 6l6.098 -5c0.468 -0.397 0.454 -1.03 -0.03 -1.414 -0.473 -0.374 -1.222 -0.374 -1.695 0l-5.235 4.293 -5.235 -4.293c-0.468 -0.397 -1.24 -0.408 -1.725 -0.025 -0.484 0.384 -0.497 1.017 -0.03 1.414l0.03 0.025 6.098 5c0.477 0.39 1.247 0.39 1.724 0z"/>
+    <path d="M9.884 18.707l6.25 -5c0.48 -0.397 0.466 -1.03 -0.032 -1.414 -0.483 -0.374 -1.25 -0.374 -1.736 0l-5.366 4.293 -5.366 -4.293c-0.48 -0.397 -1.272 -0.408 -1.768 -0.025 -0.496 0.384 -0.51 1.017 -0.031 1.414l0.031 0.025 6.25 5c0.489 0.39 1.279 0.39 1.768 0zm0 6l6.25 -5c0.48 -0.397 0.466 -1.03 -0.032 -1.414 -0.483 -0.374 -1.25 -0.374 -1.736 0l-5.366 4.293 -5.366 -4.293c-0.48 -0.397 -1.272 -0.408 -1.768 -0.025 -0.496 0.384 -0.51 1.017 -0.031 1.414l0.031 0.025 6.25 5c0.489 0.39 1.279 0.39 1.768 0z"/>
   </svg>
   <svg x="738">
-    <path d="M9.838 23.957l5.924 -5c0.455 -0.397 0.442 -1.03 -0.03 -1.414 -0.458 -0.374 -1.186 -0.374 -1.645 0l-5.087 4.293 -5.087 -4.293c-0.455 -0.397 -1.205 -0.408 -1.675 -0.025 -0.47 0.384 -0.483 1.017 -0.03 1.414l0.03 0.025 5.924 5c0.464 0.39 1.212 0.39 1.676 0zm0 6l5.924 -5c0.455 -0.397 0.442 -1.03 -0.03 -1.414 -0.458 -0.374 -1.186 -0.374 -1.645 0l-5.087 4.293 -5.087 -4.293c-0.455 -0.397 -1.205 -0.408 -1.675 -0.025 -0.47 0.384 -0.483 1.017 -0.03 1.414l0.03 0.025 5.924 5c0.464 0.39 1.212 0.39 1.676 0z"/>
+    <path d="M9.862 18.707l6.098 -5c0.468 -0.397 0.454 -1.03 -0.03 -1.414 -0.473 -0.374 -1.222 -0.374 -1.695 0l-5.235 4.293 -5.235 -4.293c-0.468 -0.397 -1.24 -0.408 -1.725 -0.025 -0.484 0.384 -0.497 1.017 -0.03 1.414l0.03 0.025 6.098 5c0.477 0.39 1.247 0.39 1.724 0zm0 6l6.098 -5c0.468 -0.397 0.454 -1.03 -0.03 -1.414 -0.473 -0.374 -1.222 -0.374 -1.695 0l-5.235 4.293 -5.235 -4.293c-0.468 -0.397 -1.24 -0.408 -1.725 -0.025 -0.484 0.384 -0.497 1.017 -0.03 1.414l0.03 0.025 6.098 5c0.477 0.39 1.247 0.39 1.724 0z"/>
   </svg>
   <svg x="756">
-    <path d="M9.812 23.957l5.74 -5c0.441 -0.397 0.429 -1.03 -0.028 -1.414 -0.444 -0.374 -1.15 -0.374 -1.595 0l-4.929 4.293 -4.93 -4.293c-0.44 -0.397 -1.167 -0.408 -1.623 -0.025 -0.455 0.384 -0.468 1.017 -0.028 1.414l0.028 0.025 5.741 5c0.45 0.39 1.175 0.39 1.624 0zm0 6l5.74 -5c0.441 -0.397 0.429 -1.03 -0.028 -1.414 -0.444 -0.374 -1.15 -0.374 -1.595 0l-4.929 4.293 -4.93 -4.293c-0.44 -0.397 -1.167 -0.408 -1.623 -0.025 -0.455 0.384 -0.468 1.017 -0.028 1.414l0.028 0.025 5.741 5c0.45 0.39 1.175 0.39 1.624 0z"/>
+    <path d="M9.838 18.707l5.924 -5c0.455 -0.397 0.442 -1.03 -0.03 -1.414 -0.458 -0.374 -1.186 -0.374 -1.645 0l-5.087 4.293 -5.087 -4.293c-0.455 -0.397 -1.205 -0.408 -1.675 -0.025 -0.47 0.384 -0.483 1.017 -0.03 1.414l0.03 0.025 5.924 5c0.464 0.39 1.212 0.39 1.676 0zm0 6l5.924 -5c0.455 -0.397 0.442 -1.03 -0.03 -1.414 -0.458 -0.374 -1.186 -0.374 -1.645 0l-5.087 4.293 -5.087 -4.293c-0.455 -0.397 -1.205 -0.408 -1.675 -0.025 -0.47 0.384 -0.483 1.017 -0.03 1.414l0.03 0.025 5.924 5c0.464 0.39 1.212 0.39 1.676 0z"/>
   </svg>
   <svg x="774">
-    <path d="M9.786 23.957l5.557 -5c0.427 -0.397 0.415 -1.03 -0.028 -1.414 -0.43 -0.374 -1.112 -0.374 -1.543 0l-4.772 4.293 -4.772 -4.293c-0.426 -0.397 -1.13 -0.408 -1.571 -0.025 -0.441 0.384 -0.454 1.017 -0.028 1.414l0.028 0.025 5.557 5c0.435 0.39 1.137 0.39 1.572 0zm0 6l5.557 -5c0.427 -0.397 0.415 -1.03 -0.028 -1.414 -0.43 -0.374 -1.112 -0.374 -1.543 0l-4.772 4.293 -4.772 -4.293c-0.426 -0.397 -1.13 -0.408 -1.571 -0.025 -0.441 0.384 -0.454 1.017 -0.028 1.414l0.028 0.025 5.557 5c0.435 0.39 1.137 0.39 1.572 0z"/>
+    <path d="M9.812 18.707l5.74 -5c0.441 -0.397 0.429 -1.03 -0.028 -1.414 -0.444 -0.374 -1.15 -0.374 -1.595 0l-4.929 4.293 -4.93 -4.293c-0.44 -0.397 -1.167 -0.408 -1.623 -0.025 -0.455 0.384 -0.468 1.017 -0.028 1.414l0.028 0.025 5.741 5c0.45 0.39 1.175 0.39 1.624 0zm0 6l5.74 -5c0.441 -0.397 0.429 -1.03 -0.028 -1.414 -0.444 -0.374 -1.15 -0.374 -1.595 0l-4.929 4.293 -4.93 -4.293c-0.44 -0.397 -1.167 -0.408 -1.623 -0.025 -0.455 0.384 -0.468 1.017 -0.028 1.414l0.028 0.025 5.741 5c0.45 0.39 1.175 0.39 1.624 0z"/>
   </svg>
   <svg x="792">
-    <path d="M9.761 23.957l5.385 -5c0.413 -0.397 0.401 -1.03 -0.027 -1.414 -0.417 -0.374 -1.078 -0.374 -1.496 0l-4.623 4.293 -4.623 -4.293c-0.413 -0.397 -1.095 -0.408 -1.523 -0.025 -0.427 0.384 -0.439 1.017 -0.027 1.414l0.027 0.025 5.385 5c0.42 0.39 1.101 0.39 1.522 0zm0 6l5.385 -5c0.413 -0.397 0.401 -1.03 -0.027 -1.414 -0.417 -0.374 -1.078 -0.374 -1.496 0l-4.623 4.293 -4.623 -4.293c-0.413 -0.397 -1.095 -0.408 -1.523 -0.025 -0.427 0.384 -0.439 1.017 -0.027 1.414l0.027 0.025 5.385 5c0.42 0.39 1.101 0.39 1.522 0z"/>
+    <path d="M9.786 18.707l5.557 -5c0.427 -0.397 0.415 -1.03 -0.028 -1.414 -0.43 -0.374 -1.112 -0.374 -1.543 0l-4.772 4.293 -4.772 -4.293c-0.426 -0.397 -1.13 -0.408 -1.571 -0.025 -0.441 0.384 -0.454 1.017 -0.028 1.414l0.028 0.025 5.557 5c0.435 0.39 1.137 0.39 1.572 0zm0 6l5.557 -5c0.427 -0.397 0.415 -1.03 -0.028 -1.414 -0.43 -0.374 -1.112 -0.374 -1.543 0l-4.772 4.293 -4.772 -4.293c-0.426 -0.397 -1.13 -0.408 -1.571 -0.025 -0.441 0.384 -0.454 1.017 -0.028 1.414l0.028 0.025 5.557 5c0.435 0.39 1.137 0.39 1.572 0z"/>
   </svg>
   <svg x="810">
-    <path d="M9.74 23.957l5.231 -5a0.968 0.968 0 0 0 -0.026 -1.414 1.082 1.082 0 0 0 -1.453 0l-4.492 4.293 -4.492 -4.293a1.082 1.082 0 0 0 -1.48 -0.025 0.97 0.97 0 0 0 -0.026 1.414l0.027 0.025 5.231 5a1.082 1.082 0 0 0 1.48 0zm0 6l5.231 -5a0.968 0.968 0 0 0 -0.026 -1.414 1.082 1.082 0 0 0 -1.453 0l-4.492 4.293 -4.492 -4.293a1.082 1.082 0 0 0 -1.48 -0.025 0.97 0.97 0 0 0 -0.026 1.414l0.027 0.025 5.231 5a1.082 1.082 0 0 0 1.48 0z"/>
+    <path d="M9.761 18.707l5.385 -5c0.413 -0.397 0.401 -1.03 -0.027 -1.414 -0.417 -0.374 -1.078 -0.374 -1.496 0l-4.623 4.293 -4.623 -4.293c-0.413 -0.397 -1.095 -0.408 -1.523 -0.025 -0.427 0.384 -0.439 1.017 -0.027 1.414l0.027 0.025 5.385 5c0.42 0.39 1.101 0.39 1.522 0zm0 6l5.385 -5c0.413 -0.397 0.401 -1.03 -0.027 -1.414 -0.417 -0.374 -1.078 -0.374 -1.496 0l-4.623 4.293 -4.623 -4.293c-0.413 -0.397 -1.095 -0.408 -1.523 -0.025 -0.427 0.384 -0.439 1.017 -0.027 1.414l0.027 0.025 5.385 5c0.42 0.39 1.101 0.39 1.522 0z"/>
   </svg>
   <svg x="828">
-    <path d="M9.723 23.957l5.11 -5a0.984 0.984 0 0 0 -0.026 -1.414 1.038 1.038 0 0 0 -1.42 0l-4.387 4.293 -4.387 -4.293a1.039 1.039 0 0 0 -1.445 -0.025 0.986 0.986 0 0 0 -0.026 1.414l0.026 0.025 5.11 5a1.039 1.039 0 0 0 1.445 0zm0 6l5.11 -5a0.984 0.984 0 0 0 -0.026 -1.414 1.038 1.038 0 0 0 -1.42 0l-4.387 4.293 -4.387 -4.293a1.039 1.039 0 0 0 -1.445 -0.025 0.986 0.986 0 0 0 -0.026 1.414l0.026 0.025 5.11 5a1.039 1.039 0 0 0 1.445 0z"/>
+    <path d="M9.74 18.707l5.231 -5a0.968 0.968 0 0 0 -0.026 -1.414 1.082 1.082 0 0 0 -1.453 0l-4.492 4.293 -4.492 -4.293a1.082 1.082 0 0 0 -1.48 -0.025 0.97 0.97 0 0 0 -0.026 1.414l0.027 0.025 5.231 5a1.082 1.082 0 0 0 1.48 0zm0 6l5.231 -5a0.968 0.968 0 0 0 -0.026 -1.414 1.082 1.082 0 0 0 -1.453 0l-4.492 4.293 -4.492 -4.293a1.082 1.082 0 0 0 -1.48 -0.025 0.97 0.97 0 0 0 -0.026 1.414l0.027 0.025 5.231 5a1.082 1.082 0 0 0 1.48 0z"/>
   </svg>
   <svg x="846">
-    <path d="M9.711 23.957l5.03 -5a0.995 0.995 0 0 0 -0.026 -1.414 1.01 1.01 0 0 0 -1.397 0l-4.318 4.293 -4.318 -4.293a1.01 1.01 0 0 0 -1.422 -0.025 0.997 0.997 0 0 0 -0.025 1.414l0.025 0.025 5.029 5a1.01 1.01 0 0 0 1.422 0zm0 6l5.03 -5a0.995 0.995 0 0 0 -0.026 -1.414 1.01 1.01 0 0 0 -1.397 0l-4.318 4.293 -4.318 -4.293a1.01 1.01 0 0 0 -1.422 -0.025 0.997 0.997 0 0 0 -0.025 1.414l0.025 0.025 5.029 5a1.01 1.01 0 0 0 1.422 0z"/>
+    <path d="M9.723 18.707l5.11 -5a0.984 0.984 0 0 0 -0.026 -1.414 1.038 1.038 0 0 0 -1.42 0l-4.387 4.293 -4.387 -4.293a1.039 1.039 0 0 0 -1.445 -0.025 0.986 0.986 0 0 0 -0.026 1.414l0.026 0.025 5.11 5a1.039 1.039 0 0 0 1.445 0zm0 6l5.11 -5a0.984 0.984 0 0 0 -0.026 -1.414 1.038 1.038 0 0 0 -1.42 0l-4.387 4.293 -4.387 -4.293a1.039 1.039 0 0 0 -1.445 -0.025 0.986 0.986 0 0 0 -0.026 1.414l0.026 0.025 5.11 5a1.039 1.039 0 0 0 1.445 0z"/>
   </svg>
   <svg x="864">
-    <path d="M9.707 23.957l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0zm0 6l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0z"/>
+    <path d="M9.711 18.707l5.03 -5a0.995 0.995 0 0 0 -0.026 -1.414 1.01 1.01 0 0 0 -1.397 0l-4.318 4.293 -4.318 -4.293a1.01 1.01 0 0 0 -1.422 -0.025 0.997 0.997 0 0 0 -0.025 1.414l0.025 0.025 5.029 5a1.01 1.01 0 0 0 1.422 0zm0 6l5.03 -5a0.995 0.995 0 0 0 -0.026 -1.414 1.01 1.01 0 0 0 -1.397 0l-4.318 4.293 -4.318 -4.293a1.01 1.01 0 0 0 -1.422 -0.025 0.997 0.997 0 0 0 -0.025 1.414l0.025 0.025 5.029 5a1.01 1.01 0 0 0 1.422 0z"/>
   </svg>
   <svg x="882">
-    <path d="M9.707 23.957l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0zm0 6l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0z"/>
+    <path d="M9.707 18.707l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0zm0 6l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0z"/>
   </svg>
   <svg x="900">
-    <path d="M9.707 23.957l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0zm0 6l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0z"/>
+    <path d="M9.707 18.707l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0zm0 6l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0z"/>
   </svg>
   <svg x="918">
-    <path d="M9.707 23.957l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0zm0 6l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0z"/>
+    <path d="M9.707 18.707l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0zm0 6l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0z"/>
   </svg>
   <svg x="936">
-    <path d="M9.707 23.957l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0zm0 6l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0z"/>
+    <path d="M9.707 18.707l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0zm0 6l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0z"/>
   </svg>
   <svg x="954">
-    <path d="M9.707 23.957l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0zm0 6l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0z"/>
+    <path d="M9.707 18.707l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0zm0 6l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0z"/>
   </svg>
   <svg x="972">
-    <path d="M9.707 23.957l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0zm0 6l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0z"/>
+    <path d="M9.707 18.707l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0zm0 6l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0z"/>
   </svg>
   <svg x="990">
-    <path d="M9.707 23.957l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0zm0 6l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0z"/>
+    <path d="M9.707 18.707l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0zm0 6l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0z"/>
   </svg>
   <svg x="1008">
-    <path d="M9.707 23.957l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0zm0 6l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0z"/>
+    <path d="M9.707 18.707l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0zm0 6l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0z"/>
   </svg>
   <svg x="1026">
-    <path d="M9.707 23.957l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0zm0 6l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0z"/>
+    <path d="M9.707 18.707l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0zm0 6l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0z"/>
   </svg>
   <svg x="1044">
-    <path d="M9.707 23.957l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0zm0 6l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0z"/>
+    <path d="M9.707 18.707l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0zm0 6l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0z"/>
   </svg>
   <svg x="1062">
-    <path d="M9.707 23.957l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0zm0 6l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0z"/>
+    <path d="M9.707 18.707l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0zm0 6l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0z"/>
   </svg>
   <svg x="1080">
-    <path d="M9.737 23.778l4.775 -5.215a1 1 0 1 0 -1.475 -1.35l-4.1 4.477 -4.477 -4.1a1 1 0 0 0 -1.414 0.038 1 1 0 0 0 0.037 1.413l0.027 0.024 5.215 4.775a1 1 0 0 0 1.412 -0.062zm0.265 5.994l4.775 -5.215a1 1 0 1 0 -1.475 -1.35l-4.1 4.477 -4.478 -4.1a1 1 0 0 0 -1.414 0.038 1 1 0 0 0 0.038 1.414l0.026 0.023 5.215 4.775a1 1 0 0 0 1.413 -0.062z"/>
+    <path d="M9.707 18.707l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0zm0 6l5 -5a1 1 0 1 0 -1.414 -1.414l-4.293 4.293 -4.293 -4.293a1 1 0 0 0 -1.414 -0.025 1 1 0 0 0 -0.025 1.414l0.025 0.025 5 5a1 1 0 0 0 1.414 0z"/>
   </svg>
   <svg x="1098">
-    <path d="M9.813 23.286l4.12 -5.747a1 1 0 1 0 -1.625 -1.165l-3.538 4.934 -4.935 -3.537a1 1 0 0 0 -1.399 0.206 1 1 0 0 0 0.206 1.4c0.009 0.007 0.02 0.013 0.028 0.02l5.748 4.12a1 1 0 0 0 1.395 -0.231zm0.976 5.92l4.12 -5.747a1 1 0 1 0 -1.625 -1.165l-3.537 4.934 -4.935 -3.537a1 1 0 0 0 -1.4 0.206 1 1 0 0 0 0.206 1.4l0.029 0.02 5.747 4.12a1 1 0 0 0 1.395 -0.231z"/>
+    <path d="M9.737 18.675l4.775 -5.215a1 1 0 1 0 -1.475 -1.35l-4.1 4.477 -4.477 -4.1a1 1 0 0 0 -1.414 0.038 1 1 0 0 0 0.037 1.414l0.027 0.023 5.215 4.775a1 1 0 0 0 1.412 -0.062zm0.265 5.994l4.775 -5.215a1 1 0 1 0 -1.475 -1.35l-4.1 4.478 -4.478 -4.1a1 1 0 0 0 -1.414 0.037 1 1 0 0 0 0.038 1.414l0.026 0.024 5.215 4.775a1 1 0 0 0 1.413 -0.063z"/>
   </svg>
   <svg x="1116">
-    <path d="M9.902 22.547l3.05 -6.38a1 1 0 1 0 -1.804 -0.862l-2.619 5.477 -5.477 -2.618a1 1 0 0 0 -1.342 0.447 1 1 0 0 0 0.447 1.341l0.032 0.016 6.38 3.05a1 1 0 0 0 1.333 -0.471zm1.998 5.657l3.05 -6.38a1 1 0 1 0 -1.804 -0.862l-2.619 5.478 -5.477 -2.619a1 1 0 0 0 -1.342 0.447 1 1 0 0 0 0.447 1.342c0.01 0.006 0.022 0.01 0.032 0.015l6.38 3.05a1 1 0 0 0 1.333 -0.47z"/>
+    <path d="M9.813 18.582l4.12 -5.747a1 1 0 1 0 -1.625 -1.165l-3.538 4.935 -4.935 -3.537a1 1 0 0 0 -1.399 0.205 1 1 0 0 0 0.206 1.4l0.028 0.02 5.748 4.12a1 1 0 0 0 1.395 -0.23zm0.976 5.92l4.12 -5.747a1 1 0 1 0 -1.625 -1.165l-3.537 4.935 -4.935 -3.537a1 1 0 0 0 -1.4 0.205 1 1 0 0 0 0.206 1.4l0.029 0.02 5.747 4.12a1 1 0 0 0 1.395 -0.23z"/>
   </svg>
   <svg x="1134">
-    <path d="M9.973 21.632l1.628 -6.881a1 1 0 1 0 -1.946 -0.46l-1.398 5.907 -5.908 -1.398a1 1 0 0 0 -1.216 0.722 1 1 0 0 0 0.721 1.216c0.012 0.004 0.024 0.006 0.035 0.008l6.88 1.629a1 1 0 0 0 1.204 -0.743zm3.152 5.106l1.628 -6.882a1 1 0 1 0 -1.946 -0.46l-1.398 5.908 -5.908 -1.398a1 1 0 0 0 -1.217 0.721 1 1 0 0 0 0.722 1.217l0.034 0.008 6.881 1.628a1 1 0 0 0 1.204 -0.742z"/>
+    <path d="M9.902 18.431l3.05 -6.38a1 1 0 1 0 -1.804 -0.862l-2.619 5.478 -5.477 -2.619a1 1 0 0 0 -1.342 0.447 1 1 0 0 0 0.447 1.342c0.01 0.006 0.022 0.01 0.032 0.015l6.38 3.05a1 1 0 0 0 1.333 -0.47zm1.998 5.658l3.05 -6.38a1 1 0 1 0 -1.804 -0.862l-2.619 5.477 -5.477 -2.618a1 1 0 0 0 -1.342 0.447 1 1 0 0 0 0.447 1.342l0.032 0.015 6.38 3.05a1 1 0 0 0 1.333 -0.471z"/>
   </svg>
   <svg x="1152">
-    <path d="M10 20.625v-7.071a1 1 0 1 0 -2 0v6.071h-6.071a1 1 0 0 0 -1.018 0.982 1 1 0 0 0 0.983 1.018h7.106a1 1 0 0 0 1 -1zm4.242 4.243v-7.071a1 1 0 1 0 -2 0v6.07h-6.07a1 1 0 0 0 -1.018 0.983 1 1 0 0 0 0.982 1.017h7.106a1 1 0 0 0 1 -1z"/>
+    <path d="M9.973 18.23l1.628 -6.88a1 1 0 1 0 -1.946 -0.46l-1.398 5.907 -5.908 -1.398a1 1 0 0 0 -1.216 0.721 1 1 0 0 0 0.721 1.216l0.035 0.009 6.88 1.628a1 1 0 0 0 1.204 -0.743zm3.152 5.106l1.628 -6.881a1 1 0 1 0 -1.946 -0.46l-1.398 5.907 -5.908 -1.398a1 1 0 0 0 -1.217 0.722 1 1 0 0 0 0.722 1.216c0.011 0.004 0.023 0.006 0.034 0.008l6.881 1.629a1 1 0 0 0 1.204 -0.743z"/>
   </svg>
   <svg x="1170">
-    <path d="M9.973 19.618l-1.628 -6.88a1 1 0 1 0 -1.946 0.46l1.398 5.908 -5.908 1.398a1 1 0 0 0 -0.764 1.19 1 1 0 0 0 1.19 0.764l0.034 -0.008 6.881 -1.629a1 1 0 0 0 0.743 -1.203zm5.106 3.152l-1.629 -6.881a1 1 0 1 0 -1.946 0.46l1.398 5.908 -5.908 1.398a1 1 0 0 0 -0.764 1.19 1 1 0 0 0 1.19 0.764l0.035 -0.008 6.88 -1.628a1 1 0 0 0 0.744 -1.203z"/>
+    <path d="M10 18v-7.071a1 1 0 1 0 -2 0v6.071h-6.071a1 1 0 0 0 -1.018 0.982 1 1 0 0 0 0.983 1.018h7.106a1 1 0 0 0 1 -1zm4.242 4.243v-7.071a1 1 0 1 0 -2 0v6.07h-6.07a1 1 0 0 0 -1.018 0.983 1 1 0 0 0 0.982 1.017h7.106a1 1 0 0 0 1 -1z"/>
   </svg>
   <svg x="1188">
-    <path d="M9.902 18.703l-3.05 -6.38a1 1 0 1 0 -1.804 0.862l2.619 5.479 -5.478 2.618a1 1 0 0 0 -0.494 1.325 1 1 0 0 0 1.325 0.495l0.032 -0.016 6.38 -3.05a1 1 0 0 0 0.47 -1.333zm5.658 1.998l-3.05 -6.38a1 1 0 1 0 -1.804 0.862l2.618 5.479 -5.477 2.618a1 1 0 0 0 -0.495 1.325 1 1 0 0 0 1.325 0.495l0.032 -0.016 6.38 -3.05a1 1 0 0 0 0.47 -1.333z"/>
+    <path d="M9.973 17.77l-1.628 -6.881a1 1 0 1 0 -1.946 0.46l1.398 5.908 -5.908 1.398a1 1 0 0 0 -0.764 1.19 1 1 0 0 0 1.19 0.764l0.034 -0.008 6.881 -1.628a1 1 0 0 0 0.743 -1.203zm5.106 3.151l-1.629 -6.88a1 1 0 1 0 -1.946 0.46l1.398 5.908 -5.908 1.398a1 1 0 0 0 -0.764 1.19 1 1 0 0 0 1.19 0.764l0.035 -0.008 6.88 -1.628a1 1 0 0 0 0.744 -1.204z"/>
   </svg>
   <svg x="1206">
-    <path d="M9.813 17.964l-4.12 -5.747a1 1 0 1 0 -1.625 1.165l3.537 4.934 -4.935 3.537a1 1 0 0 0 -0.255 1.391 1 1 0 0 0 1.392 0.255l0.028 -0.02 5.747 -4.12a1 1 0 0 0 0.23 -1.395zm5.92 0.977l-4.12 -5.748a1 1 0 1 0 -1.625 1.165l3.537 4.935 -4.935 3.537a1 1 0 0 0 -0.255 1.391 1 1 0 0 0 1.392 0.255l0.028 -0.02 5.747 -4.12a1 1 0 0 0 0.23 -1.395z"/>
+    <path d="M9.902 17.569l-3.05 -6.38a1 1 0 1 0 -1.804 0.862l2.619 5.478 -5.478 2.619a1 1 0 0 0 -0.494 1.325 1 1 0 0 0 1.325 0.494l0.032 -0.015 6.38 -3.05a1 1 0 0 0 0.47 -1.333zm5.658 1.998l-3.05 -6.38a1 1 0 1 0 -1.804 0.862l2.618 5.478 -5.477 2.619a1 1 0 0 0 -0.495 1.325 1 1 0 0 0 1.325 0.494l0.032 -0.015 6.38 -3.05a1 1 0 0 0 0.47 -1.333z"/>
   </svg>
   <svg x="1224">
-    <path d="M9.737 17.472l-4.775 -5.215a1 1 0 1 0 -1.475 1.35l4.1 4.478 -4.477 4.1a1 1 0 0 0 -0.088 1.411 1 1 0 0 0 1.412 0.088l0.026 -0.024 5.215 -4.775a1 1 0 0 0 0.062 -1.413zm5.995 0.264l-4.775 -5.215a1 1 0 1 0 -1.475 1.35l4.1 4.478 -4.478 4.1a1 1 0 0 0 -0.088 1.411 1 1 0 0 0 1.412 0.088l0.026 -0.024 5.215 -4.775a1 1 0 0 0 0.063 -1.413z"/>
+    <path d="M9.813 17.418l-4.12 -5.748a1 1 0 1 0 -1.625 1.165l3.537 4.935 -4.935 3.537a1 1 0 0 0 -0.255 1.39 1 1 0 0 0 1.392 0.256l0.028 -0.02 5.747 -4.12a1 1 0 0 0 0.23 -1.395zm5.92 0.976l-4.12 -5.747a1 1 0 1 0 -1.625 1.165l3.537 4.935 -4.935 3.536a1 1 0 0 0 -0.255 1.392 1 1 0 0 0 1.392 0.254l0.028 -0.02 5.747 -4.12a1 1 0 0 0 0.23 -1.395z"/>
   </svg>
   <svg x="1242">
-    <path d="M9.707 17.293l-5 -5a1 1 0 1 0 -1.414 1.414l4.293 4.293 -4.293 4.293a1 1 0 0 0 -0.025 1.414 1 1 0 0 0 1.414 0.025l0.025 -0.025 5 -5a1 1 0 0 0 0 -1.414zm6 0l-5 -5a1 1 0 1 0 -1.414 1.414l4.293 4.293 -4.293 4.293a1 1 0 0 0 -0.025 1.414 1 1 0 0 0 1.414 0.025l0.025 -0.025 5 -5a1 1 0 0 0 0 -1.414z"/>
+    <path d="M9.737 17.325l-4.775 -5.215a1 1 0 1 0 -1.475 1.35l4.1 4.478 -4.477 4.1a1 1 0 0 0 -0.088 1.411 1 1 0 0 0 1.412 0.087c0.01 -0.007 0.018 -0.016 0.026 -0.024l5.215 -4.775a1 1 0 0 0 0.062 -1.412zm5.995 0.264l-4.775 -5.215a1 1 0 1 0 -1.475 1.35l4.1 4.478 -4.478 4.1a1 1 0 0 0 -0.088 1.411 1 1 0 0 0 1.412 0.087c0.01 -0.007 0.018 -0.016 0.026 -0.023l5.215 -4.775a1 1 0 0 0 0.063 -1.413z"/>
   </svg>
   <svg x="1260">
     <path d="M9.707 17.293l-5 -5a1 1 0 1 0 -1.414 1.414l4.293 4.293 -4.293 4.293a1 1 0 0 0 -0.025 1.414 1 1 0 0 0 1.414 0.025l0.025 -0.025 5 -5a1 1 0 0 0 0 -1.414zm6 0l-5 -5a1 1 0 1 0 -1.414 1.414l4.293 4.293 -4.293 4.293a1 1 0 0 0 -0.025 1.414 1 1 0 0 0 1.414 0.025l0.025 -0.025 5 -5a1 1 0 0 0 0 -1.414z"/>
   </svg>
+  <svg x="1278">
+    <path d="M9.707 17.293l-5 -5a1 1 0 1 0 -1.414 1.414l4.293 4.293 -4.293 4.293a1 1 0 0 0 -0.025 1.414 1 1 0 0 0 1.414 0.025l0.025 -0.025 5 -5a1 1 0 0 0 0 -1.414zm6 0l-5 -5a1 1 0 1 0 -1.414 1.414l4.293 4.293 -4.293 4.293a1 1 0 0 0 -0.025 1.414 1 1 0 0 0 1.414 0.025l0.025 -0.025 5 -5a1 1 0 0 0 0 -1.414z"/>
+  </svg>
 </svg>
--- a/browser/themes/shared/incontentprefs/preferences.inc.css
+++ b/browser/themes/shared/incontentprefs/preferences.inc.css
@@ -110,35 +110,22 @@ caption > label {
 .subcategory {
   margin-top: 48px;
 }
 
 /* Category List */
 
 #categories {
   max-height: 100vh;
-  background-color: #fafafc;
 }
 
 #categories > scrollbox {
   overflow-x: hidden !important;
 }
 
-.category-name {
-  font-size: 1.45rem;
-}
-
-.category,
-.category:hover,
-.category[selected] {
-  background-color: transparent;
-  border-inline-start: initial;
-  padding-inline-start: 44px;
-}
-
 /**
  * We want the last category to always have non-0 getBoundingClientRect().bottom
  * so we can use the value to figure out the max-height of the list in
  * preferences.js, so use collapse instead of display: none; if it's hidden
  */
 #categories > .category[hidden="true"] {
   display: -moz-box;
   visibility: collapse;
--- a/browser/themes/shared/tabs.inc.css
+++ b/browser/themes/shared/tabs.inc.css
@@ -1,22 +1,20 @@
 %if 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/. */
 %endif
 
 :root {
   --tab-toolbar-navbar-overlap: 1px;
-/* Temporarily using the compact tab strip by default because of bug 1386964:
   --tab-min-height: 33px;
 }
 
 :root[uidensity=compact] {
-*/
   --tab-min-height: 29px;
 }
 
 :root[uidensity=touch] {
   --tab-min-height: 41px;
 }
 
 #tabbrowser-tabs,
@@ -307,16 +305,20 @@
   border-left-style: solid;
   border-right-style: solid;
   border-color: var(--tabs-border);
   background-color: var(--toolbar-bgcolor);
   background-image: var(--toolbar-bgimage);
   background-repeat: repeat-x;
 }
 
+:root:not([sizemode=normal]) .tabbrowser-tab[first-visible-tab] > .tab-stack > .tab-background[selected=true] {
+  border-inline-start-style: none;
+}
+
 .tab-line[selected=true] {
   background-color: Highlight;
 }
 
 /*
  * LightweightThemeConsumer will set the current lightweight theme's header
  * image to the lwt-header-image variable, used in each of the following rulesets.
  */
@@ -434,19 +436,16 @@
 }
 
 /* New tab button */
 
 .tabs-newtab-button,
 #TabsToolbar > #new-tab-button ,
 #TabsToolbar > toolbarpaletteitem > #new-tab-button {
   list-style-image: url(chrome://browser/skin/tabbrowser/newtab.svg);
-  -moz-context-properties: fill;
-  fill: currentColor;
-  color: inherit;
 }
 
 .tabs-newtab-button {
   width: 36px;
 }
 
 @media (min-resolution: 1.1dppx) {
   .tab-throbber[busy] {
@@ -457,18 +456,16 @@
     list-style-image: url("chrome://global/skin/icons/loading@2x.png");
   }
 }
 
 /* All tabs button and menupopup */
 
 #alltabs-button {
   list-style-image: url(chrome://browser/skin/arrow-dropdown-16.svg);
-  -moz-context-properties: fill;
-  fill: currentColor;
 }
 
 .alltabs-item > .menu-iconic-left > .menu-iconic-icon {
   list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.svg");
 }
 
 .alltabs-item[busy] > .menu-iconic-left > .menu-iconic-icon {
   list-style-image: url("chrome://global/skin/icons/loading.png");
--- a/browser/themes/shared/toolbarbutton-icons.inc.css
+++ b/browser/themes/shared/toolbarbutton-icons.inc.css
@@ -295,46 +295,47 @@ toolbar:not([brighttext]) #bookmarks-men
 #zoom-out-button {
   list-style-image: url("chrome://browser/skin/zoom-out.svg");
 }
 
 #zoom-in-button {
   list-style-image: url("chrome://browser/skin/zoom-in.svg");
 }
 
+#PlacesChevron,
 #nav-bar-overflow-button {
   list-style-image: url("chrome://browser/skin/chevron.svg");
 }
 
 #nav-bar-overflow-button[animate] > .toolbarbutton-icon {
   fill: transparent;
 }
 
 @keyframes overflow-animation {
   from {
     transform: translateX(0);
   }
   50% {
     fill: #30A3FF;
   }
   to {
-    transform: translateX(-1242px);
+    transform: translateX(-1260px);
     fill: #30A3FF;
   }
 }
 
 @keyframes overflow-animation-rtl {
   from {
     transform: scaleX(-1) translateX(0);
   }
   50% {
     fill: #30A3FF;
   }
   to {
-    transform: scaleX(-1) translateX(-1242px);
+    transform: scaleX(-1) translateX(-1260px);
     fill: #30A3FF;
   }
 }
 
 /* The animation is supposed to show the blue fill color for 520ms, then the
    fade to the toolbarbutton-fill color for the remaining 210ms. Thus with an
    animation-duration of 730ms, 71% is the point where we start the fade out. */
 @keyframes overflow-fade {
@@ -365,20 +366,20 @@ toolbar:not([brighttext]) #bookmarks-men
      is calculated and set during runtime by BrowserUtils.setToolbarButtonHeightProperty()
      before the animation begins. */
   height: var(--toolbarbutton-height);
   min-height: 36px; /* Minimum height must be equal to the height of the SVG sprite */
 }
 
 #nav-bar-overflow-button[animate] > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
   animation-name: overflow-animation;
-  animation-timing-function: steps(69);
-  animation-duration: 1150ms;
+  animation-timing-function: steps(70);
+  animation-duration: 1167ms;
   background-image: url("chrome://browser/skin/chevron-animation.svg");
-  width: 1260px;
+  width: 1278px;
 }
 
 #nav-bar-overflow-button[animate]:-moz-locale-dir(rtl) > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
   animation-name: overflow-animation-rtl;
 }
 
 #nav-bar-overflow-button[animate][fade] > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
   animation-name: overflow-fade;
--- a/browser/themes/shared/toolbarbuttons.inc.css
+++ b/browser/themes/shared/toolbarbuttons.inc.css
@@ -6,18 +6,18 @@
   --toolbarbutton-hover-background: hsla(240,5%,5%,.1);
   --toolbarbutton-active-background: hsla(240,5%,5%,.15);
 
   --toolbarbutton-inner-padding: 6px;
 
   --backbutton-background: hsla(0,100%,100%,.8);
   --backbutton-hover-background: var(--backbutton-background);
   --backbutton-active-background: var(--toolbarbutton-active-background);
+  --backbutton-border-color: hsla(240,5%,5%,.3);
 
-  --backbutton-border-color: hsla(240,5%,5%,.3);
   /* This default value of --toolbarbutton-height is defined to prevent
      CSS errors for an invalid variable. The value should not get used,
      as a more specific value should be set when the value will be used. */
   --toolbarbutton-height: 0;
 }
 
 /* Larger buttons in touch mode */
 :root[uidensity=touch] {
@@ -44,28 +44,27 @@ toolbar[brighttext] {
 #main-window:not([customizing]) .toolbarbutton-1[disabled=true] > .toolbarbutton-badge-stack > .toolbarbutton-icon {
   opacity: 0.4;
 }
 
 .toolbarbutton-1 > .toolbarbutton-icon {
   margin-inline-end: 0;
 }
 
-:-moz-any(toolbar, .widget-overflow-list) .toolbarbutton-1 > .toolbarbutton-icon,
-:-moz-any(toolbar, .widget-overflow-list) .toolbarbutton-1 > .toolbarbutton-badge-stack > .toolbarbutton-icon {
+.toolbarbutton-1 > .toolbarbutton-icon,
+.toolbarbutton-1 > .toolbarbutton-badge-stack > .toolbarbutton-icon {
   max-width: 16px;
 }
 
 #TabsToolbar .toolbarbutton-1,
 .tabbrowser-arrowscrollbox > .scrollbutton-up,
 .tabbrowser-arrowscrollbox > .scrollbutton-down {
   margin: 0 0 var(--tab-toolbar-navbar-overlap);
 }
 
-#TabsToolbar .toolbarbutton-1,
 .tabbrowser-arrowscrollbox > .scrollbutton-up,
 .tabbrowser-arrowscrollbox > .scrollbutton-down {
   -moz-appearance: none;
   padding: 0;
   min-width: 26px;
   border: 1px solid transparent;
   border-bottom-style: none;
   border-radius: var(--toolbarbutton-border-radius) var(--toolbarbutton-border-radius) 0 0;
@@ -84,120 +83,117 @@ toolbar[brighttext] {
   background-color: Highlight;
 }
 
 .findbar-button {
   -moz-appearance: none;
   padding: 0;
 }
 
-#nav-bar .toolbarbutton-1 {
+toolbar .toolbarbutton-1 {
   -moz-appearance: none;
   margin: 0;
   padding: 0 2px;
   -moz-box-pack: center;
 }
 
-#nav-bar #PanelUI-menu-button {
+#PanelUI-menu-button {
   padding-inline-start: 5px;
   padding-inline-end: 5px;
 }
 
-#nav-bar .toolbarbutton-1 > menupopup {
+toolbar .toolbarbutton-1 > menupopup {
   margin-top: -3px;
 }
 
-#nav-bar .toolbarbutton-1 > menupopup.cui-widget-panel {
+toolbar .toolbarbutton-1 > menupopup.cui-widget-panel {
   margin-top: -8px;
 }
 
 .findbar-button > .toolbarbutton-text,
 toolbarbutton.bookmark-item:not(.subviewbutton),
-#nav-bar .toolbarbutton-1 > .toolbarbutton-icon,
-#nav-bar .toolbarbutton-1 > .toolbarbutton-text,
-#nav-bar .toolbarbutton-1 > .toolbarbutton-badge-stack {
+toolbar .toolbarbutton-1 > .toolbarbutton-icon,
+toolbar .toolbarbutton-1 > .toolbarbutton-text,
+toolbar .toolbarbutton-1 > .toolbarbutton-badge-stack {
   padding: var(--toolbarbutton-inner-padding);
   border-radius: var(--toolbarbutton-border-radius);
   transition-property: background-color, border-color, box-shadow;
   transition-duration: 150ms;
 }
 
-#nav-bar .toolbarbutton-1 > .toolbarbutton-icon {
+toolbar .toolbarbutton-1 > .toolbarbutton-icon {
   /* horizontal padding + actual icon width */
   max-width: calc(2 * var(--toolbarbutton-inner-padding) + 16px);
 }
 
 .bookmark-item > .toolbarbutton-menu-dropmarker,
-#TabsToolbar .toolbarbutton-1 > .toolbarbutton-menu-dropmarker,
-#nav-bar .toolbarbutton-1 > .toolbarbutton-menu-dropmarker {
+toolbar .toolbarbutton-1 > .toolbarbutton-menu-dropmarker {
   display: none;
 }
 
-#nav-bar .toolbarbutton-1 > .toolbarbutton-text {
+toolbar .toolbarbutton-1 > .toolbarbutton-text {
   padding-top: var(--toolbarbutton-vertical-text-padding);
   padding-bottom: 0;
   /* To make the hover feedback line up with sibling buttons, it needs the same
    * height as the button icons and the same vertical padding, but as a minimum,
    * because otherwise an increase in text sizes would break things.
    */
   min-height: calc(16px + 2 * var(--toolbarbutton-inner-padding));
 }
 
-#nav-bar .toolbaritem-combined-buttons {
+toolbar .toolbaritem-combined-buttons {
   margin-left: 2px;
   margin-right: 2px;
 }
 
-#nav-bar .toolbaritem-combined-buttons > .toolbarbutton-1 {
+toolbar .toolbaritem-combined-buttons > .toolbarbutton-1 {
   padding-left: 0;
   padding-right: 0;
 }
 
-#nav-bar .toolbaritem-combined-buttons:not(:hover) > separator {
+toolbar .toolbaritem-combined-buttons:not(:hover) > separator {
   content: "";
   display: -moz-box;
   width: 1px;
   height: 16px;
   margin-inline-end: -1px;
   background-image: linear-gradient(currentColor 0, currentColor 100%);
   background-position: center;
   background-repeat: no-repeat;
   background-size: 1px 16px;
   opacity: .2;
 }
 
-#nav-bar[brighttext] .toolbaritem-combined-buttons > separator {
+toolbar[brighttext] .toolbaritem-combined-buttons > separator {
   opacity: .3;
 }
 
-#TabsToolbar .toolbarbutton-1:not([disabled=true]):hover,
-#TabsToolbar .toolbarbutton-1[open],
 .tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled=true]):hover,
 .tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled=true]):hover,
 .findbar-button:not(:-moz-any([checked="true"],[disabled="true"])):hover > .toolbarbutton-text,
 toolbarbutton.bookmark-item:not(.subviewbutton):hover:not([disabled="true"]):not([open]),
-#nav-bar .toolbarbutton-1:not([disabled=true]):not([checked]):not([open]):not(:active):hover > .toolbarbutton-icon,
-#nav-bar .toolbarbutton-1:not([disabled=true]):not([checked]):not([open]):not(:active):hover > .toolbarbutton-text,
-#nav-bar .toolbarbutton-1:not([disabled=true]):not([checked]):not([open]):not(:active):hover > .toolbarbutton-badge-stack {
+toolbar .toolbarbutton-1:not([disabled=true]):not([checked]):not([open]):not(:active):hover > .toolbarbutton-icon,
+toolbar .toolbarbutton-1:not([disabled=true]):not([checked]):not([open]):not(:active):hover > .toolbarbutton-text,
+toolbar .toolbarbutton-1:not([disabled=true]):not([checked]):not([open]):not(:active):hover > .toolbarbutton-badge-stack {
   background-color: var(--toolbarbutton-hover-background);
   color: inherit;
 }
 
 .findbar-button:not([disabled=true]):-moz-any([checked="true"],:hover:active) > .toolbarbutton-text,
 toolbarbutton.bookmark-item:not(.subviewbutton):hover:active:not([disabled="true"]),
 toolbarbutton.bookmark-item[open="true"],
-#nav-bar .toolbarbutton-1:not([disabled=true]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-icon,
-#nav-bar .toolbarbutton-1:not([disabled=true]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-text,
-#nav-bar .toolbarbutton-1:not([disabled=true]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-badge-stack {
+toolbar .toolbarbutton-1:not([disabled=true]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-icon,
+toolbar .toolbarbutton-1:not([disabled=true]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-text,
+toolbar .toolbarbutton-1:not([disabled=true]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-badge-stack {
   background-color: var(--toolbarbutton-active-background);
   transition-duration: 10ms;
   color: inherit;
 }
 
-#nav-bar .toolbarbutton-1[checked]:not(:active):hover > .toolbarbutton-icon {
+toolbar .toolbarbutton-1[checked]:not(:active):hover > .toolbarbutton-icon {
   background-color: var(--toolbarbutton-hover-background);
   transition: background-color .4s;
 }
 
 :root:not([uidensity=compact]) #back-button {
   padding-top: 3px;
   padding-bottom: 3px;
   padding-inline-start: 5px !important;
--- a/browser/themes/windows/browser-aero.css
+++ b/browser/themes/windows/browser-aero.css
@@ -1,12 +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/. */
 
+%filter substitution
+%define toolbarShadowColor hsla(209,67%,12%,0.35)
 %define glassActiveBorderColor rgb(37, 44, 51)
 %define glassInactiveBorderColor rgb(102, 102, 102)
 
 @media not all and (-moz-windows-classic) {
   #main-window[sizemode="normal"] > #tab-view-deck > #browser-panel > #navigator-toolbox > #toolbar-menubar {
     margin-top: 1px;
   }
 }
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -3,19 +3,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 @import url("chrome://global/skin/");
 
 @namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
 @namespace html url("http://www.w3.org/1999/xhtml");
 @namespace svg url("http://www.w3.org/2000/svg");
 
-%include ../shared/browser.inc
-%define toolbarShadowColor hsla(209,67%,12%,0.35)
-
 %include ../shared/browser.inc.css
 
 :root {
   --titlebar-text-color: currentColor;
 
   --tabs-border: threedshadow;
 
   --toolbar-non-lwt-bgcolor: -moz-dialog;
@@ -1040,38 +1037,16 @@ treechildren.searchbar-treebody::-moz-tr
 }
 
 /* All tabs menupopup */
 
 .alltabs-item[selected="true"] {
   font-weight: bold;
 }
 
-
-toolbarbutton.chevron {
-  list-style-image: url("chrome://global/skin/toolbar/chevron.gif") !important;
-}
-
-toolbar[brighttext] toolbarbutton.chevron {
-  list-style-image: url("chrome://global/skin/toolbar/chevron-inverted.png") !important;
-}
-
-toolbarbutton.chevron:-moz-locale-dir(rtl) > .toolbarbutton-icon {
-  transform: scaleX(-1);
-}
-
-toolbarbutton.chevron > .toolbarbutton-text,
-toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
-  display: none;
-}
-
-toolbarbutton.chevron > .toolbarbutton-icon {
-  margin: 0;
-}
-
 /* Bookmarks toolbar */
 #PlacesToolbarDropIndicator {
   list-style-image: url(chrome://browser/skin/places/toolbarDropMarker.png);
 }
 
 toolbarbutton.bookmark-item[dragover="true"][open="true"] {
   -moz-appearance: none;
   background: Highlight !important;
--- a/build/moz.configure/toolchain.configure
+++ b/build/moz.configure/toolchain.configure
@@ -1136,23 +1136,23 @@ def build_not_win_mac(target):
         return True
 
 
 option('--enable-gold',
        env='MOZ_FORCE_GOLD',
        help='Enable GNU Gold Linker when it is not already the default',
        when=build_not_win_mac)
 
+imply_option('--enable-linker',
+             depends_if('--enable-gold', when=build_not_win_mac)(lambda x: 'gold'),
+             when=build_not_win_mac)
 
-@depends('--enable-gold', c_compiler, developer_options, check_build_environment, when=build_not_win_mac)
-@checking('for ld', lambda x: x.KIND)
 @imports('os')
 @imports('shutil')
-def enable_gold(enable_gold_option, c_compiler, developer_options, build_env):
-    linker = None
+def enable_gnu_linker(enable_gold_option, c_compiler, developer_options, build_env, linker_name):
     # Used to check the kind of linker
     version_check = ['-Wl,--version']
     cmd_base = c_compiler.wrapper + [c_compiler.compiler] + c_compiler.flags
 
     def resolve_gold():
         # Try to force the usage of gold
         targetDir = os.path.join(build_env.topobjdir, 'build', 'unix', 'gold')
 
@@ -1177,17 +1177,17 @@ def enable_gold(enable_gold_option, c_co
             return namespace(
                 KIND='gold',
                 LINKER_FLAG=linker,
             )
         else:
             # The -B trick didn't work, removing the directory
             shutil.rmtree(targetDir)
 
-    if enable_gold_option or developer_options:
+    if (enable_gold_option or developer_options) and linker_name != 'bfd':
         result = resolve_gold()
 
         if result:
             return result
         # gold is only required if --enable-gold is used.
         elif enable_gold_option:
             die('Could not find gold')
         # Else fallthrough.
@@ -1208,10 +1208,35 @@ def enable_gold(enable_gold_option, c_co
             KIND='gold'
         )
 
     # For other platforms without gold or the GNU linker
     return namespace(
         KIND='other'
     )
 
-set_config('LD_IS_BFD', depends(enable_gold.KIND)(lambda x: x == 'bfd' or None))
-set_config('LINKER_LDFLAGS', enable_gold.LINKER_FLAG)
+js_option('--enable-linker', nargs=1,
+          choices=('bfd', 'gold', 'lld', 'other'),
+          help='Select the linker',
+          when=build_not_win_mac)
+
+@depends('--enable-linker', c_compiler, developer_options, check_build_environment, when=build_not_win_mac)
+@checking('for linker', lambda x: x.KIND)
+def select_linker(linker, c_compiler, developer_options, build_env):
+    linker = linker[0] if linker else 'other'
+    if linker in ('gold', 'bfd', 'other'):
+        return enable_gnu_linker(linker == 'gold', c_compiler, developer_options, build_env, linker)
+    if linker == 'lld':
+        version_check = ['-Wl,--version']
+        cmd_base = c_compiler.wrapper + [c_compiler.compiler] + c_compiler.flags
+        lld = "-fuse-ld=" + linker
+        cmd = cmd_base + [lld] + version_check
+        if 'LLD' in check_cmd_output(*cmd).decode('utf-8'):
+            return namespace(
+                KIND='lld',
+                LINKER_FLAG=lld,
+            )
+        else:
+            die("Could not use lld as linker")
+
+
+set_config('LD_IS_BFD', depends(select_linker.KIND)(lambda x: x == 'bfd' or None))
+set_config('LINKER_LDFLAGS', select_linker.LINKER_FLAG)
--- a/devtools/client/aboutdebugging/test/addons/test-devtools-webextension/bg.js
+++ b/devtools/client/aboutdebugging/test/addons/test-devtools-webextension/bg.js
@@ -3,18 +3,21 @@
 
 /* eslint-env browser */
 /* global browser */
 
 "use strict";
 
 document.body.innerText = "Background Page Body Test Content";
 
-// This function are called from the webconsole test:
-// browser_addons_debug_webextension.js
+// These functions are called from the following about:debugging tests:
+// - browser_addons_debug_webextension.js
+// - browser_addons_debug_webextension_popup.js
 
-function myWebExtensionAddonFunction() {  // eslint-disable-line no-unused-vars
+// eslint-disable-next-line no-unused-vars
+function myWebExtensionAddonFunction() {
   console.log("Background page function called", browser.runtime.getManifest());
 }
 
-function myWebExtensionShowPopup() {  // eslint-disable-line no-unused-vars
-  console.log("readyForOpenPopup");
+// eslint-disable-next-line no-unused-vars
+function myWebExtensionShowPopup() {
+  browser.test.sendMessage("readyForOpenPopup");
 }
--- a/devtools/client/aboutdebugging/test/addons/test-devtools-webextension/popup.js
+++ b/devtools/client/aboutdebugging/test/addons/test-devtools-webextension/popup.js
@@ -1,13 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /* eslint-env browser */
 /* global browser */
 
 "use strict";
 
-// This function is called from the webconsole test:
+// This function is called from the following about:debugging test:
 // browser_addons_debug_webextension.js
-function myWebExtensionPopupAddonFunction() {  // eslint-disable-line no-unused-vars
-  console.log("Popup page function called", browser.runtime.getManifest());
+//
+// eslint-disable-next-line no-unused-vars
+function myWebExtensionPopupAddonFunction() {
+  browser.test.sendMessage("popupPageFunctionCalled", browser.runtime.getManifest());
 }
--- a/devtools/client/aboutdebugging/test/browser_addons_debug_webextension.js
+++ b/devtools/client/aboutdebugging/test/browser_addons_debug_webextension.js
@@ -18,56 +18,58 @@ const {
  * - when the debug button is clicked on a webextension, the opened toolbox
  *   has a working webconsole with the background page as default target;
  */
 add_task(function* testWebExtensionsToolboxWebConsole() {
   let {
     tab, document, debugBtn,
   } = yield setupTestAboutDebuggingWebExtension(ADDON_NAME, ADDON_MANIFEST_PATH);
 
-  // Wait for a notification sent by a script evaluated the test addon via
-  // the web console.
-  let onCustomMessage = new Promise(done => {
-    Services.obs.addObserver(function listener(message, topic) {
-      let apiMessage = message.wrappedJSObject;
-      if (apiMessage.addonId != ADDON_ID) {
-        return;
-      }
-      Services.obs.removeObserver(listener, "console-api-log-event");
-      done(apiMessage.arguments);
-    }, "console-api-log-event");
-  });
-
   // Be careful, this JS function is going to be executed in the addon toolbox,
   // which lives in another process. So do not try to use any scope variable!
   let env = Cc["@mozilla.org/process/environment;1"]
               .getService(Ci.nsIEnvironment);
   let testScript = function () {
     /* eslint-disable no-undef */
+    function findMessages(hud, text, selector = ".message") {
+      const messages = hud.ui.outputNode.querySelectorAll(selector);
+      const elements = Array.prototype.filter.call(
+        messages,
+        (el) => el.textContent.includes(text)
+      );
+      return elements;
+    }
+
+    async function waitFor(condition) {
+      while (!condition()) {
+        await new Promise(done => window.setTimeout(done, 1000));
+      }
+    }
+
     toolbox.selectTool("webconsole")
-      .then(console => {
-        let { jsterm } = console.hud;
-        return jsterm.execute("myWebExtensionAddonFunction()");
+      .then(async console => {
+        let { hud } = console;
+        let { jsterm } = hud;
+        let onMessage = waitFor(() => {
+          return findMessages(hud, "Background page function called").length > 0;
+        });
+        await jsterm.execute("myWebExtensionAddonFunction()");
+        await onMessage;
+        await toolbox.destroy();
       })
-      .then(() => toolbox.destroy());
+      .catch(e => dump("Exception from browser toolbox process: " + e + "\n"));
     /* eslint-enable no-undef */
   };
   env.set("MOZ_TOOLBOX_TEST_SCRIPT", "new " + testScript);
   registerCleanupFunction(() => {
     env.set("MOZ_TOOLBOX_TEST_SCRIPT", "");
   });
 
   let onToolboxClose = BrowserToolboxProcess.once("close");
 
   debugBtn.click();
 
-  let args = yield onCustomMessage;
-  ok(true, "Received console message from the background page function as expected");
-  is(args[0], "Background page function called", "Got the expected console message");
-  is(args[1] && args[1].name, ADDON_NAME,
-     "Got the expected manifest from WebExtension API");
-
   yield onToolboxClose;
   ok(true, "Addon toolbox closed");
 
   yield uninstallAddon({document, id: ADDON_ID, name: ADDON_NAME});
   yield closeAboutDebugging(tab);
 });
--- a/devtools/client/aboutdebugging/test/browser_addons_debug_webextension_popup.js
+++ b/devtools/client/aboutdebugging/test/browser_addons_debug_webextension_popup.js
@@ -32,86 +32,99 @@ const {
  * Returns the widget id for an extension with the passed id.
  */
 function makeWidgetId(id) {
   id = id.toLowerCase();
   return id.replace(/[^a-z0-9_-]/g, "_");
 }
 
 add_task(function* testWebExtensionsToolboxSwitchToPopup() {
+  let onReadyForOpenPopup;
+  let onPopupCustomMessage;
+
+  Management.on("startup", function listener(event, extension) {
+    if (extension.name != ADDON_NAME) {
+      return;
+    }
+
+    Management.off("startup", listener);
+
+    function waitForExtensionTestMessage(expectedMessage) {
+      return new Promise(done => {
+        extension.on("test-message", function testLogListener(evt, ...args) {
+          const [message, ] = args;
+
+          if (message !== expectedMessage) {
+            return;
+          }
+
+          extension.off("test-message", testLogListener);
+          done(args);
+        });
+      });
+    }
+
+    // Wait for the test script running in the browser toolbox process
+    // to be ready for selecting the popup page in the frame list selector.
+    onReadyForOpenPopup = waitForExtensionTestMessage("readyForOpenPopup");
+
+    // Wait for a notification sent by a script evaluated the test addon via
+    // the web console.
+    onPopupCustomMessage = waitForExtensionTestMessage("popupPageFunctionCalled");
+  });
+
   let {
     tab, document, debugBtn,
   } = yield setupTestAboutDebuggingWebExtension(ADDON_NAME, ADDON_MANIFEST_PATH);
 
-  let onReadyForOpenPopup = new Promise(done => {
-    Services.obs.addObserver(function listener(message, topic) {
-      let apiMessage = message.wrappedJSObject;
-      if (apiMessage.addonId != ADDON_ID) {
-        return;
-      }
-
-      if (apiMessage.arguments[0] == "readyForOpenPopup") {
-        Services.obs.removeObserver(listener, "console-api-log-event");
-        done();
-      }
-    }, "console-api-log-event");
-  });
-
   // Be careful, this JS function is going to be executed in the addon toolbox,
   // which lives in another process. So do not try to use any scope variable!
-  let env = Cc["@mozilla.org/process/environment;1"]
-        .getService(Ci.nsIEnvironment);
+  let env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
+
   let testScript = function () {
     /* eslint-disable no-undef */
 
     let jsterm;
     let popupFramePromise;
 
     toolbox.selectTool("webconsole")
-      .then(console => {
+      .then(async (console) => {
         dump(`Clicking the noautohide button\n`);
         toolbox.doc.getElementById("command-button-noautohide").click();
         dump(`Clicked the noautohide button\n`);
 
         popupFramePromise = new Promise(resolve => {
           let listener = (event, data) => {
             if (data.frames.some(({url}) => url && url.endsWith("popup.html"))) {
               toolbox.target.off("frame-update", listener);
               resolve();
             }
           };
           toolbox.target.on("frame-update", listener);
         });
 
-        let waitForFrameListUpdate = new Promise((done) => {
-          toolbox.target.once("frame-update", () => {
-            done(console);
-          });
-        });
+        let waitForFrameListUpdate = toolbox.target.once("frame-update");
 
         jsterm = console.hud.jsterm;
         jsterm.execute("myWebExtensionShowPopup()");
 
-        // Wait the initial frame update (which list the background page).
-        return waitForFrameListUpdate;
-      })
-      .then((console) => {
-        // Wait the new frame update (once the extension popup has been opened).
-        return popupFramePromise;
-      })
-      .then(() => {
+        await Promise.all([
+          // Wait the initial frame update (which list the background page).
+          waitForFrameListUpdate,
+          // Wait the new frame update (once the extension popup has been opened).
+          popupFramePromise,
+        ]);
+
         dump(`Clicking the frame list button\n`);
         let btn = toolbox.doc.getElementById("command-button-frames");
-        let menu = toolbox.showFramesMenu({target: btn});
+        let frameMenu = toolbox.showFramesMenu({target: btn});
         dump(`Clicked the frame list button\n`);
-        return menu.once("open").then(() => {
-          return menu;
-        });
-      })
-      .then(frameMenu => {
+
+        await frameMenu.once("open");
+
         let frames = frameMenu.items;
 
         if (frames.length != 2) {
           throw Error(`Number of frames found is wrong: ${frames.length} != 2`);
         }
 
         let popupFrameBtn = frames.filter((frame) => {
           return frame.label.endsWith("popup.html");
@@ -120,66 +133,50 @@ add_task(function* testWebExtensionsTool
         if (!popupFrameBtn) {
           throw Error("Extension Popup frame not found in the listed frames");
         }
 
         let waitForNavigated = toolbox.target.once("navigate");
 
         popupFrameBtn.click();
 
-        return waitForNavigated;
+        await waitForNavigated;
+
+        await jsterm.execute("myWebExtensionPopupAddonFunction()");
+
+        await toolbox.destroy();
       })
-      .then(() => {
-        return jsterm.execute("myWebExtensionPopupAddonFunction()");
-      })
-      .then(() => toolbox.destroy())
       .catch((error) => {
         dump("Error while running code in the browser toolbox process:\n");
         dump(error + "\n");
         dump("stack:\n" + error.stack + "\n");
       });
     /* eslint-enable no-undef */
   };
   env.set("MOZ_TOOLBOX_TEST_SCRIPT", "new " + testScript);
   registerCleanupFunction(() => {
     env.set("MOZ_TOOLBOX_TEST_SCRIPT", "");
   });
 
-  // Wait for a notification sent by a script evaluated the test addon via
-  // the web console.
-  let onPopupCustomMessage = new Promise(done => {
-    Services.obs.addObserver(function listener(message, topic) {
-      let apiMessage = message.wrappedJSObject;
-      if (apiMessage.addonId != ADDON_ID) {
-        return;
-      }
-
-      if (apiMessage.arguments[0] == "Popup page function called") {
-        Services.obs.removeObserver(listener, "console-api-log-event");
-        done(apiMessage.arguments);
-      }
-    }, "console-api-log-event");
-  });
-
   let onToolboxClose = BrowserToolboxProcess.once("close");
 
   debugBtn.click();
 
   yield onReadyForOpenPopup;
 
   let browserActionId = makeWidgetId(ADDON_ID) + "-browser-action";
   let browserActionEl = window.document.getElementById(browserActionId);
 
   ok(browserActionEl, "Got the browserAction button from the browser UI");
   browserActionEl.click();
   info("Clicked on the browserAction button");
 
   let args = yield onPopupCustomMessage;
   ok(true, "Received console message from the popup page function as expected");
-  is(args[0], "Popup page function called", "Got the expected console message");
+  is(args[0], "popupPageFunctionCalled", "Got the expected console message");
   is(args[1] && args[1].name, ADDON_NAME,
      "Got the expected manifest from WebExtension API");
 
   yield onToolboxClose;
 
   ok(true, "Addon toolbox closed");
 
   yield uninstallAddon({document, id: ADDON_ID, name: ADDON_NAME});
--- a/devtools/client/shared/developer-toolbar.js
+++ b/devtools/client/shared/developer-toolbar.js
@@ -20,17 +20,17 @@ const { PluralForm } = require("devtools
 
 loader.lazyGetter(this, "prefBranch", function () {
   return Services.prefs.getBranch(null)
                     .QueryInterface(Ci.nsIPrefBranch);
 });
 
 loader.lazyRequireGetter(this, "gcliInit", "devtools/shared/gcli/commands/index");
 loader.lazyRequireGetter(this, "util", "gcli/util/util");
-loader.lazyRequireGetter(this, "ConsoleServiceListener", "devtools/server/actors/utils/webconsole-listeners", true);
+loader.lazyRequireGetter(this, "ConsoleServiceListener", "devtools/server/actors/webconsole/listeners", true);
 loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
 loader.lazyRequireGetter(this, "gDevToolsBrowser", "devtools/client/framework/devtools-browser", true);
 loader.lazyRequireGetter(this, "nodeConstants", "devtools/shared/dom-node-constants");
 loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
 
 /**
  * A collection of utilities to help working with commands
  */
--- a/devtools/client/themes/images/debugging-addons.svg
+++ b/devtools/client/themes/images/debugging-addons.svg
@@ -1,6 +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 height="16" width="16" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18" fill="#fbfbfb">
-  <path d="M12,17c0.5,0,1-0.5,1-1v-4c0,0,0.2-0.8,0.8-0.8c0.6,0,0.6,0.8,1.8,0.8 c0.6,0,1.5-0.2,1.5-2c0-1.8-0.9-2-1.5-2c-1.1,0-1.2,0.8-1.8,0.8C13.2,8.8,13,8,13,8V6c0-0.6-0.4-1-1-1H9c0,0-0.8-0.1-0.8-0.8 S9,3.6,9,2.5C9,1.9,8.8,1,7,1S5,1.9,5,2.5c0,1.1,0.8,1.2,0.8,1.8S5,5,5,5H2C1.4,5,1,5.4,1,6l0,2.5c0,0-0.1,1.5,1.1,1.5 c0.8,0,0.9-1,1.9-1c0.5,0,1,0.5,1,1.6c0,1-0.5,1.6-1,1.6c-1,0-1.1-1-1.9-1C0.9,11,1,12.5,1,12.5L1,16c0,0.6,0.4,1,1,1h3.9 c0,0,1.5,0.1,1.5-1.1c0-0.8-1-0.9-1-1.9c0-0.5,0.7-1.2,1.8-1.2s1.9,0.7,1.9,1.2c0,1-1,1.1-1,1.9c0,1.2,1.5,1.1,1.5,1.1H12z" />
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
+  <path fill="context-fill" d="M14.5 8c-.971 0-1 1-1.75 1a.765.765 0 0 1-.75-.75V5a1 1 0 0 0-1-1H7.75A.765.765 0 0 1 7 3.25c0-.75 1-.779 1-1.75C8 .635 7.1 0 6 0S4 .635 4 1.5c0 .971 1 1 1 1.75a.765.765 0 0 1-.75.75H1a1 1 0 0 0-1 1v2.25A.765.765 0 0 0 .75 8c.75 0 .779-1 1.75-1C3.365 7 4 7.9 4 9s-.635 2-1.5 2c-.971 0-1-1-1.75-1a.765.765 0 0 0-.75.75V15a1 1 0 0 0 1 1h3.25a.765.765 0 0 0 .75-.75c0-.75-1-.779-1-1.75 0-.865.9-1.5 2-1.5s2 .635 2 1.5c0 .971-1 1-1 1.75a.765.765 0 0 0 .75.75H11a1 1 0 0 0 1-1v-3.25a.765.765 0 0 1 .75-.75c.75 0 .779 1 1.75 1 .865 0 1.5-.9 1.5-2s-.635-2-1.5-2z"></path>
 </svg>
--- a/devtools/client/themes/images/debugging-tabs.svg
+++ b/devtools/client/themes/images/debugging-tabs.svg
@@ -1,3 +1,6 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
-  <path d="M17,12v2a1,1,0,0,1-1,1H2a1,1,0,0,1-1-1V12a1,1,0,0,1,1-1H1.142c2.3,0,2.536-1.773,2.874-4,0.351-2.316.083-4,3.13-4h3.707C13.917,3,13.647,4.684,14,7c0.34,2.228.582,4,2.89,4H16A1,1,0,0,1,17,12Z" fill="white"/>
+<!-- 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">
+  <path fill="context-fill" d="M15 11h-1V5a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v6H1a1 1 0 0 0 0 2h14a1 1 0 1 0 0-2z"></path>
 </svg>
--- a/devtools/client/themes/images/debugging-workers.svg
+++ b/devtools/client/themes/images/debugging-workers.svg
@@ -1,11 +1,11 @@
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg height="16" width="16" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="#fbfbfb">
+<svg height="16" width="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill">
 <path d="M14.6,6.1L13.5,5l0,0c0.1-0.1,0.2-0.4,0.2-0.6c0-0.2-0.1-0.4-0.2-0.6l-0.4-0.4c-0.3-0.3-0.8-0.3-1.1,0l0,0
 	L10.5,2c-0.2-0.2-0.3-0.2-0.5-0.2c-0.2,0-0.3,0.1-0.5,0.2L8.3,3.2C8.1,3.3,8.1,3.4,8.1,3.6S8.2,4,8.3,4.1l1.6,1.6L7.8,7.8L5.6,5.7
 	l1.5-1.5C7.3,4,7.4,3.8,7.4,3.6c0-0.2-0.1-0.4-0.2-0.6l-1-1C5.8,1.7,5.3,1.7,5,2L0.9,6.1C0.7,6.3,0.6,6.5,0.6,6.7
 	c0,0.2,0.1,0.4,0.2,0.6l1,1c0.3,0.3,0.9,0.3,1.2,0l1.4-1.4l2,2.1l-3.4,3.3c-0.3,0.3-0.3,0.8,0,1.1l0.3,0.3c0.3,0.3,0.8,0.3,1.1,0
 	l3.3-3.4l3.3,3.4c0.1,0.1,0.3,0.2,0.6,0.2c0.2,0,0.4-0.1,0.6-0.2l0.3-0.3c0.3-0.3,0.3-0.8,0-1.1L9,9l2-2.1l1.4,1.4
 	c0.1,0.1,2.3,1.1,2.7,0.7C15.5,8.6,14.8,6.3,14.6,6.1z"/>
 </svg>
--- a/devtools/client/webconsole/hudservice.js
+++ b/devtools/client/webconsole/hudservice.js
@@ -185,19 +185,18 @@ HUD_SERVICE.prototype =
         DebuggerServer.addBrowserActors();
       }
       DebuggerServer.allowChromeProcess = true;
 
       let client = new DebuggerClient(DebuggerServer.connectPipe());
       return client.connect()
         .then(() => client.getProcess())
         .then(aResponse => {
-          // Set chrome:false in order to attach to the target
-          // (i.e. send an `attach` request to the chrome actor)
-          return { form: aResponse.form, client: client, chrome: false };
+          // Use a TabActor in order to ensure calling `attach` to the ChromeActor
+          return { form: aResponse.form, client, chrome: true, isTabActor: true };
         });
     }
 
     let target;
     function getTarget(aConnection)
     {
       return TargetFactory.forRemoteTab(aConnection);
     }
--- a/devtools/client/webconsole/webconsole-connection-proxy.js
+++ b/devtools/client/webconsole/webconsole-connection-proxy.js
@@ -173,16 +173,21 @@ WebConsoleConnectionProxy.prototype = {
 
   /**
    * Attach to the Web Console actor.
    * @private
    */
   _attachConsole: function () {
     let listeners = ["PageError", "ConsoleAPI", "NetworkActivity",
                      "FileActivity"];
+    // Enable the forwarding of console messages to the parent process
+    // when we open the Browser Console or Toolbox.
+    if (this.target.chrome && !this.target.isAddon) {
+      listeners.push("ContentProcessMessages");
+    }
     this.client.attachConsole(this._consoleActor, listeners,
                               this._onAttachConsole);
   },
 
   /**
    * The "attachConsole" response handler.
    *
    * @private
--- a/devtools/server/actors/addon.js
+++ b/devtools/server/actors/addon.js
@@ -4,17 +4,17 @@
 
 "use strict";
 
 var { Ci, Cu } = require("chrome");
 var Services = require("Services");
 var { ActorPool } = require("devtools/server/actors/common");
 var { TabSources } = require("./utils/TabSources");
 var makeDebugger = require("./utils/make-debugger");
-var { ConsoleAPIListener } = require("devtools/server/actors/utils/webconsole-listeners");
+var { ConsoleAPIListener } = require("devtools/server/actors/webconsole/listeners");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
 var { assert, update } = DevToolsUtils;
 
 loader.lazyRequireGetter(this, "AddonThreadActor", "devtools/server/actors/script", true);
 loader.lazyRequireGetter(this, "unwrapDebuggerObjectGlobal", "devtools/server/actors/script", true);
 loader.lazyRequireGetter(this, "mapURIToAddonID", "devtools/server/actors/utils/map-uri-to-addon-id");
 loader.lazyRequireGetter(this, "WebConsoleActor", "devtools/server/actors/webconsole", true);
 
--- a/devtools/server/actors/moz.build
+++ b/devtools/server/actors/moz.build
@@ -2,16 +2,17 @@
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DIRS += [
     'highlighters',
     'utils',
+    'webconsole',
 ]
 
 DevToolsModules(
     'actor-registry.js',
     'addon.js',
     'addons.js',
     'animation.js',
     'breakpoint.js',
--- a/devtools/server/actors/utils/moz.build
+++ b/devtools/server/actors/utils/moz.build
@@ -10,12 +10,9 @@ DevToolsModules(
     'automation-timeline.js',
     'css-grid-utils.js',
     'make-debugger.js',
     'map-uri-to-addon-id.js',
     'shapes-geometry-utils.js',
     'stack.js',
     'TabSources.js',
     'walker-search.js',
-    'webconsole-listeners.js',
-    'webconsole-utils.js',
-    'webconsole-worker-listeners.js',
 )
--- a/devtools/server/actors/webconsole.js
+++ b/devtools/server/actors/webconsole.js
@@ -20,30 +20,31 @@ loader.lazyRequireGetter(this, "NetworkM
 loader.lazyRequireGetter(this, "NetworkMonitorChild", "devtools/shared/webconsole/network-monitor", true);
 loader.lazyRequireGetter(this, "ConsoleProgressListener", "devtools/shared/webconsole/network-monitor", true);
 loader.lazyRequireGetter(this, "StackTraceCollector", "devtools/shared/webconsole/network-monitor", true);
 loader.lazyRequireGetter(this, "events", "sdk/event/core");
 loader.lazyRequireGetter(this, "ServerLoggingListener", "devtools/shared/webconsole/server-logger", true);
 loader.lazyRequireGetter(this, "JSPropertyProvider", "devtools/shared/webconsole/js-property-provider", true);
 loader.lazyRequireGetter(this, "Parser", "resource://devtools/shared/Parser.jsm", true);
 loader.lazyRequireGetter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm", true);
-loader.lazyRequireGetter(this, "addWebConsoleCommands", "devtools/server/actors/utils/webconsole-utils", true);
-loader.lazyRequireGetter(this, "CONSOLE_WORKER_IDS", "devtools/server/actors/utils/webconsole-utils", true);
-loader.lazyRequireGetter(this, "WebConsoleUtils", "devtools/server/actors/utils/webconsole-utils", true);
+loader.lazyRequireGetter(this, "addWebConsoleCommands", "devtools/server/actors/webconsole/utils", true);
+loader.lazyRequireGetter(this, "CONSOLE_WORKER_IDS", "devtools/server/actors/webconsole/utils", true);
+loader.lazyRequireGetter(this, "WebConsoleUtils", "devtools/server/actors/webconsole/utils", true);
 loader.lazyRequireGetter(this, "EnvironmentActor", "devtools/server/actors/environment", true);
 
 // Overwrite implemented listeners for workers so that we don't attempt
 // to load an unsupported module.
 if (isWorker) {
-  loader.lazyRequireGetter(this, "ConsoleAPIListener", "devtools/server/actors/utils/webconsole-worker-listeners", true);
-  loader.lazyRequireGetter(this, "ConsoleServiceListener", "devtools/server/actors/utils/webconsole-worker-listeners", true);
+  loader.lazyRequireGetter(this, "ConsoleAPIListener", "devtools/server/actors/webconsole/worker-listeners", true);
+  loader.lazyRequireGetter(this, "ConsoleServiceListener", "devtools/server/actors/webconsole/worker-listeners", true);
 } else {
-  loader.lazyRequireGetter(this, "ConsoleAPIListener", "devtools/server/actors/utils/webconsole-listeners", true);
-  loader.lazyRequireGetter(this, "ConsoleServiceListener", "devtools/server/actors/utils/webconsole-listeners", true);
-  loader.lazyRequireGetter(this, "ConsoleReflowListener", "devtools/server/actors/utils/webconsole-listeners", true);
+  loader.lazyRequireGetter(this, "ConsoleAPIListener", "devtools/server/actors/webconsole/listeners", true);
+  loader.lazyRequireGetter(this, "ConsoleServiceListener", "devtools/server/actors/webconsole/listeners", true);
+  loader.lazyRequireGetter(this, "ConsoleReflowListener", "devtools/server/actors/webconsole/listeners", true);
+  loader.lazyRequireGetter(this, "ContentProcessListener", "devtools/server/actors/webconsole/listeners", true);
 }
 
 /**
  * The WebConsoleActor implements capabilities needed for the Web Console
  * feature.
  *
  * @constructor
  * @param object connection
@@ -363,16 +364,20 @@ WebConsoleActor.prototype =
     if (this.consoleReflowListener) {
       this.consoleReflowListener.destroy();
       this.consoleReflowListener = null;
     }
     if (this.serverLoggingListener) {
       this.serverLoggingListener.destroy();
       this.serverLoggingListener = null;
     }
+    if (this.contentProcessListener) {
+      this.contentProcessListener.destroy();
+      this.contentProcessListener = null;
+    }
 
     events.off(this.parentActor, "changed-toplevel-document",
                this._onChangedToplevelDocument);
 
     this.conn.removeActorPool(this._actorPool);
 
     if (this.parentActor.isRootActor) {
       Services.obs.removeObserver(this._onObserverNotification,
@@ -659,16 +664,26 @@ WebConsoleActor.prototype =
             break;
           }
           if (!this.serverLoggingListener) {
             this.serverLoggingListener =
               new ServerLoggingListener(this.window, this);
           }
           startedListeners.push(listener);
           break;
+        case "ContentProcessMessages":
+          // Workers don't support this message type
+          if (isWorker) {
+            break;
+          }
+          if (!this.contentProcessListener) {
+            this.contentProcessListener = new ContentProcessListener(this);
+          }
+          startedListeners.push(listener);
+          break;
       }
     }
 
     // Update the live list of running listeners
     startedListeners.forEach(this._listeners.add, this._listeners);
 
     return {
       startedListeners: startedListeners,
@@ -688,17 +703,17 @@ WebConsoleActor.prototype =
    */
   onStopListeners: function (request) {
     let stoppedListeners = [];
 
     // If no specific listeners are requested to be detached, we stop all
     // listeners.
     let toDetach = request.listeners ||
       ["PageError", "ConsoleAPI", "NetworkActivity",
-       "FileActivity", "ServerLogging"];
+       "FileActivity", "ServerLogging", "ContentProcessMessages"];
 
     while (toDetach.length > 0) {
       let listener = toDetach.shift();
       switch (listener) {
         case "PageError":
           if (this.consoleServiceListener) {
             this.consoleServiceListener.destroy();
             this.consoleServiceListener = null;
@@ -744,16 +759,23 @@ WebConsoleActor.prototype =
           break;
         case "ServerLogging":
           if (this.serverLoggingListener) {
             this.serverLoggingListener.destroy();
             this.serverLoggingListener = null;
           }
           stoppedListeners.push(listener);
           break;
+        case "ContentProcessMessages":
+          if (this.contentProcessListener) {
+            this.contentProcessListener.destroy();
+            this.contentProcessListener = null;
+          }
+          stoppedListeners.push(listener);
+          break;
       }
     }
 
     // Update the live list of running listeners
     stoppedListeners.forEach(this._listeners.delete, this._listeners);
 
     return { stoppedListeners: stoppedListeners };
   },
copy from toolkit/components/processsingleton/ContentProcessSingleton.js
copy to devtools/server/actors/webconsole/content-process-forward.js
--- a/toolkit/components/processsingleton/ContentProcessSingleton.js
+++ b/devtools/server/actors/webconsole/content-process-forward.js
@@ -2,25 +2,22 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const Cu = Components.utils;
 const Ci = Components.interfaces;
 
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
+const { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
 
 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
                                    "@mozilla.org/childprocessmessagemanager;1",
                                    "nsIMessageSender");
-
-XPCOMUtils.defineLazyModuleGetter(this, "TelemetryController",
-                                  "resource://gre/modules/TelemetryController.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "E10SUtils",
                                   "resource:///modules/E10SUtils.jsm");
 
 /*
  * The message manager has an upper limit on message sizes that it can
  * reliably forward to the parent so we limit the size of console log event
  * messages that we forward here. The web console is local and receives the
  * full console message, but addons subscribed to console event messages
@@ -35,92 +32,105 @@ XPCOMUtils.defineLazyModuleGetter(this, 
  * MSG_MGR_CONSOLE_INFO_MAX characters. We don't attempt to calculate
  * the exact amount of space the message manager implementation will require
  * for a given message so this is imperfect.
  */
 const MSG_MGR_CONSOLE_MAX_SIZE = 1024 * 1024; // 1MB
 const MSG_MGR_CONSOLE_VAR_SIZE = 8;
 const MSG_MGR_CONSOLE_INFO_MAX = 1024;
 
-function ContentProcessSingleton() {}
-ContentProcessSingleton.prototype = {
-  classID: Components.ID("{ca2a8470-45c7-11e4-916c-0800200c9a66}"),
+function ContentProcessForward() {
+  Services.obs.addObserver(this, "console-api-log-event");
+  Services.obs.addObserver(this, "xpcom-shutdown");
+  cpmm.addMessageListener("DevTools:StopForwardingContentProcessMessage", this);
+}
+ContentProcessForward.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                          Ci.nsISupportsWeakReference]),
 
+  receiveMessage(message) {
+    if (message.name == "DevTools:StopForwardingContentProcessMessage") {
+      this.uninit();
+    }
+  },
+
   observe(subject, topic, data) {
     switch (topic) {
-    case "app-startup": {
-      Services.obs.addObserver(this, "console-api-log-event");
-      Services.obs.addObserver(this, "xpcom-shutdown");
-      TelemetryController.observe(null, topic, null);
-      break;
-    }
-    case "console-api-log-event": {
-      let consoleMsg = subject.wrappedJSObject;
+      case "console-api-log-event": {
+        let consoleMsg = subject.wrappedJSObject;
 
-      let msgData = {
-        level: consoleMsg.level,
-        filename: consoleMsg.filename.substring(0, MSG_MGR_CONSOLE_INFO_MAX),
-        lineNumber: consoleMsg.lineNumber,
-        functionName: consoleMsg.functionName &&
-          consoleMsg.functionName.substring(0, MSG_MGR_CONSOLE_INFO_MAX),
-        timeStamp: consoleMsg.timeStamp,
-        addonId: consoleMsg.addonId,
-        arguments: [],
-      };
+        let msgData = {
+          level: consoleMsg.level,
+          filename: consoleMsg.filename.substring(0, MSG_MGR_CONSOLE_INFO_MAX),
+          lineNumber: consoleMsg.lineNumber,
+          functionName: consoleMsg.functionName &&
+            consoleMsg.functionName.substring(0, MSG_MGR_CONSOLE_INFO_MAX),
+          timeStamp: consoleMsg.timeStamp,
+          addonId: consoleMsg.addonId,
+          arguments: [],
+        };
+
+        // We can't send objects over the message manager, so we sanitize
+        // them out, replacing those arguments with "<unavailable>".
+        let unavailString = "<unavailable>";
+        let unavailStringLength = unavailString.length * 2; // 2-bytes per char
 
-      // We can't send objects over the message manager, so we sanitize
-      // them out, replacing those arguments with "<unavailable>".
-      let unavailString = "<unavailable>";
-      let unavailStringLength = unavailString.length * 2; // 2-bytes per char
-
-      // When the sum of argument sizes reaches MSG_MGR_CONSOLE_MAX_SIZE,
-      // replace all arguments with "<truncated>".
-      let totalArgLength = 0;
+        // When the sum of argument sizes reaches MSG_MGR_CONSOLE_MAX_SIZE,
+        // replace all arguments with "<truncated>".
+        let totalArgLength = 0;
 
-      // Walk through the arguments, checking the type and size.
-      for (let arg of consoleMsg.arguments) {
-        if ((typeof arg == "object" || typeof arg == "function") &&
-            arg !== null) {
-          if (Services.appinfo.remoteType === E10SUtils.EXTENSION_REMOTE_TYPE) {
-            // For OOP extensions: we want the developer to be able to see the
-            // logs in the Browser Console. When the Addon Toolbox will be more
-            // prominent we can revisit.
-            try {
-              // If the argument is clonable, then send it as-is. If
-              // cloning fails, fall back to the unavailable string.
-              arg = Cu.cloneInto(arg, {});
-            } catch (e) {
+        // Walk through the arguments, checking the type and size.
+        for (let arg of consoleMsg.arguments) {
+          if ((typeof arg == "object" || typeof arg == "function") &&
+              arg !== null) {
+            if (Services.appinfo.remoteType === E10SUtils.EXTENSION_REMOTE_TYPE) {
+              // For OOP extensions: we want the developer to be able to see the
+              // logs in the Browser Console. When the Addon Toolbox will be more
+              // prominent we can revisit.
+              try {
+                // If the argument is clonable, then send it as-is. If
+                // cloning fails, fall back to the unavailable string.
+                arg = Cu.cloneInto(arg, {});
+              } catch (e) {
+                arg = unavailString;
+              }
+            } else {
               arg = unavailString;
             }
+            totalArgLength += unavailStringLength;
+          } else if (typeof arg == "string") {
+            totalArgLength += arg.length * 2; // 2-bytes per char
           } else {
-            arg = unavailString;
+            totalArgLength += MSG_MGR_CONSOLE_VAR_SIZE;
           }
-          totalArgLength += unavailStringLength;
-        } else if (typeof arg == "string") {
-          totalArgLength += arg.length * 2; // 2-bytes per char
-        } else {
-          totalArgLength += MSG_MGR_CONSOLE_VAR_SIZE;
+
+          if (totalArgLength <= MSG_MGR_CONSOLE_MAX_SIZE) {
+            msgData.arguments.push(arg);
+          } else {
+            // arguments take up too much space
+            msgData.arguments = ["<truncated>"];
+            break;
+          }
         }
 
-        if (totalArgLength <= MSG_MGR_CONSOLE_MAX_SIZE) {
-          msgData.arguments.push(arg);
-        } else {
-          // arguments take up too much space
-          msgData.arguments = ["<truncated>"];
-          break;
-        }
+        cpmm.sendAsyncMessage("Console:Log", msgData);
+        break;
       }
 
-      cpmm.sendAsyncMessage("Console:Log", msgData);
-      break;
-    }
-
-    case "xpcom-shutdown":
-      Services.obs.removeObserver(this, "console-api-log-event");
-      Services.obs.removeObserver(this, "xpcom-shutdown");
-      break;
+      case "xpcom-shutdown":
+        this.uninit();
+        break;
     }
   },
+
+  uninit() {
+    Services.obs.removeObserver(this, "console-api-log-event");
+    Services.obs.removeObserver(this, "xpcom-shutdown");
+    cpmm.removeMessageListener("DevTools:StopForwardingContentProcessMessage", this);
+  }
 };
 
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ContentProcessSingleton]);
+// loadProcessScript loads in all processes, including the parent,
+// in which we don't need any forwarding
+if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
+  new ContentProcessForward();
+}
+
rename from devtools/server/actors/utils/webconsole-listeners.js
rename to devtools/server/actors/webconsole/listeners.js
--- a/devtools/server/actors/utils/webconsole-listeners.js
+++ b/devtools/server/actors/webconsole/listeners.js
@@ -3,23 +3,26 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {Cc, Ci, components} = require("chrome");
 const {isWindowIncluded} = require("devtools/shared/layout/utils");
 const Services = require("Services");
 const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
-const {CONSOLE_WORKER_IDS, WebConsoleUtils} = require("devtools/server/actors/utils/webconsole-utils");
+const {CONSOLE_WORKER_IDS, WebConsoleUtils} = require("devtools/server/actors/webconsole/utils");
 
 XPCOMUtils.defineLazyServiceGetter(this,
                                    "swm",
                                    "@mozilla.org/serviceworkers/manager;1",
                                    "nsIServiceWorkerManager");
 
+// Process script used to forward console calls from content processes to parent process
+const CONTENT_PROCESS_SCRIPT = "resource://devtools/server/actors/webconsole/content-process-forward.js";
+
 // The page errors listener
 
 /**
  * The nsIConsoleService listener. This is used to send all of the console
  * messages (JavaScript, CSS and more) to the remote Web Console instance.
  *
  * @constructor
  * @param nsIDOMWindow [window]
@@ -446,8 +449,44 @@ ConsoleReflowListener.prototype =
   /**
    * Unregister listener.
    */
   destroy: function () {
     this.docshell.removeWeakReflowObserver(this);
     this.listener = this.docshell = null;
   },
 };
+
+/**
+ * Forward console message calls from content processes to the parent process.
+ * Used by Browser console and toolbox to see messages from all processes.
+ *
+ * @constructor
+ * @param object owner
+ *        The listener owner which needs to implement:
+ *        - onConsoleAPICall(message)
+ */
+function ContentProcessListener(listener) {
+  this.listener = listener;
+
+  Services.ppmm.addMessageListener("Console:Log", this);
+  Services.ppmm.loadProcessScript(CONTENT_PROCESS_SCRIPT, true);
+}
+
+exports.ContentProcessListener = ContentProcessListener;
+
+ContentProcessListener.prototype = {
+  receiveMessage(message) {
+    let logMsg = message.data;
+    logMsg.wrappedJSObject = logMsg;
+    this.listener.onConsoleAPICall(logMsg);
+  },
+
+  destroy() {
+    // Tell the content processes to stop listening and forwarding messages
+    Services.ppmm.broadcastAsyncMessage("DevTools:StopForwardingContentProcessMessage");
+
+    Services.ppmm.removeMessageListener("Console:Log", this);
+    Services.ppmm.removeDelayedProcessScript(CONTENT_PROCESS_SCRIPT);
+
+    this.listener = null;
+  }
+};
copy from devtools/server/actors/utils/moz.build
copy to devtools/server/actors/webconsole/moz.build
--- a/devtools/server/actors/utils/moz.build
+++ b/devtools/server/actors/webconsole/moz.build
@@ -1,21 +1,12 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DevToolsModules(
-    'actor-registry-utils.js',
-    'audionodes.json',
-    'automation-timeline.js',
-    'css-grid-utils.js',
-    'make-debugger.js',
-    'map-uri-to-addon-id.js',
-    'shapes-geometry-utils.js',
-    'stack.js',
-    'TabSources.js',
-    'walker-search.js',
-    'webconsole-listeners.js',
-    'webconsole-utils.js',
-    'webconsole-worker-listeners.js',
+    'content-process-forward.js',
+    'listeners.js',
+    'utils.js',
+    'worker-listeners.js',
 )
rename from devtools/server/actors/utils/webconsole-utils.js
rename to devtools/server/actors/webconsole/utils.js
rename from devtools/server/actors/utils/webconsole-worker-listeners.js
rename to devtools/server/actors/webconsole/worker-listeners.js
--- a/devtools/server/tests/mochitest/chrome.ini
+++ b/devtools/server/tests/mochitest/chrome.ini
@@ -29,16 +29,17 @@ support-files =
   webconsole-helpers.js
   webextension-helpers.js
 [test_animation_actor-lifetime.html]
 [test_connection-manager.html]
 [test_connectToChild.html]
 [test_css-logic.html]
 [test_css-logic-media-queries.html]
 [test_css-logic-specificity.html]
+fail-if = stylo # bug 1387905
 [test_css-properties.html]
 [test_Debugger.Source.prototype.introductionScript.html]
 [test_Debugger.Source.prototype.introductionType.html]
 [test_Debugger.Source.prototype.element.html]
 [test_Debugger.Script.prototype.global.html]
 [test_device.html]
 [test_executeInGlobal-outerized_this.html]
 [test_framerate_01.html]
--- a/devtools/shared/tests/unit/test_console_filtering.js
+++ b/devtools/shared/tests/unit/test_console_filtering.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const { console, ConsoleAPI } = require("resource://gre/modules/Console.jsm");
-const { ConsoleAPIListener } = require("devtools/server/actors/utils/webconsole-listeners");
+const { ConsoleAPIListener } = require("devtools/server/actors/webconsole/listeners");
 const Services = require("Services");
 
 var seenMessages = 0;
 var seenTypes = 0;
 
 var callback = {
   onConsoleAPICall: function (message) {
     if (message.consoleID && message.consoleID == "addon/foo") {
--- a/devtools/shared/webconsole/test/test_cached_messages.html
+++ b/devtools/shared/webconsole/test/test_cached_messages.html
@@ -92,17 +92,17 @@ function doConsoleCalls() {
     },
   ];
 }
 </script>
 
 <script class="testbody" type="text/javascript">
 SimpleTest.waitForExplicitFinish();
 var {ConsoleServiceListener, ConsoleAPIListener} =
-  require("devtools/server/actors/utils/webconsole-listeners");
+  require("devtools/server/actors/webconsole/listeners");
 
 let consoleAPIListener, consoleServiceListener;
 let consoleAPICalls = 0;
 let pageErrors = 0;
 
 let handlers = {
   onConsoleAPICall: function onConsoleAPICall(message) {
     for (let msg of expectedConsoleCalls) {
--- a/devtools/shared/webconsole/test/test_commands_registration.html
+++ b/devtools/shared/webconsole/test/test_commands_registration.html
@@ -13,17 +13,17 @@
 <p id="quack"></p>
 
 <script class="testbody" type="text/javascript">
 SimpleTest.waitForExplicitFinish();
 
 let gState;
 let tests;
 
-let {WebConsoleCommands} = require("devtools/server/actors/utils/webconsole-utils");
+let {WebConsoleCommands} = require("devtools/server/actors/webconsole/utils");
 
 function evaluateJS(input) {
   return new Promise((resolve) => gState.client.evaluateJS(input, resolve));
 }
 
 function* evaluateJSAndCheckResult(input, result) {
   let response = yield evaluateJS(input);
   checkObject(response, {result});
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -4118,18 +4118,21 @@ Element::UpdateIntersectionObservation(D
     updated = entry.Data() != aThreshold;
     entry.Data() = aThreshold;
   }
   return updated;
 }
 
 void
 Element::ClearServoData() {
+  MOZ_ASSERT(IsStyledByServo());
 #ifdef MOZ_STYLO
   Servo_Element_ClearData(this);
+  UnsetFlags(ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO |
+             ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO);
 #else
   MOZ_CRASH("Accessing servo node data in non-stylo build");
 #endif
 }
 
 void
 Element::SetCustomElementData(CustomElementData* aData)
 {
--- a/dom/base/Selection.cpp
+++ b/dom/base/Selection.cpp
@@ -600,24 +600,34 @@ Selection::GetTableCellLocationFromRange
 
   // Get the child content (the cell) pointed to by starting node of range
   // We do minimal checking since GetTableSelectionType assures
   //   us that this really is a table cell
   nsCOMPtr<nsIContent> content = do_QueryInterface(aRange->GetStartContainer());
   if (!content)
     return NS_ERROR_FAILURE;
 
-  nsIContent *child = content->GetChildAt(aRange->StartOffset());
+  nsCOMPtr<nsIContent> child = content->GetChildAt(aRange->StartOffset());
   if (!child)
     return NS_ERROR_FAILURE;
 
+  // GetCellLayout depends on current frame, we need flush frame to get
+  // nsITableCellLayout
+  nsCOMPtr<nsIPresShell> presShell = mFrameSelection->GetShell();
+  if (presShell) {
+    presShell->FlushPendingNotifications(FlushType::Frames);
+
+    // Since calling FlushPendingNotifications, so check whether disconnected.
+    if (!mFrameSelection || !mFrameSelection->GetShell()) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+
   //Note: This is a non-ref-counted pointer to the frame
   nsITableCellLayout *cellLayout = mFrameSelection->GetCellLayout(child);
-  if (NS_FAILED(result))
-    return result;
   if (!cellLayout)
     return NS_ERROR_FAILURE;
 
   return cellLayout->GetCellIndexes(*aRow, *aCol);
 }
 
 nsresult
 Selection::AddTableCellRange(nsRange* aRange, bool* aDidAddRange,
@@ -2271,16 +2281,19 @@ Selection::AddRangeInternal(nsRange& aRa
   if (aDocument != rangeRoot && (!rangeRoot ||
                                  aDocument != rangeRoot->GetComposedDoc())) {
     // http://w3c.github.io/selection-api/#dom-selection-addrange
     // "...  if the root of the range's boundary points are the document
     // associated with context object. Otherwise, this method must do nothing."
     return;
   }
 
+  // AddTableCellRange might flush frame.
+  RefPtr<Selection> kungFuDeathGrip(this);
+
   // This inserts a table cell range in proper document order
   // and returns NS_OK if range doesn't contain just one table cell
   bool didAddRange;
   int32_t rangeIndex;
   nsresult result = AddTableCellRange(&aRange, &didAddRange, &rangeIndex);
   if (NS_FAILED(result)) {
     aRv.Throw(result);
     return;
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -13214,17 +13214,20 @@ nsIDocument::UpdateStyleBackendType()
   MOZ_ASSERT(mStyleBackendType == StyleBackendType::None,
              "no need to call UpdateStyleBackendType now");
 
   // Assume Gecko by default.
   mStyleBackendType = StyleBackendType::Gecko;
 
 #ifdef MOZ_STYLO
   if (nsLayoutUtils::StyloEnabled()) {
-    if (!mDocumentContainer) {
+    if (IsBeingUsedAsImage()) {
+      // Enable stylo for SVG-as-image.
+      mStyleBackendType = StyleBackendType::Servo;
+    } else if (!mDocumentContainer) {
       NS_WARNING("stylo: No docshell yet, assuming Gecko style system");
     } else if ((IsHTMLOrXHTML() || IsSVGDocument()) &&
                IsContentDocument()) {
       // Disable stylo for about: pages other than about:blank, since
       // they tend to use unsupported selectors like XUL tree pseudos.
       bool isAbout = false;
       mDocumentURI->SchemeIs("about", &isAbout);
       if (!isAbout || NS_IsAboutBlank(mDocumentURI)) {
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -308,16 +308,17 @@ skip-if = toolkit == 'android'
 skip-if = toolkit == 'android'
 [test_bug333198.html]
 [test_bug333673.html]
 [test_bug337631.html]
 [test_bug338541.xhtml]
 [test_bug338583.html]
 skip-if = toolkit == 'android'
 [test_bug338679.html]
+fail-if = stylo # bug 1370779
 [test_bug339494.html]
 [test_bug339494.xhtml]
 [test_bug340571.html]
 [test_bug343596.html]
 [test_bug345339.html]
 [test_bug346485.html]
 [test_bug352728.html]
 [test_bug352728.xhtml]
@@ -615,16 +616,17 @@ skip-if = toolkit == 'android'
 [test_bug1274806.html]
 [test_bug1281963.html]
 [test_bug1295852.html]
 [test_bug1307730.html]
 [test_bug1308069.html]
 [test_bug1314032.html]
 [test_bug1318303.html]
 [test_bug1375050.html]
+[test_bug1381710.html]
 [test_bug1384658.html]
 skip-if = toolkit == 'android'
 [test_caretPositionFromPoint.html]
 [test_change_policy.html]
 [test_clearTimeoutIntervalNoArg.html]
 [test_constructor-assignment.html]
 [test_constructor.html]
 [test_copyimage.html]
@@ -675,16 +677,17 @@ skip-if = (toolkit == 'android') # Andro
 [test_iframe_referrer.html]
 [test_iframe_referrer_changing.html]
 [test_iframe_referrer_invalid.html]
 [test_Image_constructor.html]
 [test_img_referrer.html]
 [test_innersize_scrollport.html]
 [test_integer_attr_with_leading_zero.html]
 [test_intersectionobservers.html]
+skip-if = stylo # timeout bug 1384001
 [test_link_prefetch.html]
 skip-if = !e10s # Track Bug 1281415
 [test_link_stylesheet.html]
 [test_messagemanager_targetchain.html]
 [test_meta_viewport0.html]
 skip-if = (os != 'android')    # meta-viewport tag support is mobile-only
 [test_meta_viewport1.html]
 skip-if = (os != 'android')    # meta-viewport tag support is mobile-only
@@ -698,16 +701,17 @@ skip-if = (os != 'android')    # meta-vi
 skip-if = (os != 'android')    # meta-viewport tag support is mobile-only
 [test_meta_viewport6.html]
 skip-if = (os != 'android')    # meta-viewport tag support is mobile-only
 [test_meta_viewport7.html]
 skip-if = (os != 'android')    # meta-viewport tag support is mobile-only
 [test_mozbrowser_apis_blocked.html]
 [test_mozMatchesSelector.html]
 [test_mutationobserver_anonymous.html]
+skip-if = stylo # bug 1383985
 [test_mutationobservers.html]
 [test_named_frames.html]
 [test_navigator_hardwareConcurrency.html]
 [test_navigator_language.html]
 [test_navigatorPrefOverride.html]
 [test_noAudioNotification.html]
 tags = audiochannel
 [test_noAudioNotificationOnMutedElement.html]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_bug1381710.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1381710
+-->
+<head>
+  <title>Test for Mozilla Bug 1381710</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1381710">Mozilla Bug 1381710</a>
+<div id="content">
+  <div id="nonedit"></div>
+  <div id="edit" contenteditable=true></div>
+</div>
+<pre id="test">
+<script type="application/javascript">
+function test(element)
+{
+  let selection = window.getSelection();
+  selection.removeAllRanges();
+  let range = document.createRange();
+
+  element.innerHTML = "<table><tr id=tr><td id=td>A</td><td>B</td><tr></table>";
+  let td = document.getElementById("td");
+  range.selectNode(td);
+  // Don't throw exception
+  selection.addRange(range);
+  is(selection.anchorNode, document.getElementById("tr"),
+     "anchor node should be <TR> element");
+  element.innerHTML = "";
+}
+
+test(document.getElementById("nonedit"));
+test(document.getElementById("edit"));
+</script>
+</pre>
+</body>
+</html>
--- a/dom/html/test/test_fullscreen-api.html
+++ b/dom/html/test/test_fullscreen-api.html
@@ -72,16 +72,25 @@ function shouldSkipTest(test) {
       navigator.platform.indexOf('Linux') >= 0) {
     for (let item of gLinuxE10sSkipList) {
       if (item.test == test) {
         todo(false, `${test} skipped due to ${item.reason}`);
         return true;
       }
     }
   }
+  if (SpecialPowers.DOMWindowUtils.isStyledByServo &&
+      SpecialPowers.isDebugBuild &&
+      navigator.platform.indexOf('Mac') >= 0) {
+    if (test == "file_fullscreen-backdrop.html") {
+      todo(false, `${test} skipped due to bug 1387942`);
+      return true;
+    }
+  }
+
   return false;
 }
 
 function waitForEvent(eventTarget, eventName, checkFn, callback) {
   eventTarget.addEventListener(eventName, function listener(event) {
     if (checkFn && !checkFn(event)) {
       return;
     }
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -68,16 +68,20 @@
 #include "nsBaseDragService.h"
 #include "mozilla/media/MediaChild.h"
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/WebBrowserPersistDocumentChild.h"
 #include "imgLoader.h"
 #include "GMPServiceChild.h"
 #include "NullPrincipal.h"
 
+#if !defined(XP_WIN)
+#include "mozilla/Omnijar.h"
+#endif
+
 #ifdef MOZ_GECKO_PROFILER
 #include "ChildProfilerController.h"
 #endif
 
 #if defined(MOZ_CONTENT_SANDBOX)
 #include "mozilla/SandboxSettings.h"
 #if defined(XP_WIN)
 #define TARGET_SANDBOX_EXPORTS
@@ -167,16 +171,24 @@
 
 #ifdef XP_WIN
 #include <process.h>
 #define getpid _getpid
 #include "mozilla/widget/AudioSession.h"
 #include "mozilla/audio/AudioNotificationReceiver.h"
 #endif
 
+#if defined(XP_MACOSX)
+#include <CoreServices/CoreServices.h>
+// Info.plist key associated with the developer repo path
+#define MAC_DEV_REPO_KEY "MozillaDeveloperRepoPath"
+// Info.plist key associated with the developer repo object directory
+#define MAC_DEV_OBJ_KEY "MozillaDeveloperObjPath"
+#endif /* XP_MACOSX */
+
 #ifdef MOZ_X11
 #include "mozilla/X11Util.h"
 #endif
 
 #ifdef ACCESSIBILITY
 #include "nsAccessibilityService.h"
 #ifdef XP_WIN
 #include "mozilla/a11y/AccessibleWrap.h"
@@ -3558,9 +3570,130 @@ ContentChild::GetSpecificMessageEventTar
       || aMsg.type() == PJavaScript::Msg_DropObject__ID) {
     return do_AddRef(SystemGroup::EventTargetFor(TaskCategory::Other));
   }
 
   return nullptr;
 }
 
 } // namespace dom
+
+#if !defined(XP_WIN)
+bool IsDevelopmentBuild()
+{
+  nsCOMPtr<nsIFile> path = mozilla::Omnijar::GetPath(mozilla::Omnijar::GRE);
+  // If the path doesn't exist, we're a dev build.
+  return path == nullptr;
+}
+#endif /* !XP_WIN */
+
+#if defined(XP_MACOSX)
+/*
+ * Helper function to read a string value for a given key from the .app's
+ * Info.plist.
+ */
+static nsresult
+GetStringValueFromBundlePlist(const nsAString& aKey, nsAutoCString& aValue)
+{
+  CFBundleRef mainBundle = CFBundleGetMainBundle();
+  if (mainBundle == nullptr) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // Read this app's bundle Info.plist as a dictionary
+  CFDictionaryRef bundleInfoDict = CFBundleGetInfoDictionary(mainBundle);
+  if (bundleInfoDict == nullptr) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsAutoCString keyAutoCString = NS_ConvertUTF16toUTF8(aKey);
+  CFStringRef key = CFStringCreateWithCString(kCFAllocatorDefault,
+                                              keyAutoCString.get(),
+                                              kCFStringEncodingUTF8);
+  if (key == nullptr) {
+    return NS_ERROR_FAILURE;
+  }
+
+  CFStringRef value = (CFStringRef)CFDictionaryGetValue(bundleInfoDict, key);
+  CFRelease(key);
+  if (value == nullptr) {
+    return NS_ERROR_FAILURE;
+  }
+
+  CFIndex valueLength = CFStringGetLength(value);
+  if (valueLength == 0) {
+    return NS_ERROR_FAILURE;
+  }
+
+  const char* valueCString = CFStringGetCStringPtr(value,
+                                                   kCFStringEncodingUTF8);
+  if (valueCString) {
+    aValue.Assign(valueCString);
+    return NS_OK;
+  }
+
+  CFIndex maxLength =
+    CFStringGetMaximumSizeForEncoding(valueLength, kCFStringEncodingUTF8) + 1;
+  char* valueBuffer = static_cast<char*>(moz_xmalloc(maxLength));
+  if (!valueBuffer) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  if (!CFStringGetCString(value, valueBuffer, maxLength,
+                          kCFStringEncodingUTF8)) {
+    free(valueBuffer);
+    return NS_ERROR_FAILURE;
+  }
+
+  aValue.Assign(valueBuffer);
+  free(valueBuffer);
+  return NS_OK;
+}
+
+/*
+ * Helper function for reading a path string from the .app's Info.plist
+ * and returning a directory object for that path with symlinks resolved.
+ */
+static nsresult
+GetDirFromBundlePlist(const nsAString& aKey, nsIFile **aDir)
+{
+  nsresult rv;
+
+  nsAutoCString dirPath;
+  rv = GetStringValueFromBundlePlist(aKey, dirPath);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIFile> dir;
+  rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(dirPath),
+                       false,
+                       getter_AddRefs(dir));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = dir->Normalize();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  bool isDirectory = false;
+  rv = dir->IsDirectory(&isDirectory);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (!isDirectory) {
+    return NS_ERROR_FILE_NOT_DIRECTORY;
+  }
+
+  dir.swap(*aDir);
+  return NS_OK;
+}
+
+nsresult
+GetRepoDir(nsIFile **aRepoDir)
+{
+  MOZ_ASSERT(IsDevelopmentBuild());
+  return GetDirFromBundlePlist(NS_LITERAL_STRING(MAC_DEV_REPO_KEY), aRepoDir);
+}
+
+nsresult
+GetObjDir(nsIFile **aObjDir)
+{
+  MOZ_ASSERT(IsDevelopmentBuild());
+  return GetDirFromBundlePlist(NS_LITERAL_STRING(MAC_DEV_OBJ_KEY), aObjDir);
+}
+#endif /* XP_MACOSX */
+
 } // namespace mozilla
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -35,16 +35,33 @@ class nsIURIClassifierCallback;
 struct LookAndFeelInt;
 
 namespace mozilla {
 class RemoteSpellcheckEngineChild;
 class ChildProfilerController;
 
 using mozilla::loader::PScriptCacheChild;
 
+#if !defined(XP_WIN)
+// Returns whether or not the currently running build is an unpackaged
+// developer build. This check is implemented by looking for omni.ja in the
+// the obj/dist dir. We use this routine to detect when the build dir will
+// use symlinks to the repo and object dir. On Windows, dev builds don't
+// use symlinks.
+bool IsDevelopmentBuild();
+#endif /* !XP_WIN */
+
+#if defined(XP_MACOSX)
+// Return the repo directory and the repo object directory respectively. These
+// should only be used on Mac developer builds to determine the path to the
+// repo or object directory.
+nsresult GetRepoDir(nsIFile **aRepoDir);
+nsresult GetObjDir(nsIFile **aObjDir);
+#endif /* XP_MACOSX */
+
 namespace ipc {
 class OptionalURIParams;
 class URIParams;
 }// namespace ipc
 
 namespace dom {
 
 class AlertObserver;
--- a/dom/media/BufferMediaResource.h
+++ b/dom/media/BufferMediaResource.h
@@ -38,18 +38,16 @@ protected:
 private:
   // Get the current principal for the channel
   already_AddRefed<nsIPrincipal> GetCurrentPrincipal() override
   {
     nsCOMPtr<nsIPrincipal> principal = mPrincipal;
     return principal.forget();
   }
   // These methods are called off the main thread.
-  // The mode is initially MODE_PLAYBACK.
-  void SetReadMode(MediaCacheStream::ReadMode aMode) override {}
   nsresult ReadAt(int64_t aOffset, char* aBuffer,
                   uint32_t aCount, uint32_t* aBytes) override
   {
     if (aOffset < 0 || aOffset > mLength) {
       return NS_ERROR_FAILURE;
     }
     *aBytes = std::min(mLength - static_cast<uint32_t>(aOffset), aCount);
     memcpy(aBuffer, mBuffer + aOffset, *aBytes);
--- a/dom/media/ChannelMediaDecoder.cpp
+++ b/dom/media/ChannelMediaDecoder.cpp
@@ -249,16 +249,19 @@ ChannelMediaDecoder::Load(nsIChannel* aC
   nsresult rv = MediaShutdownManager::Instance().Register(this);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   rv = OpenResource(aStreamListener);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  // Set mode to METADATA since we are about to read metadata.
+  mResource->SetReadMode(MediaCacheStream::MODE_METADATA);
+
   SetStateMachine(CreateStateMachine());
   NS_ENSURE_TRUE(GetStateMachine(), NS_ERROR_FAILURE);
 
   return InitializeStateMachine();
 }
 
 nsresult
 ChannelMediaDecoder::Load(BaseMediaResource* aOriginal)
@@ -496,12 +499,23 @@ void
 ChannelMediaDecoder::Resume()
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mResource) {
     mResource->Resume();
   }
 }
 
+void
+ChannelMediaDecoder::MetadataLoaded(
+  UniquePtr<MediaInfo> aInfo,
+  UniquePtr<MetadataTags> aTags,
+  MediaDecoderEventVisibility aEventVisibility)
+{
+  MediaDecoder::MetadataLoaded(Move(aInfo), Move(aTags), aEventVisibility);
+  // Set mode to PLAYBACK after reading metadata.
+  mResource->SetReadMode(MediaCacheStream::MODE_PLAYBACK);
+}
+
 } // namespace mozilla
 
 // avoid redefined macro in unified build
 #undef LOG
--- a/dom/media/ChannelMediaDecoder.h
+++ b/dom/media/ChannelMediaDecoder.h
@@ -52,16 +52,20 @@ class ChannelMediaDecoder : public Media
     bool mTimerArmed = false;
     const RefPtr<AbstractThread> mAbstractMainThread;
   };
 
 protected:
   void OnPlaybackEvent(MediaEventType aEvent) override;
   void DurationChanged() override;
   void DownloadProgressed() override;
+  void MetadataLoaded(UniquePtr<MediaInfo> aInfo,
+                      UniquePtr<MetadataTags> aTags,
+                      MediaDecoderEventVisibility aEventVisibility) override;
+
   RefPtr<ResourceCallback> mResourceCallback;
   RefPtr<BaseMediaResource> mResource;
 
 public:
   explicit ChannelMediaDecoder(MediaDecoderInit& aInit);
 
   MediaDecoderStateMachine* CreateStateMachine() override;
 
--- a/dom/media/MediaCache.cpp
+++ b/dom/media/MediaCache.cpp
@@ -2120,24 +2120,16 @@ MediaCacheStream::Close()
   CloseInternal(mon);
   // Queue an Update since we may have created more free space. Don't do
   // it from CloseInternal since that gets called by Update() itself
   // sometimes, and we try to not to queue updates from Update().
   mMediaCache->QueueUpdate();
 }
 
 void
-MediaCacheStream::EnsureCacheUpdate()
-{
-  if (mHasHadUpdate)
-    return;
-  mMediaCache->Update();
-}
-
-void
 MediaCacheStream::CloseInternal(ReentrantMonitorAutoEnter& aReentrantMonitor)
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
   if (mClosed)
     return;
   mClosed = true;
   // Closing a stream will change the return value of
--- a/dom/media/MediaCache.h
+++ b/dom/media/MediaCache.h
@@ -227,21 +227,16 @@ public:
   bool IsAvailableForSharing() const
   {
     return !mClosed && !mIsPrivateBrowsing &&
       (!mDidNotifyDataEnded || NS_SUCCEEDED(mNotifyDataEndedStatus));
   }
   // Get the principal for this stream. Anything accessing the contents of
   // this stream must have a principal that subsumes this principal.
   nsIPrincipal* GetCurrentPrincipal() { return mPrincipal; }
-  // Ensure a global media cache update has run with this stream present.
-  // This ensures the cache has had a chance to suspend or unsuspend this stream.
-  // Called only on main thread. This can change the state of streams, fire
-  // notifications, etc.
-  void EnsureCacheUpdate();
 
   // These callbacks are called on the main thread by the client
   // when data has been received via the channel.
   // Tells the cache what the server said the data length is going to be.
   // The actual data length may be greater (we receive more data than
   // specified) or smaller (the stream ends before we reach the given
   // length), because servers can lie. The server's reported data length
   // *and* the actual data length can even vary over time because a
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -807,31 +807,24 @@ MediaDecoder::FirstFrameLoaded(nsAutoPtr
       "mPlayState=%s",
       aInfo->mAudio.mChannels, aInfo->mAudio.mRate, aInfo->HasAudio(),
       aInfo->HasVideo(), PlayStateStr());
 
   mInfo = aInfo.forget();
 
   Invalidate();
 
-  // This can run cache callbacks.
-  GetResource()->EnsureCacheUpToDate();
-
   // The element can run javascript via events
   // before reaching here, so only change the
   // state if we're still set to the original
   // loading state.
   if (mPlayState == PLAY_STATE_LOADING) {
     ChangeState(mNextState);
   }
 
-  // Run NotifySuspendedStatusChanged now to give us a chance to notice
-  // that autoplay should run.
-  NotifySuspendedStatusChanged();
-
   // GetOwner()->FirstFrameLoaded() might call us back. Put it at the bottom of
   // this function to avoid unexpected shutdown from reentrant calls.
   if (aEventVisibility != MediaDecoderEventVisibility::Suppressed) {
     GetOwner()->FirstFrameLoaded();
   }
 }
 
 void
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -445,16 +445,22 @@ protected:
 
     // We Invoke DurationChanged explicitly, rather than using a watcher, so
     // that it takes effect immediately, rather than at the end of the current task.
     DurationChanged();
   }
 
   virtual void OnPlaybackEvent(MediaEventType aEvent);
 
+  // Called when the metadata from the media file has been loaded by the
+  // state machine. Call on the main thread only.
+  virtual void MetadataLoaded(UniquePtr<MediaInfo> aInfo,
+                              UniquePtr<MetadataTags> aTags,
+                              MediaDecoderEventVisibility aEventVisibility);
+
   /******
    * The following members should be accessed with the decoder lock held.
    ******/
 
   // The logical playback position of the media resource in units of
   // seconds. This corresponds to the "official position" in HTML5. Note that
   // we need to store this as a double, rather than an int64_t (like
   // mCurrentPosition), so that |v.currentTime = foo; v.currentTime == foo|
@@ -485,22 +491,16 @@ protected:
   // the next frame is available.
   // An arbitrary value of 250ms is used.
   static constexpr auto DEFAULT_NEXT_FRAME_AVAILABLE_BUFFERED =
     media::TimeUnit::FromMicroseconds(250000);
 
 private:
   nsCString GetDebugInfo();
 
-  // Called when the metadata from the media file has been loaded by the
-  // state machine. Call on the main thread only.
-  void MetadataLoaded(UniquePtr<MediaInfo> aInfo,
-                      UniquePtr<MetadataTags> aTags,
-                      MediaDecoderEventVisibility aEventVisibility);
-
   // Called when the owner's activity changed.
   void NotifyCompositor();
 
   void OnPlaybackErrorEvent(const MediaResult& aError);
 
   void OnDecoderDoctorEvent(DecoderDoctorEvent aEvent);
 
   void OnMediaNotSeekable()
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -336,19 +336,16 @@ public:
   explicit DecodeMetadataState(Master* aPtr) : StateObject(aPtr) { }
 
   void Enter()
   {
     MOZ_ASSERT(!mMaster->mVideoDecodeSuspended);
     MOZ_ASSERT(!mMetadataRequest.Exists());
     SLOG("Dispatching AsyncReadMetadata");
 
-    // Set mode to METADATA since we are about to read metadata.
-    Resource()->SetReadMode(MediaCacheStream::MODE_METADATA);
-
     // We disconnect mMetadataRequest in Exit() so it is fine to capture
     // a raw pointer here.
     Reader()->ReadMetadata()
       ->Then(OwnerThread(), __func__,
         [this] (MetadataHolder&& aMetadata) {
           OnMetadataRead(Move(aMetadata));
         },
         [this] (const MediaResult& aError) {
@@ -1862,27 +1859,33 @@ public:
   }
 
   void Step() override;
 
   State GetState() const override { return DECODER_STATE_BUFFERING; }
 
   void HandleAudioDecoded(AudioData* aAudio) override
   {
+    mMaster->PushAudio(aAudio);
+    if (!mMaster->HaveEnoughDecodedAudio()) {
+      mMaster->RequestAudioData();
+    }
     // This might be the sample we need to exit buffering.
     // Schedule Step() to check it.
-    mMaster->PushAudio(aAudio);
     mMaster->ScheduleStateMachine();
   }
 
   void HandleVideoDecoded(VideoData* aVideo, TimeStamp aDecodeStart) override
   {
+    mMaster->PushVideo(aVideo);
+    if (!mMaster->HaveEnoughDecodedVideo()) {
+      mMaster->RequestVideoData(media::TimeUnit());
+    }
     // This might be the sample we need to exit buffering.
     // Schedule Step() to check it.
-    mMaster->PushVideo(aVideo);
     mMaster->ScheduleStateMachine();
   }
 
   void HandleAudioCanceled() override { mMaster->RequestAudioData(); }
 
   void HandleVideoCanceled() override
   {
     mMaster->RequestVideoData(media::TimeUnit());
@@ -1919,18 +1922,16 @@ public:
     }
 
     mMaster->mVideoDecodeSuspended = true;
     mMaster->mOnPlaybackEvent.Notify(MediaEventType::EnterVideoSuspend);
     Reader()->SetVideoBlankDecode(true);
   }
 
 private:
-  void DispatchDecodeTasksIfNeeded();
-
   TimeStamp mBufferingStart;
 
   // The maximum number of second we spend buffering when we are short on
   // unbuffered data.
   const uint32_t mBufferingWait = 15;
 };
 
 /**
@@ -2219,19 +2220,16 @@ StateObject::SetSeekingState(SeekJob&& a
 }
 
 void
 MediaDecoderStateMachine::
 DecodeMetadataState::OnMetadataRead(MetadataHolder&& aMetadata)
 {
   mMetadataRequest.Complete();
 
-  // Set mode to PLAYBACK after reading metadata.
-  Resource()->SetReadMode(MediaCacheStream::MODE_PLAYBACK);
-
   mMaster->mInfo.emplace(*aMetadata.mInfo);
   mMaster->mMediaSeekable = Info().mMediaSeekable;
   mMaster->mMediaSeekableOnlyInBufferedRanges =
     Info().mMediaSeekableOnlyInBufferedRanges;
 
   if (Info().mMetadataDuration.isSome()) {
     mMaster->mDuration = Info().mMetadataDuration;
   } else if (Info().mUnadjustedMetadataEndTime.isSome()) {
@@ -2539,35 +2537,16 @@ SeekingState::SeekCompleted()
     mMaster->mOnPlaybackEvent.Notify(MediaEventType::Invalidate);
   }
 
   GoToNextState();
 }
 
 void
 MediaDecoderStateMachine::
-BufferingState::DispatchDecodeTasksIfNeeded()
-{
-  if (mMaster->IsAudioDecoding()
-      && !mMaster->HaveEnoughDecodedAudio()
-      && !mMaster->IsRequestingAudioData()
-      && !mMaster->IsWaitingAudioData()) {
-    mMaster->RequestAudioData();
-  }
-
-  if (mMaster->IsVideoDecoding()
-      && !mMaster->HaveEnoughDecodedVideo()
-      && !mMaster->IsRequestingVideoData()
-      && !mMaster->IsWaitingVideoData()) {
-    mMaster->RequestVideoData(media::TimeUnit());
-  }
-}
-
-void
-MediaDecoderStateMachine::
 BufferingState::Step()
 {
   TimeStamp now = TimeStamp::Now();
   MOZ_ASSERT(!mBufferingStart.IsNull(), "Must know buffering start time.");
 
   // With buffering heuristics we will remain in the buffering state if
   // we've not decoded enough data to begin playback, or if we've not
   // downloaded a reasonable amount of data inside our buffering time.
@@ -2577,21 +2556,19 @@ BufferingState::Step()
     if ((isLiveStream || !mMaster->mCanPlayThrough)
         && elapsed
            < TimeDuration::FromSeconds(mBufferingWait * mMaster->mPlaybackRate)
         && mMaster->HasLowBufferedData(TimeUnit::FromSeconds(mBufferingWait))
         && IsExpectingMoreData()) {
       SLOG("Buffering: wait %ds, timeout in %.3lfs",
            mBufferingWait, mBufferingWait - elapsed.ToSeconds());
       mMaster->ScheduleStateMachineIn(TimeUnit::FromMicroseconds(USECS_PER_S));
-      DispatchDecodeTasksIfNeeded();
       return;
     }
   } else if (mMaster->OutOfDecodedAudio() || mMaster->OutOfDecodedVideo()) {
-    DispatchDecodeTasksIfNeeded();
     MOZ_ASSERT(!mMaster->OutOfDecodedAudio()
                || mMaster->IsRequestingAudioData()
                || mMaster->IsWaitingAudioData());
     MOZ_ASSERT(!mMaster->OutOfDecodedVideo()
                || mMaster->IsRequestingVideoData()
                || mMaster->IsWaitingVideoData());
     SLOG("In buffering mode, waiting to be notified: outOfAudio: %d, "
          "mAudioStatus: %s, outOfVideo: %d, mVideoStatus: %s",
--- a/dom/media/MediaResource.cpp
+++ b/dom/media/MediaResource.cpp
@@ -970,22 +970,16 @@ ChannelMediaResource::GetCachedDataEnd(i
 }
 
 bool
 ChannelMediaResource::IsDataCachedToEndOfResource(int64_t aOffset)
 {
   return mCacheStream.IsDataCachedToEndOfStream(aOffset);
 }
 
-void
-ChannelMediaResource::EnsureCacheUpToDate()
-{
-  mCacheStream.EnsureCacheUpdate();
-}
-
 bool
 ChannelMediaResource::IsSuspendedByCache()
 {
   return mCacheStream.AreAllStreamsForResourceSuspended();
 }
 
 bool
 ChannelMediaResource::IsSuspended()
--- a/dom/media/MediaResource.h
+++ b/dom/media/MediaResource.h
@@ -159,18 +159,16 @@ public:
   // released on a non main thread, but the destructor will always run on
   // the main thread.
   NS_DECL_THREADSAFE_ISUPPORTS
 
   // Get the current principal for the channel
   virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal() = 0;
 
   // These methods are called off the main thread.
-  // The mode is initially MODE_PLAYBACK.
-  virtual void SetReadMode(MediaCacheStream::ReadMode aMode) = 0;
   // Read up to aCount bytes from the stream. The read starts at
   // aOffset in the stream, seeking to that location initially if
   // it is not the current stream offset. The remaining arguments,
   // results and requirements are the same as per the Read method.
   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
@@ -189,19 +187,16 @@ public:
     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;
-  // Ensures that the value returned by IsSuspendedByCache below is up to date
-  // (i.e. the cache has examined this stream at least once).
-  virtual void EnsureCacheUpToDate() {}
 
   // These can be called on any thread.
   // Cached blocks associated with this stream will not be evicted
   // while the stream is pinned.
   virtual void Pin() = 0;
   virtual void Unpin() = 0;
   // Get the length of the stream in bytes. Returns -1 if not known.
   // This can change over time; after a seek operation, a misbehaving
@@ -320,16 +315,19 @@ public:
   // since we don't expect to resume again any time soon. Otherwise we
   // may resume again soon so resources should be held for a little
   // while.
   virtual void Suspend(bool aCloseImmediately) = 0;
 
   // Resume any downloads that have been suspended.
   virtual void Resume() = 0;
 
+  // The mode is initially MODE_PLAYBACK.
+  virtual void SetReadMode(MediaCacheStream::ReadMode aMode) = 0;
+
   /**
    * Open the stream. This creates a stream listener and returns it in
    * aStreamListener; this listener needs to be notified of incoming data.
    */
   virtual nsresult Open(nsIStreamListener** aStreamListener) = 0;
 
   // If this returns false, then we shouldn't try to clone this MediaResource
   // because its underlying resources are not suitable for reuse (e.g.
@@ -502,17 +500,16 @@ public:
   void     Resume() override;
   already_AddRefed<nsIPrincipal> GetCurrentPrincipal() override;
   // Return true if the stream has been closed.
   bool     IsClosed() const { return mCacheStream.IsClosed(); }
   bool     CanClone() override;
   already_AddRefed<BaseMediaResource> CloneData(
     MediaResourceCallback* aDecoder) override;
   nsresult ReadFromCache(char* aBuffer, int64_t aOffset, uint32_t aCount) override;
-  void     EnsureCacheUpToDate() override;
 
   // Other thread
   void     SetReadMode(MediaCacheStream::ReadMode aMode) override;
   void     SetPlaybackRate(uint32_t aBytesPerSecond) override;
   nsresult ReadAt(int64_t offset, char* aBuffer,
                   uint32_t aCount, uint32_t* aBytes) override;
   // Data stored in IO&lock-encumbered MediaCacheStream, caching recommended.
   bool ShouldCacheReads() override { return true; }
--- a/dom/media/gtest/MockMediaResource.h
+++ b/dom/media/gtest/MockMediaResource.h
@@ -15,17 +15,16 @@ namespace mozilla
 class MockMediaResource : public MediaResource
 {
 public:
   explicit MockMediaResource(const char* aFileName);
   already_AddRefed<nsIPrincipal> GetCurrentPrincipal() override
   {
     return nullptr;
   }
-  void SetReadMode(MediaCacheStream::ReadMode aMode) override {}
   nsresult ReadAt(int64_t aOffset, char* aBuffer, uint32_t aCount,
                   uint32_t* aBytes) override;
   // Data stored in file, caching recommended.
   bool ShouldCacheReads() override { return true; }
   int64_t Tell() override { return 0; }
   void Pin() override {}
   void Unpin() override {}
   int64_t GetLength() override;
--- a/dom/media/hls/HLSResource.h
+++ b/dom/media/hls/HLSResource.h
@@ -43,17 +43,16 @@ private:
 
 class HLSResource final : public MediaResource
 {
 public:
   HLSResource(HLSDecoder* aDecoder, nsIChannel* aChannel, nsIURI* aURI);
   ~HLSResource();
   void Suspend();
   void Resume();
-  void SetReadMode(MediaCacheStream::ReadMode aMode) override { UNIMPLEMENTED(); }
   nsresult ReadAt(int64_t aOffset, char* aBuffer, uint32_t aCount, uint32_t* aBytes) override { UNIMPLEMENTED(); return NS_ERROR_FAILURE; }
   bool ShouldCacheReads() override { UNIMPLEMENTED(); return false; }
   int64_t Tell() override { UNIMPLEMENTED(); return -1; }
   void Pin() override { UNIMPLEMENTED(); }
   void Unpin() override { UNIMPLEMENTED(); }
   int64_t GetLength() override { UNIMPLEMENTED(); return -1; }
   int64_t GetNextCachedData(int64_t aOffset) override { UNIMPLEMENTED(); return -1; }
   int64_t GetCachedDataEnd(int64_t aOffset) override { UNIMPLEMENTED(); return -1; }
--- a/dom/media/mediasource/MediaSourceResource.h
+++ b/dom/media/mediasource/MediaSourceResource.h
@@ -27,17 +27,16 @@ class MediaSourceResource final : public
 {
 public:
   explicit MediaSourceResource(nsIPrincipal* aPrincipal = nullptr)
     : mPrincipal(aPrincipal)
     , mMonitor("MediaSourceResource")
     , mEnded(false)
     {}
 
-  void SetReadMode(MediaCacheStream::ReadMode aMode) override { UNIMPLEMENTED(); }
   nsresult ReadAt(int64_t aOffset, char* aBuffer, uint32_t aCount, uint32_t* aBytes) override { UNIMPLEMENTED(); return NS_ERROR_FAILURE; }
   bool ShouldCacheReads() override { UNIMPLEMENTED(); return false; }
   int64_t Tell() override { UNIMPLEMENTED(); return -1; }
   void Pin() override { UNIMPLEMENTED(); }
   void Unpin() override { UNIMPLEMENTED(); }
   int64_t GetLength() override { UNIMPLEMENTED(); return -1; }
   int64_t GetNextCachedData(int64_t aOffset) override { UNIMPLEMENTED(); return -1; }
   int64_t GetCachedDataEnd(int64_t aOffset) override { UNIMPLEMENTED(); return -1; }
--- a/dom/media/mediasource/SourceBufferResource.h
+++ b/dom/media/mediasource/SourceBufferResource.h
@@ -40,20 +40,16 @@ class SourceBufferResource final : publi
 public:
   SourceBufferResource();
   nsresult Close();
   already_AddRefed<nsIPrincipal> GetCurrentPrincipal() override
   {
     UNIMPLEMENTED();
     return nullptr;
   }
-  void SetReadMode(MediaCacheStream::ReadMode aMode) override
-  {
-    UNIMPLEMENTED();
-  }
   nsresult ReadAt(int64_t aOffset,
                   char* aBuffer,
                   uint32_t aCount,
                   uint32_t* aBytes) override;
   // Memory-based and no locks, caching discouraged.
   bool ShouldCacheReads() override { return false; }
   int64_t Tell() override { return mOffset; }
   void Pin() override { UNIMPLEMENTED(); }
--- a/dom/media/test/manifest.js
+++ b/dom/media/test/manifest.js
@@ -22,17 +22,17 @@ function manifestVideo() {
   return gManifestNavigatorSource.contentDocument.createElement('video');
 }
 
 // Need to get the server url composed with ip:port instead of mochi.test.
 // Since we will provide the url to Exoplayer which cannot recognize the domain
 // name "mochi.test".
 let serverUrl = SpecialPowers.Services.prefs.getCharPref("media.hls.server.url");
 var gHLSTests = [
-  { name: serverUrl + "/bipbop_4x3_variant.m3u8", type:"audio/x-mpegurl", duration:19.95334 }
+  { name: serverUrl + "/bipbop_4x3_variant.m3u8", type:"audio/x-mpegurl", duration:20.000 }
 ];
 
 // These are small test files, good for just seeing if something loads. We
 // really only need one test file per backend here.
 var gSmallTests = [
   { name:"small-shot.ogg", type:"audio/ogg", duration:0.276 },
   { name:"small-shot.m4a", type:"audio/mp4", duration:0.29 },
   { name:"small-shot.mp3", type:"audio/mpeg", duration:0.27 },
--- a/dom/media/test/reftest/reftest.list
+++ b/dom/media/test/reftest/reftest.list
@@ -1,3 +1,3 @@
-skip-if(Android) fuzzy-if(OSX,22,49977) skip-if(winWidget) fuzzy-if(webrender,70,600) HTTP(..) == short.mp4.firstframe.html short.mp4.firstframe-ref.html
-skip-if(Android) fuzzy-if(OSX,23,51392) fuzzy-if(winWidget,59,76797) fuzzy-if(webrender,60,1800) HTTP(..) == short.mp4.lastframe.html short.mp4.lastframe-ref.html
-skip-if(Android) skip-if(winWidget) fuzzy-if(webrender,55,4281) fuzzy-if(OSX,3,111852) HTTP(..) == bipbop_300_215kbps.mp4.lastframe.html bipbop_300_215kbps.mp4.lastframe-ref.html
\ No newline at end of file
+skip-if(Android) fuzzy-if(OSX,22,49977) skip-if(winWidget) fuzzy-if(gtkWidget&&layersGPUAccelerated,70,600) HTTP(..) == short.mp4.firstframe.html short.mp4.firstframe-ref.html
+skip-if(Android) fuzzy-if(OSX,23,51392) fuzzy-if(winWidget,59,76797) fuzzy-if(gtkWidget&&layersGPUAccelerated,60,1800) HTTP(..) == short.mp4.lastframe.html short.mp4.lastframe-ref.html
+skip-if(Android) skip-if(winWidget) fuzzy-if(gtkWidget&&layersGPUAccelerated,55,4281) fuzzy-if(OSX,3,111852) HTTP(..) == bipbop_300_215kbps.mp4.lastframe.html bipbop_300_215kbps.mp4.lastframe-ref.html
--- a/dom/media/webaudio/AudioContext.cpp
+++ b/dom/media/webaudio/AudioContext.cpp
@@ -214,16 +214,28 @@ AudioContext::Constructor(const GlobalOb
 
   RegisterWeakMemoryReporter(object);
 
   return object.forget();
 }
 
 /* static */ already_AddRefed<AudioContext>
 AudioContext::Constructor(const GlobalObject& aGlobal,
+                          const OfflineAudioContextOptions& aOptions,
+                          ErrorResult& aRv)
+{
+  return Constructor(aGlobal,
+                     aOptions.mNumberOfChannels,
+                     aOptions.mLength,
+                     aOptions.mSampleRate,
+                     aRv);
+}
+
+/* static */ already_AddRefed<AudioContext>
+AudioContext::Constructor(const GlobalObject& aGlobal,
                           uint32_t aNumberOfChannels,
                           uint32_t aLength,
                           float aSampleRate,
                           ErrorResult& aRv)
 {
   nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports());
   if (!window) {
     aRv.Throw(NS_ERROR_FAILURE);
--- a/dom/media/webaudio/AudioContext.h
+++ b/dom/media/webaudio/AudioContext.h
@@ -2,16 +2,17 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef AudioContext_h_
 #define AudioContext_h_
 
+#include "mozilla/dom/OfflineAudioContextBinding.h"
 #include "MediaBufferDecoder.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/TypedArray.h"
 #include "mozilla/UniquePtr.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
@@ -147,16 +148,22 @@ public:
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   using DOMEventTargetHelper::DispatchTrustedEvent;
 
   // Constructor for regular AudioContext
   static already_AddRefed<AudioContext>
   Constructor(const GlobalObject& aGlobal, ErrorResult& aRv);
 
+  // Constructor for offline AudioContext with options object
+  static already_AddRefed<AudioContext>
+  Constructor(const GlobalObject& aGlobal,
+              const OfflineAudioContextOptions& aOptions,
+              ErrorResult& aRv);
+
   // Constructor for offline AudioContext
   static already_AddRefed<AudioContext>
   Constructor(const GlobalObject& aGlobal,
               uint32_t aNumberOfChannels,
               uint32_t aLength,
               float aSampleRate,
               ErrorResult& aRv);
 
--- a/dom/media/webaudio/test/test_OfflineAudioContext.html
+++ b/dom/media/webaudio/test/test_OfflineAudioContext.html
@@ -26,43 +26,63 @@ function setOrCompareRenderedBuffer(aRen
     finish();
   } else {
     renderedBuffer = aRenderedBuffer;
   }
 }
 
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(function() {
-  var ctx = new OfflineAudioContext(2, 100, 22050);
-  ok(ctx instanceof EventTarget, "OfflineAudioContexts must be EventTargets");
-  is(ctx.length, 100, "OfflineAudioContext.length is equal to the value passed to the ctor.");
+  let ctxs = [
+    new OfflineAudioContext(2, 100, 22050),
+    new OfflineAudioContext({length: 100, sampleRate: 22050}),
+    new OfflineAudioContext({channels: 2, length: 100, sampleRate: 22050}),
+  ];
 
-  var buf = ctx.createBuffer(2, 100, ctx.sampleRate);
-  for (var i = 0; i < 2; ++i) {
-    for (var j = 0; j < 100; ++j) {
-      buf.getChannelData(i)[j] = Math.sin(2 * Math.PI * 200 * j / ctx.sampleRate);
+  for (let ctx of ctxs) {
+    ok(ctx instanceof EventTarget, "OfflineAudioContexts must be EventTargets");
+    is(ctx.length, 100, "OfflineAudioContext.length is equal to the value passed to the ctor.");
+
+    var buf = ctx.createBuffer(2, 100, ctx.sampleRate);
+    for (var i = 0; i < 2; ++i) {
+      for (var j = 0; j < 100; ++j) {
+        buf.getChannelData(i)[j] = Math.sin(2 * Math.PI * 200 * j / ctx.sampleRate);
+      }
     }
   }
 
+  is(ctxs[1].destination.channelCount, 1, "OfflineAudioContext defaults to to correct channelCount.");
+
+  let ctx = ctxs[0];
+
   expectException(function() {
     new OfflineAudioContext(2, 100, 0);
   }, DOMException.NOT_SUPPORTED_ERR);
   expectException(function() {
     new OfflineAudioContext(2, 100, -1);
   }, DOMException.NOT_SUPPORTED_ERR);
   expectException(function() {
     new OfflineAudioContext(0, 100, 44100);
   }, DOMException.NOT_SUPPORTED_ERR);
   new OfflineAudioContext(32, 100, 44100);
   expectException(function() {
     new OfflineAudioContext(33, 100, 44100);
   }, DOMException.NOT_SUPPORTED_ERR);
   expectException(function() {
     new OfflineAudioContext(2, 0, 44100);
   }, DOMException.NOT_SUPPORTED_ERR);
+  expectTypeError(function() {
+    new OfflineAudioContext({});
+  });
+  expectTypeError(function() {
+    new OfflineAudioContext({sampleRate: 44100});
+  });
+  expectTypeError(function() {
+    new OfflineAudioContext({length: 44100*40});
+  });
 
   var src = ctx.createBufferSource();
   src.buffer = buf;
   src.start(0);
   src.connect(ctx.destination);
 
   ctx.addEventListener("complete", function(e) {
     ok(e instanceof OfflineAudioCompletionEvent, "Correct event received");
--- a/dom/vr/VREventObserver.cpp
+++ b/dom/vr/VREventObserver.cpp
@@ -45,19 +45,18 @@ VREventObserver::DisconnectFromOwner()
 {
   // In the event that nsGlobalWindow is deallocated, VREventObserver may
   // still be AddRef'ed elsewhere.  Ensure that we don't UAF by
   // dereferencing mWindow.
   if (mWindow && mIs2DView) {
     // The WebVR content is closed, and we will collect the telemetry info
     // for the users who view it in 2D view only.
     Telemetry::Accumulate(Telemetry::WEBVR_USERS_VIEW_IN, 0);
-    Telemetry::Accumulate(Telemetry::WEBVR_TIME_SPEND_FOR_VIEWING_IN_2D,
-                          static_cast<uint32_t>((TimeStamp::Now() - mSpendTimeIn2DView)
-                          .ToMilliseconds()));
+    Telemetry::AccumulateTimeDelta(Telemetry::WEBVR_TIME_SPENT_VIEWING_IN_2D,
+                                   mSpendTimeIn2DView);
   }
   mWindow = nullptr;
 
   // Unregister from VRManagerChild
   if (VRManagerChild::IsCreated()) {
     VRManagerChild* vmc = VRManagerChild::Get();
     vmc->RemoveListener(this);
   }
--- a/dom/webidl/OfflineAudioContext.webidl
+++ b/dom/webidl/OfflineAudioContext.webidl
@@ -5,17 +5,24 @@
  *
  * The origin of this IDL file is
  * https://webaudio.github.io/web-audio-api/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
-[Constructor(unsigned long numberOfChannels, unsigned long length, float sampleRate),
+dictionary OfflineAudioContextOptions {
+             unsigned long numberOfChannels = 1;
+    required unsigned long length;
+    required float         sampleRate;
+};
+
+[Constructor (OfflineAudioContextOptions contextOptions),
+Constructor(unsigned long numberOfChannels, unsigned long length, float sampleRate),
 Pref="dom.webaudio.enabled"]
 interface OfflineAudioContext : BaseAudioContext {
 
     [Throws]
     Promise<AudioBuffer> startRendering();
 
     // TODO: Promise<void>        suspend (double suspendTime);
 
--- a/editor/libeditor/CompositionTransaction.cpp
+++ b/editor/libeditor/CompositionTransaction.cpp
@@ -193,31 +193,31 @@ CompositionTransaction::SetIMESelection(
                                         Text* aTextNode,
                                         uint32_t aOffsetInNode,
                                         uint32_t aLengthOfCompositionString,
                                         const TextRangeArray* aRanges)
 {
   RefPtr<Selection> selection = aEditorBase.GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NOT_INITIALIZED);
 
-  nsresult rv = selection->StartBatchChanges();
-  NS_ENSURE_SUCCESS(rv, rv);
+  SelectionBatcher selectionBatcher(selection);
 
   // First, remove all selections of IME composition.
   static const RawSelectionType kIMESelections[] = {
     nsISelectionController::SELECTION_IME_RAWINPUT,
     nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT,
     nsISelectionController::SELECTION_IME_CONVERTEDTEXT,
     nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT
   };
 
   nsCOMPtr<nsISelectionController> selCon;
   aEditorBase.GetSelectionController(getter_AddRefs(selCon));
   NS_ENSURE_TRUE(selCon, NS_ERROR_NOT_INITIALIZED);
 
+  nsresult rv = NS_OK;
   for (uint32_t i = 0; i < ArrayLength(kIMESelections); ++i) {
     nsCOMPtr<nsISelection> selectionOfIME;
     if (NS_FAILED(selCon->GetSelection(kIMESelections[i],
                                        getter_AddRefs(selectionOfIME)))) {
       continue;
     }
     rv = selectionOfIME->RemoveAllRanges();
     NS_ASSERTION(NS_SUCCEEDED(rv),
@@ -330,15 +330,12 @@ CompositionTransaction::SetIMESelection(
     // If caret range isn't specified explicitly, we should hide the caret.
     // Hiding the caret benefits a Windows build (see bug 555642 comment #6).
     // However, when there is no range, we should keep showing caret.
     if (countOfRanges) {
       aEditorBase.HideCaret(true);
     }
   }
 
-  rv = selection->EndBatchChangesInternal();
-  NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to end batch changes");
-
   return rv;
 }
 
 } // namespace mozilla
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -719,33 +719,30 @@ EditorBase::DoTransaction(Selection* aSe
     // XXX: we will need to make sure that they are disabled during
     // XXX: the init of the editor for text widgets to avoid layout
     // XXX: re-entry during initial reflow. - kin
 
     // get the selection and start a batch change
     RefPtr<Selection> selection = aSelection ? aSelection : GetSelection();
     NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
 
-    selection->StartBatchChanges();
+    SelectionBatcher selectionBatcher(selection);
 
     nsresult rv;
     if (mTxnMgr) {
       RefPtr<nsTransactionManager> txnMgr = mTxnMgr;
       rv = txnMgr->DoTransaction(aTxn);
     } else {
       rv = aTxn->DoTransaction();
     }
-    if (NS_SUCCEEDED(rv)) {
-      DoAfterDoTransaction(aTxn);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
     }
 
-    // no need to check rv here, don't lose result of operation
-    selection->EndBatchChanges();
-
-    NS_ENSURE_SUCCESS(rv, rv);
+    DoAfterDoTransaction(aTxn);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 EditorBase::EnableUndo(bool aEnable)
 {
--- a/gfx/2d/DrawTargetSkia.cpp
+++ b/gfx/2d/DrawTargetSkia.cpp
@@ -1256,17 +1256,23 @@ DrawTargetSkia::FillGlyphsWithCG(ScaledF
 
   Vector<CGGlyph,32> glyphs;
   Vector<CGPoint,32> positions;
   if (!SetupCGGlyphs(cgContext, aBuffer, glyphs, positions)) {
     ReturnCGContext(cgContext);
     return false;
   }
 
-  SetFontSmoothingBackgroundColor(cgContext, mColorSpace, aRenderingOptions);
+  if (mPushedLayers.empty()) {
+    // Respect the font smoothing background color, but only if no layer is
+    // currently pushed, because this color usually describes what's under this
+    // DrawTarget, and not what's within this DrawTarget under the currently
+    // pushed layer.
+    SetFontSmoothingBackgroundColor(cgContext, mColorSpace, aRenderingOptions);
+  }
   SetFontColor(cgContext, mColorSpace, aPattern);
 
   ScaledFontMac* macFont = static_cast<ScaledFontMac*>(aFont);
   if (ScaledFontMac::CTFontDrawGlyphsPtr != nullptr) {
     ScaledFontMac::CTFontDrawGlyphsPtr(macFont->mCTFont, glyphs.begin(),
                                        positions.begin(),
                                        aBuffer.mNumGlyphs, cgContext);
   } else {
--- a/gfx/doc/README.webrender
+++ b/gfx/doc/README.webrender
@@ -74,9 +74,9 @@ there is another crate in m-c called moz
 the same folder to store its rust dependencies. If one of the libraries that is
 required by both mozjs_sys and webrender is updated without updating the other
 project's Cargo.lock file, that results in build bustage.
 This means that any time you do this sort of manual update of packages, you need
 to make sure that mozjs_sys also has its Cargo.lock file updated if needed, hence
 the need to run the cargo update command in js/src as well. Hopefully this will
 be resolved soon.
 
-Latest Commit: 0748e02d1be5f889fc17de2eb81c0c363ee3aa80
+Latest Commit: e68c8acb021656440d26ac46e705e7ceb31891e6
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -458,16 +458,19 @@ CompositorBridgeParent::StopAndClearReso
       if (lts->mWrBridge) {
         lts->mWrBridge->Destroy();
         lts->mWrBridge = nullptr;
       }
       lts->mParent = nullptr;
     });
     mWrBridge->Destroy();
     mWrBridge = nullptr;
+    mAsyncImageManager->Destroy();
+    // WebRenderAPI should be already destructed
+    mAsyncImageManager = nullptr;
   }
 
   if (mCompositor) {
     mCompositor->DetachWidget();
     mCompositor->Destroy();
     mCompositor = nullptr;
   }
 
@@ -1649,18 +1652,19 @@ CompositorBridgeParent::RecvAdoptChild(c
     NotifyChildCreated(child);
     if (sIndirectLayerTrees[child].mLayerTree) {
       sIndirectLayerTrees[child].mLayerTree->SetLayerManager(mLayerManager, GetAnimationStorage());
       // Trigger composition to handle a case that mLayerTree was not composited yet
       // by previous CompositorBridgeParent, since nsRefreshDriver might wait composition complete.
       ScheduleComposition();
     }
     if (mWrBridge && sIndirectLayerTrees[child].mWrBridge) {
+      RefPtr<wr::WebRenderAPI> api = mWrBridge->GetWebRenderAPI()->Clone();
       sIndirectLayerTrees[child].mWrBridge->UpdateWebRender(mWrBridge->CompositorScheduler(),
-                                                            mWrBridge->GetWebRenderAPI(),
+                                                            api,
                                                             mWrBridge->AsyncImageManager(),
                                                             GetAnimationStorage());
       // Pretend we composited, since parent CompositorBridgeParent was replaced.
       CrossProcessCompositorBridgeParent* cpcp = sIndirectLayerTrees[child].mCrossProcessParent;
       if (cpcp) {
         TimeStamp now = TimeStamp::Now();
         cpcp->DidCompositeLocked(child, now, now);
       }
@@ -1690,25 +1694,25 @@ CompositorBridgeParent::AllocPWebRenderB
   MOZ_ASSERT(!mCompositor);
   MOZ_ASSERT(!mCompositorScheduler);
 
 
   MOZ_ASSERT(mWidget);
   RefPtr<widget::CompositorWidget> widget = mWidget;
   RefPtr<wr::WebRenderAPI> api = wr::WebRenderAPI::Create(
     gfxPrefs::WebRenderProfilerEnabled(), this, Move(widget), aSize);
-  RefPtr<AsyncImagePipelineManager> asyncMgr =
-    new AsyncImagePipelineManager(WebRenderBridgeParent::AllocIdNameSpace());
   if (!api) {
     mWrBridge = WebRenderBridgeParent::CreateDestroyed();
     mWrBridge.get()->AddRef(); // IPDL reference
     *aIdNamespace = mWrBridge->GetIdNamespace();
     *aTextureFactoryIdentifier = TextureFactoryIdentifier(LayersBackend::LAYERS_NONE);
     return mWrBridge;
   }
+  mAsyncImageManager = new AsyncImagePipelineManager(api->Clone());
+  RefPtr<AsyncImagePipelineManager> asyncMgr = mAsyncImageManager;
   api->SetRootPipeline(aPipelineId);
   RefPtr<CompositorAnimationStorage> animStorage = GetAnimationStorage();
   mWrBridge = new WebRenderBridgeParent(this, aPipelineId, mWidget, nullptr, Move(api), Move(asyncMgr), Move(animStorage));
   mWrBridge.get()->AddRef(); // IPDL reference
 
   *aIdNamespace = mWrBridge->GetIdNamespace();
   mCompositorScheduler = mWrBridge->CompositorScheduler();
   MOZ_ASSERT(mCompositorScheduler);
@@ -1948,20 +1952,20 @@ CompositorBridgeParent::DidComposite(Tim
     NotifyDidComposite(mPendingTransaction, aCompositeStart, aCompositeEnd);
     mPendingTransaction = 0;
   }
 }
 
 void
 CompositorBridgeParent::NotifyDidCompositeToPipeline(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch, TimeStamp& aCompositeStart, TimeStamp& aCompositeEnd)
 {
-  if (!mWrBridge) {
+  if (!mWrBridge || !mAsyncImageManager) {
     return;
   }
-  mWrBridge->AsyncImageManager()->Update(aPipelineId, aEpoch);
+  mAsyncImageManager->Update(aPipelineId, aEpoch);
 
   if (mPaused) {
     return;
   }
 
   if (mWrBridge->PipelineId() == aPipelineId) {
     uint64_t transactionId = mWrBridge->FlushTransactionIdsForEpoch(aEpoch, aCompositeEnd);
     Unused << SendDidComposite(0, transactionId, aCompositeStart, aCompositeEnd);
--- a/gfx/layers/ipc/CompositorBridgeParent.h
+++ b/gfx/layers/ipc/CompositorBridgeParent.h
@@ -60,16 +60,17 @@ namespace ipc {
 class Shmem;
 } // namespace ipc
 
 namespace layers {
 
 class APZCTreeManager;
 class APZCTreeManagerParent;
 class AsyncCompositionManager;
+class AsyncImagePipelineManager;
 class Compositor;
 class CompositorAnimationStorage;
 class CompositorBridgeParent;
 class CompositorManagerParent;
 class CompositorVsyncScheduler;
 class HostLayerManager;
 class LayerTransactionParent;
 class PAPZParent;
@@ -575,16 +576,17 @@ protected:
   // The indirect layer tree lock must be held before calling this function.
   // Callback should take (LayerTreeState* aState, const uint64_t& aLayersId)
   template <typename Lambda>
   inline void ForEachIndirectLayerTree(const Lambda& aCallback);
 
   RefPtr<HostLayerManager> mLayerManager;
   RefPtr<Compositor> mCompositor;
   RefPtr<AsyncCompositionManager> mCompositionManager;
+  RefPtr<AsyncImagePipelineManager> mAsyncImageManager;
   RefPtr<WebRenderBridgeParent> mWrBridge;
   widget::CompositorWidget* mWidget;
   TimeStamp mTestTime;
   CSSToLayoutDeviceScale mScale;
   TimeDuration mVsyncRate;
   bool mIsTesting;
 
   uint64_t mPendingTransaction;
--- a/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
@@ -220,17 +220,17 @@ CrossProcessCompositorBridgeParent::Allo
     parent = WebRenderBridgeParent::CreateDestroyed();
     parent->AddRef(); // IPDL reference
     *aIdNamespace = parent->GetIdNamespace();
     *aTextureFactoryIdentifier = TextureFactoryIdentifier(LayersBackend::LAYERS_NONE);
     return parent;
   }
   WebRenderBridgeParent* root = sIndirectLayerTrees[cbp->RootLayerTreeId()].mWrBridge.get();
 
-  RefPtr<wr::WebRenderAPI> api = root->GetWebRenderAPI();
+  RefPtr<wr::WebRenderAPI> api = root->GetWebRenderAPI()->Clone();
   RefPtr<AsyncImagePipelineManager> holder = root->AsyncImageManager();
   RefPtr<CompositorAnimationStorage> animStorage = cbp->GetAnimationStorage();
   parent = new WebRenderBridgeParent(this, aPipelineId, nullptr, root->CompositorScheduler(), Move(api), Move(holder), Move(animStorage));
   parent->AddRef(); // IPDL reference
 
   sIndirectLayerTrees[layersId].mCrossProcessParent = this;
   sIndirectLayerTrees[layersId].mWrBridge = parent;
   *aTextureFactoryIdentifier = parent->GetTextureFactoryIdentifier();
--- a/gfx/layers/wr/AsyncImagePipelineManager.cpp
+++ b/gfx/layers/wr/AsyncImagePipelineManager.cpp
@@ -18,48 +18,52 @@ namespace layers {
 AsyncImagePipelineManager::AsyncImagePipeline::AsyncImagePipeline()
  : mInitialised(false)
  , mIsChanged(false)
  , mUseExternalImage(false)
  , mFilter(wr::ImageRendering::Auto)
  , mMixBlendMode(wr::MixBlendMode::Normal)
 {}
 
-AsyncImagePipelineManager::AsyncImagePipelineManager(wr::IdNamespace aIdNamespace)
- : mIdNamespace(aIdNamespace)
+AsyncImagePipelineManager::AsyncImagePipelineManager(already_AddRefed<wr::WebRenderAPI>&& aApi)
+ : mApi(aApi)
+ , mIdNamespace(mApi->GetNamespace())
  , mResourceId(0)
  , mAsyncImageEpoch(0)
  , mDestroyed(false)
 {
   MOZ_COUNT_CTOR(AsyncImagePipelineManager);
 }
 
 AsyncImagePipelineManager::~AsyncImagePipelineManager()
 {
   MOZ_COUNT_DTOR(AsyncImagePipelineManager);
 }
 
 void
-AsyncImagePipelineManager::Destroy(wr::WebRenderAPI* aApi)
+AsyncImagePipelineManager::Destroy()
 {
-  DeleteOldAsyncImages(aApi);
+  MOZ_ASSERT(!mDestroyed);
+  DeleteOldAsyncImages();
+  mApi = nullptr;
   mDestroyed = true;
 }
 
 bool
 AsyncImagePipelineManager::HasKeysToDelete()
 {
   return !mKeysToDelete.IsEmpty();
 }
 
 void
-AsyncImagePipelineManager::DeleteOldAsyncImages(wr::WebRenderAPI* aApi)
+AsyncImagePipelineManager::DeleteOldAsyncImages()
 {
+  MOZ_ASSERT(!mDestroyed);
   for (wr::ImageKey key : mKeysToDelete) {
-    aApi->DeleteImage(key);
+    mApi->DeleteImage(key);
   }
   mKeysToDelete.Clear();
 }
 
 void
 AsyncImagePipelineManager::AddPipeline(const wr::PipelineId& aPipelineId)
 {
   if (mDestroyed) {
@@ -106,29 +110,29 @@ AsyncImagePipelineManager::AddAsyncImage
   MOZ_ASSERT(!mAsyncImagePipelines.Get(id));
   AsyncImagePipeline* holder = new AsyncImagePipeline();
   holder->mImageHost = aImageHost;
   mAsyncImagePipelines.Put(id, holder);
   AddPipeline(aPipelineId);
 }
 
 void
-AsyncImagePipelineManager::RemoveAsyncImagePipeline(wr::WebRenderAPI* aApi, const wr::PipelineId& aPipelineId)
+AsyncImagePipelineManager::RemoveAsyncImagePipeline(const wr::PipelineId& aPipelineId)
 {
   if (mDestroyed) {
     return;
   }
 
   uint64_t id = wr::AsUint64(aPipelineId);
   if (auto entry = mAsyncImagePipelines.Lookup(id)) {
     AsyncImagePipeline* holder = entry.Data();
     ++mAsyncImageEpoch; // Update webrender epoch
-    aApi->ClearRootDisplayList(wr::NewEpoch(mAsyncImageEpoch), aPipelineId);
+    mApi->ClearRootDisplayList(wr::NewEpoch(mAsyncImageEpoch), aPipelineId);
     for (wr::ImageKey key : holder->mKeys) {
-      aApi->DeleteImage(key);
+      mApi->DeleteImage(key);
     }
     entry.Remove();
     RemovePipeline(aPipelineId, wr::NewEpoch(mAsyncImageEpoch));
   }
 }
 
 void
 AsyncImagePipelineManager::UpdateAsyncImagePipeline(const wr::PipelineId& aPipelineId,
@@ -150,28 +154,28 @@ AsyncImagePipelineManager::UpdateAsyncIm
   pipeline->mScBounds = aScBounds;
   pipeline->mScTransform = aScTransform;
   pipeline->mScaleToSize = aScaleToSize;
   pipeline->mFilter = aFilter;
   pipeline->mMixBlendMode = aMixBlendMode;
 }
 
 bool
-AsyncImagePipelineManager::GenerateImageKeyForTextureHost(wr::WebRenderAPI* aApi, TextureHost* aTexture, nsTArray<wr::ImageKey>& aKeys)
+AsyncImagePipelineManager::GenerateImageKeyForTextureHost(TextureHost* aTexture, nsTArray<wr::ImageKey>& aKeys)
 {
   MOZ_ASSERT(aKeys.IsEmpty());
   MOZ_ASSERT(aTexture);
 
   WebRenderTextureHost* wrTexture = aTexture->AsWebRenderTextureHost();
 
   if (!gfxEnv::EnableWebRenderRecording() && wrTexture) {
     wrTexture->GetWRImageKeys(aKeys, std::bind(&AsyncImagePipelineManager::GenerateImageKey, this));
     MOZ_ASSERT(!aKeys.IsEmpty());
     Range<const wr::ImageKey> keys(&aKeys[0], aKeys.Length());
-    wrTexture->AddWRImage(aApi, keys, wrTexture->GetExternalImageKey());
+    wrTexture->AddWRImage(mApi, keys, wrTexture->GetExternalImageKey());
     return true;
   } else {
     RefPtr<gfx::DataSourceSurface> dSurf = aTexture->GetAsSurface();
     if (!dSurf) {
       NS_ERROR("TextureHost does not return DataSourceSurface");
       return false;
     }
     gfx::DataSourceSurface::MappedSurface map;
@@ -180,25 +184,24 @@ AsyncImagePipelineManager::GenerateImage
       return false;
     }
     gfx::IntSize size = dSurf->GetSize();
     wr::ImageDescriptor descriptor(size, map.mStride, dSurf->GetFormat());
     auto slice = Range<uint8_t>(map.mData, size.height * map.mStride);
 
     wr::ImageKey key = GenerateImageKey();
     aKeys.AppendElement(key);
-    aApi->AddImage(key, descriptor, slice);
+    mApi->AddImage(key, descriptor, slice);
     dSurf->Unmap();
   }
   return false;
 }
 
 bool
-AsyncImagePipelineManager::UpdateImageKeys(wr::WebRenderAPI* aApi,
-                                           bool& aUseExternalImage,
+AsyncImagePipelineManager::UpdateImageKeys(bool& aUseExternalImage,
                                            AsyncImagePipeline* aImageMgr,
                                            nsTArray<wr::ImageKey>& aKeys,
                                            nsTArray<wr::ImageKey>& aKeysToDelete)
 {
   MOZ_ASSERT(aKeys.IsEmpty());
   MOZ_ASSERT(aImageMgr);
   TextureHost* texture = aImageMgr->mImageHost->GetAsTextureHostForComposite();
 
@@ -226,42 +229,41 @@ AsyncImagePipelineManager::UpdateImageKe
   aImageMgr->mKeys.Clear();
   aImageMgr->mCurrentTexture = nullptr;
 
   // No txture to render
   if (!texture) {
     return true;
   }
 
-  aUseExternalImage = aImageMgr->mUseExternalImage = GenerateImageKeyForTextureHost(aApi, texture, aKeys);
+  aUseExternalImage = aImageMgr->mUseExternalImage = GenerateImageKeyForTextureHost(texture, aKeys);
   MOZ_ASSERT(!aKeys.IsEmpty());
   aImageMgr->mKeys.AppendElements(aKeys);
   aImageMgr->mCurrentTexture = texture;
   return true;
 }
 
 void
-AsyncImagePipelineManager::ApplyAsyncImages(wr::WebRenderAPI* aApi)
+AsyncImagePipelineManager::ApplyAsyncImages()
 {
   if (mDestroyed || mAsyncImagePipelines.Count() == 0) {
     return;
   }
 
   ++mAsyncImageEpoch; // Update webrender epoch
   wr::Epoch epoch = wr::NewEpoch(mAsyncImageEpoch);
   nsTArray<wr::ImageKey> keysToDelete;
 
   for (auto iter = mAsyncImagePipelines.Iter(); !iter.Done(); iter.Next()) {
     wr::PipelineId pipelineId = wr::AsPipelineId(iter.Key());
     AsyncImagePipeline* pipeline = iter.Data();
 
     nsTArray<wr::ImageKey> keys;
     bool useExternalImage = false;
-    bool updateDisplayList = UpdateImageKeys(aApi,
-                                             useExternalImage,
+    bool updateDisplayList = UpdateImageKeys(useExternalImage,
                                              pipeline,
                                              keys,
                                              keysToDelete);
     if (!updateDisplayList) {
       continue;
     }
 
     wr::LayoutSize contentSize { pipeline->mScBounds.width, pipeline->mScBounds.height };
@@ -301,21 +303,21 @@ AsyncImagePipelineManager::ApplyAsyncIma
                           keys[0]);
       }
       builder.PopStackingContext();
     }
 
     wr::BuiltDisplayList dl;
     wr::LayoutSize builderContentSize;
     builder.Finalize(builderContentSize, dl);
-    aApi->SetRootDisplayList(gfx::Color(0.f, 0.f, 0.f, 0.f), epoch, LayerSize(pipeline->mScBounds.width, pipeline->mScBounds.height),
+    mApi->SetRootDisplayList(gfx::Color(0.f, 0.f, 0.f, 0.f), epoch, LayerSize(pipeline->mScBounds.width, pipeline->mScBounds.height),
                              pipelineId, builderContentSize,
                              dl.dl_desc, dl.dl.inner.data, dl.dl.inner.length);
   }
-  DeleteOldAsyncImages(aApi);
+  DeleteOldAsyncImages();
   mKeysToDelete.SwapElements(keysToDelete);
 }
 
 void
 AsyncImagePipelineManager::HoldExternalImage(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch, WebRenderTextureHost* aTexture)
 {
   if (mDestroyed) {
     return;
--- a/gfx/layers/wr/AsyncImagePipelineManager.h
+++ b/gfx/layers/wr/AsyncImagePipelineManager.h
@@ -7,16 +7,17 @@
 #define MOZILLA_GFX_WEBRENDERCOMPOSITABLE_HOLDER_H
 
 #include <queue>
 
 #include "CompositableHost.h"
 #include "mozilla/gfx/Point.h"
 #include "mozilla/layers/TextureHost.h"
 #include "mozilla/Maybe.h"
+#include "mozilla/webrender/WebRenderAPI.h"
 #include "mozilla/webrender/WebRenderTypes.h"
 #include "nsClassHashtable.h"
 
 namespace mozilla {
 
 namespace wr {
 class DisplayListBuilder;
 class WebRenderAPI;
@@ -29,23 +30,23 @@ class CompositorVsyncScheduler;
 class WebRenderImageHost;
 class WebRenderTextureHost;
 
 class AsyncImagePipelineManager final
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AsyncImagePipelineManager)
 
-  explicit AsyncImagePipelineManager(wr::IdNamespace aIdNamespace);
+  explicit AsyncImagePipelineManager(already_AddRefed<wr::WebRenderAPI>&& aApi);
 
 protected:
   ~AsyncImagePipelineManager();
 
 public:
-  void Destroy(wr::WebRenderAPI* aApi);
+  void Destroy();
   bool HasKeysToDelete();
 
   void AddPipeline(const wr::PipelineId& aPipelineId);
   void RemovePipeline(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch);
 
   void HoldExternalImage(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch, WebRenderTextureHost* aTexture);
   void Update(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch);
 
@@ -65,49 +66,49 @@ public:
       mCompositeUntilTime = aTimeStamp;
     }
   }
   TimeStamp GetCompositeUntilTime() const {
     return mCompositeUntilTime;
   }
 
   void AddAsyncImagePipeline(const wr::PipelineId& aPipelineId, WebRenderImageHost* aImageHost);
-  void RemoveAsyncImagePipeline(wr::WebRenderAPI* aApi, const wr::PipelineId& aPipelineId);
+  void RemoveAsyncImagePipeline(const wr::PipelineId& aPipelineId);
 
   void UpdateAsyncImagePipeline(const wr::PipelineId& aPipelineId,
                                 const LayerRect& aScBounds,
                                 const gfx::Matrix4x4& aScTransform,
                                 const gfx::MaybeIntSize& aScaleToSize,
                                 const wr::ImageRendering& aFilter,
                                 const wr::MixBlendMode& aMixBlendMode);
-  void ApplyAsyncImages(wr::WebRenderAPI* aApi);
+  void ApplyAsyncImages();
 
   void AppendImageCompositeNotification(const ImageCompositeNotificationInfo& aNotification)
   {
     mImageCompositeNotifications.AppendElement(aNotification);
   }
 
   void FlushImageNotifications(nsTArray<ImageCompositeNotificationInfo>* aNotifications)
   {
     aNotifications->AppendElements(Move(mImageCompositeNotifications));
   }
 
 private:
-  void DeleteOldAsyncImages(wr::WebRenderAPI* aApi);
+  void DeleteOldAsyncImages();
 
   uint32_t GetNextResourceId() { return ++mResourceId; }
   wr::IdNamespace GetNamespace() { return mIdNamespace; }
   wr::ImageKey GenerateImageKey()
   {
     wr::ImageKey key;
     key.mNamespace = GetNamespace();
     key.mHandle = GetNextResourceId();
     return key;
   }
-  bool GenerateImageKeyForTextureHost(wr::WebRenderAPI* aApi, TextureHost* aTexture, nsTArray<wr::ImageKey>& aKeys);
+  bool GenerateImageKeyForTextureHost(TextureHost* aTexture, nsTArray<wr::ImageKey>& aKeys);
 
   struct ForwardingTextureHost {
     ForwardingTextureHost(const wr::Epoch& aEpoch, TextureHost* aTexture)
       : mEpoch(aEpoch)
       , mTexture(aTexture)
     {}
     wr::Epoch mEpoch;
     CompositableTextureHostRef mTexture;
@@ -130,22 +131,22 @@ private:
     gfx::MaybeIntSize mScaleToSize;
     wr::ImageRendering mFilter;
     wr::MixBlendMode mMixBlendMode;
     RefPtr<WebRenderImageHost> mImageHost;
     CompositableTextureHostRef mCurrentTexture;
     nsTArray<wr::ImageKey> mKeys;
   };
 
-  bool UpdateImageKeys(wr::WebRenderAPI* aApi,
-                       bool& aUseExternalImage,
+  bool UpdateImageKeys(bool& aUseExternalImage,
                        AsyncImagePipeline* aImageMgr,
                        nsTArray<wr::ImageKey>& aKeys,
                        nsTArray<wr::ImageKey>& aKeysToDelete);
 
+  RefPtr<wr::WebRenderAPI> mApi;
   wr::IdNamespace mIdNamespace;
   uint32_t mResourceId;
 
   nsClassHashtable<nsUint64HashKey, PipelineTexturesHolder> mPipelineTexturesHolders;
   nsClassHashtable<nsUint64HashKey, AsyncImagePipeline> mAsyncImagePipelines;
   uint32_t mAsyncImageEpoch;
   nsTArray<wr::ImageKey> mKeysToDelete;
   bool mDestroyed;
--- a/gfx/layers/wr/ScrollingLayersHelper.cpp
+++ b/gfx/layers/wr/ScrollingLayersHelper.cpp
@@ -16,16 +16,17 @@ namespace mozilla {
 namespace layers {
 
 ScrollingLayersHelper::ScrollingLayersHelper(WebRenderLayer* aLayer,
                                              wr::DisplayListBuilder& aBuilder,
                                              const StackingContextHelper& aStackingContext)
   : mLayer(aLayer)
   , mBuilder(&aBuilder)
   , mPushedLayerLocalClip(false)
+  , mClipsPushed(0)
 {
   if (!mLayer->WrManager()->AsyncPanZoomEnabled()) {
     // If APZ is disabled then we don't need to push the scrolling clips. We
     // still want to push the layer's local clip though.
     PushLayerLocalClip(aStackingContext);
     return;
   }
 
@@ -102,33 +103,88 @@ ScrollingLayersHelper::ScrollingLayersHe
     Maybe<wr::WrClipId> clipId = mBuilder->TopmostClipId();
     // Default to 0 if there is no ancestor, because 0 refers to the root scrollframe.
     mBuilder->PushClipAndScrollInfo(scrollsWith.valueOr(0), clipId.ptrOr(nullptr));
   } else {
     PushLayerLocalClip(aStackingContext);
   }
 }
 
+ScrollingLayersHelper::ScrollingLayersHelper(nsDisplayItem* aItem,
+                                             wr::DisplayListBuilder& aBuilder,
+                                             const StackingContextHelper& aStackingContext,
+                                             WebRenderLayerManager::ClipIdMap& aCache)
+  : mLayer(nullptr)
+  , mBuilder(&aBuilder)
+  , mPushedLayerLocalClip(false)
+  , mClipsPushed(0)
+{
+  DefineAndPushChain(aItem->GetClipChain(), aBuilder, aStackingContext,
+      aItem->Frame()->PresContext()->AppUnitsPerDevPixel(), aCache);
+}
+
+void
+ScrollingLayersHelper::DefineAndPushChain(const DisplayItemClipChain* aChain,
+                                          wr::DisplayListBuilder& aBuilder,
+                                          const StackingContextHelper& aStackingContext,
+                                          int32_t aAppUnitsPerDevPixel,
+                                          WebRenderLayerManager::ClipIdMap& aCache)
+{
+  if (!aChain) {
+    return;
+  }
+  auto it = aCache.find(aChain);
+  Maybe<wr::WrClipId> clipId = (it != aCache.end() ? Some(it->second) : Nothing());
+  if (clipId && clipId == aBuilder.TopmostClipId()) {
+    // it was already in the cache and pushed on the WR clip stack, so we don't
+    // need to recurse any further.
+    return;
+  }
+  // Recurse up the clip chain to make sure all ancestor clips are defined and
+  // pushed onto the WR clip stack. Note that the recursion can invalidate the
+  // iterator `it`.
+  DefineAndPushChain(aChain->mParent, aBuilder, aStackingContext, aAppUnitsPerDevPixel, aCache);
+
+  if (!aChain->mClip.HasClip()) {
+    // This item in the chain is a no-op, skip over it
+    return;
+  }
+  if (!clipId) {
+    // If we don't have a clip id for this chain item yet, define the clip in WR
+    // and save the id
+    LayoutDeviceRect clip = LayoutDeviceRect::FromAppUnits(
+        aChain->mClip.GetClipRect(), aAppUnitsPerDevPixel);
+    // TODO: deal with rounded corners here
+    clipId = Some(aBuilder.DefineClip(aStackingContext.ToRelativeLayoutRect(clip)));
+    aCache[aChain] = clipId.value();
+  }
+  // Finally, push the clip onto the WR stack
+  MOZ_ASSERT(clipId);
+  aBuilder.PushClip(clipId.value());
+  mClipsPushed++;
+}
+
 void
 ScrollingLayersHelper::PushLayerLocalClip(const StackingContextHelper& aStackingContext)
 {
   Layer* layer = mLayer->GetLayer();
   Maybe<ParentLayerRect> clip;
   if (const Maybe<ParentLayerIntRect>& rect = layer->GetClipRect()) {
     clip = Some(IntRectToRect(rect.ref()));
   } else if (layer->GetMaskLayer()) {
     // this layer has a mask, but no clip rect. so let's use the transformed
     // visible bounds as the clip rect.
     clip = Some(layer->GetLocalTransformTyped().TransformBounds(mLayer->Bounds()));
   }
   if (clip) {
     Maybe<wr::WrImageMask> mask = mLayer->BuildWrMaskLayer(aStackingContext);
     LayerRect clipRect = ViewAs<LayerPixel>(clip.ref(),
         PixelCastJustification::MovingDownToChildren);
-    mBuilder->PushClip(aStackingContext.ToRelativeLayoutRect(clipRect), mask.ptrOr(nullptr));
+    mBuilder->PushClip(mBuilder->DefineClip(
+        aStackingContext.ToRelativeLayoutRect(clipRect), nullptr, mask.ptrOr(nullptr)));
     mPushedLayerLocalClip = true;
   }
 }
 
 void
 ScrollingLayersHelper::PushLayerClip(const LayerClip& aClip,
                                      const StackingContextHelper& aSc)
 {
@@ -136,21 +192,31 @@ ScrollingLayersHelper::PushLayerClip(con
         PixelCastJustification::MovingDownToChildren));
   Maybe<wr::WrImageMask> mask;
   if (Maybe<size_t> maskLayerIndex = aClip.GetMaskLayerIndex()) {
     Layer* maskLayer = mLayer->GetLayer()->GetAncestorMaskLayerAt(maskLayerIndex.value());
     WebRenderLayer* maskWrLayer = WebRenderLayer::ToWebRenderLayer(maskLayer);
     // TODO: check this transform is correct in all cases
     mask = maskWrLayer->RenderMaskLayer(aSc, maskLayer->GetTransform());
   }
-  mBuilder->PushClip(aSc.ToRelativeLayoutRect(clipRect), mask.ptrOr(nullptr));
+  mBuilder->PushClip(mBuilder->DefineClip(
+      aSc.ToRelativeLayoutRect(clipRect), nullptr, mask.ptrOr(nullptr)));
 }
 
 ScrollingLayersHelper::~ScrollingLayersHelper()
 {
+  if (!mLayer) {
+    // For layers-free mode
+    while (mClipsPushed > 0) {
+      mBuilder->PopClip();
+      mClipsPushed--;
+    }
+    return;
+  }
+
   Layer* layer = mLayer->GetLayer();
   if (!mLayer->WrManager()->AsyncPanZoomEnabled()) {
     if (mPushedLayerLocalClip) {
       mBuilder->PopClip();
     }
     return;
   }
 
--- a/gfx/layers/wr/ScrollingLayersHelper.h
+++ b/gfx/layers/wr/ScrollingLayersHelper.h
@@ -2,43 +2,56 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef GFX_SCROLLINGLAYERSHELPER_H
 #define GFX_SCROLLINGLAYERSHELPER_H
 
 #include "mozilla/Attributes.h"
+#include "mozilla/layers/WebRenderLayerManager.h"
 
 namespace mozilla {
 
+struct DisplayItemClipChain;
+
 namespace wr {
 class DisplayListBuilder;
 }
 
 namespace layers {
 
 struct LayerClip;
 class StackingContextHelper;
 class WebRenderLayer;
 
 class MOZ_RAII ScrollingLayersHelper
 {
 public:
   ScrollingLayersHelper(WebRenderLayer* aLayer,
                         wr::DisplayListBuilder& aBuilder,
                         const StackingContextHelper& aSc);
+  ScrollingLayersHelper(nsDisplayItem* aItem,
+                        wr::DisplayListBuilder& aBuilder,
+                        const StackingContextHelper& aStackingContext,
+                        WebRenderLayerManager::ClipIdMap& aCache);
   ~ScrollingLayersHelper();
 
 private:
+  void DefineAndPushChain(const DisplayItemClipChain* aChain,
+                          wr::DisplayListBuilder& aBuilder,
+                          const StackingContextHelper& aStackingContext,
+                          int32_t aAppUnitsPerDevPixel,
+                          WebRenderLayerManager::ClipIdMap& aCache);
   void PushLayerLocalClip(const StackingContextHelper& aStackingContext);
   void PushLayerClip(const LayerClip& aClip,
                      const StackingContextHelper& aSc);
 
   WebRenderLayer* mLayer;
   wr::DisplayListBuilder* mBuilder;
   bool mPushedLayerLocalClip;
+  int mClipsPushed;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -107,18 +107,16 @@ public:
       }
     }
   }
 private:
   WebRenderBridgeParent* mWebRenderBridgeParent;
   InfallibleTArray<OpDestroy>* mActorsToDestroy;
 };
 
-/* static */ uint32_t WebRenderBridgeParent::sIdNameSpace = 0;
-
 WebRenderBridgeParent::WebRenderBridgeParent(CompositorBridgeParentBase* aCompositorBridge,
                                              const wr::PipelineId& aPipelineId,
                                              widget::CompositorWidget* aWidget,
                                              CompositorVsyncScheduler* aScheduler,
                                              RefPtr<wr::WebRenderAPI>&& aApi,
                                              RefPtr<AsyncImagePipelineManager>&& aImageMgr,
                                              RefPtr<CompositorAnimationStorage>&& aAnimStorage)
   : mCompositorBridge(aCompositorBridge)
@@ -126,17 +124,17 @@ WebRenderBridgeParent::WebRenderBridgePa
   , mWidget(aWidget)
   , mApi(aApi)
   , mAsyncImageManager(aImageMgr)
   , mCompositorScheduler(aScheduler)
   , mAnimStorage(aAnimStorage)
   , mChildLayerObserverEpoch(0)
   , mParentLayerObserverEpoch(0)
   , mWrEpoch(0)
-  , mIdNamespace(AllocIdNameSpace())
+  , mIdNamespace(aApi->GetNamespace())
   , mPaused(false)
   , mDestroyed(false)
   , mForceRendering(false)
 {
   MOZ_ASSERT(mAsyncImageManager);
   MOZ_ASSERT(mAnimStorage);
   mAsyncImageManager->AddPipeline(mPipelineId);
   if (mWidget) {
@@ -145,17 +143,17 @@ WebRenderBridgeParent::WebRenderBridgePa
   }
 }
 
 WebRenderBridgeParent::WebRenderBridgeParent()
   : mCompositorBridge(nullptr)
   , mChildLayerObserverEpoch(0)
   , mParentLayerObserverEpoch(0)
   , mWrEpoch(0)
-  , mIdNamespace(AllocIdNameSpace())
+  , mIdNamespace{0}
   , mPaused(false)
   , mDestroyed(true)
   , mForceRendering(false)
 {
 }
 
 /* static */ WebRenderBridgeParent*
 WebRenderBridgeParent::CreateDestroyed()
@@ -789,17 +787,17 @@ WebRenderBridgeParent::RecvRemovePipelin
   }
 
   WebRenderImageHost* wrHost = mAsyncCompositables.Get(wr::AsUint64(aPipelineId)).get();
   if (!wrHost) {
     return IPC_OK();
   }
 
   wrHost->ClearWrBridge();
-  mAsyncImageManager->RemoveAsyncImagePipeline(mApi, aPipelineId);
+  mAsyncImageManager->RemoveAsyncImagePipeline(aPipelineId);
   mAsyncCompositables.Remove(wr::AsUint64(aPipelineId));
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvAddExternalImageIdForCompositable(const ExternalImageId& aImageId,
                                                              const CompositableHandle& aHandle)
 {
@@ -881,17 +879,17 @@ WebRenderBridgeParent::UpdateWebRender(C
   MOZ_ASSERT(aAnimStorage);
 
   if (mDestroyed) {
     return;
   }
 
   // Update id name space to identify obsoleted keys.
   // Since usage of invalid keys could cause crash in webrender.
-  mIdNamespace = AllocIdNameSpace();
+  mIdNamespace = aApi->GetNamespace();
   // XXX Remove it when webrender supports sharing/moving Keys between different webrender instances.
   // XXX It requests client to update/reallocate webrender related resources,
   // but parent side does not wait end of the update.
   // The code could become simpler if we could serialise old keys deallocation and new keys allocation.
   // But we do not do it, it is because client side deallocate old layers/webrender keys
   // after new layers/webrender keys allocation.
   // Without client side's layout refactoring, we could not finish all old layers/webrender keys removals
   // before new layer/webrender keys allocation. In future, we could address the problem.
@@ -1113,17 +1111,17 @@ WebRenderBridgeParent::CompositeToTarget
     return;
   }
 
   bool scheduleComposite = false;
   nsTArray<wr::WrOpacityProperty> opacityArray;
   nsTArray<wr::WrTransformProperty> transformArray;
 
   mAsyncImageManager->SetCompositionTime(TimeStamp::Now());
-  mAsyncImageManager->ApplyAsyncImages(mApi);
+  mAsyncImageManager->ApplyAsyncImages();
 
   SampleAnimations(opacityArray, transformArray);
   if (!transformArray.IsEmpty() || !opacityArray.IsEmpty()) {
     scheduleComposite = true;
   }
 
   if (PushAPZStateToWR(transformArray)) {
     scheduleComposite = true;
@@ -1310,32 +1308,33 @@ WebRenderBridgeParent::ClearResources()
   for (auto iter = mExternalImageIds.Iter(); !iter.Done(); iter.Next()) {
     iter.Data()->ClearWrBridge();
   }
   mExternalImageIds.Clear();
   for (auto iter = mAsyncCompositables.Iter(); !iter.Done(); iter.Next()) {
     wr::PipelineId pipelineId = wr::AsPipelineId(iter.Key());
     RefPtr<WebRenderImageHost> host = iter.Data();
     host->ClearWrBridge();
-    mAsyncImageManager->RemoveAsyncImagePipeline(mApi, pipelineId);
+    mAsyncImageManager->RemoveAsyncImagePipeline(pipelineId);
   }
   mAsyncCompositables.Clear();
 
   mAsyncImageManager->RemovePipeline(mPipelineId, wr::NewEpoch(wrEpoch));
 
   for (std::unordered_set<uint64_t>::iterator iter = mActiveAnimations.begin(); iter != mActiveAnimations.end(); iter++) {
     mAnimStorage->ClearById(*iter);
   }
   mActiveAnimations.clear();
 
   if (mWidget) {
     mCompositorScheduler->Destroy();
   }
   mAnimStorage = nullptr;
   mCompositorScheduler = nullptr;
+  mAsyncImageManager = nullptr;
   mApi = nullptr;
   mCompositorBridge = nullptr;
 }
 
 bool
 WebRenderBridgeParent::ShouldParentObserveEpoch()
 {
   if (mParentLayerObserverEpoch == mChildLayerObserverEpoch) {
@@ -1449,13 +1448,16 @@ WebRenderBridgeParent::GetNextWrEpoch()
   MOZ_RELEASE_ASSERT(mWrEpoch != UINT32_MAX);
   return ++mWrEpoch;
 }
 
 void
 WebRenderBridgeParent::ExtractImageCompositeNotifications(nsTArray<ImageCompositeNotificationInfo>* aNotifications)
 {
   MOZ_ASSERT(mWidget);
+  if (mDestroyed) {
+    return;
+  }
   mAsyncImageManager->FlushImageNotifications(aNotifications);
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/wr/WebRenderBridgeParent.h
+++ b/gfx/layers/wr/WebRenderBridgeParent.h
@@ -182,20 +182,16 @@ public:
   wr::IdNamespace GetIdNamespace()
   {
     return mIdNamespace;
   }
 
   void UpdateAPZ();
   const WebRenderScrollData& GetScrollData() const;
 
-  static wr::IdNamespace AllocIdNameSpace() {
-    return wr::IdNamespace { ++sIdNameSpace };
-  }
-
   void FlushRendering(bool aIsSync);
 
   void ScheduleComposition();
 
   void UpdateWebRender(CompositorVsyncScheduler* aScheduler,
                        wr::WebRenderAPI* aApi,
                        AsyncImagePipelineManager* aImageMgr,
                        CompositorAnimationStorage* aAnimStorage);
@@ -291,16 +287,14 @@ private:
   wr::IdNamespace mIdNamespace;
 
   bool mPaused;
   bool mDestroyed;
   bool mForceRendering;
 
   // Can only be accessed on the compositor thread.
   WebRenderScrollData mScrollData;
-
-  static uint32_t sIdNameSpace;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // mozilla_layers_WebRenderBridgeParent_h
--- a/gfx/layers/wr/WebRenderLayerManager.cpp
+++ b/gfx/layers/wr/WebRenderLayerManager.cpp
@@ -259,21 +259,25 @@ WebRenderLayerManager::CreateWebRenderCo
       // If we're going to create a new layer data for this item, stash the
       // ASR so that if we recurse into a sublist they will know where to stop
       // walking up their ASR chain when building scroll metadata.
       if (forceNewLayerData) {
         mAsrStack.push_back(asr);
       }
     }
 
-    // Note: this call to CreateWebRenderCommands can recurse back into
-    // this function if the |item| is a wrapper for a sublist.
-    if (!item->CreateWebRenderCommands(aBuilder, aSc, mParentCommands, this,
-                                       aDisplayListBuilder)) {
-      PushItemAsImage(item, aBuilder, aSc, aDisplayListBuilder);
+    { // scope the ScrollingLayersHelper
+      ScrollingLayersHelper clip(item, aBuilder, aSc, mClipIdCache);
+
+      // Note: this call to CreateWebRenderCommands can recurse back into
+      // this function if the |item| is a wrapper for a sublist.
+      if (!item->CreateWebRenderCommands(aBuilder, aSc, mParentCommands, this,
+                                         aDisplayListBuilder)) {
+        PushItemAsImage(item, aBuilder, aSc, aDisplayListBuilder);
+      }
     }
 
     if (apzEnabled && forceNewLayerData) {
       // Pop the thing we pushed before the recursion, so the topmost item on
       // the stack is enclosing display item's ASR (or the stack is empty)
       mAsrStack.pop_back();
       const ActiveScrolledRoot* stopAtAsr =
           mAsrStack.empty() ? nullptr : mAsrStack.back();
@@ -619,16 +623,17 @@ WebRenderLayerManager::EndTransactionInt
       mLayerScrollData.back().InitializeRoot(mLayerScrollData.size() - 1);
       // Append the WebRenderLayerScrollData items into WebRenderScrollData
       // in reverse order, from topmost to bottommost. This is in keeping with
       // the semantics of WebRenderScrollData.
       for (auto i = mLayerScrollData.crbegin(); i != mLayerScrollData.crend(); i++) {
         mScrollData.AddLayerData(*i);
       }
       mLayerScrollData.clear();
+      mClipIdCache.clear();
     } else {
       for (auto iter = mLastCanvasDatas.Iter(); !iter.Done(); iter.Next()) {
         RefPtr<WebRenderCanvasData> canvasData = iter.Get()->GetKey();
         WebRenderCanvasRendererAsync* canvas = canvasData->GetCanvasRenderer();
         canvas->UpdateCompositableClient();
       }
     }
 
--- a/gfx/layers/wr/WebRenderLayerManager.h
+++ b/gfx/layers/wr/WebRenderLayerManager.h
@@ -268,16 +268,31 @@ private:
   // inside a layers-free transaction.
   std::vector<WebRenderLayerScrollData> mLayerScrollData;
   // We use this as a temporary data structure to track the current display
   // item's ASR as we recurse in CreateWebRenderCommandsFromDisplayList. We
   // need this so that WebRenderLayerScrollData items that deeper in the
   // tree don't duplicate scroll metadata that their ancestors already have.
   std::vector<const ActiveScrolledRoot*> mAsrStack;
 
+public:
+  // Note: two DisplayItemClipChain* A and B might actually be "equal" (as per
+  // DisplayItemClipChain::Equal(A, B)) even though they are not the same pointer
+  // (A != B). In this hopefully-rare case, they will get separate entries
+  // in this map when in fact we could collapse them. However, to collapse
+  // them involves writing a custom hash function for the pointer type such that
+  // A and B hash to the same things whenever DisplayItemClipChain::Equal(A, B)
+  // is true, and that will incur a performance penalty for all the hashmap
+  // operations, so is probably not worth it. With the current code we might
+  // end up creating multiple clips in WR that are effectively identical but
+  // have separate clip ids. Hopefully this won't happen very often.
+  typedef std::unordered_map<const DisplayItemClipChain*, wr::WrClipId> ClipIdMap;
+private:
+  ClipIdMap mClipIdCache;
+
   // Layers that have been mutated. If we have an empty transaction
   // then a display item layer will no longer be valid
   // if it was a mutated layers.
   void AddMutatedLayer(Layer* aLayer);
   void ClearMutatedLayers();
   LayerRefArray mMutatedLayers;
   bool mTransactionIncomplete;
 
--- a/gfx/layers/wr/WebRenderMessageUtils.h
+++ b/gfx/layers/wr/WebRenderMessageUtils.h
@@ -32,185 +32,75 @@ struct ParamTraits<mozilla::wr::ByteBuff
     return ReadParam(aMsg, aIter, &length)
         && aResult->Allocate(length)
         && aMsg->ReadBytesInto(aIter, aResult->mData, length);
   }
 };
 
 template<>
 struct ParamTraits<mozilla::wr::IdNamespace>
+  : public PlainOldDataSerializer<mozilla::wr::IdNamespace>
 {
-  static void
-  Write(Message* aMsg, const mozilla::wr::IdNamespace& aParam)
-  {
-    WriteParam(aMsg, aParam.mHandle);
-  }
-
-  static bool
-  Read(const Message* aMsg, PickleIterator* aIter, mozilla::wr::IdNamespace* aResult)
-  {
-    return ReadParam(aMsg, aIter, &aResult->mHandle);
-  }
 };
 
 template<>
 struct ParamTraits<mozilla::wr::ImageKey>
+  : public PlainOldDataSerializer<mozilla::wr::ImageKey>
 {
-  static void
-  Write(Message* aMsg, const mozilla::wr::ImageKey& aParam)
-  {
-    WriteParam(aMsg, aParam.mNamespace);
-    WriteParam(aMsg, aParam.mHandle);
-  }
-
-  static bool
-  Read(const Message* aMsg, PickleIterator* aIter, mozilla::wr::ImageKey* aResult)
-  {
-    return ReadParam(aMsg, aIter, &aResult->mNamespace)
-        && ReadParam(aMsg, aIter, &aResult->mHandle);
-  }
 };
 
 template<>
 struct ParamTraits<mozilla::wr::FontKey>
+  : public PlainOldDataSerializer<mozilla::wr::FontKey>
 {
-  static void
-  Write(Message* aMsg, const mozilla::wr::FontKey& aParam)
-  {
-    WriteParam(aMsg, aParam.mNamespace);
-    WriteParam(aMsg, aParam.mHandle);
-  }
-
-  static bool
-  Read(const Message* aMsg, PickleIterator* aIter, mozilla::wr::FontKey* aResult)
-  {
-    return ReadParam(aMsg, aIter, &aResult->mNamespace)
-        && ReadParam(aMsg, aIter, &aResult->mHandle);
-  }
 };
 
 template<>
 struct ParamTraits<mozilla::wr::ExternalImageId>
+  : public PlainOldDataSerializer<mozilla::wr::ExternalImageId>
 {
-  static void
-  Write(Message* aMsg, const mozilla::wr::ExternalImageId& aParam)
-  {
-    WriteParam(aMsg, aParam.mHandle);
-  }
-
-  static bool
-  Read(const Message* aMsg, PickleIterator* aIter, mozilla::wr::ExternalImageId* aResult)
-  {
-    return ReadParam(aMsg, aIter, &aResult->mHandle);
-  }
 };
 
 template<>
 struct ParamTraits<mozilla::wr::PipelineId>
+  : public PlainOldDataSerializer<mozilla::wr::PipelineId>
 {
-  static void
-  Write(Message* aMsg, const mozilla::wr::PipelineId& aParam)
-  {
-    WriteParam(aMsg, aParam.mNamespace);
-    WriteParam(aMsg, aParam.mHandle);
-  }
-
-  static bool
-  Read(const Message* aMsg, PickleIterator* aIter, mozilla::wr::PipelineId* aResult)
-  {
-    return ReadParam(aMsg, aIter, &aResult->mNamespace)
-        && ReadParam(aMsg, aIter, &aResult->mHandle);
-  }
 };
 
 template<>
 struct ParamTraits<mozilla::wr::ImageFormat>
   : public ContiguousEnumSerializer<
         mozilla::wr::ImageFormat,
         mozilla::wr::ImageFormat::Invalid,
         mozilla::wr::ImageFormat::Sentinel>
 {
 };
 
 template<>
 struct ParamTraits<mozilla::wr::LayoutSize>
+  : public PlainOldDataSerializer<mozilla::wr::LayoutSize>
 {
-  static void
-  Write(Message* aMsg, const mozilla::wr::LayoutSize& aParam)
-  {
-    WriteParam(aMsg, aParam.width);
-    WriteParam(aMsg, aParam.height);
-  }
-
-  static bool
-  Read(const Message* aMsg, PickleIterator* aIter, mozilla::wr::LayoutSize* aResult)
-  {
-    return ReadParam(aMsg, aIter, &aResult->width)
-        && ReadParam(aMsg, aIter, &aResult->height);
-  }
 };
 
 template<>
 struct ParamTraits<mozilla::wr::LayoutRect>
+  : public PlainOldDataSerializer<mozilla::wr::LayoutRect>
 {
-  static void
-  Write(Message* aMsg, const mozilla::wr::LayoutRect& aParam)
-  {
-    WriteParam(aMsg, aParam.origin.x);
-    WriteParam(aMsg, aParam.origin.y);
-    WriteParam(aMsg, aParam.size.width);
-    WriteParam(aMsg, aParam.size.height);
-  }
-
-  static bool
-  Read(const Message* aMsg, PickleIterator* aIter, mozilla::wr::LayoutRect* aResult)
-  {
-    return ReadParam(aMsg, aIter, &aResult->origin.x)
-        && ReadParam(aMsg, aIter, &aResult->origin.y)
-        && ReadParam(aMsg, aIter, &aResult->size.width)
-        && ReadParam(aMsg, aIter, &aResult->size.height);
-  }
 };
 
 template<>
 struct ParamTraits<mozilla::wr::LayoutPoint>
+  : public PlainOldDataSerializer<mozilla::wr::LayoutPoint>
 {
-  static void
-  Write(Message* aMsg, const mozilla::wr::LayoutPoint& aParam)
-  {
-    WriteParam(aMsg, aParam.x);
-    WriteParam(aMsg, aParam.y);
-  }
-
-  static bool
-  Read(const Message* aMsg, PickleIterator* aIter, mozilla::wr::LayoutPoint* aResult)
-  {
-    return ReadParam(aMsg, aIter, &aResult->x) &&
-           ReadParam(aMsg, aIter, &aResult->y);
-  }
 };
 
 template<>
 struct ParamTraits<mozilla::wr::WrImageMask>
+  : public PlainOldDataSerializer<mozilla::wr::WrImageMask>
 {
-  static void
-  Write(Message* aMsg, const mozilla::wr::WrImageMask& aParam)
-  {
-    WriteParam(aMsg, aParam.image);
-    WriteParam(aMsg, aParam.rect);
-    WriteParam(aMsg, aParam.repeat);
-  }
-
-  static bool
-  Read(const Message* aMsg, PickleIterator* aIter, mozilla::wr::WrImageMask* aResult)
-  {
-    return ReadParam(aMsg, aIter, &aResult->image)
-        && ReadParam(aMsg, aIter, &aResult->rect)
-        && ReadParam(aMsg, aIter, &aResult->repeat);
-  }
 };
 
 template<>
 struct ParamTraits<mozilla::wr::ImageRendering>
   : public ContiguousEnumSerializer<
         mozilla::wr::ImageRendering,
         mozilla::wr::ImageRendering::Auto,
         mozilla::wr::ImageRendering::Sentinel>
@@ -223,27 +113,15 @@ struct ParamTraits<mozilla::wr::MixBlend
         mozilla::wr::MixBlendMode,
         mozilla::wr::MixBlendMode::Normal,
         mozilla::wr::MixBlendMode::Sentinel>
 {
 };
 
 template<>
 struct ParamTraits<mozilla::wr::BuiltDisplayListDescriptor>
+  : public PlainOldDataSerializer<mozilla::wr::BuiltDisplayListDescriptor>
 {
-  static void
-  Write(Message* aMsg, const mozilla::wr::BuiltDisplayListDescriptor& aParam)
-  {
-    WriteParam(aMsg, aParam.builder_start_time);
-    WriteParam(aMsg, aParam.builder_finish_time);
-  }
-
-  static bool
-  Read(const Message* aMsg, PickleIterator* aIter, mozilla::wr::BuiltDisplayListDescriptor* aResult)
-  {
-    return ReadParam(aMsg, aIter, &aResult->builder_start_time)
-        && ReadParam(aMsg, aIter, &aResult->builder_finish_time);
-  }
 };
 
 } // namespace IPC
 
 #endif // GFX_WEBRENDERMESSAGEUTILS_H
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -2442,16 +2442,24 @@ gfxPlatform::InitWebRenderConfig()
     featureWebRender.UserEnable("Enabled by pref");
   } else {
     const char* env = PR_GetEnv("MOZ_WEBRENDER");
     if (env && *env == '1') {
       featureWebRender.UserEnable("Enabled by envvar");
     }
   }
 
+  // HW_COMPOSITING being disabled implies interfacing with the GPU might break
+  if (!gfxConfig::IsEnabled(Feature::HW_COMPOSITING)) {
+    featureWebRender.ForceDisable(
+      FeatureStatus::Unavailable,
+      "Hardware compositing is disabled",
+      NS_LITERAL_CSTRING("FEATURE_FAILURE_WEBRENDER_NEED_HWCOMP"));
+  }
+
   // WebRender relies on the GPU process when on Windows
 #ifdef XP_WIN
   if (!gfxConfig::IsEnabled(Feature::GPU_PROCESS)) {
     featureWebRender.ForceDisable(
       FeatureStatus::Unavailable,
       "GPU Process is disabled",
       NS_LITERAL_CSTRING("FEATURE_FAILURE_GPU_PROCESS_DISABLED"));
   }
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -330,17 +330,17 @@ private:
   DECL_GFX_PREF(Live, "apz.overscroll.stretch_factor",         APZOverscrollStretchFactor, float, 0.5f);
   DECL_GFX_PREF(Live, "apz.paint_skipping.enabled",            APZPaintSkipping, bool, true);
   DECL_GFX_PREF(Live, "apz.peek_messages.enabled",             APZPeekMessages, bool, true);
   DECL_GFX_PREF(Live, "apz.popups.enabled",                    APZPopupsEnabled, bool, false);
   DECL_GFX_PREF(Live, "apz.printtree",                         APZPrintTree, bool, false);
   DECL_GFX_PREF(Live, "apz.record_checkerboarding",            APZRecordCheckerboarding, bool, false);
   DECL_GFX_PREF(Live, "apz.test.fails_with_native_injection",  APZTestFailsWithNativeInjection, bool, false);
   DECL_GFX_PREF(Live, "apz.test.logging_enabled",              APZTestLoggingEnabled, bool, false);
-  DECL_GFX_PREF(Live, "apz.touch_move_tolerance",              APZTouchMoveTolerance, float, 0.0);
+  DECL_GFX_PREF(Live, "apz.touch_move_tolerance",              APZTouchMoveTolerance, float, 0.1f);
   DECL_GFX_PREF(Live, "apz.touch_start_tolerance",             APZTouchStartTolerance, float, 1.0f/4.5f);
   DECL_GFX_PREF(Live, "apz.velocity_bias",                     APZVelocityBias, float, 0.0f);
   DECL_GFX_PREF(Live, "apz.velocity_relevance_time_ms",        APZVelocityRelevanceTime, uint32_t, 150);
   DECL_GFX_PREF(Live, "apz.x_skate_highmem_adjust",            APZXSkateHighMemAdjust, float, 0.0f);
   DECL_GFX_PREF(Live, "apz.x_skate_size_multiplier",           APZXSkateSizeMultiplier, float, 1.5f);
   DECL_GFX_PREF(Live, "apz.x_stationary_size_multiplier",      APZXStationarySizeMultiplier, float, 3.0f);
   DECL_GFX_PREF(Live, "apz.y_skate_highmem_adjust",            APZYSkateHighMemAdjust, float, 0.0f);
   DECL_GFX_PREF(Live, "apz.y_skate_size_multiplier",           APZYSkateSizeMultiplier, float, 2.5f);
--- a/gfx/vr/gfxVROculus.cpp
+++ b/gfx/vr/gfxVROculus.cpp
@@ -239,19 +239,18 @@ VROculusSession::StartPresentation(const
 
 void
 VROculusSession::StopPresentation()
 {
   if (mPresenting) {
     mLastPresentationEnd = TimeStamp::Now();
     mPresenting = false;
     Telemetry::Accumulate(Telemetry::WEBVR_USERS_VIEW_IN, 1);
-    Telemetry::Accumulate(Telemetry::WEBVR_TIME_SPEND_FOR_VIEWING_IN_OCULUS,
-                          static_cast<uint32_t>((TimeStamp::Now() - mPresentationStart)
-                          .ToMilliseconds()));
+    Telemetry::AccumulateTimeDelta(Telemetry::WEBVR_TIME_SPENT_VIEWING_IN_OCULUS,
+                                   mPresentationStart);
     Refresh();
   }
 }
 
 VROculusSession::~VROculusSession()
 {
   Uninitialize(true);
 }
--- a/gfx/vr/gfxVROpenVR.cpp
+++ b/gfx/vr/gfxVROpenVR.cpp
@@ -277,19 +277,18 @@ VRDisplayOpenVR::StopPresentation()
   if (!mIsPresenting) {
     return;
   }
 
   mVRCompositor->ClearLastSubmittedFrame();
 
   mIsPresenting = false;
   Telemetry::Accumulate(Telemetry::WEBVR_USERS_VIEW_IN, 2);
-  Telemetry::Accumulate(Telemetry::WEBVR_TIME_SPEND_FOR_VIEWING_IN_OPENVR,
-                        static_cast<uint32_t>((TimeStamp::Now() - mPresentationStart)
-                        .ToMilliseconds()));
+  Telemetry::AccumulateTimeDelta(Telemetry::WEBVR_TIME_SPENT_VIEWING_IN_OPENVR,
+                                 mPresentationStart);
 }
 
 bool
 VRDisplayOpenVR::SubmitFrame(void* aTextureHandle,
                              ::vr::ETextureType aTextureType,
                              const IntSize& aSize,
                              const gfx::Rect& aLeftEyeRect,
                              const gfx::Rect& aRightEyeRect)
--- a/gfx/webrender/Cargo.toml
+++ b/gfx/webrender/Cargo.toml
@@ -13,17 +13,17 @@ profiler = ["thread_profiler/thread_prof
 webgl = ["offscreen_gl_context", "webrender_api/webgl"]
 
 [dependencies]
 app_units = "0.5"
 bincode = "0.8"
 bit-set = "0.4"
 byteorder = "1.0"
 euclid = "0.15.1"
-fnv = "1.0"
+fxhash = "0.2.1"
 gleam = "0.4.7"
 lazy_static = "0.2"
 log = "0.3"
 num-traits = "0.1.32"
 offscreen_gl_context = {version = "0.11", features = ["serde", "osmesa"], optional = true}
 time = "0.1"
 rayon = "0.8"
 webrender_api = {path = "../webrender_api"}
@@ -33,16 +33,16 @@ thread_profiler = "0.1.1"
 plane-split = "0.6"
 
 [dev-dependencies]
 angle = {git = "https://github.com/servo/angle", branch = "servo"}
 rand = "0.3"                # for the benchmarks
 servo-glutin = "0.11"     # for the example apps
 
 [target.'cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))'.dependencies]
-freetype = { version = "0.2", default-features = false }
+freetype = { version = "0.3", default-features = false }
 
 [target.'cfg(target_os = "windows")'.dependencies]
 dwrote = "0.4"
 
 [target.'cfg(target_os = "macos")'.dependencies]
 core-graphics = "0.8.0"
 core-text = { version = "6.1", default-features = false }
--- a/gfx/webrender/build.rs
+++ b/gfx/webrender/build.rs
@@ -9,32 +9,32 @@ use std::fs::{canonicalize, read_dir, Fi
 
 fn write_shaders(glsl_files: Vec<PathBuf>, shader_file_path: &Path) {
     let mut shader_file = File::create(shader_file_path).unwrap();
 
     write!(shader_file, "/// AUTO GENERATED BY build.rs\n\n").unwrap();
     write!(shader_file, "use std::collections::HashMap;\n").unwrap();
     write!(shader_file, "lazy_static! {{\n").unwrap();
     write!(shader_file, "  pub static ref SHADERS: HashMap<&'static str, &'static str> = {{\n").unwrap();
-    write!(shader_file, "    let mut h = HashMap::with_capacity({});\n", glsl_files.len()).unwrap();
+    write!(shader_file, "    let mut h = HashMap::new();\n").unwrap();
     for glsl in glsl_files {
         let shader_name = glsl.file_name().unwrap().to_str().unwrap();
         // strip .glsl
         let shader_name = shader_name.replace(".glsl", "");
         let full_path = canonicalize(&glsl).unwrap();
         let full_name = full_path.as_os_str().to_str().unwrap();
         // if someone is building on a network share, I'm sorry.
         let full_name = full_name.replace("\\\\?\\", "");
         let full_name = full_name.replace("\\", "/");
         write!(shader_file, "    h.insert(\"{}\", include_str!(\"{}\"));\n",
                shader_name, full_name).unwrap();
     }
-    write!(shader_file, "    h\n").unwrap(); 
-    write!(shader_file, "  }};\n").unwrap(); 
-    write!(shader_file, "}}\n").unwrap(); 
+    write!(shader_file, "    h\n").unwrap();
+    write!(shader_file, "  }};\n").unwrap();
+    write!(shader_file, "}}\n").unwrap();
 }
 
 fn main() {
     let out_dir = env::var("OUT_DIR").unwrap_or("out".to_owned());
 
     let shaders_file = Path::new(&out_dir).join("shaders.rs");
     let mut glsl_files = vec![];
 
--- a/gfx/webrender/examples/animation.rs
+++ b/gfx/webrender/examples/animation.rs
@@ -15,17 +15,19 @@ mod boilerplate;
 use boilerplate::HandyDandyRectBuilder;
 use std::sync::Mutex;
 use webrender::api::*;
 
 // This example creates a 100x100 white rect and allows the user to move it
 // around by using the arrow keys. It does this by using the animation API.
 
 fn body(_api: &RenderApi,
+        _document_id: &DocumentId,
         builder: &mut DisplayListBuilder,
+        _resouces: &mut ResourceUpdates,
         _pipeline_id: &PipelineId,
         _layout_size: &LayoutSize) {
     // Create a 100x100 stacking context with an animatable transform property.
     // Note the magic "42" we use as the animation key. That is used to update
     // the transform in the keyboard event handler code.
     let bounds = (0,0).to(100, 100);
     builder.push_stacking_context(ScrollPolicy::Scrollable,
                                   bounds,
@@ -40,33 +42,31 @@ fn body(_api: &RenderApi,
 
     builder.pop_stacking_context();
 }
 
 lazy_static! {
     static ref TRANSFORM: Mutex<LayoutTransform> = Mutex::new(LayoutTransform::identity());
 }
 
-fn event_handler(event: &glutin::Event,
-                 api: &RenderApi)
-{
+fn event_handler(event: &glutin::Event, document_id: DocumentId, api: &RenderApi) {
     match *event {
         glutin::Event::KeyboardInput(glutin::ElementState::Pressed, _, Some(key)) => {
             let offset = match key {
                  glutin::VirtualKeyCode::Down => (0.0, 10.0),
                  glutin::VirtualKeyCode::Up => (0.0, -10.0),
                  glutin::VirtualKeyCode::Right => (10.0, 0.0),
                  glutin::VirtualKeyCode::Left => (-10.0, 0.0),
                  _ => return,
             };
             // Update the transform based on the keyboard input and push it to
             // webrender using the generate_frame API. This will recomposite with
             // the updated transform.
             let new_transform = TRANSFORM.lock().unwrap().post_translate(LayoutVector3D::new(offset.0, offset.1, 0.0));
-            api.generate_frame(Some(DynamicProperties {
+            api.generate_frame(document_id, Some(DynamicProperties {
                 transforms: vec![
                   PropertyValue {
                     key: PropertyBindingKey::new(42),
                     value: new_transform,
                   },
                 ],
                 floats: vec![],
             }));
--- a/gfx/webrender/examples/basic.rs
+++ b/gfx/webrender/examples/basic.rs
@@ -169,33 +169,37 @@ fn load_file(name: &str) -> Vec<u8> {
     buffer
 }
 
 fn main() {
     boilerplate::main_wrapper(body, event_handler, None);
 }
 
 fn body(api: &RenderApi,
+        _document_id: &DocumentId,
         builder: &mut DisplayListBuilder,
+        resources: &mut ResourceUpdates,
         _pipeline_id: &PipelineId,
         layout_size: &LayoutSize) {
     let bounds = LayoutRect::new(LayoutPoint::zero(), *layout_size);
     builder.push_stacking_context(ScrollPolicy::Scrollable,
                                   bounds,
                                   None,
                                   TransformStyle::Flat,
                                   None,
                                   MixBlendMode::Normal,
                                   Vec::new());
 
     let image_mask_key = api.generate_image_key();
-    api.add_image(image_mask_key,
-                  ImageDescriptor::new(2, 2, ImageFormat::A8, true),
-                  ImageData::new(vec![0, 80, 180, 255]),
-                  None);
+    resources.add_image(
+        image_mask_key,
+        ImageDescriptor::new(2, 2, ImageFormat::A8, true),
+        ImageData::new(vec![0, 80, 180, 255]),
+        None
+    );
     let mask = ImageMask {
         image: image_mask_key,
         rect: (75, 75).by(100, 100),
         repeat: false,
     };
     let complex = ComplexClipRegion::new((50, 50).to(150, 150), BorderRadius::uniform(20.0));
     let id = builder.define_clip(None, bounds, vec![complex], Some(mask));
     builder.push_clip_id(id);
@@ -225,17 +229,17 @@ fn body(api: &RenderApi,
 
     let bounds = (100, 100).to(200, 200);
     builder.push_border(bounds, None, border_widths, border_details);
 
 
     if false { // draw text?
         let font_key = api.generate_font_key();
         let font_bytes = load_file("res/FreeSans.ttf");
-        api.add_raw_font(font_key, font_bytes, 0);
+        resources.add_raw_font(font_key, font_bytes, 0);
 
         let text_bounds = (100, 200).by(700, 300);
         let glyphs = vec![
             GlyphInstance {
                 index: 48,
                 point: LayoutPoint::new(100.0, 100.0),
             },
             GlyphInstance {
@@ -317,26 +321,26 @@ fn body(api: &RenderApi,
     builder.pop_clip_id();
     builder.pop_stacking_context();
 }
 
 lazy_static! {
     static ref TOUCH_STATE: Mutex<TouchState> = Mutex::new(TouchState::new());
 }
 
-fn event_handler(event: &glutin::Event, api: &RenderApi) {
+fn event_handler(event: &glutin::Event, document_id: DocumentId, api: &RenderApi) {
     match *event {
         glutin::Event::Touch(touch) => {
             match TOUCH_STATE.lock().unwrap().handle_event(touch) {
                 TouchResult::Pan(pan) => {
-                    api.set_pan(pan);
-                    api.generate_frame(None);
+                    api.set_pan(document_id, pan);
+                    api.generate_frame(document_id, None);
                 }
                 TouchResult::Zoom(zoom) => {
-                    api.set_pinch_zoom(ZoomFactor::new(zoom));
-                    api.generate_frame(None);
+                    api.set_pinch_zoom(document_id, ZoomFactor::new(zoom));
+                    api.generate_frame(document_id, None);
                 }
                 TouchResult::None => {}
             }
         }
         _ => ()
     }
 }
--- a/gfx/webrender/examples/blob.rs
+++ b/gfx/webrender/examples/blob.rs
@@ -208,29 +208,31 @@ impl api::BlobImageRenderer for Checkerb
 
         // If we break out of the loop above it means the channel closed unexpectedly.
         Err(api::BlobImageError::Other("Channel closed".into()))
     }
     fn delete_font(&mut self, _font: api::FontKey) { }
 }
 
 fn body(api: &api::RenderApi,
+        _document_id: &api::DocumentId,
         builder: &mut api::DisplayListBuilder,
+        resources: &mut api::ResourceUpdates,
         _pipeline_id: &api::PipelineId,
         layout_size: &api::LayoutSize) {
     let blob_img1 = api.generate_image_key();
-    api.add_image(
+    resources.add_image(
         blob_img1,
         api::ImageDescriptor::new(500, 500, api::ImageFormat::BGRA8, true),
         api::ImageData::new_blob_image(serialize_blob(api::ColorU::new(50, 50, 150, 255))),
         Some(128),
     );
 
     let blob_img2 = api.generate_image_key();
-    api.add_image(
+    resources.add_image(
         blob_img2,
         api::ImageDescriptor::new(200, 200, api::ImageFormat::BGRA8, true),
         api::ImageData::new_blob_image(serialize_blob(api::ColorU::new(50, 150, 50, 255))),
         None,
     );
 
     let bounds = api::LayoutRect::new(api::LayoutPoint::zero(), *layout_size);
     builder.push_stacking_context(api::ScrollPolicy::Scrollable,
@@ -257,19 +259,17 @@ fn body(api: &api::RenderApi,
         api::LayoutSize::new(0.0, 0.0),
         api::ImageRendering::Auto,
         blob_img2,
     );
 
     builder.pop_stacking_context();
 }
 
-fn event_handler(_event: &glutin::Event,
-                 _api: &api::RenderApi)
-{
+fn event_handler(_event: &glutin::Event, _document_id: api::DocumentId, _api: &api::RenderApi) {
 }
 
 fn main() {
     let worker_config = ThreadPoolConfig::new().thread_name(|idx|{
         format!("WebRender:Worker#{}", idx)
     });
 
     let workers = Arc::new(ThreadPool::new(worker_config).unwrap());
--- a/gfx/webrender/examples/common/boilerplate.rs
+++ b/gfx/webrender/examples/common/boilerplate.rs
@@ -3,16 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use gleam::gl;
 use glutin;
 use std::env;
 use std::path::PathBuf;
 use webrender;
 use webrender::api::*;
+use webrender::renderer::{PROFILER_DBG, RENDER_TARGET_DBG, TEXTURE_CACHE_DBG};
 
 struct Notifier {
     window_proxy: glutin::WindowProxy,
 }
 
 impl Notifier {
     fn new(window_proxy: glutin::WindowProxy) -> Notifier {
         Notifier {
@@ -47,20 +48,23 @@ impl HandyDandyRectBuilder for (i32, i32
 
     fn by(&self, w: i32, h: i32) -> LayoutRect {
         LayoutRect::new(LayoutPoint::new(self.0 as f32, self.1 as f32),
                         LayoutSize::new(w as f32, h as f32))
     }
 }
 
 pub fn main_wrapper(builder_callback: fn(&RenderApi,
+                                         &DocumentId,
                                          &mut DisplayListBuilder,
+                                         &mut ResourceUpdates,
                                          &PipelineId,
                                          &LayoutSize) -> (),
                     event_handler: fn(&glutin::Event,
+                                      DocumentId,
                                       &RenderApi) -> (),
                     options: Option<webrender::RendererOptions>)
 {
     let args: Vec<String> = env::args().collect();
     let res_path = if args.len() > 1 {
         Some(PathBuf::from(&args[1]))
     } else {
         None
@@ -94,39 +98,44 @@ pub fn main_wrapper(builder_callback: fn
         resource_override_path: res_path,
         debug: true,
         precache_shaders: true,
         device_pixel_ratio: window.hidpi_factor(),
         .. options.unwrap_or(webrender::RendererOptions::default())
     };
 
     let size = DeviceUintSize::new(width, height);
-    let (mut renderer, sender) = webrender::renderer::Renderer::new(gl, opts, size).unwrap();
+    let (mut renderer, sender) = webrender::renderer::Renderer::new(gl, opts).unwrap();
     let api = sender.create_api();
+    let document_id = api.add_document(size);
 
     let notifier = Box::new(Notifier::new(window.create_window_proxy()));
     renderer.set_render_notifier(notifier);
 
     let epoch = Epoch(0);
     let root_background_color = ColorF::new(0.3, 0.0, 0.0, 1.0);
 
     let pipeline_id = PipelineId(0, 0);
     let layout_size = LayoutSize::new(width as f32, height as f32);
     let mut builder = DisplayListBuilder::new(pipeline_id, layout_size);
+    let mut resources = ResourceUpdates::new();
 
-    builder_callback(&api, &mut builder, &pipeline_id, &layout_size);
+    builder_callback(&api, &document_id, &mut builder, &mut resources, &pipeline_id, &layout_size);
 
     api.set_display_list(
+        document_id,
+        epoch,
         Some(root_background_color),
-        epoch,
         LayoutSize::new(width as f32, height as f32),
         builder.finalize(),
-        true);
-    api.set_root_pipeline(pipeline_id);
-    api.generate_frame(None);
+        true,
+        resources
+    );
+    api.set_root_pipeline(document_id, pipeline_id);
+    api.generate_frame(document_id, None);
 
     'outer: for event in window.wait_events() {
         let mut events = Vec::new();
         events.push(event);
 
         for event in window.poll_events() {
             events.push(event);
         }
@@ -134,22 +143,36 @@ pub fn main_wrapper(builder_callback: fn
         for event in events {
             match event {
                 glutin::Event::Closed |
                 glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Escape)) |
                 glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Q)) => break 'outer,
 
                 glutin::Event::KeyboardInput(glutin::ElementState::Pressed,
                                              _, Some(glutin::VirtualKeyCode::P)) => {
-                    let enable_profiler = !renderer.get_profiler_enabled();
-                    renderer.set_profiler_enabled(enable_profiler);
-                    api.generate_frame(None);
-                }
+                    let mut flags = renderer.get_debug_flags();
+                    flags.toggle(PROFILER_DBG);
+                    renderer.set_debug_flags(flags);
+                },
+                glutin::Event::KeyboardInput(glutin::ElementState::Pressed,
+                                             _, Some(glutin::VirtualKeyCode::O)) => {
+                    let mut flags = renderer.get_debug_flags();
+                    flags.toggle(RENDER_TARGET_DBG);
+                    renderer.set_debug_flags(flags);
+                },
+                glutin::Event::KeyboardInput(glutin::ElementState::Pressed,
+                                             _, Some(glutin::VirtualKeyCode::I)) => {
+                    let mut flags = renderer.get_debug_flags();
+                    flags.toggle(TEXTURE_CACHE_DBG);
+                    renderer.set_debug_flags(flags);
+                },
 
-                _ => event_handler(&event, &api),
+                _ => event_handler(&event, document_id, &api),
             }
         }
 
         renderer.update();
         renderer.render(DeviceUintSize::new(width, height));
         window.swap_buffers().ok();
     }
+
+    renderer.deinit();
 }
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/examples/iframe.rs
@@ -0,0 +1,74 @@
+/* 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 gleam;
+extern crate glutin;
+extern crate webrender;
+
+#[path="common/boilerplate.rs"]
+mod boilerplate;
+
+use boilerplate::HandyDandyRectBuilder;
+use webrender::api::*;
+
+// This example uses the push_iframe API to nest a second pipeline's displaylist
+// inside the root pipeline's display list. When it works, a green square is
+// shown. If it fails, a red square is shown.
+
+fn body(api: &RenderApi,
+        document_id: &DocumentId,
+        builder: &mut DisplayListBuilder,
+        _resources: &mut ResourceUpdates,
+        pipeline_id: &PipelineId,
+        _layout_size: &LayoutSize) {
+
+    // All the sub_* things are for the nested pipeline
+    let sub_size = DeviceUintSize::new(100, 100);
+    let sub_bounds = (0,0).to(sub_size.width as i32, sub_size.height as i32);
+
+    let sub_pipeline_id = PipelineId(pipeline_id.0, 42);
+    let mut sub_builder = DisplayListBuilder::new(sub_pipeline_id, sub_bounds.size);
+
+    sub_builder.push_stacking_context(ScrollPolicy::Scrollable,
+                                      sub_bounds,
+                                      None,
+                                      TransformStyle::Flat,
+                                      None,
+                                      MixBlendMode::Normal,
+                                      Vec::new());
+    // green rect visible == success
+    sub_builder.push_rect(sub_bounds, None, ColorF::new(0.0, 1.0, 0.0, 1.0));
+    sub_builder.pop_stacking_context();
+
+    api.set_display_list(
+        *document_id,
+        Epoch(0),
+        None,
+        sub_bounds.size,
+        sub_builder.finalize(),
+        true,
+        ResourceUpdates::new(),
+    );
+
+    let bounds = sub_bounds;
+    // And this is for the root pipeline
+    builder.push_stacking_context(ScrollPolicy::Scrollable,
+                                  bounds,
+                                  Some(PropertyBinding::Binding(PropertyBindingKey::new(42))),
+                                  TransformStyle::Flat,
+                                  None,
+                                  MixBlendMode::Normal,
+                                  Vec::new());
+    // red rect under the iframe: if this is visible, things have gone wrong
+    builder.push_rect(bounds, None, ColorF::new(1.0, 0.0, 0.0, 1.0));
+    builder.push_iframe(bounds, None, sub_pipeline_id);
+    builder.pop_stacking_context();
+}
+
+fn event_handler(_event: &glutin::Event, _document_id: DocumentId, _api: &RenderApi) {
+}
+
+fn main() {
+    boilerplate::main_wrapper(body, event_handler, None);
+}
--- a/gfx/webrender/examples/nested_display_list.rs
+++ b/gfx/webrender/examples/nested_display_list.rs
@@ -12,17 +12,19 @@ extern crate lazy_static;
 #[path="common/boilerplate.rs"]
 mod boilerplate;
 
 use boilerplate::HandyDandyRectBuilder;
 use std::sync::Mutex;
 use webrender::api::*;
 
 fn body(_api: &RenderApi,
+        _document_id: &DocumentId,
         builder: &mut DisplayListBuilder,
+        _resources: &mut ResourceUpdates,
         pipeline_id: &PipelineId,
         layout_size: &LayoutSize) {
     let bounds = LayoutRect::new(LayoutPoint::zero(), *layout_size);
     builder.push_stacking_context(ScrollPolicy::Scrollable,
                                   bounds,
                                   None,
                                   TransformStyle::Flat,
                                   None,
@@ -85,30 +87,29 @@ fn body(_api: &RenderApi,
 
     builder.pop_stacking_context();
 }
 
 lazy_static! {
     static ref CURSOR_POSITION: Mutex<WorldPoint> = Mutex::new(WorldPoint::zero());
 }
 
-fn event_handler(event: &glutin::Event,
-                 api: &RenderApi)
-{
+fn event_handler(event: &glutin::Event, document_id: DocumentId, api: &RenderApi) {
     match *event {
         glutin::Event::KeyboardInput(glutin::ElementState::Pressed, _, Some(key)) => {
             let offset = match key {
                  glutin::VirtualKeyCode::Down => (0.0, -10.0),
                  glutin::VirtualKeyCode::Up => (0.0, 10.0),
                  glutin::VirtualKeyCode::Right => (-10.0, 0.0),
                  glutin::VirtualKeyCode::Left => (10.0, 0.0),
                  _ => return,
             };
 
-            api.scroll(ScrollLocation::Delta(LayoutVector2D::new(offset.0, offset.1)),
+            api.scroll(document_id,
+                       ScrollLocation::Delta(LayoutVector2D::new(offset.0, offset.1)),
                        *CURSOR_POSITION.lock().unwrap(),
                        ScrollEventPhase::Start);
         }
         glutin::Event::MouseMoved(x, y) => {
             *CURSOR_POSITION.lock().unwrap() = WorldPoint::new(x as f32, y as f32);
         }
         glutin::Event::MouseWheel(delta, _, event_cursor_position) => {
             if let Some((x, y)) = event_cursor_position {
@@ -116,17 +117,18 @@ fn event_handler(event: &glutin::Event,
             }
 
             const LINE_HEIGHT: f32 = 38.0;
             let (dx, dy) = match delta {
                 glutin::MouseScrollDelta::LineDelta(dx, dy) => (dx, dy * LINE_HEIGHT),
                 glutin::MouseScrollDelta::PixelDelta(dx, dy) => (dx, dy),
             };
 
-            api.scroll(ScrollLocation::Delta(LayoutVector2D::new(dx, dy)),
+            api.scroll(document_id,
+                       ScrollLocation::Delta(LayoutVector2D::new(dx, dy)),
                        *CURSOR_POSITION.lock().unwrap(),
                        ScrollEventPhase::Start);
         }
         _ => ()
     }
 }
 
 fn main() {
--- a/gfx/webrender/examples/scrolling.rs
+++ b/gfx/webrender/examples/scrolling.rs
@@ -12,17 +12,19 @@ extern crate lazy_static;
 #[path="common/boilerplate.rs"]
 mod boilerplate;
 
 use boilerplate::HandyDandyRectBuilder;
 use std::sync::Mutex;
 use webrender::api::*;
 
 fn body(_api: &RenderApi,
+        _document_id: &DocumentId,
         builder: &mut DisplayListBuilder,
+        _resources: &mut ResourceUpdates,
         _pipeline_id: &PipelineId,
         layout_size: &LayoutSize) {
     let bounds = LayoutRect::new(LayoutPoint::zero(), *layout_size);
     builder.push_stacking_context(ScrollPolicy::Scrollable,
                                   bounds,
                                   None,
                                   TransformStyle::Flat,
                                   None,
@@ -94,28 +96,29 @@ fn body(_api: &RenderApi,
 
     builder.pop_stacking_context();
 }
 
 lazy_static! {
     static ref CURSOR_POSITION: Mutex<WorldPoint> = Mutex::new(WorldPoint::zero());
 }
 
-fn event_handler(event: &glutin::Event, api: &RenderApi) {
+fn event_handler(event: &glutin::Event, document_id: DocumentId, api: &RenderApi) {
     match *event {
         glutin::Event::KeyboardInput(glutin::ElementState::Pressed, _, Some(key)) => {
             let offset = match key {
                  glutin::VirtualKeyCode::Down => (0.0, -10.0),
                  glutin::VirtualKeyCode::Up => (0.0, 10.0),
                  glutin::VirtualKeyCode::Right => (-10.0, 0.0),
                  glutin::VirtualKeyCode::Left => (10.0, 0.0),
                  _ => return,
             };
 
-            api.scroll(ScrollLocation::Delta(LayoutVector2D::new(offset.0, offset.1)),
+            api.scroll(document_id,
+                       ScrollLocation::Delta(LayoutVector2D::new(offset.0, offset.1)),
                        *CURSOR_POSITION.lock().unwrap(),
                        ScrollEventPhase::Start);
         }
         glutin::Event::MouseMoved(x, y) => {
             *CURSOR_POSITION.lock().unwrap() = WorldPoint::new(x as f32, y as f32);
         }
         glutin::Event::MouseWheel(delta, _, event_cursor_position) => {
             if let Some((x, y)) = event_cursor_position {
@@ -123,17 +126,18 @@ fn event_handler(event: &glutin::Event, 
             }
 
             const LINE_HEIGHT: f32 = 38.0;
             let (dx, dy) = match delta {
                 glutin::MouseScrollDelta::LineDelta(dx, dy) => (dx, dy * LINE_HEIGHT),
                 glutin::MouseScrollDelta::PixelDelta(dx, dy) => (dx, dy),
             };
 
-            api.scroll(ScrollLocation::Delta(LayoutVector2D::new(dx, dy)),
+            api.scroll(document_id,
+                       ScrollLocation::Delta(LayoutVector2D::new(dx, dy)),
                        *CURSOR_POSITION.lock().unwrap(),
                        ScrollEventPhase::Start);
         }
         _ => ()
     }
 }
 
 fn main() {
--- a/gfx/webrender/examples/yuv.rs
+++ b/gfx/webrender/examples/yuv.rs
@@ -157,52 +157,53 @@ impl TouchState {
     }
 }
 
 fn main() {
     boilerplate::main_wrapper(body, event_handler, None);
 }
 
 fn body(api: &RenderApi,
+        _document_id: &DocumentId,
         builder: &mut DisplayListBuilder,
+        resources: &mut ResourceUpdates,
         _pipeline_id: &PipelineId,
         layout_size: &LayoutSize) {
     let bounds = LayoutRect::new(LayoutPoint::zero(), *layout_size);
     builder.push_stacking_context(ScrollPolicy::Scrollable,
                                   bounds,
                                   None,
                                   TransformStyle::Flat,
                                   None,
                                   MixBlendMode::Normal,
                                   Vec::new());
 
-
     let yuv_chanel1 = api.generate_image_key();
     let yuv_chanel2 = api.generate_image_key();
     let yuv_chanel2_1 = api.generate_image_key();
     let yuv_chanel3 = api.generate_image_key();
-    api.add_image(
+    resources.add_image(
         yuv_chanel1,
         ImageDescriptor::new(100, 100, ImageFormat::A8, true),
         ImageData::new(vec![127; 100 * 100]),
         None,
     );
-    api.add_image(
+    resources.add_image(
         yuv_chanel2,
         ImageDescriptor::new(100, 100, ImageFormat::RG8, true),
         ImageData::new(vec![0; 100 * 100 * 2]),
         None,
     );
-    api.add_image(
+    resources.add_image(
         yuv_chanel2_1,
         ImageDescriptor::new(100, 100, ImageFormat::A8, true),
         ImageData::new(vec![127; 100 * 100]),
         None,
     );
-    api.add_image(
+    resources.add_image(
         yuv_chanel3,
         ImageDescriptor::new(100, 100, ImageFormat::A8, true),
         ImageData::new(vec![127; 100 * 100]),
         None,
     );
 
     builder.push_yuv_image(
         LayoutRect::new(LayoutPoint::new(100.0, 0.0), LayoutSize::new(100.0, 100.0)),
@@ -223,26 +224,26 @@ fn body(api: &RenderApi,
     builder.pop_stacking_context();
 }
 
 lazy_static! {
     static ref TOUCH_STATE: Mutex<TouchState> = Mutex::new(TouchState::new());
 }
 
 
-fn event_handler(event: &glutin::Event, api: &RenderApi) {
+fn event_handler(event: &glutin::Event, document_id: DocumentId, api: &RenderApi) {
     match *event {
         glutin::Event::Touch(touch) => {
             match TOUCH_STATE.lock().unwrap().handle_event(touch) {
                 TouchResult::Pan(pan) => {
-                    api.set_pan(pan);
-                    api.generate_frame(None);
+                    api.set_pan(document_id, pan);
+                    api.generate_frame(document_id, None);
                 }
                 TouchResult::Zoom(zoom) => {
-                    api.set_pinch_zoom(ZoomFactor::new(zoom));
-                    api.generate_frame(None);
+                    api.set_pinch_zoom(document_id, ZoomFactor::new(zoom));
+                    api.generate_frame(document_id, None);
                 }
                 TouchResult::None => {}
             }
         }
         _ => ()
     }
 }
--- a/gfx/webrender/res/cs_text_run.vs.glsl
+++ b/gfx/webrender/res/cs_text_run.vs.glsl
@@ -19,17 +19,20 @@ void main(void) {
     // below to normalize the glyph offsets relative to the original text
     // shadow rect, which is the union of all elements that make up this
     // text shadow. This allows the text shadow to be rendered at an
     // arbitrary location in a render target (provided by the render
     // task render_target_origin field).
     PrimitiveGeometry shadow_geom = fetch_primitive_geometry(text_shadow_address);
     TextShadow shadow = fetch_text_shadow(text_shadow_address + VECS_PER_PRIM_HEADER);
 
-    Glyph glyph = fetch_glyph(prim.specific_prim_address, glyph_index);
+    Glyph glyph = fetch_glyph(prim.specific_prim_address,
+                              glyph_index,
+                              text.subpx_dir);
+
     GlyphResource res = fetch_glyph_resource(resource_address);
 
     // Glyphs size is already in device-pixels.
     // The render task origin is in device-pixels. Offset that by
     // the glyph offset, relative to its primitive bounding rect.
     vec2 size = res.uv_rect.zw - res.uv_rect.xy;
     vec2 local_pos = glyph.offset + vec2(res.offset.x, -res.offset.y) / uDevicePixelRatio;
     vec2 origin = prim.task.render_target_origin +
--- a/gfx/webrender/res/prim_shared.glsl
+++ b/gfx/webrender/res/prim_shared.glsl
@@ -54,16 +54,20 @@
 #define EXTEND_MODE_CLAMP  0
 #define EXTEND_MODE_REPEAT 1
 
 #define LINE_STYLE_SOLID        0
 #define LINE_STYLE_DOTTED       1
 #define LINE_STYLE_DASHED       2
 #define LINE_STYLE_WAVY         3
 
+#define SUBPX_DIR_NONE        0
+#define SUBPX_DIR_HORIZONTAL  1
+#define SUBPX_DIR_VERTICAL    2
+
 uniform sampler2DArray sCacheA8;
 uniform sampler2DArray sCacheRGBA8;
 
 uniform sampler2D sGradients;
 
 struct RectWithSize {
     vec2 p0;
     vec2 size;
@@ -414,24 +418,40 @@ BorderCorners get_border_corners(Border 
         bl_inner
     );
 }
 
 struct Glyph {
     vec2 offset;
 };
 
-Glyph fetch_glyph(int specific_prim_address, int glyph_index) {
+Glyph fetch_glyph(int specific_prim_address,
+                  int glyph_index,
+                  int subpx_dir) {
     // Two glyphs are packed in each texel in the GPU cache.
     int glyph_address = specific_prim_address +
                         VECS_PER_TEXT_RUN +
                         glyph_index / 2;
     vec4 data = fetch_from_resource_cache_1(glyph_address);
     // Select XY or ZW based on glyph index.
     vec2 glyph = mix(data.xy, data.zw, bvec2(glyph_index % 2 == 1));
+
+    // In subpixel mode, the subpixel offset has already been
+    // accounted for while rasterizing the glyph.
+    switch (subpx_dir) {
+        case SUBPX_DIR_NONE:
+            break;
+        case SUBPX_DIR_HORIZONTAL:
+            glyph.x = trunc(glyph.x);
+            break;
+        case SUBPX_DIR_VERTICAL:
+            glyph.y = trunc(glyph.y);
+            break;
+    }
+
     return Glyph(glyph);
 }
 
 RectWithSize fetch_instance_geometry(int address) {
     vec4 data = fetch_from_resource_cache_1(address);
     return RectWithSize(data.xy, data.zw);
 }
 
@@ -797,21 +817,22 @@ struct Line {
 Line fetch_line(int address) {
     vec4 data[2] = fetch_from_resource_cache_2(address);
     return Line(data[0], data[1].x, data[1].y);
 }
 
 struct TextRun {
     vec4 color;
     vec2 offset;
+    int subpx_dir;
 };
 
 TextRun fetch_text_run(int address) {
     vec4 data[2] = fetch_from_resource_cache_2(address);
-    return TextRun(data[0], data[1].xy);
+    return TextRun(data[0], data[1].xy, int(data[1].z));
 }
 
 struct Image {
     vec4 stretch_size_and_tile_spacing;  // Size of the actual image and amount of space between
                                          //     tiled instances of this image.
     vec4 sub_rect;                          // If negative, ignored.
 };
 
--- a/gfx/webrender/res/ps_border_edge.vs.glsl
+++ b/gfx/webrender/res/ps_border_edge.vs.glsl
@@ -32,17 +32,20 @@ void write_alpha_select(float style) {
             vAlphaSelect = 0.0;
             break;
         default:
             vAlphaSelect = 1.0;
             break;
     }
 }
 
-void write_color(vec4 color, float style, bool flip) {
+// write_color function is duplicated to work around a Mali-T880 GPU driver program link error.
+// See https://github.com/servo/webrender/issues/1403 for more info.
+// TODO: convert back to a single function once the driver issues are resolved, if ever.
+void write_color0(vec4 color, float style, bool flip) {
     vec2 modulate;
 
     switch (int(style)) {
         case BORDER_STYLE_GROOVE:
         {
             modulate = flip ? vec2(1.3, 0.7) : vec2(0.7, 1.3);
             break;
         }
@@ -52,16 +55,37 @@ void write_color(vec4 color, float style
             break;
         }
         default:
             modulate = vec2(1.0);
             break;
     }
 
     vColor0 = vec4(color.rgb * modulate.x, color.a);
+}
+
+void write_color1(vec4 color, float style, bool flip) {
+    vec2 modulate;
+
+    switch (int(style)) {
+        case BORDER_STYLE_GROOVE:
+        {
+            modulate = flip ? vec2(1.3, 0.7) : vec2(0.7, 1.3);
+            break;
+        }
+        case BORDER_STYLE_RIDGE:
+        {
+            modulate = flip ? vec2(0.7, 1.3) : vec2(1.3, 0.7);
+            break;
+        }
+        default:
+            modulate = vec2(1.0);
+            break;
+    }
+
     vColor1 = vec4(color.rgb * modulate.y, color.a);
 }
 
 void write_clip_params(float style,
                        float border_width,
                        float edge_length,
                        float edge_offset,
                        float center_line) {
@@ -171,17 +195,18 @@ void main(void) {
                               segment_rect.size.x,
                               segment_rect.p0.x,
                               segment_rect.p0.y + 0.5 * segment_rect.size.y);
             break;
         }
     }
 
     write_alpha_select(style);
-    write_color(color, style, color_flip);
+    write_color0(color, style, color_flip);
+    write_color1(color, style, color_flip);
 
 #ifdef WR_FEATURE_TRANSFORM
     TransformVertexInfo vi = write_transform_vertex(segment_rect,
                                                     prim.local_clip_rect,
                                                     prim.z,
                                                     prim.layer,
                                                     prim.task,
                                                     prim.local_rect);
--- a/gfx/webrender/res/ps_text_run.vs.glsl
+++ b/gfx/webrender/res/ps_text_run.vs.glsl
@@ -1,39 +1,24 @@
 #line 1
 /* 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/. */
 
-#define RENDER_MODE_MONO        0
-#define RENDER_MODE_ALPHA       1
-#define RENDER_MODE_SUBPIXEL    2
-
 void main(void) {
     Primitive prim = load_primitive();
     TextRun text = fetch_text_run(prim.specific_prim_address);
 
     int glyph_index = prim.user_data0;
-    int render_mode = prim.user_data1;
-    int resource_address = prim.user_data2;
-
-    Glyph glyph = fetch_glyph(prim.specific_prim_address, glyph_index);
-    GlyphResource res = fetch_glyph_resource(resource_address);
+    int resource_address = prim.user_data1;
 
-    switch (render_mode) {
-        case RENDER_MODE_ALPHA:
-            break;
-        case RENDER_MODE_MONO:
-            break;
-        case RENDER_MODE_SUBPIXEL:
-            // In subpixel mode, the subpixel offset has already been
-            // accounted for while rasterizing the glyph.
-            glyph.offset = trunc(glyph.offset);
-            break;
-    }
+    Glyph glyph = fetch_glyph(prim.specific_prim_address,
+                              glyph_index,
+                              text.subpx_dir);
+    GlyphResource res = fetch_glyph_resource(resource_address);
 
     vec2 local_pos = glyph.offset +
                      text.offset +
                      vec2(res.offset.x, -res.offset.y) / uDevicePixelRatio;
 
     RectWithSize local_rect = RectWithSize(local_pos,
                                            (res.uv_rect.zw - res.uv_rect.xy) / uDevicePixelRatio);
 
--- a/gfx/webrender/src/clip_scroll_tree.rs
+++ b/gfx/webrender/src/clip_scroll_tree.rs
@@ -1,26 +1,24 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use clip_scroll_node::{ClipScrollNode, NodeType, ScrollingState};
-use fnv::FnvHasher;
+use internal_types::{FastHashSet, FastHashMap};
 use print_tree::PrintTree;
-use std::collections::{HashMap, HashSet};
-use std::hash::BuildHasherDefault;
 use api::{ClipId, LayerPoint, LayerRect, LayerToScrollTransform};
 use api::{LayerToWorldTransform, PipelineId, ScrollClamping, ScrollEventPhase};
 use api::{LayerVector2D, ScrollLayerState, ScrollLocation, WorldPoint};
 
-pub type ScrollStates = HashMap<ClipId, ScrollingState, BuildHasherDefault<FnvHasher>>;
+pub type ScrollStates = FastHashMap<ClipId, ScrollingState>;
 
 pub struct ClipScrollTree {
-    pub nodes: HashMap<ClipId, ClipScrollNode, BuildHasherDefault<FnvHasher>>,
-    pub pending_scroll_offsets: HashMap<ClipId, (LayerPoint, ScrollClamping)>,
+    pub nodes: FastHashMap<ClipId, ClipScrollNode>,
+    pub pending_scroll_offsets: FastHashMap<ClipId, (LayerPoint, ScrollClamping)>,
 
     /// The ClipId of the currently scrolling node. Used to allow the same
     /// node to scroll even if a touch operation leaves the boundaries of that node.
     pub currently_scrolling_node_id: Option<ClipId>,
 
     /// The current frame id, used for giving a unique id to all new dynamically
     /// added frames and clips. The ClipScrollTree increments this by one every
     /// time a new dynamic frame is created.
@@ -31,30 +29,30 @@ pub struct ClipScrollTree {
     pub root_reference_frame_id: ClipId,
 
     /// The root scroll node which is the first child of the root reference frame.
     /// Initially this ID is not valid, which is indicated by ```nodes``` being empty.
     pub topmost_scrolling_node_id: ClipId,
 
     /// A set of pipelines which should be discarded the next time this
     /// tree is drained.
-    pub pipelines_to_discard: HashSet<PipelineId>,
+    pub pipelines_to_discard: FastHashSet<PipelineId>,
 }
 
 impl ClipScrollTree {
     pub fn new() -> ClipScrollTree {
-        let dummy_pipeline = PipelineId(0, 0);
+        let dummy_pipeline = PipelineId::dummy();
         ClipScrollTree {
-            nodes: HashMap::default(),
-            pending_scroll_offsets: HashMap::new(),
+            nodes: FastHashMap::default(),
+            pending_scroll_offsets: FastHashMap::default(),
             currently_scrolling_node_id: None,
             root_reference_frame_id: ClipId::root_reference_frame(dummy_pipeline),
             topmost_scrolling_node_id: ClipId::root_scroll_node(dummy_pipeline),
             current_new_node_item: 1,
-            pipelines_to_discard: HashSet::new(),
+            pipelines_to_discard: FastHashSet::default(),
         }
     }
 
     pub fn root_reference_frame_id(&self) -> ClipId {
         // TODO(mrobinson): We should eventually make this impossible to misuse.
         debug_assert!(!self.nodes.is_empty());
         debug_assert!(self.nodes.contains_key(&self.root_reference_frame_id));
         self.root_reference_frame_id
@@ -63,18 +61,18 @@ impl ClipScrollTree {
     pub fn topmost_scrolling_node_id(&self) -> ClipId {
         // TODO(mrobinson): We should eventually make this impossible to misuse.
         debug_assert!(!self.nodes.is_empty());
         debug_assert!(self.nodes.contains_key(&self.topmost_scrolling_node_id));
         self.topmost_scrolling_node_id
     }
 
     pub fn collect_nodes_bouncing_back(&self)
-                                       -> HashSet<ClipId, BuildHasherDefault<FnvHasher>> {
-        let mut nodes_bouncing_back = HashSet::default();
+                                       -> FastHashSet<ClipId> {
+        let mut nodes_bouncing_back = FastHashSet::default();
         for (clip_id, node) in self.nodes.iter() {
             if let NodeType::ScrollFrame(ref scrolling) = node.node_type {
                 if scrolling.bouncing_back {
                     nodes_bouncing_back.insert(*clip_id);
                 }
             }
         }
         nodes_bouncing_back
@@ -118,17 +116,17 @@ impl ClipScrollTree {
             }
         }
         result
     }
 
     pub fn drain(&mut self) -> ScrollStates {
         self.current_new_node_item = 1;
 
-        let mut scroll_states = HashMap::default();
+        let mut scroll_states = FastHashMap::default();
         for (layer_id, old_node) in &mut self.nodes.drain() {
             if self.pipelines_to_discard.contains(&layer_id.pipeline_id()) {
                 continue;
             }
 
             if let NodeType::ScrollFrame(scrolling) = old_node.node_type {
                 scroll_states.insert(layer_id, scrolling);
             }
--- a/gfx/webrender/src/debug_render.rs
+++ b/gfx/webrender/src/debug_render.rs
@@ -1,44 +1,99 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use debug_font_data;
-use device::{Device, GpuMarker, ProgramId, VAOId, TextureId, VertexFormat};
-use device::{TextureFilter, VertexUsageHint, TextureTarget};
+use device::{Device, GpuMarker, Program, VAOId, TextureId, VertexDescriptor};
+use device::{TextureFilter, VertexAttribute, VertexUsageHint, VertexAttributeKind, TextureTarget};
 use euclid::{Transform3D, Point2D, Size2D, Rect};
 use internal_types::{ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE, TextureSampler};
-use internal_types::{DebugFontVertex, DebugColorVertex, RenderTargetMode};
+use internal_types::RenderTargetMode;
 use std::f32;
 use api::{ColorU, ImageFormat, DeviceUintSize};
 
+const DESC_FONT: VertexDescriptor = VertexDescriptor {
+    vertex_attributes: &[
+        VertexAttribute { name: "aPosition", count: 2, kind: VertexAttributeKind::F32 },
+        VertexAttribute { name: "aColor", count: 4, kind: VertexAttributeKind::U8Norm },
+        VertexAttribute { name: "aColorTexCoord", count: 2, kind: VertexAttributeKind::F32 },
+    ],
+    instance_attributes: &[]
+};
+
+const DESC_COLOR: VertexDescriptor = VertexDescriptor {
+    vertex_attributes: &[
+        VertexAttribute { name: "aPosition", count: 2, kind: VertexAttributeKind::F32 },
+        VertexAttribute { name: "aColor", count: 4, kind: VertexAttributeKind::U8Norm },
+    ],
+    instance_attributes: &[]
+};
+
+#[repr(C)]
+pub struct DebugFontVertex {
+    pub x: f32,
+    pub y: f32,
+    pub color: ColorU,
+    pub u: f32,
+    pub v: f32,
+}
+
+impl DebugFontVertex {
+    pub fn new(x: f32, y: f32, u: f32, v: f32, color: ColorU) -> DebugFontVertex {
+        DebugFontVertex {
+            x,
+            y,
+            color,
+            u,
+            v,
+        }
+    }
+}
+
+#[repr(C)]
+pub struct DebugColorVertex {
+    pub x: f32,
+    pub y: f32,
+    pub color: ColorU,
+}
+
+impl DebugColorVertex {
+    pub fn new(x: f32, y: f32, color: ColorU) -> DebugColorVertex {
+        DebugColorVertex {
+            x,
+            y,
+            color,
+        }
+    }
+}
+
 pub struct DebugRenderer {
     font_vertices: Vec<DebugFontVertex>,
     font_indices: Vec<u32>,
-    font_program_id: ProgramId,
+    font_program: Program,
     font_vao: VAOId,
     font_texture_id: TextureId,
 
     tri_vertices: Vec<DebugColorVertex>,
     tri_indices: Vec<u32>,
     tri_vao: VAOId,
     line_vertices: Vec<DebugColorVertex>,
     line_vao: VAOId,
-    color_program_id: ProgramId,
+    color_program: Program,
 }
 
 impl DebugRenderer {
     pub fn new(device: &mut Device) -> DebugRenderer {
-        let font_program_id = device.create_program("debug_font", "shared_other", VertexFormat::DebugFont).unwrap();
-        let color_program_id = device.create_program("debug_color", "shared_other", VertexFormat::DebugColor).unwrap();
+        let font_program = device.create_program("debug_font", "shared_other", &DESC_FONT).unwrap();
+        let color_program = device.create_program("debug_color", "shared_other", &DESC_COLOR).unwrap();
 
-        let font_vao = device.create_vao(VertexFormat::DebugFont, 32);
-        let line_vao = device.create_vao(VertexFormat::DebugColor, 32);
-        let tri_vao = device.create_vao(VertexFormat::DebugColor, 32);
+        let font_vao = device.create_vao(&DESC_FONT, 32);
+        let line_vao = device.create_vao(&DESC_COLOR, 32);
+        let tri_vao = device.create_vao(&DESC_COLOR, 32);
 
         let font_texture_id = device.create_texture_ids(1, TextureTarget::Default)[0];
         device.init_texture(font_texture_id,
                             debug_font_data::BMP_WIDTH,
                             debug_font_data::BMP_HEIGHT,
                             ImageFormat::A8,
                             TextureFilter::Linear,
                             RenderTargetMode::None,
@@ -46,24 +101,29 @@ impl DebugRenderer {
 
         DebugRenderer {
             font_vertices: Vec::new(),
             font_indices: Vec::new(),
             line_vertices: Vec::new(),
             tri_vao,
             tri_vertices: Vec::new(),
             tri_indices: Vec::new(),
-            font_program_id,
-            color_program_id,
+            font_program,
+            color_program,
             font_vao,
             line_vao,
             font_texture_id,
         }
     }
 
+    pub fn deinit(&mut self, device: &mut Device) {
+        device.delete_program(&mut self.font_program);
+        device.delete_program(&mut self.color_program);
+    }
+
     pub fn line_height(&self) -> f32 {
         debug_font_data::FONT_SIZE as f32 * 1.1
     }
 
     pub fn add_text(&mut self,
                     x: f32,
                     y: f32,
                     text: &str,
@@ -165,40 +225,43 @@ impl DebugRenderer {
                                             viewport_size.width as f32,
                                             viewport_size.height as f32,
                                             0.0,
                                             ORTHO_NEAR_PLANE,
                                             ORTHO_FAR_PLANE);
 
         // Triangles
         if !self.tri_vertices.is_empty() {
-            device.bind_program(self.color_program_id, &projection);
+            device.bind_program(&self.color_program);
+            device.set_uniforms(&self.color_program, &projection);
             device.bind_vao(self.tri_vao);
             device.update_vao_indices(self.tri_vao,
                                       &self.tri_indices,
                                       VertexUsageHint::Dynamic);
             device.update_vao_main_vertices(self.tri_vao,
                                             &self.tri_vertices,
                                             VertexUsageHint::Dynamic);
             device.draw_triangles_u32(0, self.tri_indices.len() as i32);
         }
 
         // Lines
         if !self.line_vertices.is_empty() {
-            device.bind_program(self.color_program_id, &projection);
+            device.bind_program(&self.color_program);
+            device.set_uniforms(&self.color_program, &projection);
             device.bind_vao(self.line_vao);
             device.update_vao_main_vertices(self.line_vao,
                                             &self.line_vertices,
                                             VertexUsageHint::Dynamic);
             device.draw_nonindexed_lines(0, self.line_vertices.len() as i32);
         }
 
         // Glyph
         if !self.font_indices.is_empty() {
-            device.bind_program(self.font_program_id, &projection);
+            device.bind_program(&self.font_program);
+            device.set_uniforms(&self.font_program, &projection);
             device.bind_texture(TextureSampler::Color0, self.font_texture_id);
             device.bind_vao(self.font_vao);
             device.update_vao_indices(self.font_vao,
                                       &self.font_indices,
                                       VertexUsageHint::Dynamic);
             device.update_vao_main_vertices(self.font_vao,
                                             &self.font_vertices,
                                             VertexUsageHint::Dynamic);
--- a/gfx/webrender/src/device.rs
+++ b/gfx/webrender/src/device.rs
@@ -1,32 +1,27 @@
 /* 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 euclid::Transform3D;
-use fnv::FnvHasher;
 use gleam::gl;
-use internal_types::{PackedVertex, RenderTargetMode, TextureSampler, DEFAULT_TEXTURE};
-use internal_types::{BlurAttribute, ClipAttribute, VertexAttribute};
-use internal_types::{DebugFontVertex, DebugColorVertex};
+use internal_types::{RenderTargetMode, TextureSampler, DEFAULT_TEXTURE, FastHashMap};
 //use notify::{self, Watcher};
 use super::shader_source;
-use std::collections::HashMap;
 use std::fs::File;
-use std::hash::BuildHasherDefault;
 use std::io::Read;
 use std::iter::repeat;
 use std::mem;
 use std::ops::Add;
 use std::path::PathBuf;
 use std::ptr;
 use std::rc::Rc;
 //use std::sync::mpsc::{channel, Sender};
-//use std::thread;
+use std::thread;
 use api::{ColorF, ImageFormat};
 use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceUintSize};
 
 #[derive(Debug, Copy, Clone, PartialEq, Ord, Eq, PartialOrd)]
 pub struct FrameId(usize);
 
 impl FrameId {
     pub fn new(value: usize) -> FrameId {
@@ -84,23 +79,34 @@ impl TextureTarget {
 }
 
 #[derive(Copy, Clone, Debug, PartialEq)]
 pub enum TextureFilter {
     Nearest,
     Linear,
 }
 
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub enum VertexFormat {
-    Triangles,
-    DebugFont,
-    DebugColor,
-    Blur,
-    Clip,
+#[derive(Debug)]
+pub enum VertexAttributeKind {
+    F32,
+    U8Norm,
+    I32,
+}
+
+#[derive(Debug)]
+pub struct VertexAttribute {
+    pub name: &'static str,
+    pub count: u32,
+    pub kind: VertexAttributeKind,
+}
+
+#[derive(Debug)]
+pub struct VertexDescriptor {
+    pub vertex_attributes: &'static [VertexAttribute],
+    pub instance_attributes: &'static [VertexAttribute],
 }
 
 enum FBOTarget {
     Read,
     Draw,
 }
 
 pub fn get_gl_format_bgra(gl: &gl::Gl) -> gl::GLuint {
@@ -142,156 +148,107 @@ fn get_shader_source(shader_name: &str, 
     get_optional_shader_source(shader_name, base_path)
         .expect(&format!("Couldn't get required shader: {}", shader_name))
 }
 
 pub trait FileWatcherHandler : Send {
     fn file_changed(&self, path: PathBuf);
 }
 
-impl VertexFormat {
-    fn bind(&self, gl: &gl::Gl, main: VBOId, instance: VBOId, offset: gl::GLuint, instance_stride: gl::GLint) {
+impl VertexAttributeKind {
+    fn size_in_bytes(&self) -> u32 {
+        match *self {
+            VertexAttributeKind::F32 => 4,
+            VertexAttributeKind::U8Norm => 1,
+            VertexAttributeKind::I32 => 4,
+        }
+    }
+}
+
+impl VertexAttribute {
+    fn size_in_bytes(&self) -> u32 {
+        self.count * self.kind.size_in_bytes()
+    }
+
+    fn bind_to_vao(&self,
+                   attr_index: gl::GLuint,
+                   divisor: gl::GLuint,
+                   stride: gl::GLint,
+                   offset: gl::GLuint,
+                   gl: &gl::Gl) {
+        gl.enable_vertex_attrib_array(attr_index);
+        gl.vertex_attrib_divisor(attr_index, divisor);
+
+        match self.kind {
+            VertexAttributeKind::F32 => {
+                gl.vertex_attrib_pointer(attr_index,
+                                         self.count as gl::GLint,
+                                         gl::FLOAT,
+                                         false,
+                                         stride,
+                                         offset);
+            }
+            VertexAttributeKind::U8Norm => {
+                gl.vertex_attrib_pointer(attr_index,
+                                         self.count as gl::GLint,
+                                         gl::UNSIGNED_BYTE,
+                                         true,
+                                         stride,
+                                         offset);
+            }
+            VertexAttributeKind::I32 => {
+                gl.vertex_attrib_i_pointer(attr_index,
+                                           self.count as gl::GLint,
+                                           gl::INT,
+                                           stride,
+                                           offset);
+            }
+        }
+    }
+}
+
+impl VertexDescriptor {
+    fn bind(&self,
+            gl: &gl::Gl,
+            main: VBOId,
+            instance: VBOId) {
         main.bind(gl);
 
-        match *self {
-            VertexFormat::DebugFont => {
-                gl.enable_vertex_attrib_array(VertexAttribute::Position as gl::GLuint);
-                gl.enable_vertex_attrib_array(VertexAttribute::Color as gl::GLuint);
-                gl.enable_vertex_attrib_array(VertexAttribute::ColorTexCoord as gl::GLuint);
-
-                gl.vertex_attrib_divisor(VertexAttribute::Position as gl::GLuint, 0);
-                gl.vertex_attrib_divisor(VertexAttribute::Color as gl::GLuint, 0);
-                gl.vertex_attrib_divisor(VertexAttribute::ColorTexCoord as gl::GLuint, 0);
-
-                let vertex_stride = mem::size_of::<DebugFontVertex>() as gl::GLuint;
+        let vertex_stride: u32 = self.vertex_attributes
+                                    .iter()
+                                    .map(|attr| attr.size_in_bytes()).sum();
+        let mut vertex_offset = 0;
 
-                gl.vertex_attrib_pointer(VertexAttribute::Position as gl::GLuint,
-                                          2,
-                                          gl::FLOAT,
-                                          false,
-                                          vertex_stride as gl::GLint,
-                                          0 + vertex_stride * offset);
-                gl.vertex_attrib_pointer(VertexAttribute::Color as gl::GLuint,
-                                          4,
-                                          gl::UNSIGNED_BYTE,
-                                          true,
-                                          vertex_stride as gl::GLint,
-                                          8 + vertex_stride * offset);
-                gl.vertex_attrib_pointer(VertexAttribute::ColorTexCoord as gl::GLuint,
-                                          2,
-                                          gl::FLOAT,
-                                          false,
-                                          vertex_stride as gl::GLint,
-                                          12 + vertex_stride * offset);
-            }
-            VertexFormat::DebugColor => {
-                gl.enable_vertex_attrib_array(VertexAttribute::Position as gl::GLuint);
-                gl.enable_vertex_attrib_array(VertexAttribute::Color as gl::GLuint);
-
-                gl.vertex_attrib_divisor(VertexAttribute::Position as gl::GLuint, 0);
-                gl.vertex_attrib_divisor(VertexAttribute::Color as gl::GLuint, 0);
-
-                let vertex_stride = mem::size_of::<DebugColorVertex>() as gl::GLuint;
-
-                gl.vertex_attrib_pointer(VertexAttribute::Position as gl::GLuint,
-                                          2,
-                                          gl::FLOAT,
-                                          false,
-                                          vertex_stride as gl::GLint,
-                                          0 + vertex_stride * offset);
-                gl.vertex_attrib_pointer(VertexAttribute::Color as gl::GLuint,
-                                          4,
-                                          gl::UNSIGNED_BYTE,
-                                          true,
-                                          vertex_stride as gl::GLint,
-                                          8 + vertex_stride * offset);
-            }
-            VertexFormat::Triangles => {
-                let vertex_stride = mem::size_of::<PackedVertex>() as gl::GLuint;
-                gl.enable_vertex_attrib_array(VertexAttribute::Position as gl::GLuint);
-                gl.vertex_attrib_divisor(VertexAttribute::Position as gl::GLuint, 0);
-
-                gl.vertex_attrib_pointer(VertexAttribute::Position as gl::GLuint,
-                                          2,
-                                          gl::FLOAT,
-                                          false,
-                                          vertex_stride as gl::GLint,
-                                          0);
-
-                instance.bind(gl);
-                let mut offset = 0;
+        for (i, attr) in self.vertex_attributes.iter().enumerate() {
+            let attr_index = i as gl::GLuint;
+            attr.bind_to_vao(attr_index,
+                             0,
+                             vertex_stride as gl::GLint,
+                             vertex_offset,
+                             gl);
+            vertex_offset += attr.size_in_bytes();
+        }
 
-                for &attrib in [VertexAttribute::Data0,
-                                VertexAttribute::Data1,
-                               ].into_iter() {
-                    gl.enable_vertex_attrib_array(attrib as gl::GLuint);
-                    gl.vertex_attrib_divisor(attrib as gl::GLuint, 1);
-                    gl.vertex_attrib_i_pointer(attrib as gl::GLuint,
-                                                4,
-                                                gl::INT,
-                                                instance_stride,
-                                                offset);
-                    offset += 16;
-                }
-            }
-            VertexFormat::Blur => {
-                let vertex_stride = mem::size_of::<PackedVertex>() as gl::GLuint;
-                gl.enable_vertex_attrib_array(BlurAttribute::Position as gl::GLuint);
-                gl.vertex_attrib_divisor(BlurAttribute::Position as gl::GLuint, 0);
+        if !self.instance_attributes.is_empty() {
+            instance.bind(gl);
+            let instance_stride: u32 = self.instance_attributes
+                                           .iter()
+                                           .map(|attr| attr.size_in_bytes()).sum();
+            let mut instance_offset = 0;
 
-                gl.vertex_attrib_pointer(BlurAttribute::Position as gl::GLuint,
-                                          2,
-                                          gl::FLOAT,
-                                          false,
-                                          vertex_stride as gl::GLint,
-                                          0);
-
-                instance.bind(gl);
+            let base_attr = self.vertex_attributes.len() as u32;
 
-                for (i, &attrib) in [BlurAttribute::RenderTaskIndex,
-                                     BlurAttribute::SourceTaskIndex,
-                                     BlurAttribute::Direction,
-                                    ].into_iter().enumerate() {
-                    gl.enable_vertex_attrib_array(attrib as gl::GLuint);
-                    gl.vertex_attrib_divisor(attrib as gl::GLuint, 1);
-                    gl.vertex_attrib_i_pointer(attrib as gl::GLuint,
-                                                1,
-                                                gl::INT,
-                                                instance_stride,
-                                                (i * 4) as gl::GLuint);
-                }
-            }
-            VertexFormat::Clip => {
-                let vertex_stride = mem::size_of::<PackedVertex>() as gl::GLuint;
-                gl.enable_vertex_attrib_array(ClipAttribute::Position as gl::GLuint);
-                gl.vertex_attrib_divisor(ClipAttribute::Position as gl::GLuint, 0);
-
-                gl.vertex_attrib_pointer(ClipAttribute::Position as gl::GLuint,
-                                          2,
-                                          gl::FLOAT,
-                                          false,
-                                          vertex_stride as gl::GLint,
-                                          0);
-
-                instance.bind(gl);
-
-                for (i, &attrib) in [ClipAttribute::RenderTaskIndex,
-                                     ClipAttribute::LayerIndex,
-                                     ClipAttribute::DataIndex,
-                                     ClipAttribute::SegmentIndex,
-                                     ClipAttribute::ResourceAddress,
-                                    ].into_iter().enumerate() {
-                    gl.enable_vertex_attrib_array(attrib as gl::GLuint);
-                    gl.vertex_attrib_divisor(attrib as gl::GLuint, 1);
-                    gl.vertex_attrib_i_pointer(attrib as gl::GLuint,
-                                                1,
-                                                gl::INT,
-                                                instance_stride,
-                                                (i * 4) as gl::GLuint);
-                }
+            for (i, attr) in self.instance_attributes.iter().enumerate() {
+                let attr_index = base_attr + i as u32;
+                attr.bind_to_vao(attr_index,
+                                 1,
+                                 instance_stride as gl::GLint,
+                                 instance_offset,
+                                 gl);
+                instance_offset += attr.size_in_bytes();
             }
         }
     }
 }
 
 impl TextureId {
     pub fn bind(&self, gl: &gl::Gl) {
         gl.bind_texture(self.target, self.name);
@@ -309,40 +266,28 @@ impl TextureId {
             name: 0,
             target: gl::TEXTURE_2D,
         }
     }
 
     pub fn is_valid(&self) -> bool { *self != TextureId::invalid() }
 }
 
-impl ProgramId {
-    fn bind(&self, gl: &gl::Gl) {
-        gl.use_program(self.0);
-    }
-}
-
 impl VBOId {
     fn bind(&self, gl: &gl::Gl) {
         gl.bind_buffer(gl::ARRAY_BUFFER, self.0);
     }
 }
 
 impl IBOId {
     fn bind(&self, gl: &gl::Gl) {
         gl.bind_buffer(gl::ELEMENT_ARRAY_BUFFER, self.0);
     }
 }
 
-impl UBOId {
-    fn _bind(&self, gl: &gl::Gl) {
-        gl.bind_buffer(gl::UNIFORM_BUFFER, self.0);
-    }
-}
-
 impl FBOId {
     fn bind(&self, gl: &gl::Gl, target: FBOTarget) {
         let target = match target {
             FBOTarget::Read => gl::READ_FRAMEBUFFER,
             FBOTarget::Draw => gl::DRAW_FRAMEBUFFER,
         };
         gl.bind_framebuffer(target, self.0);
     }
@@ -366,80 +311,62 @@ impl Drop for Texture {
         if !self.fbo_ids.is_empty() {
             let fbo_ids: Vec<_> = self.fbo_ids.iter().map(|&FBOId(fbo_id)| fbo_id).collect();
             self.gl.delete_framebuffers(&fbo_ids[..]);
         }
         self.gl.delete_textures(&[self.id]);
     }
 }
 
-struct Program {
-    gl: Rc<gl::Gl>,
+pub struct Program {
     id: gl::GLuint,
     u_transform: gl::GLint,
     u_device_pixel_ratio: gl::GLint,
     name: String,
     vs_source: String,
     fs_source: String,
     prefix: Option<String>,
     vs_id: Option<gl::GLuint>,
     fs_id: Option<gl::GLuint>,
 }
 
 impl Program {
     fn attach_and_bind_shaders(&mut self,
                                vs_id: gl::GLuint,
                                fs_id: gl::GLuint,
-                               vertex_format: VertexFormat) -> Result<(), ShaderError> {
-        self.gl.attach_shader(self.id, vs_id);
-        self.gl.attach_shader(self.id, fs_id);
-
-        match vertex_format {
-            VertexFormat::Triangles |
-            VertexFormat::DebugFont |
-            VertexFormat::DebugColor => {
-                self.gl.bind_attrib_location(self.id, VertexAttribute::Position as gl::GLuint, "aPosition");
-                self.gl.bind_attrib_location(self.id, VertexAttribute::Color as gl::GLuint, "aColor");
-                self.gl.bind_attrib_location(self.id, VertexAttribute::ColorTexCoord as gl::GLuint, "aColorTexCoord");
+                               descriptor: &VertexDescriptor,
+                               gl: &gl::Gl) -> Result<(), ShaderError> {
+        gl.attach_shader(self.id, vs_id);
+        gl.attach_shader(self.id, fs_id);
 
-                self.gl.bind_attrib_location(self.id, VertexAttribute::Data0 as gl::GLuint, "aData0");
-                self.gl.bind_attrib_location(self.id, VertexAttribute::Data1 as gl::GLuint, "aData1");
-            }
-            VertexFormat::Blur => {
-                self.gl.bind_attrib_location(self.id, BlurAttribute::Position as gl::GLuint, "aPosition");
-                self.gl.bind_attrib_location(self.id, BlurAttribute::RenderTaskIndex as gl::GLuint, "aBlurRenderTaskIndex");
-                self.gl.bind_attrib_location(self.id, BlurAttribute::SourceTaskIndex as gl::GLuint, "aBlurSourceTaskIndex");
-                self.gl.bind_attrib_location(self.id, BlurAttribute::Direction as gl::GLuint, "aBlurDirection");
-            }
-            VertexFormat::Clip => {
-                self.gl.bind_attrib_location(self.id, ClipAttribute::Position as gl::GLuint, "aPosition");
-                self.gl.bind_attrib_location(self.id, ClipAttribute::RenderTaskIndex as gl::GLuint, "aClipRenderTaskIndex");
-                self.gl.bind_attrib_location(self.id, ClipAttribute::LayerIndex as gl::GLuint, "aClipLayerIndex");
-                self.gl.bind_attrib_location(self.id, ClipAttribute::DataIndex as gl::GLuint, "aClipDataIndex");
-                self.gl.bind_attrib_location(self.id, ClipAttribute::SegmentIndex as gl::GLuint, "aClipSegmentIndex");
-                self.gl.bind_attrib_location(self.id, ClipAttribute::ResourceAddress as gl::GLuint, "aClipResourceAddress");
-            }
+        for (i, attr) in descriptor.vertex_attributes
+                                   .iter()
+                                   .chain(descriptor.instance_attributes.iter())
+                                   .enumerate() {
+            gl.bind_attrib_location(self.id,
+                                    i as gl::GLuint,
+                                    attr.name);
         }
 
-        self.gl.link_program(self.id);
-        if self.gl.get_program_iv(self.id, gl::LINK_STATUS) == (0 as gl::GLint) {
-            let error_log = self.gl.get_program_info_log(self.id);
+        gl.link_program(self.id);
+        if gl.get_program_iv(self.id, gl::LINK_STATUS) == (0 as gl::GLint) {
+            let error_log = gl.get_program_info_log(self.id);
             println!("Failed to link shader program: {:?}\n{}", self.name, error_log);
-            self.gl.detach_shader(self.id, vs_id);
-            self.gl.detach_shader(self.id, fs_id);
+            gl.detach_shader(self.id, vs_id);
+            gl.detach_shader(self.id, fs_id);
             return Err(ShaderError::Link(self.name.clone(), error_log));
         }
 
         Ok(())
     }
 }
 
 impl Drop for Program {
     fn drop(&mut self) {
-        self.gl.delete_program(self.id);
+        debug_assert!(thread::panicking() || self.id == 0);
     }
 }
 
 struct VAO {
     gl: Rc<gl::Gl>,
     id: gl::GLuint,
     ibo_id: IBOId,
     main_vbo_id: VBOId,
@@ -469,37 +396,31 @@ impl Drop for VAO {
 
 #[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Copy, Clone)]
 pub struct TextureId {
     name: gl::GLuint,
     target: gl::GLuint,
 }
 
 #[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
-pub struct ProgramId(pub gl::GLuint);
-
-#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
 pub struct VAOId(gl::GLuint);
 
 #[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
 pub struct FBOId(gl::GLuint);
 
 #[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
 pub struct RBOId(gl::GLuint);
 
 #[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
 pub struct VBOId(gl::GLuint);
 
 #[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
 struct IBOId(gl::GLuint);
 
 #[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
-pub struct UBOId(gl::GLuint);
-
-#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
 pub struct PBOId(gl::GLuint);
 
 const MAX_EVENTS_PER_FRAME: usize = 256;
 const MAX_PROFILE_FRAMES: usize = 4;
 
 pub trait NamedTag {
     fn get_label(&self) -> &str;
 }
@@ -834,50 +755,48 @@ impl FileWatcherThread {
 
     fn add_watch(&self, path: PathBuf) {
         self.api_tx.send(FileWatcherCmd::AddWatch(path)).ok();
     }
 }
 */
 
 pub struct Capabilities {
-    pub max_ubo_size: usize,
     pub supports_multisampling: bool,
 }
 
 #[derive(Clone, Debug)]
 pub enum ShaderError {
     Compilation(String, String), // name, error mssage
     Link(String, String), // name, error message
 }
 
 pub struct Device {
     gl: Rc<gl::Gl>,
     // device state
     bound_textures: [TextureId; 16],
-    bound_program: ProgramId,
+    bound_program: gl::GLuint,
     bound_vao: VAOId,
     bound_pbo: PBOId,
     bound_read_fbo: FBOId,
     bound_draw_fbo: FBOId,
     default_read_fbo: gl::GLuint,
     default_draw_fbo: gl::GLuint,
     device_pixel_ratio: f32,
 
     // HW or API capabilties
     capabilities: Capabilities,
 
     // debug
     inside_frame: bool,
 
     // resources
     resource_override_path: Option<PathBuf>,
-    textures: HashMap<TextureId, Texture, BuildHasherDefault<FnvHasher>>,
-    programs: HashMap<ProgramId, Program, BuildHasherDefault<FnvHasher>>,
-    vaos: HashMap<VAOId, VAO, BuildHasherDefault<FnvHasher>>,
+    textures: FastHashMap<TextureId, Texture>,
+    vaos: FastHashMap<VAOId, VAO>,
 
     // misc.
     shader_preamble: String,
     //file_watcher: FileWatcherThread,
 
     // Used on android only
     #[allow(dead_code)]
     next_vao_id: gl::GLuint,
@@ -893,44 +812,41 @@ impl Device {
     pub fn new(gl: Rc<gl::Gl>,
                resource_override_path: Option<PathBuf>,
                _file_changed_handler: Box<FileWatcherHandler>) -> Device {
         //let file_watcher = FileWatcherThread::new(file_changed_handler);
 
         let shader_preamble = get_shader_source(SHADER_PREAMBLE, &resource_override_path);
         //file_watcher.add_watch(resource_path);
 
-        let max_ubo_size = gl.get_integer_v(gl::MAX_UNIFORM_BLOCK_SIZE) as usize;
         let max_texture_size = gl.get_integer_v(gl::MAX_TEXTURE_SIZE) as u32;
 
         Device {
             gl,
             resource_override_path,
             // This is initialized to 1 by default, but it is set
             // every frame by the call to begin_frame().
             device_pixel_ratio: 1.0,
             inside_frame: false,
 
             capabilities: Capabilities {
-                max_ubo_size,
                 supports_multisampling: false, //TODO
             },
 
             bound_textures: [ TextureId::invalid(); 16 ],
-            bound_program: ProgramId(0),
+            bound_program: 0,
             bound_vao: VAOId(0),
             bound_pbo: PBOId(0),
             bound_read_fbo: FBOId(0),
             bound_draw_fbo: FBOId(0),
             default_read_fbo: 0,
             default_draw_fbo: 0,
 
-            textures: HashMap::default(),
-            programs: HashMap::default(),
-            vaos: HashMap::default(),
+            textures: FastHashMap::default(),
+            vaos: FastHashMap::default(),
 
             shader_preamble,
 
             next_vao_id: 1,
             //file_watcher: file_watcher,
 
             max_texture_size,
             frame_id: FrameId(0),
@@ -999,17 +915,17 @@ impl Device {
         // Texture state
         for i in 0..self.bound_textures.len() {
             self.bound_textures[i] = TextureId::invalid();
             self.gl.active_texture(gl::TEXTURE0 + i as gl::GLuint);
             self.gl.bind_texture(gl::TEXTURE_2D, 0);
         }
 
         // Shader state
-        self.bound_program = ProgramId(0);
+        self.bound_program = 0;
         self.gl.use_program(0);
 
         // Vertex state
         self.bound_vao = VAOId(0);
         self.clear_vertex_array();
 
         // FBO state
         self.bound_read_fbo = FBOId(self.default_read_fbo);
@@ -1067,30 +983,23 @@ impl Device {
             fbo_id.bind(self.gl(), FBOTarget::Draw);
         }
 
         if let Some(dimensions) = dimensions {
             self.gl.viewport(0, 0, dimensions.width as gl::GLint, dimensions.height as gl::GLint);
         }
     }
 
-    pub fn bind_program(&mut self,
-                        program_id: ProgramId,
-                        projection: &Transform3D<f32>) {
+    pub fn bind_program(&mut self, program: &Program) {
         debug_assert!(self.inside_frame);
 
-        if self.bound_program != program_id {
-            self.bound_program = program_id;
-            program_id.bind(&*self.gl);
+        if self.bound_program != program.id {
+            self.gl.use_program(program.id);
+            self.bound_program = program.id;
         }
-
-        let program = self.programs.get(&program_id).unwrap();
-        self.set_uniforms(program,
-                          projection,
-                          self.device_pixel_ratio);
     }
 
     pub fn create_texture_ids(&mut self,
                               count: i32,
                               target: TextureTarget) -> Vec<TextureId> {
         let id_list = self.gl.gen_textures(count);
         let mut texture_ids = Vec::new();
 
@@ -1431,25 +1340,33 @@ impl Device {
         texture.format = ImageFormat::Invalid;
         texture.width = 0;
         texture.height = 0;
     }
 
     pub fn create_program(&mut self,
                           base_filename: &str,
                           include_filename: &str,
-                          vertex_format: VertexFormat) -> Result<ProgramId, ShaderError> {
-        self.create_program_with_prefix(base_filename, &[include_filename], None, vertex_format)
+                          descriptor: &VertexDescriptor) -> Result<Program, ShaderError> {
+        self.create_program_with_prefix(base_filename,
+                                        &[include_filename],
+                                        None,
+                                        descriptor)
+    }
+
+    pub fn delete_program(&mut self, program: &mut Program) {
+        self.gl.delete_program(program.id);
+        program.id = 0;
     }
 
     pub fn create_program_with_prefix(&mut self,
                                       base_filename: &str,
                                       include_filenames: &[&str],
                                       prefix: Option<String>,
-                                      vertex_format: VertexFormat) -> Result<ProgramId, ShaderError> {
+                                      descriptor: &VertexDescriptor) -> Result<Program, ShaderError> {
         debug_assert!(self.inside_frame);
 
         let pid = self.gl.create_program();
 
         let mut vs_name = String::from(base_filename);
         vs_name.push_str(".vs");
         let mut fs_name = String::from(base_filename);
         fs_name.push_str(".fs");
@@ -1459,47 +1376,39 @@ impl Device {
             let src = get_shader_source(inc_filename, &self.resource_override_path);
             include.push_str(&src);
         }
 
         if let Some(shared_src) = get_optional_shader_source(base_filename, &self.resource_override_path) {
             include.push_str(&shared_src);
         }
 
-        let program = Program {
-            gl: Rc::clone(&self.gl),
+        let mut program = Program {
             name: base_filename.to_owned(),
             id: pid,
             u_transform: -1,
             u_device_pixel_ratio: -1,
             vs_source: get_shader_source(&vs_name, &self.resource_override_path),
             fs_source: get_shader_source(&fs_name, &self.resource_override_path),
             prefix,
             vs_id: None,
             fs_id: None,
         };
 
-        let program_id = ProgramId(pid);
+        try!{ self.load_program(&mut program, include, descriptor) };
 
-        debug_assert!(self.programs.contains_key(&program_id) == false);
-        self.programs.insert(program_id, program);
-
-        try!{ self.load_program(program_id, include, vertex_format) };
-
-        Ok(program_id)
+        Ok(program)
     }
 
     fn load_program(&mut self,
-                    program_id: ProgramId,
+                    program: &mut Program,
                     include: String,
-                    vertex_format: VertexFormat) -> Result<(), ShaderError> {
+                    descriptor: &VertexDescriptor) -> Result<(), ShaderError> {
         debug_assert!(self.inside_frame);
 
-        let program = self.programs.get_mut(&program_id).unwrap();
-
         let mut vs_preamble = Vec::new();
         let mut fs_preamble = Vec::new();
 
         vs_preamble.push("#define WR_VERTEX_SHADER\n".to_owned());
         fs_preamble.push("#define WR_FRAGMENT_SHADER\n".to_owned());
 
         if let Some(ref prefix) = program.prefix {
             vs_preamble.push(prefix.clone());
@@ -1527,19 +1436,19 @@ impl Device {
         if let Some(vs_id) = program.vs_id {
             self.gl.detach_shader(program.id, vs_id);
         }
 
         if let Some(fs_id) = program.fs_id {
             self.gl.detach_shader(program.id, fs_id);
         }
 
-        if let Err(bind_error) = program.attach_and_bind_shaders(vs_id, fs_id, vertex_format) {
+        if let Err(bind_error) = program.attach_and_bind_shaders(vs_id, fs_id, descriptor, &*self.gl) {
             if let (Some(vs_id), Some(fs_id)) = (program.vs_id, program.fs_id) {
-                try! { program.attach_and_bind_shaders(vs_id, fs_id, vertex_format) };
+                try! { program.attach_and_bind_shaders(vs_id, fs_id, descriptor, &*self.gl) };
             } else {
                return Err(bind_error);
             }
         } else {
             if let Some(vs_id) = program.vs_id {
                 self.gl.delete_shader(vs_id);
             }
 
@@ -1549,17 +1458,17 @@ impl Device {
 
             program.vs_id = Some(vs_id);
             program.fs_id = Some(fs_id);
         }
 
         program.u_transform = self.gl.get_uniform_location(program.id, "uTransform");
         program.u_device_pixel_ratio = self.gl.get_uniform_location(program.id, "uDevicePixelRatio");
 
-        program_id.bind(&*self.gl);
+        self.bind_program(program);
         let u_color_0 = self.gl.get_uniform_location(program.id, "sColor0");
         if u_color_0 != -1 {
             self.gl.uniform_1i(u_color_0, TextureSampler::Color0 as i32);
         }
         let u_color1 = self.gl.get_uniform_location(program.id, "sColor1");
         if u_color1 != -1 {
             self.gl.uniform_1i(u_color1, TextureSampler::Color1 as i32);
         }
@@ -1630,36 +1539,34 @@ impl Device {
             }
         }
 
         for program_id in programs_to_update {
             self.load_program(program_id, false);
         }
     }*/
 
-    pub fn get_uniform_location(&self, program_id: ProgramId, name: &str) -> UniformLocation {
-        let ProgramId(program_id) = program_id;
-        UniformLocation(self.gl.get_uniform_location(program_id, name))
+    pub fn get_uniform_location(&self, program: &Program, name: &str) -> UniformLocation {
+        UniformLocation(self.gl.get_uniform_location(program.id, name))
     }
 
     pub fn set_uniform_2f(&self, uniform: UniformLocation, x: f32, y: f32) {
         debug_assert!(self.inside_frame);
         let UniformLocation(location) = uniform;
         self.gl.uniform_2f(location, x, y);
     }
 
-    fn set_uniforms(&self,
-                    program: &Program,
-                    transform: &Transform3D<f32>,
-                    device_pixel_ratio: f32) {
+    pub fn set_uniforms(&self,
+                        program: &Program,
+                        transform: &Transform3D<f32>) {
         debug_assert!(self.inside_frame);
         self.gl.uniform_matrix_4fv(program.u_transform,
-                               false,
-                               &transform.to_row_major_array());
-        self.gl.uniform_1f(program.u_device_pixel_ratio, device_pixel_ratio);
+                                   false,
+                                   &transform.to_row_major_array());
+        self.gl.uniform_1f(program.u_device_pixel_ratio, self.device_pixel_ratio);
     }
 
     pub fn create_pbo(&mut self) -> PBOId {
         let id = self.gl.gen_buffers(1)[0];
         PBOId(id)
     }
 
     pub fn destroy_pbo(&mut self, id: PBOId) {
@@ -1792,34 +1699,33 @@ impl Device {
             self.bound_vao = vao_id;
 
             let VAOId(id) = vao_id;
             self.gl.bind_vertex_array(id);
         }
     }
 
     fn create_vao_with_vbos(&mut self,
-                            format: VertexFormat,
+                            descriptor: &VertexDescriptor,
                             main_vbo_id: VBOId,
                             instance_vbo_id: VBOId,
                             ibo_id: IBOId,
-                            vertex_offset: gl::GLuint,
                             instance_stride: gl::GLint,
                             owns_vertices: bool,
                             owns_instances: bool,
                             owns_indices: bool)
                             -> VAOId {
         debug_assert!(self.inside_frame);
 
         let vao_ids = self.gl.gen_vertex_arrays(1);
         let vao_id = vao_ids[0];
 
         self.gl.bind_vertex_array(vao_id);
 
-        format.bind(self.gl(), main_vbo_id, instance_vbo_id, vertex_offset, instance_stride);
+        descriptor.bind(self.gl(), main_vbo_id, instance_vbo_id);
         ibo_id.bind(self.gl()); // force it to be a part of VAO
 
         let vao = VAO {
             gl: Rc::clone(&self.gl),
             id: vao_id,
             ibo_id,
             main_vbo_id,
             instance_vbo_id,
@@ -1834,39 +1740,57 @@ impl Device {
         let vao_id = VAOId(vao_id);
 
         debug_assert!(!self.vaos.contains_key(&vao_id));
         self.vaos.insert(vao_id, vao);
 
         vao_id
     }
 
-    pub fn create_vao(&mut self, format: VertexFormat, inst_stride: gl::GLint) -> VAOId {
+    pub fn create_vao(&mut self,
+                      descriptor: &VertexDescriptor,
+                      inst_stride: gl::GLint) -> VAOId {
         debug_assert!(self.inside_frame);
 
         let buffer_ids = self.gl.gen_buffers(3);
         let ibo_id = IBOId(buffer_ids[0]);
         let main_vbo_id = VBOId(buffer_ids[1]);
         let intance_vbo_id = VBOId(buffer_ids[2]);
 
-        self.create_vao_with_vbos(format, main_vbo_id, intance_vbo_id, ibo_id, 0, inst_stride, true, true, true)
+        self.create_vao_with_vbos(descriptor,
+                                  main_vbo_id,
+                                  intance_vbo_id,
+                                  ibo_id,
+                                  inst_stride,
+                                  true,
+                                  true,
+                                  true)
     }
 
-    pub fn create_vao_with_new_instances(&mut self, format: VertexFormat, inst_stride: gl::GLint,
+    pub fn create_vao_with_new_instances(&mut self,
+                                         descriptor: &VertexDescriptor,
+                                         inst_stride: gl::GLint,
                                          base_vao: VAOId) -> VAOId {
         debug_assert!(self.inside_frame);
 
         let buffer_ids = self.gl.gen_buffers(1);
         let intance_vbo_id = VBOId(buffer_ids[0]);
         let (main_vbo_id, ibo_id) = {
             let vao = self.vaos.get(&base_vao).unwrap();
             (vao.main_vbo_id, vao.ibo_id)
         };
 
-        self.create_vao_with_vbos(format, main_vbo_id, intance_vbo_id, ibo_id, 0, inst_stride, false, true, false)
+        self.create_vao_with_vbos(descriptor,
+                                  main_vbo_id,
+                                  intance_vbo_id,
+                                  ibo_id,
+                                  inst_stride,
+                                  false,
+                                  true,
+                                  false)
     }
 
     pub fn update_vao_main_vertices<V>(&mut self,
                                        vao_id: VAOId,
                                        vertices: &[V],
                                        usage_hint: VertexUsageHint) {
         debug_assert!(self.inside_frame);
 
@@ -1949,54 +1873,16 @@ impl Device {
             self.gl.bind_texture(gl::TEXTURE_2D, 0);
         }
 
         self.gl.active_texture(gl::TEXTURE0);
 
         self.frame_id.0 += 1;
     }
 
-    pub fn assign_ubo_binding(&self, program_id: ProgramId, name: &str, value: u32) -> u32 {
-        let index = self.gl.get_uniform_block_index(program_id.0, name);
-        self.gl.uniform_block_binding(program_id.0, index, value);
-        index
-    }
-
-    pub fn create_ubo<T>(&self, data: &[T], binding: u32) -> UBOId {
-        let ubo = self.gl.gen_buffers(1)[0];
-        self.gl.bind_buffer(gl::UNIFORM_BUFFER, ubo);
-        gl::buffer_data(self.gl(), gl::UNIFORM_BUFFER, data, gl::STATIC_DRAW);
-        self.gl.bind_buffer_base(gl::UNIFORM_BUFFER, binding, ubo);
-        UBOId(ubo)
-    }
-
-    pub fn reset_ubo(&self, binding: u32) {
-        self.gl.bind_buffer(gl::UNIFORM_BUFFER, 0);
-        self.gl.bind_buffer_base(gl::UNIFORM_BUFFER, binding, 0);
-    }
-
-    pub fn delete_buffer(&self, buffer: UBOId) {
-        self.gl.delete_buffers(&[buffer.0]);
-    }
-
-    #[cfg(target_os = "android")]
-    pub fn set_multisample(&self, enable: bool) {
-    }
-
-    #[cfg(not(target_os = "android"))]
-    pub fn set_multisample(&self, enable: bool) {
-        if self.capabilities.supports_multisampling {
-            if enable {
-                self.gl.enable(gl::MULTISAMPLE);
-            } else {
-                self.gl.disable(gl::MULTISAMPLE);
-            }
-        }
-    }
-
     pub fn clear_target(&self,
                         color: Option<[f32; 4]>,
                         depth: Option<f32>) {
         let mut clear_bits = 0;
 
         if let Some(color) = color {
             self.gl.clear_color(color[0], color[1], color[2], color[3]);
             clear_bits |= gl::COLOR_BUFFER_BIT;
--- a/gfx/webrender/src/frame.rs
+++ b/gfx/webrender/src/frame.rs
@@ -6,27 +6,23 @@ use api::{BuiltDisplayList, BuiltDisplay
 use api::{ComplexClipRegion, DeviceUintRect, DeviceUintSize, DisplayItemRef, Epoch, FilterOp};
 use api::{ImageDisplayItem, ItemRange, LayerPoint, LayerRect, LayerSize, LayerToScrollTransform};
 use api::{LayerVector2D, LayoutSize, LayoutTransform, LocalClip, MixBlendMode, PipelineId};
 use api::{PropertyBinding, ScrollClamping, ScrollEventPhase, ScrollLayerState, ScrollLocation};
 use api::{ScrollPolicy, ScrollSensitivity, SpecificDisplayItem, StackingContext, TileOffset};
 use api::{TransformStyle, WorldPoint};
 use clip_scroll_tree::{ClipScrollTree, ScrollStates};
 use euclid::rect;
-use fnv::FnvHasher;
 use gpu_cache::GpuCache;
-use internal_types::{RendererFrame};
+use internal_types::{FastHashMap, RendererFrame};
 use frame_builder::{FrameBuilder, FrameBuilderConfig};
 use mask_cache::ClipRegion;
 use profiler::{GpuCacheProfileCounters, TextureCacheProfileCounters};
-use resource_cache::ResourceCache;
+use resource_cache::{ResourceCache, TiledImageMap};
 use scene::{Scene, SceneProperties};
-use std::cmp;
-use std::collections::HashMap;
-use std::hash::BuildHasherDefault;
 use tiling::{CompositeOps, DisplayListMap, PrimitiveFlags};
 use util::{ComplexClipRegionHelpers, subtract_rect};
 
 #[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
 pub struct FrameId(pub u32);
 
 static DEFAULT_SCROLLBAR_COLOR: ColorF = ColorF { r: 0.3, g: 0.3, b: 0.3, a: 0.6 };
 
@@ -86,31 +82,31 @@ impl NestedDisplayListInfo {
             self.convert_id_to_nested(id)
         }
     }
 }
 
 struct FlattenContext<'a> {
     scene: &'a Scene,
     builder: &'a mut FrameBuilder,
-    resource_cache: &'a mut ResourceCache,
+    tiled_image_map: TiledImageMap,
     replacements: Vec<(ClipId, ClipId)>,
     nested_display_list_info: Vec<NestedDisplayListInfo>,
     current_nested_display_list_index: u64,
 }
 
 impl<'a> FlattenContext<'a> {
     fn new(scene: &'a Scene,
            builder: &'a mut FrameBuilder,
-           resource_cache: &'a mut ResourceCache)
+           resource_cache: &ResourceCache)
            -> FlattenContext<'a> {
         FlattenContext {
             scene,
             builder,
-            resource_cache,
+            tiled_image_map: resource_cache.get_tiled_image_map(),
             replacements: Vec::new(),
             nested_display_list_info: Vec::new(),
             current_nested_display_list_index: 0,
         }
     }
 
     fn push_nested_display_list_ids(&mut self, info: ClipAndScrollInfo) {
         self.current_nested_display_list_index += 1;
@@ -172,17 +168,17 @@ impl<'a> FlattenContext<'a> {
                                 .get(complex_clips)
                                 .collect()
     }
 }
 
 // TODO: doc
 pub struct Frame {
     pub clip_scroll_tree: ClipScrollTree,
-    pub pipeline_epoch_map: HashMap<PipelineId, Epoch, BuildHasherDefault<FnvHasher>>,
+    pub pipeline_epoch_map: FastHashMap<PipelineId, Epoch>,
     id: FrameId,
     frame_builder_config: FrameBuilderConfig,
     frame_builder: Option<FrameBuilder>,
 }
 
 trait StackingContextHelpers {
     fn mix_blend_mode_for_compositing(&self) -> Option<MixBlendMode>;
     fn filter_ops_for_compositing(&self,
@@ -217,17 +213,17 @@ impl StackingContextHelpers for Stacking
         }
         filters
     }
 }
 
 impl Frame {
     pub fn new(config: FrameBuilderConfig) -> Frame {
         Frame {
-            pipeline_epoch_map: HashMap::default(),
+            pipeline_epoch_map: FastHashMap::default(),
             clip_scroll_tree: ClipScrollTree::new(),
             id: FrameId(0),
             frame_builder: None,
             frame_builder_config: config,
         }
     }
 
     pub fn reset(&mut self) -> ScrollStates {
@@ -534,29 +530,26 @@ impl Frame {
         match *item.item() {
             SpecificDisplayItem::WebGL(ref info) => {
                 context.builder.add_webgl_rectangle(clip_and_scroll,
                                                     item_rect_with_offset,
                                                     &clip_with_offset,
                                                     info.context_id);
             }
             SpecificDisplayItem::Image(ref info) => {
-                let image = context.resource_cache.get_image_properties(info.image_key);
-                if let Some(tile_size) = image.tiling {
+                if let Some(tiling) = context.tiled_image_map.get(&info.image_key) {
                     // The image resource is tiled. We have to generate an image primitive
                     // for each tile.
-                    let image_size = DeviceUintSize::new(image.descriptor.width,
-                                                         image.descriptor.height);
                     self.decompose_image(clip_and_scroll,
-                                         context,
+                                         &mut context.builder,
                                          &item_rect_with_offset,
                                          &clip_with_offset,
                                          info,
-                                         image_size,
-                                         tile_size as u32);
+                                         tiling.image_size,
+                                         tiling.tile_size as u32);
                 } else {
                     context.builder.add_image(clip_and_scroll,
                                               item_rect_with_offset,
                                               &clip_with_offset,
                                               &info.stretch_size,
                                               &info.tile_spacing,
                                               None,
                                               info.image_key,
@@ -873,27 +866,27 @@ impl Frame {
     /// decomposition. This lets us generate the minimum amount of primitives by, for  example,
     /// decompositing the repetition horizontally while repeating vertically in the shader (for
     /// an image where the width is too bug but the height is not).
     ///
     /// decompose_image and decompose_image_row handle image repetitions while decompose_tiled_image
     /// takes care of the decomposition required by the internal tiling of the image.
     fn decompose_image(&mut self,
                        clip_and_scroll: ClipAndScrollInfo,
-                       context: &mut FlattenContext,
+                       builder: &mut FrameBuilder,
                        item_rect: &LayerRect,
                        item_local_clip: &LocalClip,
                        info: &ImageDisplayItem,
                        image_size: DeviceUintSize,
                        tile_size: u32) {
         let no_vertical_tiling = image_size.height <= tile_size;
         let no_vertical_spacing = info.tile_spacing.height == 0.0;
         if no_vertical_tiling && no_vertical_spacing {
             self.decompose_image_row(clip_and_scroll,
-                                     context,
+                                     builder,
                                      item_rect,
                                      item_local_clip,
                                      info,
                                      image_size,
                                      tile_size);
             return;
         }
 
@@ -903,39 +896,39 @@ impl Frame {
         for i in 0..num_repetitions {
             if let Some(row_rect) = rect(
                 item_rect.origin.x,
                 item_rect.origin.y + (i as f32) * layout_stride,
                 item_rect.size.width,
                 info.stretch_size.height
             ).intersection(item_rect) {
                 self.decompose_image_row(clip_and_scroll,
-                                         context,
+                                         builder,
                                          &row_rect,
                                          item_local_clip,
                                          info,
                                          image_size,
                                          tile_size);
             }
         }
     }
 
     fn decompose_image_row(&mut self,
                            clip_and_scroll: ClipAndScrollInfo,
-                           context: &mut FlattenContext,
+                           builder: &mut FrameBuilder,
                            item_rect: &LayerRect,
                            item_local_clip: &LocalClip,
                            info: &ImageDisplayItem,
                            image_size: DeviceUintSize,
                            tile_size: u32) {
         let no_horizontal_tiling = image_size.width <= tile_size;
         let no_horizontal_spacing = info.tile_spacing.width == 0.0;
         if no_horizontal_tiling && no_horizontal_spacing {
             self.decompose_tiled_image(clip_and_scroll,
-                                       context,
+                                       builder,
                                        item_rect,
                                        item_local_clip,
                                        info,
                                        image_size,
                                        tile_size);
             return;
         }
 
@@ -945,29 +938,29 @@ impl Frame {
         for i in 0..num_repetitions {
             if let Some(decomposed_rect) = rect(
                 item_rect.origin.x + (i as f32) * layout_stride,
                 item_rect.origin.y,
                 info.stretch_size.width,
                 item_rect.size.height,
             ).intersection(item_rect) {
                 self.decompose_tiled_image(clip_and_scroll,
-                                           context,
+                                           builder,
                                            &decomposed_rect,
                                            item_local_clip,
                                            info,
                                            image_size,
                                            tile_size);
             }
         }
     }
 
     fn decompose_tiled_image(&mut self,
                              clip_and_scroll: ClipAndScrollInfo,
-                             context: &mut FlattenContext,
+                             builder: &mut FrameBuilder,
                              item_rect: &LayerRect,
                              item_local_clip: &LocalClip,
                              info: &ImageDisplayItem,
                              image_size: DeviceUintSize,
                              tile_size: u32) {
         // The image resource is tiled. We have to generate an image primitive
         // for each tile.
         // We need to do this because the image is broken up into smaller tiles in the texture
@@ -1000,24 +993,24 @@ impl Frame {
         // Apparently web authors do that...
 
         let mut repeat_x = false;
         let mut repeat_y = false;
 
         if info.stretch_size.width < item_rect.size.width {
             // If this assert blows up it means we haven't properly decomposed the image in decompose_image_row.
             debug_assert!(image_size.width <= tile_size);
-            // we don't actually tile in this dimmension so repeating can be done in the shader.
+            // we don't actually tile in this dimension so repeating can be done in the shader.
             repeat_x = true;
         }
 
         if info.stretch_size.height < item_rect.size.height {
             // If this assert blows up it means we haven't properly decomposed the image in decompose_image.
             debug_assert!(image_size.height <= tile_size);
-            // we don't actually tile in this dimmension so repeating can be done in the shader.
+            // we don't actually tile in this dimension so repeating can be done in the shader.
             repeat_y = true;
         }
 
         let tile_size_f32 = tile_size as f32;
 
         // Note: this rounds down so it excludes the partially filled tiles on the right and
         // bottom edges (we handle them separately below).
         let num_tiles_x = (image_size.width / tile_size) as u16;
@@ -1036,76 +1029,76 @@ impl Frame {
         // The size in pixels of the tiles on the right and bottom edges, smaller
         // than the regular tile size if the image is not a multiple of the tile size.
         // Zero means the image size is a multiple of the tile size.
         let leftover = DeviceUintSize::new(image_size.width % tile_size, image_size.height % tile_size);
 
         for ty in 0..num_tiles_y {
             for tx in 0..num_tiles_x {
                 self.add_tile_primitive(clip_and_scroll,
-                                        context,
+                                        builder,
                                         item_rect,
                                         item_local_clip,
                                         info,
                                         TileOffset::new(tx, ty),
                                         stretched_tile_size,
                                         1.0, 1.0,
                                         repeat_x, repeat_y);
             }
             if leftover.width != 0 {
                 // Tiles on the right edge that are smaller than the tile size.
                 self.add_tile_primitive(clip_and_scroll,
-                                        context,
+                                        builder,
                                         item_rect,
                                         item_local_clip,
                                         info,
                                         TileOffset::new(num_tiles_x, ty),
                                         stretched_tile_size,
                                         (leftover.width as f32) / tile_size_f32,
                                         1.0,
                                         repeat_x, repeat_y);
             }
         }
 
         if leftover.height != 0 {
             for tx in 0..num_tiles_x {
                 // Tiles on the bottom edge that are smaller than the tile size.
                 self.add_tile_primitive(clip_and_scroll,
-                                        context,
+                                        builder,
                                         item_rect,
                                         item_local_clip,
                                         info,
                                         TileOffset::new(tx, num_tiles_y),
                                         stretched_tile_size,
                                         1.0,
                                         (leftover.height as f32) / tile_size_f32,
                                         repeat_x,
                                         repeat_y);
             }
 
             if leftover.width != 0 {
                 // Finally, the bottom-right tile with a "leftover" size.
                 self.add_tile_primitive(clip_and_scroll,
-                                        context,
+                                        builder,
                                         item_rect,
                                         item_local_clip,
                                         info,
                                         TileOffset::new(num_tiles_x, num_tiles_y),
                                         stretched_tile_size,
                                         (leftover.width as f32) / tile_size_f32,
                                         (leftover.height as f32) / tile_size_f32,
                                         repeat_x,
                                         repeat_y);
             }
         }
     }
 
     fn add_tile_primitive(&mut self,
                           clip_and_scroll: ClipAndScrollInfo,
-                          context: &mut FlattenContext,
+                          builder: &mut FrameBuilder,
                           item_rect: &LayerRect,
                           item_local_clip: &LocalClip,
                           info: &ImageDisplayItem,
                           tile_offset: TileOffset,
                           stretched_tile_size: LayerSize,
                           tile_ratio_width: f32,
                           tile_ratio_height: f32,
                           repeat_x: bool,
@@ -1139,25 +1132,25 @@ impl Frame {
 
         if repeat_y {
             assert_eq!(tile_offset.y, 0);
             prim_rect.size.height = item_rect.size.height;
         }
 
         // Fix up the primitive's rect if it overflows the original item rect.
         if let Some(prim_rect) = prim_rect.intersection(item_rect) {
-            context.builder.add_image(clip_and_scroll,
-                                      prim_rect,
-                                      item_local_clip,
-                                      &stretched_size,
-                                      &info.tile_spacing,
-                                      None,
-                                      info.image_key,
-                                      info.image_rendering,
-                                      Some(tile_offset));
+            builder.add_image(clip_and_scroll,
+                              prim_rect,
+                              item_local_clip,
+                              &stretched_size,
+                              &info.tile_spacing,
+                              None,
+                              info.image_key,
+                              info.image_rendering,
+                              Some(tile_offset));
         }
     }
 
     pub fn build(&mut self,
                  resource_cache: &mut ResourceCache,
                  gpu_cache: &mut GpuCache,
                  display_lists: &DisplayListMap,
                  device_pixel_ratio: f32,
@@ -1167,20 +1160,16 @@ impl Frame {
                  -> RendererFrame {
         self.clip_scroll_tree.update_all_node_transforms(pan);
         let frame = self.build_frame(resource_cache,
                                      gpu_cache,
                                      display_lists,
                                      device_pixel_ratio,
                                      texture_cache_profile,
                                      gpu_cache_profile);
-        // Expire any resources that haven't been used for `cache_expiry_frames`.
-        let num_frames_back = self.frame_builder_config.cache_expiry_frames;
-        let expiry_frame = FrameId(cmp::max(num_frames_back, self.id.0) - num_frames_back);
-        resource_cache.expire_old_resources(expiry_frame);
         frame
     }
 
     fn build_frame(&mut self,
                    resource_cache: &mut ResourceCache,
                    gpu_cache: &mut GpuCache,
                    display_lists: &DisplayListMap,
                    device_pixel_ratio: f32,
--- a/gfx/webrender/src/frame_builder.rs
+++ b/gfx/webrender/src/frame_builder.rs
@@ -1,40 +1,37 @@
 /* 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 api::{BorderDetails, BorderDisplayItem, BoxShadowClipMode, ClipAndScrollInfo, ClipId, ColorF};
 use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceUintRect, DeviceUintSize};
 use api::{ExtendMode, FontKey, FontRenderMode, GlyphInstance, GlyphOptions, GradientStop};
-use api::{ImageKey, ImageRendering, ItemRange, LayerPoint, LayerRect, LayerSize};
+use api::{ImageKey, ImageRendering, ItemRange, LayerPoint, LayerRect, LayerSize, SubpixelDirection};
 use api::{LayerToScrollTransform, LayerVector2D, LayoutVector2D, LineOrientation, LineStyle};
 use api::{LocalClip, PipelineId, RepeatMode, ScrollSensitivity, TextShadow, TileOffset};
 use api::{TransformStyle, WebGLContextId, WorldPixel, YuvColorSpace, YuvData};
 use app_units::Au;
-use fnv::FnvHasher;
 use frame::FrameId;
 use gpu_cache::GpuCache;
-use internal_types::HardwareCompositeOp;
+use internal_types::{FastHashMap, HardwareCompositeOp};
 use mask_cache::{ClipMode, ClipRegion, ClipSource, MaskCacheInfo};
 use plane_split::{BspSplitter, Polygon, Splitter};
 use prim_store::{GradientPrimitiveCpu, ImagePrimitiveCpu, LinePrimitive, PrimitiveKind};
 use prim_store::{ImagePrimitiveKind, PrimitiveContainer, PrimitiveIndex};
 use prim_store::{PrimitiveStore, RadialGradientPrimitiveCpu, TextRunMode};
 use prim_store::{RectanglePrimitive, TextRunPrimitiveCpu, TextShadowPrimitiveCpu};
 use prim_store::{BoxShadowPrimitiveCpu, TexelRect, YuvImagePrimitiveCpu};
 use profiler::{FrameProfileCounters, GpuCacheProfileCounters, TextureCacheProfileCounters};
 use render_task::{AlphaRenderItem, ClipWorkItem, MaskCacheKey, RenderTask, RenderTaskIndex};
 use render_task::{RenderTaskId, RenderTaskLocation};
 use resource_cache::ResourceCache;
 use clip_scroll_node::{ClipInfo, ClipScrollNode, NodeType};
 use clip_scroll_tree::ClipScrollTree;
 use std::{cmp, f32, i32, mem, usize};
-use std::collections::HashMap;
-use std::hash::BuildHasherDefault;
 use euclid::{SideOffsets2D, vec2, vec3};
 use tiling::{ContextIsolation, StackingContextIndex};
 use tiling::{ClipScrollGroup, ClipScrollGroupIndex, CompositeOps, DisplayListMap, Frame};
 use tiling::{PackedLayer, PackedLayerIndex, PrimitiveFlags, PrimitiveRunCmd, RenderPass};
 use tiling::{RenderTargetContext, RenderTaskCollection, ScrollbarPrimitive, StackingContext};
 use util::{self, pack_as_float, subtract_rect, recycle_vec};
 use util::{MatrixHelpers, RectHelpers};
 
@@ -113,19 +110,18 @@ pub struct FrameBuilder {
     screen_size: DeviceUintSize,
     background_color: Option<ColorF>,
     prim_store: PrimitiveStore,
     cmds: Vec<PrimitiveRunCmd>,
     config: FrameBuilderConfig,
 
     stacking_context_store: Vec<StackingContext>,
     clip_scroll_group_store: Vec<ClipScrollGroup>,
-    clip_scroll_group_indices: HashMap<ClipAndScrollInfo,
-                                       ClipScrollGroupIndex,
-                                       BuildHasherDefault<FnvHasher>>,
+    clip_scroll_group_indices: FastHashMap<ClipAndScrollInfo,
+                                           ClipScrollGroupIndex>,
     packed_layers: Vec<PackedLayer>,
 
     // A stack of the current text-shadow primitives.
     shadow_prim_stack: Vec<PrimitiveIndex>,
 
     scrollbar_prims: Vec<ScrollbarPrimitive>,
 
     /// A stack of scroll nodes used during display list processing to properly
@@ -146,17 +142,17 @@ impl FrameBuilder {
                screen_size: DeviceUintSize,
                background_color: Option<ColorF>,
                config: FrameBuilderConfig) -> FrameBuilder {
         match previous {
             Some(prev) => {
                 FrameBuilder {
                     stacking_context_store: recycle_vec(prev.stacking_context_store),
                     clip_scroll_group_store: recycle_vec(prev.clip_scroll_group_store),
-                    clip_scroll_group_indices: HashMap::default(),
+                    clip_scroll_group_indices: FastHashMap::default(),
                     cmds: recycle_vec(prev.cmds),
                     packed_layers: recycle_vec(prev.packed_layers),
                     shadow_prim_stack: recycle_vec(prev.shadow_prim_stack),
                     scrollbar_prims: recycle_vec(prev.scrollbar_prims),
                     reference_frame_stack: recycle_vec(prev.reference_frame_stack),
                     stacking_context_stack: recycle_vec(prev.stacking_context_stack),
                     prim_store: prev.prim_store.recycle(),
                     screen_size,
@@ -164,17 +160,17 @@ impl FrameBuilder {
                     config,
                     has_root_stacking_context: false,
                 }
             }
             None => {
                 FrameBuilder {
                     stacking_context_store: Vec::new(),
                     clip_scroll_group_store: Vec::new(),
-                    clip_scroll_group_indices: HashMap::default(),
+                    clip_scroll_group_indices: FastHashMap::default(),
                     cmds: Vec::new(),
                     packed_layers: Vec::new(),
                     shadow_prim_stack: Vec::new(),
                     scrollbar_prims: Vec::new(),
                     reference_frame_stack: Vec::new(),
                     stacking_context_stack: Vec::new(),
                     prim_store: PrimitiveStore::new(),
                     screen_size,
@@ -903,32 +899,40 @@ impl FrameBuilder {
                 if stacking_context.composite_ops.count() > 0 {
                     normal_render_mode = FontRenderMode::Alpha;
                 }
             }
         }
 
         // Shadows never use subpixel AA, but need to respect the alpha/mono flag
         // for reftests.
-        let shadow_render_mode = match self.config.default_font_render_mode {
-            FontRenderMode::Subpixel | FontRenderMode::Alpha => FontRenderMode::Alpha,
-            FontRenderMode::Mono => FontRenderMode::Mono,
+        let (shadow_render_mode, subpx_dir) = match self.config.default_font_render_mode {
+            FontRenderMode::Subpixel | FontRenderMode::Alpha => {
+                // TODO(gw): Expose subpixel direction in API once WR supports
+                //           vertical text runs.
+                (FontRenderMode::Alpha, SubpixelDirection::Horizontal)
+            }
+            FontRenderMode::Mono => {
+                (FontRenderMode::Mono, SubpixelDirection::None)
+            }
         };
 
         let prim = TextRunPrimitiveCpu {
             font_key,
             logical_font_size: size,
             glyph_range,
             glyph_count,
-            glyph_instances: Vec::new(),
+            glyph_gpu_blocks: Vec::new(),
+            glyph_keys: Vec::new(),
             glyph_options,
             normal_render_mode,
             shadow_render_mode,
             offset: run_offset,
             color: *color,
+            subpx_dir,
         };
 
         // Text shadows that have a blur radius of 0 need to be rendered as normal
         // text elements to get pixel perfect results for reftests. It's also a big
         // performance win to avoid blurs and render target allocations where
         // possible. For any text shadows that have zero blur, create a normal text
         // primitive with the shadow's color and offset. These need to be added
         // *before* the visual text primitive in order to get the correct paint
@@ -1358,17 +1362,17 @@ impl FrameBuilder {
         // A stack of the alpha batcher tasks. We create them on the way down,
         // and then actually populate with items and dependencies on the way up.
         let mut alpha_task_stack = Vec::new();
         // A map of "preserve-3d" contexts. We are baking these into render targets
         // and only compositing once we are out of "preserve-3d" hierarchy.
         // The stacking contexts that fall into this category are
         //  - ones with `ContextIsolation::Items`, for their actual items to be backed
         //  - immediate children of `ContextIsolation::Items`
-        let mut preserve_3d_map: HashMap<StackingContextIndex, RenderTask> = HashMap::new();
+        let mut preserve_3d_map: FastHashMap<StackingContextIndex, RenderTask> = FastHashMap::default();
         // The plane splitter stack, using a simple BSP tree.
         let mut splitter_stack = Vec::new();
 
         debug!("build_render_task()");
 
         for cmd in &self.cmds {
             match *cmd {
                 PrimitiveRunCmd::PushStackingContext(stacking_context_index) => {
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/src/glyph_cache.rs
@@ -0,0 +1,104 @@
+/* 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 api::{FontInstanceKey, GlyphKey};
+use frame::FrameId;
+use gpu_cache::GpuCache;
+use internal_types::FastHashMap;
+use resource_cache::{Resource, ResourceClassCache};
+use texture_cache::{TextureCache, TextureCacheItemId};
+
+pub struct CachedGlyphInfo {
+    pub texture_cache_id: Option<TextureCacheItemId>,
+    pub last_access: FrameId,
+}
+
+impl Resource for CachedGlyphInfo {
+    fn free(&self, texture_cache: &mut TextureCache) {
+        if let Some(id) = self.texture_cache_id {
+            texture_cache.free(id);
+        }
+    }
+    fn get_last_access_time(&self) -> FrameId {
+        self.last_access
+    }
+    fn set_last_access_time(&mut self, frame_id: FrameId) {
+        self.last_access = frame_id;
+    }
+    fn add_to_gpu_cache(&self,
+                        texture_cache: &mut TextureCache,
+                        gpu_cache: &mut GpuCache) {
+        if let Some(texture_cache_id) = self.texture_cache_id {
+            let item = texture_cache.get_mut(texture_cache_id);
+            if let Some(mut request) = gpu_cache.request(&mut item.uv_rect_handle) {
+                request.push(item.uv_rect);
+                request.push([item.user_data[0], item.user_data[1], 0.0, 0.0]);
+            }
+        }
+    }
+}
+
+pub type GlyphKeyCache = ResourceClassCache<GlyphKey, CachedGlyphInfo>;
+
+pub struct GlyphCache {
+    pub glyph_key_caches: FastHashMap<FontInstanceKey, GlyphKeyCache>,
+}
+
+impl GlyphCache {
+    pub fn new() -> GlyphCache {
+        GlyphCache {
+            glyph_key_caches: FastHashMap::default(),
+        }
+    }
+
+    pub fn get_glyph_key_cache_for_font_mut(&mut self,
+                                            font: FontInstanceKey) -> &mut GlyphKeyCache {
+        self.glyph_key_caches
+            .entry(font)
+            .or_insert(ResourceClassCache::new())
+    }
+
+    pub fn get_glyph_key_cache_for_font(&self,
+                                        font: &FontInstanceKey) -> &GlyphKeyCache {
+        self.glyph_key_caches
+            .get(font)
+            .expect("BUG: Unable to find glyph key cache!")
+    }
+
+    pub fn update(&mut self,
+                  texture_cache: &mut TextureCache,
+                  gpu_cache: &mut GpuCache,
+                  current_frame_id: FrameId,
+                  expiry_frame_id: FrameId) {
+        let mut caches_to_remove = Vec::new();
+
+        for (font, glyph_key_cache) in &mut self.glyph_key_caches {
+            glyph_key_cache.update(texture_cache,
+                                   gpu_cache,
+                                   current_frame_id,
+                                   expiry_frame_id);
+
+            if glyph_key_cache.is_empty() {
+                caches_to_remove.push(font.clone());
+            }
+        }
+
+        for key in caches_to_remove {
+            self.glyph_key_caches.remove(&key).unwrap();
+        }
+    }
+
+    pub fn clear_fonts<F>(&mut self, texture_cache: &mut TextureCache, key_fun: F)
+    where for<'r> F: Fn(&'r &FontInstanceKey) -> bool
+    {
+        let caches_to_destroy = self.glyph_key_caches.keys()
+            .filter(&key_fun)
+            .cloned()
+            .collect::<Vec<_>>();
+        for key in caches_to_destroy {
+            let mut cache = self.glyph_key_caches.remove(&key).unwrap();
+            cache.clear(texture_cache);
+        }
+    }
+}
--- a/gfx/webrender/src/glyph_rasterizer.rs
+++ b/gfx/webrender/src/glyph_rasterizer.rs
@@ -1,37 +1,33 @@
 /* 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/. */
 
 #[cfg(test)]
 use app_units::Au;
 use device::TextureFilter;
-use fnv::FnvHasher;
 use frame::FrameId;
+use glyph_cache::{CachedGlyphInfo, GlyphCache};
+use internal_types::FastHashSet;
 use platform::font::{FontContext, RasterizedGlyph};
 use profiler::TextureCacheProfileCounters;
 use rayon::ThreadPool;
 use rayon::prelude::*;
-use resource_cache::ResourceClassCache;
-use std::hash::BuildHasherDefault;
 use std::sync::{Arc, Mutex, MutexGuard};
 use std::sync::mpsc::{channel, Receiver, Sender};
 use std::collections::hash_map::Entry;
-use std::collections::HashSet;
 use std::mem;
-use texture_cache::{TextureCacheItemId, TextureCache};
+use texture_cache::TextureCache;
 #[cfg(test)]
-use api::{ColorF, FontRenderMode, IdNamespace};
-use api::{FontInstanceKey, LayoutPoint};
+use api::{ColorF, LayoutPoint, FontRenderMode, IdNamespace, SubpixelDirection};
+use api::{FontInstanceKey};
 use api::{FontKey, FontTemplate};
 use api::{ImageData, ImageDescriptor, ImageFormat};
-use api::{GlyphKey, GlyphInstance, GlyphDimensions};
-
-pub type GlyphCache = ResourceClassCache<GlyphRequest, Option<TextureCacheItemId>>;
+use api::{GlyphKey, GlyphDimensions};
 
 pub struct FontContexts {
     // These worker are mostly accessed from their corresponding worker threads.
     // The goal is that there should be no noticeable contention on the muteces.
     worker_contexts: Vec<Mutex<FontContext>>,
 
     // This worker should be accessed by threads that don't belong to thre thread pool
     // (in theory that's only the render backend thread so no contention expected either).
@@ -74,27 +70,27 @@ impl FontContexts {
         self.workers.current_thread_index()
     }
 }
 
 pub struct GlyphRasterizer {
     workers: Arc<ThreadPool>,
     font_contexts: Arc<FontContexts>,
 
-    // Receives the rendered glyphs.
-    glyph_rx: Receiver<Vec<GlyphRasterJob>>,
-    glyph_tx: Sender<Vec<GlyphRasterJob>>,
-
     // Maintain a set of glyphs that have been requested this
     // frame. This ensures the glyph thread won't rasterize
     // the same glyph more than once in a frame. This is required
     // because the glyph cache hash table is not updated
     // until the end of the frame when we wait for glyph requests
     // to be resolved.
-    pending_glyphs: HashSet<GlyphRequest>,
+    pending_glyphs: FastHashSet<GlyphRequest>,
+
+    // Receives the rendered glyphs.
+    glyph_rx: Receiver<Vec<GlyphRasterJob>>,
+    glyph_tx: Sender<Vec<GlyphRasterJob>>,
 
     // We defer removing fonts to the end of the frame so that:
     // - this work is done outside of the critical path,
     // - we don't have to worry about the ordering of events if a font is used on
     //   a frame where it is used (although it seems unlikely).
     fonts_to_remove: Vec<FontKey>,
 }
 
@@ -112,19 +108,19 @@ impl GlyphRasterizer {
         GlyphRasterizer {
             font_contexts: Arc::new(
                 FontContexts {
                     worker_contexts: contexts,
                     shared_context: Mutex::new(FontContext::new()),
                     workers: Arc::clone(&workers),
                 }
             ),
+            pending_glyphs: FastHashSet::default(),
             glyph_rx,
             glyph_tx,
-            pending_glyphs: HashSet::new(),
             workers,
             fonts_to_remove: Vec::new(),
         }
     }
 
     pub fn add_font(&mut self, font_key: FontKey, template: FontTemplate) {
         let font_contexts = Arc::clone(&self.font_contexts);
         // It's important to synchronously add the font for the shared context because
@@ -146,39 +142,30 @@ impl GlyphRasterizer {
         self.fonts_to_remove.push(font_key);
     }
 
     pub fn request_glyphs(
         &mut self,
         glyph_cache: &mut GlyphCache,
         current_frame_id: FrameId,
         font: FontInstanceKey,
-        glyph_instances: &[GlyphInstance],
-        requested_items: &mut HashSet<TextureCacheItemId, BuildHasherDefault<FnvHasher>>,
-    ) {
+        glyph_keys: &[GlyphKey]) {
         assert!(self.font_contexts.lock_shared_context().has_font(&font.font_key));
+        let mut glyphs = Vec::new();
 
-        let mut glyphs = Vec::with_capacity(glyph_instances.len());
+        let glyph_key_cache = glyph_cache.get_glyph_key_cache_for_font_mut(font.clone());
 
         // select glyphs that have not been requested yet.
-        for glyph in glyph_instances {
-            let glyph_request = GlyphRequest::new(font.clone(),
-                                                  glyph.index,
-                                                  glyph.point);
-
-            match glyph_cache.entry(glyph_request.clone(), current_frame_id) {
-                Entry::Occupied(entry) => {
-                    if let &Some(texture_cache_item_id) = entry.get() {
-                        requested_items.insert(texture_cache_item_id);
-                    }
-                }
+        for key in glyph_keys {
+            match glyph_key_cache.entry(key.clone(), current_frame_id) {
+                Entry::Occupied(..) => {}
                 Entry::Vacant(..) => {
-                    if !self.pending_glyphs.contains(&glyph_request) {
-                        self.pending_glyphs.insert(glyph_request.clone());
-                        glyphs.push(glyph_request.clone());
+                    let request = GlyphRequest::new(&font, key);
+                    if self.pending_glyphs.insert(request.clone()) {
+                        glyphs.push(request);
                     }
                 }
             }
         }
 
         if glyphs.is_empty() {
             return;
         }
@@ -220,22 +207,22 @@ impl GlyphRasterizer {
         self.font_contexts.lock_shared_context().get_glyph_index(font_key, ch)
     }
 
     pub fn resolve_glyphs(
         &mut self,
         current_frame_id: FrameId,
         glyph_cache: &mut GlyphCache,
         texture_cache: &mut TextureCache,
-        requested_items: &mut HashSet<TextureCacheItemId, BuildHasherDefault<FnvHasher>>,
         texture_cache_profile: &mut TextureCacheProfileCounters,
     ) {
         let mut rasterized_glyphs = Vec::with_capacity(self.pending_glyphs.len());
 
         // Pull rasterized glyphs from the queue.
+
         while !self.pending_glyphs.is_empty() {
             // TODO: rather than blocking until all pending glyphs are available
             // we could try_recv and steal work from the thread pool to take advantage
             // of the fact that this thread is alive and we avoid the added latency
             // of blocking it.
             let raster_jobs = self.glyph_rx.recv().expect("BUG: Should be glyphs pending!");
             for job in raster_jobs {
                 debug_assert!(self.pending_glyphs.contains(&job.request));
@@ -266,24 +253,28 @@ impl GlyphRasterizer {
                             is_opaque: false,
                             offset: 0,
                         },
                         TextureFilter::Linear,
                         ImageData::Raw(Arc::new(glyph.bytes)),
                         [glyph.left, glyph.top],
                         texture_cache_profile,
                     );
-                    requested_items.insert(image_id);
                     Some(image_id)
                 } else {
                     None
                 }
             );
 
-            glyph_cache.insert(job.request, image_id, current_frame_id);
+            let glyph_key_cache = glyph_cache.get_glyph_key_cache_for_font_mut(job.request.font);
+
+            glyph_key_cache.insert(job.request.key, CachedGlyphInfo {
+                texture_cache_id: image_id,
+                last_access: current_frame_id,
+            });
         }
 
         // Now that we are done with the critical path (rendering the glyphs),
         // we can schedule removing the fonts if needed.
         if !self.fonts_to_remove.is_empty() {