Merge mozilla-central to mozilla-inbound. r=merge a=merge CLOSED TREE
authorNoemi Erli <nerli@mozilla.com>
Wed, 15 Nov 2017 12:15:40 +0200
changeset 391992 76d469663b3c54093aad44d58dd932cb659465a6
parent 391991 f61598207d724074e5537bd71ad9b602518b400e (current diff)
parent 391897 45715ece25fcb064eee4f977ebd842d44a87f22b (diff)
child 391993 430e5be1f7bb6c2ebd991bc5e2a2222b83d2fdf2
push id55429
push usercbrindusan@mozilla.com
push dateWed, 15 Nov 2017 22:42:39 +0000
treeherderautoland@aa73c7a7f725 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone59.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 CLOSED TREE
dom/webidl/MediaStreamList.webidl
js/src/old-configure.in
media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp
media/webrtc/signaling/src/peerconnection/MediaPipelineFactory.cpp
media/webrtc/signaling/src/peerconnection/MediaPipelineFactory.h
media/webrtc/signaling/src/peerconnection/MediaStreamList.cpp
media/webrtc/signaling/src/peerconnection/MediaStreamList.h
mfbt/objs.mozbuild
mfbt/staticruntime/moz.build
mobile/android/chrome/content/bindings/checkbox.xml
mobile/android/chrome/content/bindings/settings.xml
mobile/android/services/src/androidTest/java/org/mozilla/gecko/background/sync/helpers/DefaultBeginDelegate.java
mobile/android/services/src/androidTest/java/org/mozilla/gecko/background/sync/helpers/DefaultSessionCreationDelegate.java
mobile/android/services/src/androidTest/java/org/mozilla/gecko/background/sync/helpers/ExpectBeginDelegate.java
mobile/android/services/src/androidTest/java/org/mozilla/gecko/background/sync/helpers/ExpectBeginFailDelegate.java
mobile/android/services/src/androidTest/java/org/mozilla/gecko/background/sync/helpers/SimpleSuccessBeginDelegate.java
mobile/android/services/src/androidTest/java/org/mozilla/gecko/background/sync/helpers/SimpleSuccessCreationDelegate.java
mobile/android/services/src/main/java/org/mozilla/gecko/sync/middleware/MiddlewareRepository.java
mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/BookmarksRepository.java
mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/HistoryRepository.java
mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/android/ThreadedRepository.java
mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/delegates/DeferrableRepositorySessionCreationDelegate.java
mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/delegates/DeferredRepositorySessionBeginDelegate.java
mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/delegates/RepositorySessionBeginDelegate.java
mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/delegates/RepositorySessionCreationDelegate.java
mobile/android/services/src/test/java/org/mozilla/android/sync/test/helpers/ExpectSuccessRepositorySessionBeginDelegate.java
mobile/android/services/src/test/java/org/mozilla/android/sync/test/helpers/ExpectSuccessRepositorySessionCreationDelegate.java
old-configure.in
testing/talos/talos/tests/tabswitch/content/options.xul
toolkit/mozapps/extensions/content/gmpPrefs.xul
toolkit/mozapps/extensions/content/setting.xml
toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1/bootstrap.js
toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1/chrome.manifest
toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1/install.rdf
toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1/options.xul
toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1/settings.dtd
toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_custom/binding.xml
toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_custom/bootstrap.js
toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_custom/chrome.manifest
toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_custom/install.rdf
toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_custom/options.xul
toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_custom/string.dtd
toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_info/bootstrap.js
toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_info/install.rdf
toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1_info/options.xul
toolkit/mozapps/extensions/test/browser/browser_bug714593.js
toolkit/mozapps/extensions/test/browser/browser_inlinesettings.js
toolkit/mozapps/extensions/test/browser/browser_inlinesettings_custom.js
toolkit/mozapps/extensions/test/browser/browser_inlinesettings_info.js
toolkit/mozapps/extensions/test/browser/browser_openDialog.js
--- a/accessible/tests/mochitest/relations/test_tabbrowser.xul
+++ b/accessible/tests/mochitest/relations/test_tabbrowser.xul
@@ -44,17 +44,17 @@
       }
 
       this.finalCheck = function testTabRelations_finalCheck(aEvent)
       {
         ////////////////////////////////////////////////////////////////////////
         // 'labelled by'/'label for' relations for xul:tab and xul:tabpanel
 
         var tabs = tabBrowser().tabContainer.childNodes;
-        var panels = tabBrowser().mTabBox.tabpanels.childNodes;
+        var panels = tabBrowser().tabbox.tabpanels.childNodes;
 
         testRelation(panels[0], RELATION_LABELLED_BY, tabs[0]);
         testRelation(tabs[0], RELATION_LABEL_FOR, panels[0]);
         testRelation(panels[1], RELATION_LABELLED_BY, tabs[1]);
         testRelation(tabs[1], RELATION_LABEL_FOR, panels[1]);
       }
 
       this.getID = function testTabRelations_getID()
--- a/accessible/tests/mochitest/tree/test_tabbrowser.xul
+++ b/accessible/tests/mochitest/tree/test_tabbrowser.xul
@@ -208,17 +208,17 @@
                     }
                   ]
                 }
               ]
             }
           ]
         };
 
-        testAccessibleTree(tabBrowser().mTabBox.tabpanels, tabboxAccTree);
+        testAccessibleTree(tabBrowser().tabbox.tabpanels, tabboxAccTree);
       }
 
       this.getID = function testTabHierarchy_getID()
       {
         return "hierarchy of tabs";
       }
     }
 
--- a/accessible/windows/msaa/Compatibility.cpp
+++ b/accessible/windows/msaa/Compatibility.cpp
@@ -178,17 +178,17 @@ Compatibility::Init()
     sConsumers |= DOLPHIN;
 
   if (::GetModuleHandleW(L"STSA32"))
     sConsumers |= SEROTEK;
 
   if (::GetModuleHandleW(L"nvdaHelperRemote"))
     sConsumers |= NVDA;
 
-  if (::GetModuleHandleW(L"OsmHooks"))
+  if (::GetModuleHandleW(L"OsmHooks") || ::GetModuleHandleW(L"OsmHks64"))
     sConsumers |= COBRA;
 
   if (::GetModuleHandleW(L"WebFinderRemote"))
     sConsumers |= ZOOMTEXT;
 
   if (::GetModuleHandleW(L"Kazahook"))
     sConsumers |= KAZAGURU;
 
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1459,16 +1459,18 @@ pref("media.eme.enabled", true);
 #endif
 
 #ifdef NIGHTLY_BUILD
 pref("media.eme.vp9-in-mp4.enabled", true);
 #else
 pref("media.eme.vp9-in-mp4.enabled", false);
 #endif
 
+pref("media.eme.hdcp-policy-check.enabled", false);
+
 // Whether we should run a test-pattern through EME GMPs before assuming they'll
 // decode H.264.
 pref("media.gmp.trial-create.enabled", true);
 
 // Note: when media.gmp-*.visible is true, provided we're running on a
 // supported platform/OS version, the corresponding CDM appears in the
 // plugins list, Firefox will download the GMP/CDM if enabled, and our
 // UI to re-enable EME prompts the user to re-enable EME if it's disabled
--- a/browser/base/content/browser-ctrlTab.js
+++ b/browser/base/content/browser-ctrlTab.js
@@ -542,17 +542,17 @@ var ctrlTab = {
 
     var tabContainer = gBrowser.tabContainer;
     tabContainer[toggleEventListener]("TabOpen", this);
     tabContainer[toggleEventListener]("TabAttrModified", this);
     tabContainer[toggleEventListener]("TabSelect", this);
     tabContainer[toggleEventListener]("TabClose", this);
 
     document[toggleEventListener]("keypress", this);
-    gBrowser.mTabBox.handleCtrlTab = !enable;
+    gBrowser.tabbox.handleCtrlTab = !enable;
 
     if (enable)
       PageThumbs.addExpirationFilter(this);
     else
       PageThumbs.removeExpirationFilter(this);
 
     // If we're not running, hide the "Show All Tabs" menu item,
     // as Shift+Ctrl+Tab will be handled by the tab bar.
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -64,17 +64,17 @@
       <field name="mURIFixup" readonly="true">
         Components.classes["@mozilla.org/docshell/urifixup;1"]
                   .getService(Components.interfaces.nsIURIFixup);
       </field>
       <field name="_unifiedComplete" readonly="true">
          Components.classes["@mozilla.org/autocomplete/search;1?name=unifiedcomplete"]
                    .getService(Components.interfaces.mozIPlacesAutoComplete);
       </field>
-      <field name="mTabBox" readonly="true">
+      <field name="tabbox" readonly="true">
         document.getAnonymousElementByAttribute(this, "anonid", "tabbox");
       </field>
       <field name="mPanelContainer" readonly="true">
         document.getAnonymousElementByAttribute(this, "anonid", "panelcontainer");
       </field>
       <field name="mCurrentTab">
         null
       </field>
@@ -1417,16 +1417,22 @@
               }
             }
 
             updateUserContextUIIndicator();
             gIdentityHandler.updateSharingIndicator();
 
             this.tabContainer._setPositionalAttributes();
 
+            // Enable touch events to start a native dragging
+            // session to allow the user to easily drag the selected tab.
+            // This is currently only supported on Windows.
+            oldTab.removeAttribute("touchdownstartsdrag");
+            this.mCurrentTab.setAttribute("touchdownstartsdrag", "true");
+
             if (!gMultiProcessBrowser) {
               document.commandDispatcher.unlock();
 
               let event = new CustomEvent("TabSwitchDone", {
                 bubbles: true,
                 cancelable: true
               });
               this.dispatchEvent(event);
@@ -3926,20 +3932,20 @@
 
       <property name="selectedTab">
         <getter>
           return this.mCurrentTab;
         </getter>
         <setter>
           <![CDATA[
           if (gNavToolbox.collapsed && !this._allowTabChange) {
-            return this.mTabBox.selectedTab;
+            return this.tabbox.selectedTab;
           }
           // Update the tab
-          this.mTabBox.selectedTab = val;
+          this.tabbox.selectedTab = val;
           return val;
           ]]>
         </setter>
       </property>
 
       <property name="selectedBrowser"
                 onget="return this.mCurrentBrowser;"
                 readonly="true"/>
@@ -4616,17 +4622,17 @@
 
               // Switch to the tab we've decided to make visible.
               if (this.visibleTab !== showTab) {
                 this.tabbrowser._adjustFocusBeforeTabSwitch(this.visibleTab, showTab);
                 this.visibleTab = showTab;
 
                 this.maybeVisibleTabs.add(showTab);
 
-                let tabs = this.tabbrowser.mTabBox.tabs;
+                let tabs = this.tabbrowser.tabbox.tabs;
                 let tabPanel = this.tabbrowser.mPanelContainer;
                 let showPanel = tabs.getRelatedElement(showTab);
                 let index = Array.indexOf(tabPanel.childNodes, showPanel);
                 if (index != -1) {
                   this.log(`Switch to tab ${index} - ${this.tinfo(showTab)}`);
                   tabPanel.setAttribute("selectedIndex", index);
                   if (showTab === this.requestedTab) {
                     if (this._requestingTab) {
@@ -6420,17 +6426,17 @@
         ]]>
       </destructor>
 
       <field name="tabbrowser" readonly="true">
         document.getElementById(this.getAttribute("tabbrowser"));
       </field>
 
       <field name="tabbox" readonly="true">
-        this.tabbrowser.mTabBox;
+        this.tabbrowser.tabbox;
       </field>
 
       <field name="contextMenu" readonly="true">
         document.getElementById("tabContextMenu");
       </field>
 
       <field name="arrowScrollbox">
         document.getAnonymousElementByAttribute(this, "anonid", "arrowscrollbox");
--- a/browser/base/content/test/general/browser_bug462673.js
+++ b/browser/base/content/test/general/browser_bug462673.js
@@ -24,13 +24,13 @@ add_task(async function() {
   var newBrowser = newTab.linkedBrowser;
   win.gBrowser.removeTab(tab);
   ok(!win.closed, "Window stays open");
   if (!win.closed) {
     is(win.gBrowser.tabContainer.childElementCount, 1, "Window has one tab");
     is(win.gBrowser.browsers.length, 1, "Window has one browser");
     is(win.gBrowser.selectedTab, newTab, "Remaining tab is selected");
     is(win.gBrowser.selectedBrowser, newBrowser, "Browser for remaining tab is selected");
-    is(win.gBrowser.mTabBox.selectedPanel, newBrowser.parentNode.parentNode.parentNode.parentNode, "Panel for remaining tab is selected");
+    is(win.gBrowser.tabbox.selectedPanel, newBrowser.parentNode.parentNode.parentNode.parentNode, "Panel for remaining tab is selected");
   }
 
   await promiseWindowClosed(win);
 });
--- a/browser/base/content/test/general/browser_tabkeynavigation.js
+++ b/browser/base/content/test/general/browser_tabkeynavigation.js
@@ -52,21 +52,21 @@ add_task(async function test() {
   EventUtils.synthesizeKey("VK_PAGE_UP", { ctrlKey: true });
   is(gBrowser.selectedTab, tab2,
      "Tab2 should be activated by pressing Ctrl+PageUp on Tab3");
 
   EventUtils.synthesizeKey("VK_PAGE_UP", { ctrlKey: true });
   is(gBrowser.selectedTab, tab1,
      "Tab1 should be activated by pressing Ctrl+PageUp on Tab2");
 
-  if (gBrowser.mTabBox._handleMetaAltArrows) {
+  if (gBrowser.tabbox._handleMetaAltArrows) {
     gBrowser.selectedTab = tab1;
     browser1.focus();
 
-    let ltr = window.getComputedStyle(gBrowser.mTabBox).direction == "ltr";
+    let ltr = window.getComputedStyle(gBrowser.tabbox).direction == "ltr";
     let advanceKey = ltr ? "VK_RIGHT" : "VK_LEFT";
     let reverseKey = ltr ? "VK_LEFT" : "VK_RIGHT";
 
     is(gBrowser.selectedTab, tab1,
        "Tab1 should be activated");
     EventUtils.synthesizeKey(advanceKey, { altKey: true, metaKey: true });
     is(gBrowser.selectedTab, tab2,
        "Tab2 should be activated by pressing Ctrl+" + advanceKey + " on Tab1");
@@ -104,17 +104,17 @@ add_task(async function test() {
 
   if (navigator.platform.indexOf("Mac") == 0) {
     gBrowser.selectedTab = tab1;
     browser1.focus();
 
     // XXX Currently, Command + "{" and "}" don't work if keydown event is
     //     consumed because following keypress event isn't fired.
 
-    let ltr = window.getComputedStyle(gBrowser.mTabBox).direction == "ltr";
+    let ltr = window.getComputedStyle(gBrowser.tabbox).direction == "ltr";
     let advanceKey = ltr ? "}" : "{";
     let reverseKey = ltr ? "{" : "}";
 
     is(gBrowser.selectedTab, tab1,
        "Tab1 should be activated");
 
     EventUtils.synthesizeKey(advanceKey, { metaKey: true });
     is(gBrowser.selectedTab, tab2,
--- a/browser/components/customizableui/CustomizeMode.jsm
+++ b/browser/components/customizableui/CustomizeMode.jsm
@@ -891,21 +891,19 @@ CustomizeMode.prototype = {
 
     if (aNode.hasAttribute("flex")) {
       wrapper.setAttribute("flex", aNode.getAttribute("flex"));
     }
 
     let removable = aPlace == "palette" || CustomizableUI.isWidgetRemovable(aNode);
     wrapper.setAttribute("removable", removable);
 
-    if (AppConstants.platform == "win") {
-      // Allow touch events to initiate dragging in customize mode.
-      // This is only supported on Windows for now.
-      wrapper.setAttribute("touchdownstartsdrag", "true");
-    }
+    // Allow touch events to initiate dragging in customize mode.
+    // This is only supported on Windows for now.
+    wrapper.setAttribute("touchdownstartsdrag", "true");
 
     let contextMenuAttrName = "";
     if (aNode.getAttribute("context")) {
       contextMenuAttrName = "context";
     } else if (aNode.getAttribute("contextmenu")) {
       contextMenuAttrName = "contextmenu";
     }
     let currentContextMenu = aNode.getAttribute(contextMenuAttrName);
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -3274,17 +3274,17 @@ var SessionStoreInternal = {
     let tabsData = winData.tabs = [];
 
     // update the internal state data for this window
     for (let tab of tabs) {
       let tabData = TabState.collect(tab);
       tabMap.set(tab, tabData);
       tabsData.push(tabData);
     }
-    winData.selected = tabbrowser.mTabBox.selectedIndex + 1;
+    winData.selected = tabbrowser.tabbox.selectedIndex + 1;
 
     this._updateWindowFeatures(aWindow);
 
     // Make sure we keep __SS_lastSessionWindowID around for cases like entering
     // or leaving PB mode.
     if (aWindow.__SS_lastSessionWindowID)
       this._windows[aWindow.__SSi].__lastSessionWindowID =
         aWindow.__SS_lastSessionWindowID;
--- a/browser/components/uitour/UITour.jsm
+++ b/browser/components/uitour/UITour.jsm
@@ -5,42 +5,44 @@
 "use strict";
 
 this.EXPORTED_SYMBOLS = ["UITour"];
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/AppConstants.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/TelemetryController.jsm");
 Cu.import("resource://gre/modules/Timer.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 Cu.importGlobalProperties(["URL"]);
 
+XPCOMUtils.defineLazyModuleGetter(this, "BrowserUITelemetry",
+  "resource:///modules/BrowserUITelemetry.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
+  "resource:///modules/CustomizableUI.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts",
+  "resource://gre/modules/FxAccounts.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager",
   "resource://gre/modules/LightweightThemeManager.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "ResetProfile",
-  "resource://gre/modules/ResetProfile.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
-  "resource:///modules/CustomizableUI.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "UITelemetry",
-  "resource://gre/modules/UITelemetry.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "BrowserUITelemetry",
-  "resource:///modules/BrowserUITelemetry.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "PageActions",
+  "resource:///modules/PageActions.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
   "resource://gre/modules/PrivateBrowsingUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ProfileAge",
   "resource://gre/modules/ProfileAge.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ReaderParent",
   "resource:///modules/ReaderParent.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "PageActions",
-  "resource:///modules/PageActions.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts",
-  "resource://gre/modules/FxAccounts.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "ResetProfile",
+  "resource://gre/modules/ResetProfile.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "UITelemetry",
+  "resource://gre/modules/UITelemetry.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
+  "resource://gre/modules/UpdateUtils.jsm");
 
 // See LOG_LEVELS in Console.jsm. Common examples: "All", "Info", "Warn", & "Error".
 const PREF_LOG_LEVEL      = "browser.uitour.loglevel";
 const PREF_SEENPAGEIDS    = "browser.uitour.seenPageIDs";
 
 const BACKGROUND_PAGE_ACTIONS_ALLOWED = new Set([
   "forceShowReaderIcon",
   "getConfiguration",
@@ -1615,27 +1617,28 @@ this.UITour = {
       default:
         log.error("setConfiguration: Unknown configuration requested: " + aConfiguration);
         break;
     }
   },
 
   getAppInfo(aMessageManager, aWindow, aCallbackID) {
     (async () => {
-      let props = ["defaultUpdateChannel", "version"];
-      let appinfo = {};
-      props.forEach(property => appinfo[property] = Services.appinfo[property]);
+      let appinfo = {version: Services.appinfo.version};
 
       // Identifier of the partner repack, as stored in preference "distribution.id"
       // and included in Firefox and other update pings. Note this is not the same as
       // Services.appinfo.distributionID (value of MOZ_DISTRIBUTION_ID is set at build time).
       let distribution =
           Services.prefs.getDefaultBranch("distribution.").getCharPref("id", "default");
       appinfo.distribution = distribution;
 
+      // Update channel, in a way that preserves 'beta' for RC beta builds:
+      appinfo.defaultUpdateChannel = UpdateUtils.getUpdateChannel(false /* no partner ID */);
+
       let isDefaultBrowser = null;
       try {
         let shell = aWindow.getShellService();
         if (shell) {
           isDefaultBrowser = shell.isDefaultBrowser(false);
         }
       } catch (e) {}
       appinfo.defaultBrowser = isDefaultBrowser;
--- a/browser/components/uitour/test/browser_UITour.js
+++ b/browser/components/uitour/test/browser_UITour.js
@@ -4,16 +4,17 @@
 "use strict";
 
 var gTestTab;
 var gContentAPI;
 var gContentWindow;
 
 Components.utils.import("resource://testing-common/TelemetryArchiveTesting.jsm", this);
 Components.utils.import("resource://gre/modules/ProfileAge.jsm", this);
+Components.utils.import("resource://gre/modules/UpdateUtils.jsm", this);
 
 
 function test() {
   UITourTest();
 }
 
 var tests = [
   function test_untrusted_host(done) {
@@ -286,21 +287,19 @@ var tests = [
     is(popup.popupBoxObject.anchorNode, document.getElementById("searchbar"), "Popup should be anchored to the searchbar");
     is(title.textContent, "search title", "Popup should have correct title");
     is(desc.textContent, "search text", "Popup should have correct description text");
 
     await SpecialPowers.popPrefEnv();
   }),
   function test_getConfigurationVersion(done) {
     function callback(result) {
-      let props = ["defaultUpdateChannel", "version"];
-      for (let property of props) {
-        ok(typeof(result[property]) !== "undefined", "Check " + property + " isn't undefined.");
-        is(result[property], Services.appinfo[property], "Should have the same " + property + " property.");
-      }
+      ok(typeof result.version !== "undefined", "Check version isn't undefined.");
+      is(result.version, Services.appinfo.version, "Should have the same version property.");
+      is(result.defaultUpdateChannel, UpdateUtils.getUpdateChannel(false), "Should have the correct update channel.");
       done();
     }
 
     gContentAPI.getConfiguration("appinfo", callback);
   },
   function test_getConfigurationDistribution(done) {
     gContentAPI.getConfiguration("appinfo", (result) => {
       ok(typeof(result.distribution) !== "undefined", "Check distribution isn't undefined.");
--- a/browser/extensions/formautofill/skin/shared/autocomplete-item.css
+++ b/browser/extensions/formautofill/skin/shared/autocomplete-item.css
@@ -150,16 +150,17 @@ xul|richlistitem[originaltype="autofill-
 
 .autofill-footer > .autofill-option-button {
   box-sizing: border-box;
   padding: 0 10px;
   min-height: 40px;
   background-color: #EDEDED;
   font-size: calc(var(--option-btn-font-size) / var(--default-font-size) * 1em);
   color: var(--option-btn-text-color);
+  text-align: center;
 }
 
 .autofill-footer[no-warning="true"] > .autofill-warning {
   display: none;
 }
 
 .autofill-insecure-item {
   box-sizing: border-box;
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -81,17 +81,17 @@
 #titlebar-buttonbox-container {
   -moz-box-align: center;
 }
 
 /* These would be margin-inline-start/end if it wasn't for the fact that OS X
  * doesn't reverse the order of the items in the titlebar in RTL mode. */
 .titlebar-placeholder[type="caption-buttons"],
 #titlebar-buttonbox {
-  margin-left: 7px;
+  margin-left: 12px;
 }
 
 /* The fullscreen button doesnt show on Yosemite(10.10) or above so dont give it a
    border there */
 @media not all and (-moz-mac-yosemite-theme) {
   .titlebar-placeholder[type="fullscreen-button"] {
     margin-right: 4px;
   }
--- a/build/mobile/remoteautomation.py
+++ b/build/mobile/remoteautomation.py
@@ -160,19 +160,19 @@ class RemoteAutomation(Automation):
                 print "Error pulling %s" % traces
             except IOError:
                 print "Error pulling %s" % traces
         else:
             print "%s not found" % traces
 
     def deleteTombstones(self):
         # delete any existing tombstone files from device
-        remoteDir = "/data/tombstones"
+        tombstones = "/data/tombstones/*"
         try:
-            self._devicemanager.shellCheckOutput(['rm', '-r', remoteDir], root=True,
+            self._devicemanager.shellCheckOutput(['rm', '-r', tombstones], root=True,
                                                  timeout=DeviceManager.short_timeout)
         except DMError:
             # This may just indicate that the tombstone directory is missing
             pass
 
     def checkForTombstones(self):
         # pull any tombstones from device and move to MOZ_UPLOAD_DIR
         remoteDir = "/data/tombstones"
--- a/build/moz.configure/old.configure
+++ b/build/moz.configure/old.configure
@@ -301,46 +301,46 @@ def old_configure_options(*options):
 @imports(_from='__builtin__', _import='compile')
 @imports(_from='__builtin__', _import='open')
 @imports('logging')
 @imports('os')
 @imports('subprocess')
 @imports('sys')
 @imports(_from='mozbuild.shellutil', _import='quote')
 @imports(_from='mozbuild.shellutil', _import='split')
+@imports(_from='mozbuild.util', _import='encode')
 def old_configure(prepare_configure, extra_old_configure_args, all_options,
                   *options):
     cmd = prepare_configure
 
     # old-configure only supports the options listed in @old_configure_options
     # so we don't need to pass it every single option we've been passed. Only
     # the ones that are not supported by python configure need to.
     cmd += [
         value.format(name)
         for name, value in zip(all_options, options)
         if value.origin != 'default'
     ]
 
-    env = os.environ
+    env = dict(os.environ)
     extra_env = {}
 
     # We also pass it the options from js/moz.configure so that it can pass
     # them down to js/src/configure. Note this list is empty when running
     # js/src/configure, in which case we don't need to pass those options
     # to old-configure since old-configure doesn't handle them anyways.
     if extra_old_configure_args:
         for arg in extra_old_configure_args:
             if arg.startswith('-'):
                 cmd.append(arg)
             else:
                 k, v = arg.split('=', 1)
                 extra_env[k] = v
 
     if extra_env:
-        env = dict(env)
         env.update(extra_env)
 
     # For debugging purpose, in case it's not what we'd expect.
     log.debug('Running %s', quote(*cmd))
     if extra_env:
         log.debug('with extra environment: %s',
                   ' '.join('%s=%s' % pair for pair in extra_env.iteritems()))
 
@@ -351,17 +351,17 @@ def old_configure(prepare_configure, ext
     logger = logging.getLogger('moz.configure')
     for handler in logger.handlers:
         if isinstance(handler, logging.FileHandler):
             handler.close()
             logger.removeHandler(handler)
 
     log_size = os.path.getsize('config.log')
 
-    ret = subprocess.call(cmd)
+    ret = subprocess.call(cmd, env=encode(env))
     if ret:
         with log.queue_debug():
             with encoded_open('config.log', 'r') as fh:
                 fh.seek(log_size)
                 for line in fh:
                     log.debug(line.rstrip())
             log.error('old-configure failed')
         sys.exit(ret)
--- a/build/moz.configure/toolchain.configure
+++ b/build/moz.configure/toolchain.configure
@@ -76,30 +76,30 @@ add_old_configure_assignment('_YASM_MAJO
 add_old_configure_assignment('_YASM_MINOR_VERSION',
                              yasm_version.minor)
 
 
 @depends(yasm, target)
 def yasm_asflags(yasm, target):
     if yasm:
         asflags = {
-            ('OSX', 'x86'): '-f macho32',
-            ('OSX', 'x86_64'): '-f macho64',
-            ('WINNT', 'x86'): '-f win32',
-            ('WINNT', 'x86_64'): '-f x64',
+            ('OSX', 'x86'): ['-f', 'macho32'],
+            ('OSX', 'x86_64'): ['-f', 'macho64'],
+            ('WINNT', 'x86'): ['-f', 'win32'],
+            ('WINNT', 'x86_64'): ['-f', 'x64'],
         }.get((target.os, target.cpu), None)
         if asflags is None:
             # We're assuming every x86 platform we support that's
             # not Windows or Mac is ELF.
             if target.cpu == 'x86':
-                asflags = '-f elf32'
+                asflags = ['-f', 'elf32']
             elif target.cpu == 'x86_64':
-                asflags = '-f elf64'
+                asflags = ['-f', 'elf64']
         if asflags:
-            asflags += ' -rnasm -pnasm'
+            asflags += ['-rnasm', '-pnasm']
         return asflags
 
 
 set_config('YASM_ASFLAGS', yasm_asflags)
 
 
 @depends(yasm_asflags)
 def have_yasm(value):
@@ -1346,17 +1346,17 @@ def rust_compiler_flags(opt_level_option
     # Code here derives various compiler options given other configure options.
     # The options defined here effectively override defaults specified in
     # Cargo.toml files.
 
     opt_level = None
     debug_assertions = None
     debug_info = None
 
-    if opt_level_option.origin != 'default':
+    if opt_level_option:
         opt_level = opt_level_option[0]
     else:
         opt_level = '1' if optimize else '0'
 
     # opt-level=0 implies -C debug-assertions, which may not be desired
     # unless Rust debugging is enabled.
     if opt_level == '0' and not debug_rust:
         debug_assertions = False
--- a/client.mk
+++ b/client.mk
@@ -27,70 +27,41 @@ endif
 
 
 CWD := $(CURDIR)
 
 ifeq "$(CWD)" "/"
 CWD   := /.
 endif
 
-ifndef TOPSRCDIR
-ifeq (,$(wildcard client.mk))
-TOPSRCDIR := $(patsubst %/,%,$(dir $(MAKEFILE_LIST)))
-else
-TOPSRCDIR := $(CWD)
-endif
-endif
-
 PYTHON ?= $(shell which python2.7 > /dev/null 2>&1 && echo python2.7 || echo python)
 
-CONFIG_GUESS := $(shell $(TOPSRCDIR)/build/autoconf/config.guess)
-
-####################################
-# Sanity checks
-
-# Windows checks.
-ifneq (,$(findstring mingw,$(CONFIG_GUESS)))
-
-# Set this for baseconfig.mk
-HOST_OS_ARCH=WINNT
-endif
-
 ####################################
 # Load mozconfig Options
 
 # See build pages, http://www.mozilla.org/build/ for how to set up mozconfig.
 
 define CR
 
 
 endef
 
 # As $(shell) doesn't preserve newlines, use sed to replace them with an
 # unlikely sequence (||), which is then replaced back to newlines by make
 # before evaluation. $(shell) replacing newlines with spaces, || is always
 # followed by a space (since sed doesn't remove newlines), except on the
 # last line, so replace both '|| ' and '||'.
-MOZCONFIG_CONTENT := $(subst ||,$(CR),$(subst || ,$(CR),$(shell $(TOPSRCDIR)/mach environment --format=client.mk | sed 's/$$/||/')))
-$(eval $(MOZCONFIG_CONTENT))
-
-export FOUND_MOZCONFIG
+MOZCONFIG_CONTENT := $(subst ||,$(CR),$(subst || ,$(CR),$(shell cat $(OBJDIR)/.mozconfig-client-mk | sed 's/$$/||/')))
+include $(OBJDIR)/.mozconfig-client-mk
 
 # As '||' was used as a newline separator, it means it's not occurring in
 # lines themselves. It can thus safely be used to replaces normal spaces,
 # to then replace newlines with normal spaces. This allows to get a list
 # of mozconfig output lines.
 MOZCONFIG_OUT_LINES := $(subst $(CR), ,$(subst $(NULL) $(NULL),||,$(MOZCONFIG_CONTENT)))
-# Filter-out comments from those lines.
-START_COMMENT = \#
-MOZCONFIG_OUT_FILTERED := $(filter-out $(START_COMMENT)%,$(MOZCONFIG_OUT_LINES))
-
-ifdef AUTOCLOBBER
-export AUTOCLOBBER=1
-endif
 
 ifdef MOZ_PARALLEL_BUILD
   MOZ_MAKE_FLAGS := $(filter-out -j%,$(MOZ_MAKE_FLAGS))
   MOZ_MAKE_FLAGS += -j$(MOZ_PARALLEL_BUILD)
 endif
 
 # Automatically add -jN to make flags if not defined. N defaults to number of cores.
 ifeq (,$(findstring -j,$(MOZ_MAKE_FLAGS)))
@@ -105,58 +76,44 @@ endif
 endif
 
 MOZ_MAKE = $(MAKE) $(MOZ_MAKE_FLAGS) -C $(OBJDIR)
 
 # 'configure' scripts generated by autoconf.
 CONFIGURES := $(TOPSRCDIR)/configure
 CONFIGURES += $(TOPSRCDIR)/js/src/configure
 
-# Make targets that are going to be passed to the real build system
-OBJDIR_TARGETS = install export libs clean realclean distclean upload sdk installer package package-compare stage-package source-package l10n-check automation/build
-
 #######################################################################
 # Rules
 
 # The default rule is build
 build::
 
 ifndef MACH
 $(error client.mk must be used via `mach`. Try running \
 `./mach $(firstword $(MAKECMDGOALS) $(.DEFAULT_GOAL))`)
 endif
 
-# Include baseconfig.mk for its $(MAKE) validation.
-include $(TOPSRCDIR)/config/baseconfig.mk
-
-# Define mkdir
-include $(TOPSRCDIR)/config/makefiles/makeutils.mk
-include $(TOPSRCDIR)/config/makefiles/autotargets.mk
-
-# For now, only output "export" lines and lines containing UPLOAD_EXTRA_FILES
-# from mach environment --format=client.mk output.
+# For now, only output "export" lines and lines containing UPLOAD_EXTRA_FILES.
 MOZCONFIG_MK_LINES := $(filter export||% UPLOAD_EXTRA_FILES% %UPLOAD_EXTRA_FILES%,$(MOZCONFIG_OUT_LINES))
-$(OBJDIR)/.mozconfig.mk: $(TOPSRCDIR)/client.mk $(FOUND_MOZCONFIG) $(call mkdir_deps,$(OBJDIR)) $(OBJDIR)/CLOBBER
+$(OBJDIR)/.mozconfig.mk: $(TOPSRCDIR)/client.mk $(FOUND_MOZCONFIG)
 	$(if $(MOZCONFIG_MK_LINES),( $(foreach line,$(MOZCONFIG_MK_LINES), echo '$(subst ||, ,$(line))';) )) > $@
 
 # Include that makefile so that it is created. This should not actually change
 # the environment since MOZCONFIG_CONTENT, which MOZCONFIG_OUT_LINES derives
 # from, has already been eval'ed.
 include $(OBJDIR)/.mozconfig.mk
 
 # Print out any options loaded from mozconfig.
 all build clean distclean export libs install realclean::
-ifneq (,$(strip $(MOZCONFIG_OUT_FILTERED)))
+ifneq (,$(strip $(MOZCONFIG_OUT_LINES)))
 	$(info Adding client.mk options from $(FOUND_MOZCONFIG):)
-	$(foreach line,$(MOZCONFIG_OUT_FILTERED),$(info $(NULL) $(NULL) $(NULL) $(NULL) $(subst ||, ,$(line))))
+	$(foreach line,$(MOZCONFIG_OUT_LINES),$(info $(NULL) $(NULL) $(NULL) $(NULL) $(subst ||, ,$(line))))
 endif
 
-# helper target for mobile
-build_and_deploy: build package install
-
 # In automation, manage an sccache daemon. The starting of the server
 # needs to be in a make file so sccache inherits the jobserver.
 ifdef MOZBUILD_MANAGE_SCCACHE_DAEMON
 build::
 	# Terminate any sccache server that might still be around.
 	-$(MOZBUILD_MANAGE_SCCACHE_DAEMON) --stop-server > /dev/null 2>&1
 	# Start a new server, ensuring it gets the jobserver file descriptors
 	# from make (but don't use the + prefix when make -n is used, so that
@@ -181,17 +138,16 @@ EXTRA_CONFIG_DEPS := \
 $(CONFIGURES): %: %.in $(EXTRA_CONFIG_DEPS)
 	@echo Generating $@
 	cp -f $< $@
 	chmod +x $@
 
 CONFIG_STATUS_DEPS := \
   $(wildcard $(TOPSRCDIR)/*/confvars.sh) \
   $(CONFIGURES) \
-  $(TOPSRCDIR)/CLOBBER \
   $(TOPSRCDIR)/nsprpub/configure \
   $(TOPSRCDIR)/config/milestone.txt \
   $(TOPSRCDIR)/browser/config/version.txt \
   $(TOPSRCDIR)/browser/config/version_display.txt \
   $(TOPSRCDIR)/build/virtualenv_packages.txt \
   $(TOPSRCDIR)/python/mozbuild/mozbuild/virtualenv.py \
   $(TOPSRCDIR)/testing/mozbase/packages.txt \
   $(OBJDIR)/.mozconfig.json \
@@ -209,50 +165,44 @@ CONFIGURE_ENV_ARGS += \
 #   $(TOPSRCDIR) will set @srcdir@ to "."; otherwise, it is set to the full
 #   path of $(TOPSRCDIR).
 ifeq ($(TOPSRCDIR),$(OBJDIR))
   CONFIGURE = ./configure
 else
   CONFIGURE = $(TOPSRCDIR)/configure
 endif
 
-$(OBJDIR)/CLOBBER: $(TOPSRCDIR)/CLOBBER
-	$(PYTHON) $(TOPSRCDIR)/config/pythonpath.py -I $(TOPSRCDIR)/testing/mozbase/mozfile \
-	    $(TOPSRCDIR)/python/mozbuild/mozbuild/controller/clobber.py $(TOPSRCDIR) $(OBJDIR)
-
 configure-files: $(CONFIGURES)
 
 configure-preqs = \
-  $(OBJDIR)/CLOBBER \
   configure-files \
-  $(call mkdir_deps,$(OBJDIR)) \
   save-mozconfig \
   $(OBJDIR)/.mozconfig.json \
   $(NULL)
 
 CREATE_MOZCONFIG_JSON = $(shell $(TOPSRCDIR)/mach environment --format=json -o $(OBJDIR)/.mozconfig.json)
 # Force CREATE_MOZCONFIG_JSON above to be resolved, without side effects in
 # case the result is non empty, and allowing an override on the make command
 # line not running the command (using := $(shell) still runs the shell command).
 ifneq (,$(CREATE_MOZCONFIG_JSON))
 endif
 
-$(OBJDIR)/.mozconfig.json: $(call mkdir_deps,$(OBJDIR)) ;
+$(OBJDIR)/.mozconfig.json: ;
 
 save-mozconfig: $(FOUND_MOZCONFIG)
 ifdef FOUND_MOZCONFIG
 	-cp $(FOUND_MOZCONFIG) $(OBJDIR)/.mozconfig
 endif
 
 configure:: $(configure-preqs)
 	$(call BUILDSTATUS,TIERS configure)
 	$(call BUILDSTATUS,TIER_START configure)
 	@echo cd $(OBJDIR);
 	@echo $(CONFIGURE) $(CONFIGURE_ARGS)
-	@cd $(OBJDIR) && $(BUILD_PROJECT_ARG) $(CONFIGURE_ENV_ARGS) $(CONFIGURE) $(CONFIGURE_ARGS) \
+	@cd $(OBJDIR) && $(CONFIGURE_ENV_ARGS) $(CONFIGURE) $(CONFIGURE_ARGS) \
 	  || ( echo '*** Fix above errors and then restart with\
                "$(MAKE) -f client.mk build"' && exit 1 )
 	@touch $(OBJDIR)/Makefile
 	$(call BUILDSTATUS,TIER_FINISH configure)
 
 ifneq (,$(MAKEFILE))
 $(OBJDIR)/Makefile: $(OBJDIR)/config.status
 
@@ -268,35 +218,27 @@ ifneq (,$(CONFIG_STATUS))
 endif
 
 ####################################
 # Build it
 
 build::  $(OBJDIR)/Makefile $(OBJDIR)/config.status
 	+$(MOZ_MAKE)
 
-####################################
-# Other targets
-
-# Pass these target onto the real build system
-$(OBJDIR_TARGETS):: $(OBJDIR)/Makefile $(OBJDIR)/config.status
-	+$(MOZ_MAKE) $@
-
 ifdef MOZ_AUTOMATION
 build::
-	$(MAKE) -f $(TOPSRCDIR)/client.mk automation/build
+	+$(MOZ_MAKE) automation/build
 endif
 
 ifdef MOZBUILD_MANAGE_SCCACHE_DAEMON
 build::
 	# Terminate sccache server. This prints sccache stats.
 	-$(MOZBUILD_MANAGE_SCCACHE_DAEMON) --stop-server
 endif
 
 # This makefile doesn't support parallel execution. It does pass
 # MOZ_MAKE_FLAGS to sub-make processes, so they will correctly execute
 # in parallel.
 .NOTPARALLEL:
 
 .PHONY: \
     build \
-    configure \
-    $(OBJDIR_TARGETS)
+    configure
--- a/config/config.mk
+++ b/config/config.mk
@@ -127,35 +127,16 @@ endif # _MSC_VER
 CC := $(CC_WRAPPER) $(CC)
 CXX := $(CXX_WRAPPER) $(CXX)
 MKDIR ?= mkdir
 SLEEP ?= sleep
 TOUCH ?= touch
 
 PYTHON_PATH = $(PYTHON) $(topsrcdir)/config/pythonpath.py
 
-# determine debug-related options
-_DEBUG_ASFLAGS :=
-
-ifneq (,$(MOZ_DEBUG)$(MOZ_DEBUG_SYMBOLS))
-  ifeq ($(AS),$(YASM))
-    ifeq ($(OS_ARCH)_$(GNU_CC),WINNT_)
-      _DEBUG_ASFLAGS += -g cv8
-    else
-      ifneq ($(OS_ARCH),Darwin)
-        _DEBUG_ASFLAGS += -g dwarf2
-      endif
-    endif
-  else
-    _DEBUG_ASFLAGS += $(MOZ_DEBUG_FLAGS)
-  endif
-endif
-
-ASFLAGS += $(_DEBUG_ASFLAGS)
-
 #
 # Build using PIC by default
 #
 _ENABLE_PIC=1
 
 # Don't build SIMPLE_PROGRAMS with PGO, since they don't need it anyway,
 # and we don't have the same build logic to re-link them in the second pass.
 ifdef SIMPLE_PROGRAMS
@@ -218,17 +199,17 @@ INCLUDES = \
 include $(MOZILLA_DIR)/config/static-checking-config.mk
 
 LDFLAGS		= $(COMPUTED_LDFLAGS) $(PGO_LDFLAGS) $(MK_LDFLAGS)
 
 COMPILE_CFLAGS	= $(COMPUTED_CFLAGS) $(PGO_CFLAGS) $(_DEPEND_CFLAGS) $(MK_COMPILE_DEFINES)
 COMPILE_CXXFLAGS = $(COMPUTED_CXXFLAGS) $(PGO_CFLAGS) $(_DEPEND_CFLAGS) $(MK_COMPILE_DEFINES)
 COMPILE_CMFLAGS = $(OS_COMPILE_CMFLAGS) $(MOZBUILD_CMFLAGS)
 COMPILE_CMMFLAGS = $(OS_COMPILE_CMMFLAGS) $(MOZBUILD_CMMFLAGS)
-ASFLAGS += $(MOZBUILD_ASFLAGS)
+ASFLAGS = $(COMPUTED_ASFLAGS)
 
 HOST_CFLAGS = $(COMPUTED_HOST_CFLAGS) $(_DEPEND_CFLAGS)
 HOST_CXXFLAGS = $(COMPUTED_HOST_CXXFLAGS) $(_DEPEND_CFLAGS)
 
 # We only add color flags if neither the flag to disable color
 # (e.g. "-fno-color-diagnostics" nor a flag to control color
 # (e.g. "-fcolor-diagnostics=never") is present.
 define colorize_flags
--- a/devtools/client/inspector/test/browser.ini
+++ b/devtools/client/inspector/test/browser.ini
@@ -79,16 +79,17 @@ skip-if = os == "mac" # Full keyboard na
 [browser_inspector_highlighter-cssgrid_01.js]
 [browser_inspector_highlighter-cssgrid_02.js]
 [browser_inspector_highlighter-cssshape_01.js]
 [browser_inspector_highlighter-cssshape_02.js]
 [browser_inspector_highlighter-cssshape_03.js]
 [browser_inspector_highlighter-cssshape_04.js]
 [browser_inspector_highlighter-cssshape_05.js]
 [browser_inspector_highlighter-cssshape_06.js]
+[browser_inspector_highlighter-cssshape_07.js]
 [browser_inspector_highlighter-cssshape_iframe_01.js]
 [browser_inspector_highlighter-csstransform_01.js]
 [browser_inspector_highlighter-csstransform_02.js]
 [browser_inspector_highlighter-embed.js]
 [browser_inspector_highlighter-eyedropper-clipboard.js]
 subsuite = clipboard
 skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts
 [browser_inspector_highlighter-eyedropper-csp.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/test/browser_inspector_highlighter-cssshape_07.js
@@ -0,0 +1,117 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+// Test that shapes are updated correctly for scaling on one axis in transform mode.
+
+const TEST_URL = URL_ROOT + "doc_inspector_highlighter_cssshapes.html";
+const HIGHLIGHTER_TYPE = "ShapesHighlighter";
+const SHAPE_IDS = ["#polygon-transform", "#ellipse"];
+
+add_task(function* () {
+  let inspector = yield openInspectorForURL(TEST_URL);
+  let helper = yield getHighlighterHelperFor(HIGHLIGHTER_TYPE)(inspector);
+  let {testActor} = inspector;
+
+  yield testOneDimScale(testActor, helper);
+
+  helper.finalize();
+});
+
+function* testOneDimScale(testActor, helper) {
+  for (let shape of SHAPE_IDS) {
+    info(`Displaying ${shape}`);
+    yield helper.show(shape, {mode: "cssClipPath", transformMode: true});
+    let { mouse } = helper;
+
+    let { top, left, width, height } = yield getBoundingBoxInPx(testActor, helper, shape);
+
+    // if the top or left edges are not visible, move the shape so it is.
+    if (top < 0 || left < 0) {
+      let x = left + width / 2;
+      let y = top + height / 2;
+      let dx = Math.max(0, -left);
+      let dy = Math.max(0, -top);
+      yield mouse.down(x, y, shape);
+      yield mouse.move(x + dx, y + dy, shape);
+      yield mouse.up(x + dx, y + dy, shape);
+      yield testActor.reflow();
+      left += dx;
+      top += dy;
+    }
+    let dx = width / 10;
+    let dy = height / 10;
+
+    info("Scaling from w");
+    yield mouse.down(left, top + height / 2, shape);
+    yield mouse.move(left + dx, top + height / 2, shape);
+    yield mouse.up(left + dx, top + height / 2, shape);
+    yield testActor.reflow();
+
+    let wBB = yield getBoundingBoxInPx(testActor, helper, shape);
+    is(wBB.top, top, `${shape} top not moved down after w scale`);
+    isnot(wBB.left, left, `${shape} left moved right after w scale`);
+    isnot(wBB.width, width, `${shape} width reduced after w scale`);
+    is(wBB.height, height, `${shape} height not reduced after w scale`);
+
+    info("Scaling from e");
+    yield mouse.down(wBB.left + wBB.width, wBB.top + wBB.height / 2, shape);
+    yield mouse.move(wBB.left + wBB.width - dx, wBB.top + wBB.height / 2, shape);
+    yield mouse.up(wBB.left + wBB.width - dx, wBB.top + wBB.height / 2, shape);
+    yield testActor.reflow();
+
+    let eBB = yield getBoundingBoxInPx(testActor, helper, shape);
+    is(eBB.top, wBB.top, `${shape} top not moved down after e scale`);
+    is(eBB.left, wBB.left, `${shape} left not moved right after e scale`);
+    isnot(eBB.width, wBB.width, `${shape} width reduced after e scale`);
+    is(eBB.height, wBB.height, `${shape} height not reduced after e scale`);
+
+    info("Scaling from s");
+    yield mouse.down(eBB.left + eBB.width / 2, eBB.top + eBB.height, shape);
+    yield mouse.move(eBB.left + eBB.width / 2, eBB.top + eBB.height - dy, shape);
+    yield mouse.up(eBB.left + eBB.width / 2, eBB.top + eBB.height - dy, shape);
+    yield testActor.reflow();
+
+    let sBB = yield getBoundingBoxInPx(testActor, helper, shape);
+    is(sBB.top, eBB.top, `${shape} top not moved down after w scale`);
+    is(sBB.left, eBB.left, `${shape} left not moved right after w scale`);
+    is(sBB.width, eBB.width, `${shape} width not reduced after w scale`);
+    isnot(sBB.height, eBB.height, `${shape} height reduced after w scale`);
+
+    info("Scaling from n");
+    yield mouse.down(sBB.left + sBB.width / 2, sBB.top, shape);
+    yield mouse.move(sBB.left + sBB.width / 2, sBB.top + dy, shape);
+    yield mouse.up(sBB.left + sBB.width / 2, sBB.top + dy, shape);
+    yield testActor.reflow();
+
+    let nBB = yield getBoundingBoxInPx(testActor, helper, shape);
+    isnot(nBB.top, sBB.top, `${shape} top moved down after n scale`);
+    is(nBB.left, sBB.left, `${shape} left not moved right after n scale`);
+    is(nBB.width, sBB.width, `${shape} width reduced after n scale`);
+    isnot(nBB.height, sBB.height, `${shape} height not reduced after n scale`);
+  }
+}
+
+function* getBoundingBoxInPx(testActor, helper, shape = "#polygon") {
+  let bbTop = parseFloat(yield helper.getElementAttribute("shapes-bounding-box", "y"));
+  let bbLeft = parseFloat(yield helper.getElementAttribute("shapes-bounding-box", "x"));
+  let bbWidth = parseFloat(yield helper.getElementAttribute("shapes-bounding-box",
+    "width"));
+  let bbHeight = parseFloat(yield helper.getElementAttribute("shapes-bounding-box",
+    "height"));
+
+  let quads = yield testActor.getAllAdjustedQuads(shape);
+  let { width, height } = quads.content[0].bounds;
+  let computedStyle = yield helper.highlightedNode.getComputedStyle();
+  let paddingTop = parseFloat(computedStyle["padding-top"].value);
+  let paddingLeft = parseFloat(computedStyle["padding-left"].value);
+
+  return {
+    top: paddingTop + height * bbTop / 100,
+    left: paddingLeft + width * bbLeft / 100,
+    width: width * bbWidth / 100,
+    height: height * bbHeight / 100
+  };
+}
--- a/devtools/server/actors/highlighters/shapes.js
+++ b/devtools/server/actors/highlighters/shapes.js
@@ -521,63 +521,72 @@ class ShapesHighlighter extends AutoRefr
       // 3) Scale each point by multiplying by the scaling proportion.
       // 4) Translate the shape back such that the anchor is in its original position.
 
       let { bb } = this[_dragging];
       let { minX, minY, maxX, maxY } = bb;
       let { width, height } = this.zoomAdjustedDimensions;
 
       // How much points on each axis should be translated before scaling
-      let transX = (type === "scale-se" || type === "scale-ne") ?
+      let transX = (type === "scale-se" || type === "scale-ne" || type === "scale-e") ?
       minX / 100 * width : maxX / 100 * width;
-      let transY = (type === "scale-se" || type === "scale-sw") ?
+      let transY = (type === "scale-se" || type === "scale-sw" || type === "scale-s") ?
       minY / 100 * height : maxY / 100 * height;
 
       let { percentX, percentY } = this.convertPageCoordsToPercent(x, y);
       let { percentX: percentPageX,
           percentY: percentPageY } = this.convertPageCoordsToPercent(pageX, pageY);
       // distance from original click to current mouse position, in %
-      let distanceX = (type === "scale-se" || type === "scale-ne") ?
+      let distanceX = (type === "scale-se" || type === "scale-ne" || type === "scale-e") ?
       percentPageX - percentX : percentX - percentPageX;
-      let distanceY = (type === "scale-se" || type === "scale-sw") ?
+      let distanceY = (type === "scale-se" || type === "scale-sw" || type === "scale-s") ?
       percentPageY - percentY : percentY - percentPageY;
 
       // scale = 1 + proportion of distance to bounding box width/height of shape
       let scaleX = 1 + distanceX / (maxX - minX);
       let scaleY = 1 + distanceY / (maxY - minY);
       let scale = (scaleX + scaleY) / 2;
+      let axis = "xy";
+      if (type === "scale-e" || type === "scale-w") {
+        scale = scaleX;
+        axis = "x";
+      } else if (type === "scale-n" || type === "scale-s") {
+        scale = scaleY;
+        axis = "y";
+      }
 
       if (this.shapeType === "polygon") {
-        this._scalePolygon(pageX, pageY, transX, transY, scale);
+        this._scalePolygon(pageX, pageY, transX, transY, scale, axis);
       } else if (this.shapeType === "circle") {
         this._scaleCircle(pageX, pageY, transX, transY, scale);
       } else if (this.shapeType === "ellipse") {
-        this._scaleEllipse(pageX, pageY, transX, transY, scale);
+        this._scaleEllipse(pageX, pageY, transX, transY, scale, axis);
       } else if (this.shapeType === "inset") {
         this._scaleInset(pageX, pageY, transX, transY, scale);
       }
     }
   }
 
   /**
    * Scale a polygon depending on mouse position after clicking on a corner handle.
    * @param {Number} pageX the x coordinate of the mouse
    * @param {Number} pageY the y coordinate of the mouse
    * @param {Number} transX the number of pixels to translate on the x axis before scaling
    * @param {Number} transY the number of pixels to translate on the y axis before scaling
    * @param {Number} scale the proportion to scale by
+   * @param {String} axis the axis to scale on. "x", "y", or "xy" for both.
    */
-  _scalePolygon(pageX, pageY, transX, transY, scale) {
+  _scalePolygon(pageX, pageY, transX, transY, scale, axis) {
     let { pointsInfo } = this[_dragging];
 
     let polygonDef = (this.fillRule) ? `${this.fillRule}, ` : "";
     polygonDef += pointsInfo.map(point => {
       let { unitX, unitY, valueX, valueY, ratioX, ratioY } = point;
       let [newX, newY] = scalePoint(valueX, valueY, transX * ratioX,
-                                    transY * ratioY, scale);
+                                    transY * ratioY, scale, axis);
       return `${newX}${unitX} ${newY}${unitY}`;
     }).join(", ");
     polygonDef = (this.geometryBox) ? `polygon(${polygonDef}) ${this.geometryBox}` :
                                       `polygon(${polygonDef})`;
 
     this.currentNode.style.setProperty(this.property, polygonDef, "important");
   }
 
@@ -608,23 +617,24 @@ class ShapesHighlighter extends AutoRefr
 
   /**
    * Scale an ellipse depending on mouse position after clicking on a corner handle.
    * @param {Number} pageX the x coordinate of the mouse
    * @param {Number} pageY the y coordinate of the mouse
    * @param {Number} transX the number of pixels to translate on the x axis before scaling
    * @param {Number} transY the number of pixels to translate on the y axis before scaling
    * @param {Number} scale the proportion to scale by
+   * @param {String} axis the axis to scale on. "x", "y", or "xy" for both.
    */
-  _scaleEllipse(pageX, pageY, transX, transY, scale) {
+  _scaleEllipse(pageX, pageY, transX, transY, scale, axis) {
     let { unitX, unitY, unitRX, unitRY, valueX, valueY,
           ratioX, ratioY, ratioRX, ratioRY } = this[_dragging];
 
     let [newCx, newCy] = scalePoint(valueX, valueY, transX * ratioX,
-                                    transY * ratioY, scale);
+                                    transY * ratioY, scale, axis);
     // As part of scaling, the center is translated to be tangent to the lines y=0 & x=0.
     // To get the new radii, we scale the new center back to that point and get the
     // distances to the line x=0 and y=0.
     let newRx = `${Math.abs((newCx / ratioX - transX) * ratioRX)}${unitRX}`;
     let newRy = `${Math.abs((newCy / ratioY - transY) * ratioRY)}${unitRY}`;
     newCx = `${newCx}${unitX}`;
     newCy = `${newCy}${unitY}`;
 
@@ -1077,16 +1087,20 @@ class ShapesHighlighter extends AutoRefr
       let centerY = (minY + maxY) / 2;
 
       const points = [
         { pointName: "translate", x: centerX, y: centerY, cursor: "move" },
         { pointName: "scale-se", x: maxX, y: maxY, cursor: "nwse-resize" },
         { pointName: "scale-ne", x: maxX, y: minY, cursor: "nesw-resize" },
         { pointName: "scale-sw", x: minX, y: maxY, cursor: "nesw-resize" },
         { pointName: "scale-nw", x: minX, y: minY, cursor: "nwse-resize" },
+        { pointName: "scale-n", x: centerX, y: minY, cursor: "ns-resize" },
+        { pointName: "scale-s", x: centerX, y: maxY, cursor: "ns-resize" },
+        { pointName: "scale-e", x: maxX, y: centerY, cursor: "ew-resize" },
+        { pointName: "scale-w", x: minX, y: centerY, cursor: "ew-resize" }
       ];
 
       for (let { pointName, x, y, cursor } of points) {
         if (point === pointName) {
           this._drawHoverMarker([[x, y]]);
           this.setCursor(cursor);
         }
       }
@@ -1221,24 +1235,31 @@ class ShapesHighlighter extends AutoRefr
     let { width, height } = this.zoomAdjustedDimensions;
     let zoom = getCurrentZoom(this.win);
     let clickRadiusX = BASE_MARKER_SIZE / zoom * 100 / width;
     let clickRadiusY = BASE_MARKER_SIZE / zoom * 100 / height;
 
     let centerX = (minX + maxX) / 2;
     let centerY = (minY + maxY) / 2;
 
-    const points = [
+    let points = [
       { point: "translate", x: centerX, y: centerY },
       { point: "scale-se", x: maxX, y: maxY },
       { point: "scale-ne", x: maxX, y: minY },
       { point: "scale-sw", x: minX, y: maxY },
       { point: "scale-nw", x: minX, y: minY },
     ];
 
+    if (this.shapeType === "polygon" || this.shapeType === "ellipse") {
+      points.push({ point: "scale-n", x: centerX, y: minY },
+                  { point: "scale-s", x: centerX, y: maxY },
+                  { point: "scale-e", x: maxX, y: centerY },
+                  { point: "scale-w", x: minX, y: centerY });
+    }
+
     for (let { point, x, y } of points) {
       if (pageX >= x - clickRadiusX && pageX <= x + clickRadiusX &&
           pageY >= y - clickRadiusY && pageY <= y + clickRadiusY) {
         return point;
       }
     }
 
     return "";
@@ -1874,16 +1895,20 @@ class ShapesHighlighter extends AutoRefr
     boundingBox.setAttribute("width", maxX - minX);
     boundingBox.setAttribute("height", maxY - minY);
     boundingBox.removeAttribute("hidden");
 
     let centerX = (minX + maxX) / 2;
     let centerY = (minY + maxY) / 2;
     let markerPoints = [[centerX, centerY], [minX, minY],
                         [maxX, minY], [minX, maxY], [maxX, maxY]];
+    if (this.shapeType === "polygon" || this.shapeType === "ellipse") {
+      markerPoints.push([minX, centerY], [maxX, centerY],
+                        [centerX, minY], [centerX, maxY]);
+    }
     this._drawMarkers(markerPoints, width, height, zoom);
 
     if (this.shapeType === "polygon") {
       let points = this.coordinates.map(point => point.join(",")).join(" ");
 
       let polygonEl = this.getElement("polygon");
       polygonEl.setAttribute("points", points);
       polygonEl.removeAttribute("hidden");
--- a/devtools/server/actors/utils/shapes-utils.js
+++ b/devtools/server/actors/utils/shapes-utils.js
@@ -125,21 +125,22 @@ const roundTo = (value, exp) => {
 /**
  * Scale a given x/y coordinate pair by translating, multiplying by the given factor,
  * then translating back.
  * @param {Number} x the x coordinate
  * @param {Number} y the y coordinate
  * @param {Number} transX the amount to translate the x coord by
  * @param {Number} transY the amount ot translate the y coord by
  * @param {Number} scale the scaling factor
+ * @param {String} axis the axis to scale on. "x", "y", or "xy" for both.
  * @returns {Array} of the form [newX, newY], containing the coord pair after scaling.
  */
-const scalePoint = (x, y, transX, transY, scale) => {
-  let newX = (x - transX) * scale + transX;
-  let newY = (y - transY) * scale + transY;
+const scalePoint = (x, y, transX, transY, scale, axis = "xy") => {
+  let newX = (axis === "y") ? x : (x - transX) * scale + transX;
+  let newY = (axis === "x") ? y : (y - transY) * scale + transY;
   return [newX, newY];
 };
 
 exports.getDistance = getDistance;
 exports.clickedOnEllipseEdge = clickedOnEllipseEdge;
 exports.distanceToLine = distanceToLine;
 exports.projection = projection;
 exports.clickedOnPoint = clickedOnPoint;
--- a/dom/animation/test/mozilla/file_restyles.html
+++ b/dom/animation/test/mozilla/file_restyles.html
@@ -50,37 +50,37 @@ function observeStyling(frameCount, onFr
     SpecialPowers.wrap(window)
                  .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
                  .getInterface(SpecialPowers.Ci.nsIWebNavigation)
                  .QueryInterface(SpecialPowers.Ci.nsIDocShell);
 
   docShell.recordProfileTimelineMarkers = true;
   docShell.popProfileTimelineMarkers();
 
-  return new Promise(function(resolve) {
-    return waitForAnimationFrames(frameCount, onFrame).then(function() {
+  return new Promise(resolve => {
+    return waitForAnimationFrames(frameCount, onFrame).then(() => {
       var markers = docShell.popProfileTimelineMarkers();
       docShell.recordProfileTimelineMarkers = false;
-      var stylingMarkers = markers.filter(function(marker, index) {
+      var stylingMarkers = markers.filter((marker, index) => {
         return marker.name == 'Styles' && marker.isAnimationOnly;
       });
       resolve(stylingMarkers);
     });
   });
 }
 
 function ensureElementRemoval(aElement) {
-  return new Promise(function(resolve) {
+  return new Promise(resolve => {
     aElement.remove();
     waitForAllPaintsFlushed(resolve);
   });
 }
 
 function waitForWheelEvent(aTarget) {
-  return new Promise(function(resolve, reject) {
+  return new Promise(resolve => {
     // Get the scrollable target element position in this window coordinate
     // system to send a wheel event to the element.
     var targetRect = aTarget.getBoundingClientRect();
     var centerX = targetRect.left + targetRect.width / 2;
     var centerY = targetRect.top + targetRect.height / 2;
 
     sendWheelAndPaintNoFlush(aTarget, centerX, centerY,
                              { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
@@ -101,17 +101,17 @@ function add_task_if_omta_enabled(test) 
     info(test.name + " is skipped because OMTA is disabled");
     return;
   }
   add_task(test);
 }
 
 // We need to wait for all paints before running tests to avoid contaminations
 // from styling of this document itself.
-waitForAllPaints(function() {
+waitForAllPaints(() => {
   add_task(async function restyling_for_main_thread_animations() {
     var div = addDiv(null, { style: 'animation: background-color 100s' });
     var animation = div.getAnimations()[0];
 
     await animation.ready;
     ok(!SpecialPowers.wrap(animation).isRunningOnCompositor);
 
     var markers = await observeStyling(5);
@@ -191,17 +191,17 @@ waitForAllPaints(function() {
 
     var animation = div.getAnimations()[0];
     var initialRect = div.getBoundingClientRect();
 
     await animation.finished;
 
     var mouseX = initialRect.left + initialRect.width / 2;
     var mouseY = initialRect.top + initialRect.height / 2;
-    var markers = await observeStyling(5, function() {
+    var markers = await observeStyling(5, () => {
       // We can't use synthesizeMouse here since synthesizeMouse causes
       // layout flush.
       synthesizeMouseAtPoint(mouseX++, mouseY++,
                              { type: 'mousemove' }, window);
     });
 
     is(markers.length, 0,
        'Bug 1219236: Finished transitions should never cause restyles ' +
@@ -214,17 +214,17 @@ waitForAllPaints(function() {
     var animation = div.getAnimations()[0];
 
     var initialRect = div.getBoundingClientRect();
 
     await animation.finished;
 
     var mouseX = initialRect.left + initialRect.width / 2;
     var mouseY = initialRect.top + initialRect.height / 2;
-    var markers = await observeStyling(5, function() {
+    var markers = await observeStyling(5, () => {
       // We can't use synthesizeMouse here since synthesizeMouse causes
       // layout flush.
       synthesizeMouseAtPoint(mouseX++, mouseY++,
                              { type: 'mousemove' }, window);
     });
 
     is(markers.length, 0,
        'Bug 1219236: Finished animations should never cause restyles ' +
@@ -487,17 +487,17 @@ waitForAllPaints(function() {
       { style: 'overflow-y: scroll; height: 20px;' });
     var div = addDiv(null,
       { style: 'animation: background-color 100s; position: relative; top: 100px;' });
     parentElement.appendChild(div);
     var animation = div.getAnimations()[0];
 
     await animation.ready;
 
-    var markers = await observeStyling(1, function() {
+    var markers = await observeStyling(1, () => {
       parentElement.style.height = '100px';
     });
 
     is(markers.length, 1,
        'Animations running on the main-thread which was in scrolled out ' +
        'elements should update restyling soon after the element moved in ' +
        'view by resizing');
 
@@ -707,21 +707,35 @@ waitForAllPaints(function() {
 
   add_task(async function necessary_update_should_be_invoked() {
     var div = addDiv(null, { style: 'animation: background-color 100s' });
     var animation = div.getAnimations()[0];
     await animation.ready;
     await waitForAnimationFrames(5);
     // Apply another animation style
     div.style.animation = 'background-color 110s';
-    var animation = div.getAnimations()[0];
-    var markers = await observeStyling(5);
-    is(markers.length, 5,
-       'Applying animation style with different duration '  +
-       'should cause restyles on every frame.');
+    var markers = await observeStyling(1);
+    if (isServo) {
+      // There should be two restyles.
+      // 1) Animation-only restyle for before applying the new animation style
+      // 2) Animation-only restyle for after applying the new animation style
+      is(markers.length, 2,
+         'Applying animation style with different duration '  +
+         'should restyle twice');
+    } else {
+      // There should be three restyles.
+      // 1) Animation-only restyle for before applying the new animation style
+      // 2) Restyle for applying the new animation style
+      //    Note: In gecko styling for animations is not separated.
+      // 3) Restyle triggered by updating an existing animation (specifically
+      //    the animation-duration)
+      is(markers.length, 3,
+         'Applying animation style with different duration '  +
+         'should restyles three times');
+    }
     await ensureElementRemoval(div);
   });
 
   add_task_if_omta_enabled(
     async function changing_cascading_result_for_main_thread_animation() {
       var div = addDiv(null, { style: 'background-color: blue' });
       var animation = div.animate({ opacity: [0, 1],
                                     backgroundColor: ['green', 'red'] },
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -319,16 +319,29 @@ nsDOMWindowUtils::GetDocCharsetIsForced(
 
   nsIDocument* doc = GetDocument();
   *aIsForced = doc &&
     doc->GetDocumentCharacterSetSource() >= kCharsetFromParentForced;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsDOMWindowUtils::GetPhysicalMillimeterInCSSPixels(float* aPhysicalMillimeter)
+{
+  nsPresContext* presContext = GetPresContext();
+  if (!presContext) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  *aPhysicalMillimeter = presContext->AppUnitsToFloatCSSPixels(
+    presContext->PhysicalMillimetersToAppUnits(1));
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDOMWindowUtils::GetDocumentMetadata(const nsAString& aName,
                                       nsAString& aValue)
 {
   nsIDocument* doc = GetDocument();
   if (doc) {
     RefPtr<nsAtom> name = NS_Atomize(aName);
     doc->GetHeaderData(name, aValue);
     return NS_OK;
--- a/dom/html/ImageDocument.cpp
+++ b/dom/html/ImageDocument.cpp
@@ -407,33 +407,37 @@ ImageDocument::DOMRestoreImageTo(int32_t
 {
   RestoreImageTo(aX, aY);
   return NS_OK;
 }
 
 void
 ImageDocument::ScrollImageTo(int32_t aX, int32_t aY, bool restoreImage)
 {
-  float ratio = GetRatio();
-
   if (restoreImage) {
     RestoreImage();
     FlushPendingNotifications(FlushType::Layout);
   }
 
   nsCOMPtr<nsIPresShell> shell = GetShell();
   if (!shell) {
     return;
   }
 
   nsIScrollableFrame* sf = shell->GetRootScrollFrameAsScrollable();
   if (!sf) {
     return;
   }
 
+  float ratio = GetRatio();
+  // Don't try to scroll image if the document is not visible (mVisibleWidth or
+  // mVisibleHeight is zero).
+  if (ratio <= 0.0) {
+    return;
+  }
   nsRect portRect = sf->GetScrollPortRect();
   sf->ScrollTo(nsPoint(nsPresContext::CSSPixelsToAppUnits(aX/ratio) - portRect.width/2,
                        nsPresContext::CSSPixelsToAppUnits(aY/ratio) - portRect.height/2),
                nsIScrollableFrame::INSTANT);
 }
 
 void
 ImageDocument::RestoreImage()
--- a/dom/html/test/chrome.ini
+++ b/dom/html/test/chrome.ini
@@ -1,6 +1,8 @@
 [DEFAULT]
 support-files =
   file_anchor_ping.html
+  image.png
 
 [test_anchor_ping.html]
 skip-if = os == 'android'
+[test_bug1414077.html]
new file mode 100644
--- /dev/null
+++ b/dom/html/test/test_bug1414077.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1414077
+-->
+<head>
+<meta charset="utf-8">
+<title>Test for Bug 1414077</title>
+<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+<script type="application/javascript">
+
+/** Test for Bug 1414077 **/
+
+SimpleTest.waitForExplicitFinish();
+
+SpecialPowers.pushPrefEnv({"set":[["browser.enable_automatic_image_resizing", true]]}, function() {
+  var testWin = document.querySelector("iframe");
+  testWin.height = 0;
+  testWin.width = 0;
+  testWin.src = "image.png";
+  testWin.onload = function() {
+    var testDoc = testWin.contentDocument;
+
+    // testDoc should be a image document.
+    ok(testDoc.imageIsOverflowing, "image is overflowing");
+    ok(testDoc.imageIsResized, "image is resized to fit visible area by default");
+
+    // Restore image to original size.
+    testDoc.restoreImage();
+    ok(testDoc.imageIsOverflowing, "image is overflowing");
+    ok(!testDoc.imageIsResized, "image is restored to original size");
+
+    // Resize the image to fit visible area
+    testDoc.shrinkToFit();
+    ok(testDoc.imageIsOverflowing, "image is overflowing");
+    ok(testDoc.imageIsResized, "image is resized to fit visible area");
+
+    SimpleTest.finish();
+  };
+});
+
+</script>
+</head>
+
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1414077">Mozilla Bug 1414077</a>
+<iframe></iframe>
+</body>
+</html>
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -75,16 +75,21 @@ interface nsIDOMWindowUtils : nsISupport
   /**
    * Whether the charset of the window's current document has been forced by
    * the user.
    * Cannot be accessed from unprivileged context (not content-accessible)
    */
   readonly attribute boolean docCharsetIsForced;
 
   /**
+   * Return the conversion of a physical millimeter in CSS pixels.
+   */
+  readonly attribute float physicalMillimeterInCSSPixels;
+
+  /**
    * Function to get metadata associated with the window's current document
    * @param aName the name of the metadata.  This should be all lowercase.
    * @return the value of the metadata, or the empty string if it's not set
    *
    * Will throw a DOM security error if called without chrome privileges.
    */
   AString getDocumentMetadata(in AString aName);
 
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -3339,27 +3339,16 @@ TabChild::ReinitRenderingForDeviceReset(
       return;
     }
   }
 
   // Proceed with destroying and recreating the layer manager.
   ReinitRendering();
 }
 
-void
-TabChild::CompositorUpdated(const TextureFactoryIdentifier& aNewIdentifier,
-                            uint64_t aDeviceResetSeqNo)
-{
-  RefPtr<LayerManager> lm = mPuppetWidget->GetLayerManager();
-
-  mTextureFactoryIdentifier = aNewIdentifier;
-  lm->UpdateTextureFactoryIdentifier(aNewIdentifier, aDeviceResetSeqNo);
-  FrameLayerBuilder::InvalidateAllLayers(lm);
-}
-
 NS_IMETHODIMP
 TabChild::OnShowTooltip(int32_t aXCoords, int32_t aYCoords, const char16_t *aTipText,
                         const char16_t *aTipDir)
 {
     nsString str(aTipText);
     nsString dir(aTipDir);
     SendShowTooltip(aXCoords, aYCoords, str, dir);
     return NS_OK;
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -623,18 +623,16 @@ public:
 
   void DidRequestComposite(const TimeStamp& aCompositeReqStart,
                            const TimeStamp& aCompositeReqEnd);
 
   void ClearCachedResources();
   void InvalidateLayers();
   void ReinitRendering();
   void ReinitRenderingForDeviceReset();
-  void CompositorUpdated(const TextureFactoryIdentifier& aNewIdentifier,
-                         uint64_t aDeviceResetSeqNo);
 
   static inline TabChild* GetFrom(nsIDOMWindow* aWindow)
   {
     nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(aWindow);
     nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(webNav);
     return GetFrom(docShell);
   }
 
--- a/dom/media/eme/CDMProxy.h
+++ b/dom/media/eme/CDMProxy.h
@@ -220,16 +220,22 @@ public:
   virtual  CDMCaps& Capabilites() = 0;
 
   // Main thread only.
   virtual void OnKeyStatusesChange(const nsAString& aSessionId) = 0;
 
   virtual void GetSessionIdsForKeyId(const nsTArray<uint8_t>& aKeyId,
                                      nsTArray<nsCString>& aSessionIds) = 0;
 
+  // Main thread only.
+  // Calls MediaKeys->ResolvePromiseWithKeyStatus(aPromiseId, aKeyStatus) after
+  // the CDM has processed the request.
+  virtual void GetStatusForPolicy(PromiseId aPromiseId,
+                                  const nsAString& aMinHdcpVersion) = 0;
+
 #ifdef DEBUG
   virtual bool IsOnOwnerThread() = 0;
 #endif
 
   virtual uint32_t GetDecryptorId() { return 0; }
 
   virtual ChromiumCDMProxy* AsChromiumCDMProxy() { return nullptr; }
 
--- a/dom/media/eme/MediaKeys.cpp
+++ b/dom/media/eme/MediaKeys.cpp
@@ -592,10 +592,56 @@ MediaKeys::GetSessionsInfo(nsString& ses
       sessionsInfo.Append(
         NS_ConvertUTF8toUTF16((nsDependentCString(keyStatus))));
       sessionsInfo.AppendLiteral(")");
     }
     sessionsInfo.AppendLiteral(")");
   }
 }
 
+already_AddRefed<Promise>
+MediaKeys::GetStatusForPolicy(const MediaKeysPolicy& aPolicy,
+                              ErrorResult& aRv)
+{
+  RefPtr<DetailedPromise> promise(MakePromise(aRv,
+    NS_LITERAL_CSTRING("MediaKeys::GetStatusForPolicy()")));
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
+  // Currently, only widevine CDM supports for this API.
+  if (!IsWidevineKeySystem(mKeySystem)) {
+    EME_LOG("MediaKeys[%p]::GetStatusForPolicy() HDCP policy check on unsupported keysystem ", this);
+    NS_WARNING("Tried to query without a CDM");
+    promise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
+                         NS_LITERAL_CSTRING("HDCP policy check on unsupported keysystem"));
+    return promise.forget();
+  }
+
+  if (!mProxy) {
+   NS_WARNING("Tried to use a MediaKeys without a CDM");
+   promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
+                        NS_LITERAL_CSTRING("Null CDM in MediaKeys.GetStatusForPolicy()"));
+   return promise.forget();
+  }
+
+  EME_LOG("GetStatusForPolicy minHdcpVersion = %s.", NS_ConvertUTF16toUTF8(aPolicy.mMinHdcpVersion).get());
+  mProxy->GetStatusForPolicy(StorePromise(promise), aPolicy.mMinHdcpVersion);
+  return promise.forget();
+}
+
+void
+MediaKeys::ResolvePromiseWithKeyStatus(PromiseId aId, MediaKeyStatus aMediaKeyStatus)
+{
+  RefPtr<DetailedPromise> promise(RetrievePromise(aId));
+  if (!promise) {
+    return;
+  }
+  RefPtr<MediaKeys> keys(this);
+  EME_LOG("MediaKeys[%p]::ResolvePromiseWithKeyStatus() resolve promise id=%d, keystatus=%" PRIu8,
+          this,
+          aId,
+          static_cast<uint8_t>(aMediaKeyStatus));
+  promise->MaybeResolve(aMediaKeyStatus);
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/eme/MediaKeys.h
+++ b/dom/media/eme/MediaKeys.h
@@ -11,29 +11,31 @@
 #include "nsISupports.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/RefPtr.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsRefPtrHashtable.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/MediaKeysBinding.h"
+#include "mozilla/dom/MediaKeyStatusMapBinding.h" // For MediaKeyStatus
 #include "mozilla/dom/MediaKeySystemAccessBinding.h"
 #include "mozIGeckoMediaPluginService.h"
 #include "mozilla/DetailedPromise.h"
 #include "mozilla/WeakPtr.h"
 
 namespace mozilla {
 
 class CDMProxy;
 
 namespace dom {
 
 class ArrayBufferViewOrArrayBuffer;
 class MediaKeySession;
+struct MediaKeysPolicy;
 class HTMLMediaElement;
 
 typedef nsRefPtrHashtable<nsStringHashKey, MediaKeySession> KeySessionHashMap;
 typedef nsRefPtrHashtable<nsUint32HashKey, dom::DetailedPromise> PromiseHashMap;
 typedef nsRefPtrHashtable<nsUint32HashKey, MediaKeySession> PendingKeySessionsHashMap;
 typedef nsDataHashtable<nsUint32HashKey, uint32_t> PendingPromiseIdTokenHashMap;
 typedef uint32_t PromiseId;
 
@@ -125,16 +127,22 @@ public:
   // Shutdown which is called from the script/dom side.
   void Terminated();
 
   // Returns true if this MediaKeys has been bound to a media element.
   bool IsBoundToMediaElement() const;
 
   void GetSessionsInfo(nsString& sessionsInfo);
 
+  // JavaScript: MediaKeys.GetStatusForPolicy()
+  already_AddRefed<Promise> GetStatusForPolicy(const MediaKeysPolicy& aPolicy,
+                                               ErrorResult& aR);
+  // Called by CDMProxy when CDM successfully GetStatusForPolicy.
+  void ResolvePromiseWithKeyStatus(PromiseId aId, dom::MediaKeyStatus aMediaKeyStatus);
+
 private:
 
   // Instantiate CDMProxy instance.
   // It could be MediaDrmCDMProxy (Widevine on Fennec) or ChromiumCDMProxy (the rest).
   already_AddRefed<CDMProxy> CreateCDMProxy(nsIEventTarget* aMainThread);
 
   // Removes promise from mPromises, and returns it.
   already_AddRefed<DetailedPromise> RetrievePromise(PromiseId aId);
--- a/dom/media/eme/mediadrm/MediaDrmCDMProxy.cpp
+++ b/dom/media/eme/mediadrm/MediaDrmCDMProxy.cpp
@@ -103,26 +103,26 @@ MediaDrmCDMProxy::CreateSession(uint32_t
 
 void
 MediaDrmCDMProxy::LoadSession(PromiseId aPromiseId,
                               dom::MediaKeySessionType aSessionType,
                               const nsAString& aSessionId)
 {
   // TODO: Implement LoadSession.
   RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
-                NS_LITERAL_CSTRING("Currently Fennec did not support LoadSession"));
+                NS_LITERAL_CSTRING("Currently Fennec does not support LoadSession"));
 }
 
 void
 MediaDrmCDMProxy::SetServerCertificate(PromiseId aPromiseId,
                                      nsTArray<uint8_t>& aCert)
 {
   // TODO: Implement SetServerCertificate.
   RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
-                NS_LITERAL_CSTRING("Currently Fennec did not support SetServerCertificate"));
+                NS_LITERAL_CSTRING("Currently Fennec does not support SetServerCertificate"));
 }
 
 void
 MediaDrmCDMProxy::UpdateSession(const nsAString& aSessionId,
                               PromiseId aPromiseId,
                               nsTArray<uint8_t>& aResponse)
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -163,17 +163,17 @@ MediaDrmCDMProxy::CloseSession(const nsA
 }
 
 void
 MediaDrmCDMProxy::RemoveSession(const nsAString& aSessionId,
                               PromiseId aPromiseId)
 {
   // TODO: Implement RemoveSession.
   RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
-                NS_LITERAL_CSTRING("Currently Fennec did not support RemoveSession"));
+                NS_LITERAL_CSTRING("Currently Fennec does not support RemoveSession"));
 }
 
 void
 MediaDrmCDMProxy::Shutdown()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mOwnerThread);
   nsCOMPtr<nsIRunnable> task(
@@ -367,16 +367,25 @@ MediaDrmCDMProxy::OnKeyStatusesChange(co
 void
 MediaDrmCDMProxy::GetSessionIdsForKeyId(const nsTArray<uint8_t>& aKeyId,
                                       nsTArray<nsCString>& aSessionIds)
 {
   CDMCaps::AutoLock caps(Capabilites());
   caps.GetSessionIdsForKeyId(aKeyId, aSessionIds);
 }
 
+void
+MediaDrmCDMProxy::GetStatusForPolicy(PromiseId aPromiseId,
+                                     const nsAString& aMinHdcpVersion)
+{
+  // TODO: Implement GetStatusForPolicy.
+  RejectPromise(aPromiseId, NS_ERROR_DOM_NOT_SUPPORTED_ERR,
+                NS_LITERAL_CSTRING("Currently Fennec does not support GetStatusForPolicy"));
+}
+
 #ifdef DEBUG
 bool
 MediaDrmCDMProxy::IsOnOwnerThread()
 {
   return NS_GetCurrentThread() == mOwnerThread;
 }
 #endif
 
--- a/dom/media/eme/mediadrm/MediaDrmCDMProxy.h
+++ b/dom/media/eme/mediadrm/MediaDrmCDMProxy.h
@@ -110,16 +110,19 @@ public:
 
   CDMCaps& Capabilites() override;
 
   void OnKeyStatusesChange(const nsAString& aSessionId) override;
 
   void GetSessionIdsForKeyId(const nsTArray<uint8_t>& aKeyId,
                              nsTArray<nsCString>& aSessionIds) override;
 
+  void GetStatusForPolicy(PromiseId aPromiseId,
+                          const nsAString& aMinHdcpVersion) override;
+
 #ifdef DEBUG
   bool IsOnOwnerThread() override;
 #endif
 
   const nsString& GetMediaDrmStubId() const;
 
 private:
   virtual ~MediaDrmCDMProxy();
--- a/dom/media/gmp/ChromiumCDMCallback.h
+++ b/dom/media/gmp/ChromiumCDMCallback.h
@@ -17,16 +17,19 @@ public:
   virtual ~ChromiumCDMCallback() {}
 
   virtual void SetSessionId(uint32_t aPromiseId,
                             const nsCString& aSessionId) = 0;
 
   virtual void ResolveLoadSessionPromise(uint32_t aPromiseId,
                                          bool aSuccessful) = 0;
 
+  virtual void ResolvePromiseWithKeyStatus(uint32_t aPromiseId,
+                                           uint32_t aKeyStatus) = 0;
+
   virtual void ResolvePromise(uint32_t aPromiseId) = 0;
 
   virtual void RejectPromise(uint32_t aPromiseId,
                              nsresult aError,
                              const nsCString& aErrorMessage) = 0;
 
   virtual void SessionMessage(const nsACString& aSessionId,
                               uint32_t aMessageType,
--- a/dom/media/gmp/ChromiumCDMCallbackProxy.cpp
+++ b/dom/media/gmp/ChromiumCDMCallbackProxy.cpp
@@ -112,16 +112,26 @@ ToDOMMediaKeyStatus(uint32_t aStatus)
     case cdm::kReleased:
       return dom::MediaKeyStatus::Released;
   }
   MOZ_ASSERT_UNREACHABLE("Invalid cdm::KeyStatus enum value.");
   return dom::MediaKeyStatus::Internal_error;
 }
 
 void
+ChromiumCDMCallbackProxy::ResolvePromiseWithKeyStatus(uint32_t aPromiseId,
+                                                      uint32_t aKeyStatus)
+{
+  DispatchToMainThread("ChromiumCDMProxy::OnResolvePromiseWithKeyStatus",
+                       &ChromiumCDMProxy::OnResolvePromiseWithKeyStatus,
+                       aPromiseId,
+                       ToDOMMediaKeyStatus(aKeyStatus));
+}
+
+void
 ChromiumCDMCallbackProxy::SessionKeysChange(const nsCString& aSessionId,
                                             nsTArray<mozilla::gmp::CDMKeyInformation> && aKeysInfo)
 {
   bool keyStatusesChange = false;
   {
     CDMCaps::AutoLock caps(mProxy->Capabilites());
     for (const auto& keyInfo : aKeysInfo) {
       keyStatusesChange |=
--- a/dom/media/gmp/ChromiumCDMCallbackProxy.h
+++ b/dom/media/gmp/ChromiumCDMCallbackProxy.h
@@ -22,16 +22,19 @@ public:
   }
 
   void SetSessionId(uint32_t aPromiseId,
                     const nsCString& aSessionId) override;
 
   void ResolveLoadSessionPromise(uint32_t aPromiseId,
                                  bool aSuccessful) override;
 
+  void ResolvePromiseWithKeyStatus(uint32_t aPromiseId,
+                                   uint32_t aKeyStatus) override;
+
   void ResolvePromise(uint32_t aPromiseId) override;
 
   void RejectPromise(uint32_t aPromiseId,
                      nsresult aError,
                      const nsCString& aErrorMessage) override;
 
   void SessionMessage(const nsACString& aSessionId,
                       uint32_t aMessageType,
--- a/dom/media/gmp/ChromiumCDMChild.cpp
+++ b/dom/media/gmp/ChromiumCDMChild.cpp
@@ -221,17 +221,23 @@ ChromiumCDMChild::CallOnMessageLoopThrea
     mPlugin->GMPMessageLoop()->PostTask(t.forget());
   }
 }
 
 // cdm::Host_9 interface
 void
 ChromiumCDMChild::OnResolveKeyStatusPromise(uint32_t aPromiseId,
                                             cdm::KeyStatus aKeyStatus) {
-  //TODO: The callback of GetStatusForPolicy, will implement it in Bug 1404230.
+  GMP_LOG("ChromiumCDMChild::OnResolveKeyStatusPromise(pid=%" PRIu32 "keystatus=%d)",
+          aPromiseId,
+          aKeyStatus);
+  CallOnMessageLoopThread("gmp::ChromiumCDMChild::OnResolveKeyStatusPromise",
+                          &ChromiumCDMChild::SendOnResolvePromiseWithKeyStatus,
+                          aPromiseId,
+                          static_cast<uint32_t>(aKeyStatus));
 }
 
 bool
 ChromiumCDMChild::OnResolveNewSessionPromiseInternal(uint32_t aPromiseId,
                                                      const nsCString& aSessionId)
 {
   MOZ_ASSERT(IsOnMessageLoopThread());
   if (mLoadSessionPromiseIds.Contains(aPromiseId)) {
@@ -619,16 +625,74 @@ ChromiumCDMChild::RecvRemoveSession(cons
           aPromiseId,
           aSessionId.get());
   if (mCDM) {
     mCDM->RemoveSession(aPromiseId, aSessionId.get(), aSessionId.Length());
   }
   return IPC_OK();
 }
 
+// See https://cs.chromium.org/chromium/src/media/blink/webcontentdecryptionmodule_impl.cc?rcl=9d4e17194fbae2839d269e0b625520eac09efa9b&l=40
+static cdm::HdcpVersion
+ToCDMHdcpVersion(const nsCString& aMinHdcpVersion)
+{
+  // String compare with ignoring case.
+  if (aMinHdcpVersion.IsEmpty()) {
+    return cdm::HdcpVersion::kHdcpVersionNone;
+  }
+  if (aMinHdcpVersion.EqualsIgnoreCase("hdcp-1.0")) {
+    return cdm::HdcpVersion::kHdcpVersion1_0;
+  }
+  if (aMinHdcpVersion.EqualsIgnoreCase("hdcp-1.1")) {
+    return cdm::HdcpVersion::kHdcpVersion1_1;
+  }
+  if (aMinHdcpVersion.EqualsIgnoreCase("hdcp-1.2")) {
+    return cdm::HdcpVersion::kHdcpVersion1_2;
+  }
+  if (aMinHdcpVersion.EqualsIgnoreCase("hdcp-1.3")) {
+    return cdm::HdcpVersion::kHdcpVersion1_3;
+  }
+  if (aMinHdcpVersion.EqualsIgnoreCase("hdcp-1.4")) {
+    return cdm::HdcpVersion::kHdcpVersion1_4;
+  }
+  if (aMinHdcpVersion.EqualsIgnoreCase("hdcp-2.0")) {
+    return cdm::HdcpVersion::kHdcpVersion2_0;
+  }
+  if (aMinHdcpVersion.EqualsIgnoreCase("hdcp-2.1")) {
+    return cdm::HdcpVersion::kHdcpVersion2_1;
+  }
+  if (aMinHdcpVersion.EqualsIgnoreCase("hdcp-2.2")) {
+    return cdm::HdcpVersion::kHdcpVersion2_2;
+  }
+
+  // Invalid hdcp version string.
+  return cdm::HdcpVersion::kHdcpVersionNone;
+}
+
+mozilla::ipc::IPCResult
+ChromiumCDMChild::RecvGetStatusForPolicy(const uint32_t& aPromiseId,
+                                         const nsCString& aMinHdcpVersion)
+{
+  MOZ_ASSERT(IsOnMessageLoopThread());
+  GMP_LOG("ChromiumCDMChild::RecvGetStatusForPolicy(pid=%" PRIu32 ", MinHdcpVersion=%s)",
+          aPromiseId,
+          aMinHdcpVersion.get());
+  if (mCDM) {
+    cdm::Policy policy;
+    // We didn't check the return value of ToCDMHdcpVersion.
+    // Let CDM to handle the cdm::HdcpVersion::kHdcpVersionNone case.
+    // ChromiumCDM8BackwardsCompat::GetStatusForPolicy will reject the promise
+    // since this API is only supported by CDM version 9.
+    // CDM will callback by OnResolveKeyStatusPromise when it successfully executes.
+    policy.min_hdcp_version = ToCDMHdcpVersion(aMinHdcpVersion);
+    mCDM->GetStatusForPolicy(aPromiseId, policy);
+  }
+  return IPC_OK();
+}
+
 static void
 InitInputBuffer(const CDMInputBuffer& aBuffer,
                 nsTArray<cdm::SubsampleEntry>& aSubSamples,
                 cdm::InputBuffer& aInputBuffer)
 {
   aInputBuffer.data = aBuffer.mData().get<uint8_t>();
   aInputBuffer.data_size = aBuffer.mData().Size<uint8_t>();
 
--- a/dom/media/gmp/ChromiumCDMChild.h
+++ b/dom/media/gmp/ChromiumCDMChild.h
@@ -123,16 +123,18 @@ protected:
                                  const nsCString& aSessionId) override;
   ipc::IPCResult RecvUpdateSession(const uint32_t& aPromiseId,
                                    const nsCString& aSessionId,
                                    nsTArray<uint8_t>&& aResponse) override;
   ipc::IPCResult RecvCloseSession(const uint32_t& aPromiseId,
                                   const nsCString& aSessionId) override;
   ipc::IPCResult RecvRemoveSession(const uint32_t& aPromiseId,
                                    const nsCString& aSessionId) override;
+  ipc::IPCResult RecvGetStatusForPolicy(const uint32_t& aPromiseId,
+                                        const nsCString& aMinHdcpVersion) override;
   ipc::IPCResult RecvDecrypt(const uint32_t& aId,
                              const CDMInputBuffer& aBuffer) override;
   ipc::IPCResult RecvInitializeVideoDecoder(
     const CDMVideoDecoderConfig& aConfig) override;
   ipc::IPCResult RecvDeinitializeVideoDecoder() override;
   ipc::IPCResult RecvResetVideoDecoder() override;
   ipc::IPCResult RecvDecryptAndDecodeFrame(
     const CDMInputBuffer& aBuffer) override;
--- a/dom/media/gmp/ChromiumCDMParent.cpp
+++ b/dom/media/gmp/ChromiumCDMParent.cpp
@@ -181,16 +181,35 @@ ChromiumCDMParent::RemoveSession(const n
   if (!SendRemoveSession(aPromiseId, aSessionId)) {
     RejectPromise(
       aPromiseId,
       NS_ERROR_DOM_INVALID_STATE_ERR,
       NS_LITERAL_CSTRING("Failed to send removeSession to CDM process"));
   }
 }
 
+void
+ChromiumCDMParent::GetStatusForPolicy(uint32_t aPromiseId,
+                                      const nsCString& aMinHdcpVersion)
+{
+  GMP_LOG("ChromiumCDMParent::GetStatusForPolicy(this=%p)", this);
+  if (mIsShutdown) {
+    RejectPromise(aPromiseId,
+                  NS_ERROR_DOM_INVALID_STATE_ERR,
+                  NS_LITERAL_CSTRING("CDM is shutdown."));
+    return;
+  }
+  if (!SendGetStatusForPolicy(aPromiseId, aMinHdcpVersion)) {
+    RejectPromise(
+      aPromiseId,
+      NS_ERROR_DOM_INVALID_STATE_ERR,
+      NS_LITERAL_CSTRING("Failed to send getStatusForPolicy to CDM process"));
+  }
+}
+
 bool
 ChromiumCDMParent::InitCDMInputBuffer(gmp::CDMInputBuffer& aBuffer,
                                       MediaRawData* aSample)
 {
   const CryptoSample& crypto = aSample->mCrypto;
   if (crypto.mEncryptedSizes.Length() != crypto.mPlainSizes.Length()) {
     GMP_LOG("InitCDMInputBuffer clear/cipher subsamples don't match");
     return false;
@@ -270,16 +289,34 @@ ChromiumCDMParent::Recv__delete__()
   if (mContentParent) {
     mContentParent->ChromiumCDMDestroyed(this);
     mContentParent = nullptr;
   }
   return IPC_OK();
 }
 
 ipc::IPCResult
+ChromiumCDMParent::RecvOnResolvePromiseWithKeyStatus(const uint32_t& aPromiseId,
+                                                     const uint32_t& aKeyStatus)
+{
+  GMP_LOG("ChromiumCDMParent::RecvOnResolvePromiseWithKeyStatus(this=%p, pid=%u, "
+          "keystatus=%u)",
+          this,
+          aPromiseId,
+          aKeyStatus);
+  if (!mCDMCallback || mIsShutdown) {
+    return IPC_OK();
+  }
+
+  mCDMCallback->ResolvePromiseWithKeyStatus(aPromiseId, aKeyStatus);
+
+  return IPC_OK();
+}
+
+ipc::IPCResult
 ChromiumCDMParent::RecvOnResolveNewSessionPromise(const uint32_t& aPromiseId,
                                                   const nsCString& aSessionId)
 {
   GMP_LOG("ChromiumCDMParent::RecvOnResolveNewSessionPromise(this=%p, pid=%u, "
           "sid=%s)",
           this,
           aPromiseId,
           aSessionId.get());
--- a/dom/media/gmp/ChromiumCDMParent.h
+++ b/dom/media/gmp/ChromiumCDMParent.h
@@ -61,16 +61,19 @@ public:
   void UpdateSession(const nsCString& aSessionId,
                      uint32_t aPromiseId,
                      const nsTArray<uint8_t>& aResponse);
 
   void CloseSession(const nsCString& aSessionId, uint32_t aPromiseId);
 
   void RemoveSession(const nsCString& aSessionId, uint32_t aPromiseId);
 
+  void GetStatusForPolicy(uint32_t aPromiseId,
+                          const nsCString& aMinHdcpVersion);
+
   RefPtr<DecryptPromise> Decrypt(MediaRawData* aSample);
 
   // TODO: Add functions for clients to send data to CDM, and
   // a Close() function.
   RefPtr<MediaDataDecoder::InitPromise> InitializeVideoDecoder(
     const gmp::CDMVideoDecoderConfig& aConfig,
     const VideoInfo& aInfo,
     RefPtr<layers::ImageContainer> aImageContainer);
@@ -85,16 +88,19 @@ public:
   RefPtr<ShutdownPromise> ShutdownVideoDecoder();
 
   void Shutdown();
 
 protected:
   ~ChromiumCDMParent() {}
 
   ipc::IPCResult Recv__delete__() override;
+  ipc::IPCResult RecvOnResolvePromiseWithKeyStatus(
+    const uint32_t& aPromiseId,
+    const uint32_t& aKeyStatus) override;
   ipc::IPCResult RecvOnResolveNewSessionPromise(
     const uint32_t& aPromiseId,
     const nsCString& aSessionId) override;
   ipc::IPCResult RecvResolveLoadSessionPromise(
     const uint32_t& aPromiseId,
     const bool& aSuccessful) override;
   ipc::IPCResult RecvOnResolvePromise(const uint32_t& aPromiseId) override;
   ipc::IPCResult RecvOnRejectPromise(const uint32_t& aPromiseId,
--- a/dom/media/gmp/ChromiumCDMProxy.cpp
+++ b/dom/media/gmp/ChromiumCDMProxy.cpp
@@ -461,16 +461,27 @@ ChromiumCDMProxy::OnResolveLoadSessionPr
   MOZ_ASSERT(NS_IsMainThread());
   if (mKeys.IsNull()) {
     return;
   }
   mKeys->OnSessionLoaded(aPromiseId, aSuccess);
 }
 
 void
+ChromiumCDMProxy::OnResolvePromiseWithKeyStatus(uint32_t aPromiseId,
+                                                dom::MediaKeyStatus aKeyStatus)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  if (mKeys.IsNull()) {
+    return;
+  }
+  mKeys->ResolvePromiseWithKeyStatus(aPromiseId, aKeyStatus);
+}
+
+void
 ChromiumCDMProxy::OnSessionMessage(const nsAString& aSessionId,
                                    dom::MediaKeyMessageType aMessageType,
                                    const nsTArray<uint8_t>& aMessage)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mKeys.IsNull()) {
     return;
   }
@@ -594,16 +605,41 @@ void
 ChromiumCDMProxy::GetSessionIdsForKeyId(const nsTArray<uint8_t>& aKeyId,
                                         nsTArray<nsCString>& aSessionIds)
 {
   CDMCaps::AutoLock caps(Capabilites());
   caps.GetSessionIdsForKeyId(aKeyId, aSessionIds);
 }
 
 void
+ChromiumCDMProxy::GetStatusForPolicy(PromiseId aPromiseId,
+                                     const nsAString& aMinHdcpVersion)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  EME_LOG("ChromiumCDMProxy::GetStatusForPolicy(pid=%u) minHdcpVersion=%s",
+          aPromiseId,
+          NS_ConvertUTF16toUTF8(aMinHdcpVersion).get());
+
+  RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent();
+  if (!cdm) {
+    RejectPromise(aPromiseId,
+                  NS_ERROR_DOM_INVALID_STATE_ERR,
+                  NS_LITERAL_CSTRING("Null CDM in GetStatusForPolicy"));
+    return;
+  }
+
+  mGMPThread->Dispatch(NewRunnableMethod<uint32_t, nsCString>(
+    "gmp::ChromiumCDMParent::GetStatusForPolicy",
+    cdm,
+    &gmp::ChromiumCDMParent::GetStatusForPolicy,
+    aPromiseId,
+    NS_ConvertUTF16toUTF8(aMinHdcpVersion)));
+}
+
+void
 ChromiumCDMProxy::Terminated()
 {
   if (!mKeys.IsNull()) {
     mKeys->Terminated();
   }
 }
 
 already_AddRefed<gmp::ChromiumCDMParent>
--- a/dom/media/gmp/ChromiumCDMProxy.h
+++ b/dom/media/gmp/ChromiumCDMProxy.h
@@ -100,26 +100,32 @@ public:
 
   CDMCaps& Capabilites() override;
 
   void OnKeyStatusesChange(const nsAString& aSessionId) override;
 
   void GetSessionIdsForKeyId(const nsTArray<uint8_t>& aKeyId,
                              nsTArray<nsCString>& aSessionIds) override;
 
+  void GetStatusForPolicy(PromiseId aPromiseId,
+                          const nsAString& aMinHdcpVersion) override;
+
 #ifdef DEBUG
   bool IsOnOwnerThread() override;
 #endif
 
   ChromiumCDMProxy* AsChromiumCDMProxy() override { return this; }
 
   // Threadsafe. Note this may return a reference to a shutdown
   // CDM, which will fail on all operations.
   already_AddRefed<gmp::ChromiumCDMParent> GetCDMParent();
 
+  void OnResolvePromiseWithKeyStatus(uint32_t aPromiseId,
+                                     dom::MediaKeyStatus aKeyStatus);
+
 private:
   void OnCDMCreated(uint32_t aPromiseId);
 
   ~ChromiumCDMProxy();
 
   GMPCrashHelper* mCrashHelper;
 
   Mutex mCDMMutex;
--- a/dom/media/gmp/PChromiumCDM.ipdl
+++ b/dom/media/gmp/PChromiumCDM.ipdl
@@ -53,19 +53,26 @@ child:
   async Drain();
 
   async Destroy();
 
   async GiveBuffer(Shmem aShmem);
 
   async PurgeShmems();
 
+  // cdm::ContentDecryptionModule9
+  async GetStatusForPolicy(uint32_t aPromiseId,
+                           nsCString aMinHdcpVersion);
+
 parent:
   async __delete__();
 
+  // cdm::Host9
+  async OnResolvePromiseWithKeyStatus(uint32_t aPromiseId, uint32_t aKeyStatus);
+
   // cdm::Host8
   async OnResolveNewSessionPromise(uint32_t aPromiseId, nsCString aSessionId);
 
   async OnResolvePromise(uint32_t aPromiseId);
 
   async OnRejectPromise(uint32_t aPromiseId,
                         uint32_t aError,
                         uint32_t aSystemCode,
--- a/dom/media/gtest/TestCDMStorage.cpp
+++ b/dom/media/gtest/TestCDMStorage.cpp
@@ -1067,16 +1067,19 @@ private:
     }
 
     void SetSessionId(uint32_t aPromiseId,
                       const nsCString& aSessionId) override { }
 
     void ResolveLoadSessionPromise(uint32_t aPromiseId,
                                    bool aSuccessful) override { }
 
+    void ResolvePromiseWithKeyStatus(uint32_t aPromiseId,
+                                     uint32_t aKeyStatus) override { }
+
     void ResolvePromise(uint32_t aPromiseId) override { }
 
     void RejectPromise(uint32_t aPromiseId,
                        nsresult aError,
                        const nsCString& aErrorMessage) override {  }
 
     void SessionMessage(const nsACString& aSessionId,
                         uint32_t aMessageType,
--- a/dom/media/test/mochitest.ini
+++ b/dom/media/test/mochitest.ini
@@ -805,16 +805,19 @@ tags=msg capturestream
 skip-if = toolkit == 'android' # bug 1149374
 scheme=https
 [test_eme_unsetMediaKeys_then_capture.html]
 skip-if = toolkit == 'android' # bug 1149374
 scheme=https
 [test_eme_waitingforkey.html]
 skip-if = toolkit == 'android' # bug 1149374
 scheme=https
+[test_eme_getstatusforpolicy.html]
+skip-if = toolkit == 'android' # bug 1149374
+scheme=https
 [test_empty_resource.html]
 [test_error_in_video_document.html]
 [test_error_on_404.html]
 [test_fastSeek.html]
 skip-if = toolkit == 'android' # android(bug 1232305)
 [test_fastSeek-forwards.html]
 skip-if = toolkit == 'android' # bug 1337590, android(bug 1232305)
 [test_imagecapture.html]
new file mode 100644
--- /dev/null
+++ b/dom/media/test/test_eme_getstatusforpolicy.html
@@ -0,0 +1,57 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test Encrypted Media Extensions</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="text/javascript" src="manifest.js"></script>
+  <script type="text/javascript" src="eme.js"></script>
+</head>
+<body>
+<pre id="test">
+<video id="v" controls></video>
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+function createMediaKeysAndSet() {
+  return navigator.requestMediaKeySystemAccess(CLEARKEY_KEYSYSTEM, gCencMediaKeySystemConfig)
+  .then(function (access) {
+    return access.createMediaKeys();
+  })
+  .then(function (mediaKeys) {
+    document.getElementById("v").setMediaKeys(mediaKeys);
+    return mediaKeys;
+  });
+}
+
+function test() {
+  SetupEMEPref(() => {
+    createMediaKeysAndSet()
+      .then((m) => {
+        let video = document.getElementById("v");
+        is(video.mediaKeys, m, "Should have set MediaKeys on media element");
+        // getStatusForPolicy() is not suppored by ClearKey key system.
+        // The promise will always be rejected with NotSupportedError.
+        return video.mediaKeys.getStatusForPolicy({minHdcpVersion: "hdcp-2.0"});
+      })
+      .then((mediaKeyStatus) => {
+        ok(false, "Promise of getStatusForPolicy should not be resolved with clearkey key system");
+      })
+      // Promise rejected with NotSupportedError as expected.
+      .catch(reason => is("NotSupportedError", reason.name,
+                          "Promise should be rejected with NotSupportedError."))
+      .then(() => SimpleTest.finish());
+  });
+}
+
+SpecialPowers.pushPrefEnv({"set":
+    [
+      ["media.eme.hdcp-policy-check.enabled", true],
+    ]
+  }, test);
+
+</script>
+</pre>
+</body>
+</html>
\ No newline at end of file
--- a/dom/webidl/MediaKeys.webidl
+++ b/dom/webidl/MediaKeys.webidl
@@ -13,17 +13,25 @@
 // Note: "persistent-usage-record" session type is unsupported yet, as
 // it's marked as "at risk" in the spec, and Chrome doesn't support it. 
 enum MediaKeySessionType {
   "temporary",
   "persistent-license",
   // persistent-usage-record,
 };
 
+// https://github.com/WICG/media-capabilities/blob/master/eme-extension-policy-check.md
+dictionary MediaKeysPolicy {
+  DOMString minHdcpVersion = "";
+};
+
 interface MediaKeys {
   readonly attribute DOMString keySystem;
 
   [NewObject, Throws]
   MediaKeySession createSession(optional MediaKeySessionType sessionType = "temporary");
 
   [NewObject]
   Promise<void> setServerCertificate(BufferSource serverCertificate);
+
+  [Pref="media.eme.hdcp-policy-check.enabled", NewObject]
+  Promise<MediaKeyStatus> getStatusForPolicy(optional MediaKeysPolicy policy);
 };
--- a/gfx/doc/README.webrender
+++ b/gfx/doc/README.webrender
@@ -170,9 +170,9 @@ 2. Sometimes autoland tip has changed en
    has an env var you can set to do this). In theory you can get the same
    result by resolving the conflict manually but Cargo.lock files are usually not
    trivial to merge by hand. If it's just the third_party/rust dir that has conflicts
    you can delete it and run |mach vendor rust| again to repopulate it.
 
 -------------------------------------------------------------------------------
 
 The version of WebRender currently in the tree is:
-f58ed651b47f47382b63dd2bce6e4ed10ee18c78
+8a39cf24f493e894a66c2465dd310a2b2923e558
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -727,18 +727,17 @@ public:
 
   virtual void DidComposite(uint64_t aTransactionId,
                             const mozilla::TimeStamp& aCompositeStart,
                             const mozilla::TimeStamp& aCompositeEnd) {}
 
   virtual void AddDidCompositeObserver(DidCompositeObserver* aObserver) { MOZ_CRASH("GFX: LayerManager"); }
   virtual void RemoveDidCompositeObserver(DidCompositeObserver* aObserver) { MOZ_CRASH("GFX: LayerManager"); }
 
-  virtual void UpdateTextureFactoryIdentifier(const TextureFactoryIdentifier& aNewIdentifier,
-											  uint64_t aDeviceResetSeqNo) {}
+  virtual void UpdateTextureFactoryIdentifier(const TextureFactoryIdentifier& aNewIdentifier) {}
 
   virtual TextureFactoryIdentifier GetTextureFactoryIdentifier()
   {
     return TextureFactoryIdentifier();
   }
 
   virtual void SetTransactionIdAllocator(TransactionIdAllocator* aAllocator) {}
 
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -98,25 +98,20 @@ ClientLayerManager::ClientLayerManager(n
   , mTargetRotation(ROTATION_0)
   , mRepeatTransaction(false)
   , mIsRepeatTransaction(false)
   , mTransactionIncomplete(false)
   , mCompositorMightResample(false)
   , mNeedsComposite(false)
   , mQueuedAsyncPaints(false)
   , mPaintSequenceNumber(0)
-  , mDeviceResetSequenceNumber(0)
   , mForwarder(new ShadowLayerForwarder(this))
 {
   MOZ_COUNT_CTOR(ClientLayerManager);
   mMemoryPressureObserver = new MemoryPressureObserver(this);
-
-  if (XRE_IsContentProcess()) {
-    mDeviceResetSequenceNumber = CompositorBridgeChild::Get()->DeviceResetSequenceNumber();
-  }
 }
 
 
 ClientLayerManager::~ClientLayerManager()
 {
   mMemoryPressureObserver->Destroy();
   ClearCachedResources();
   // Stop receiveing AsyncParentMessage at Forwarder.
@@ -236,33 +231,16 @@ ClientLayerManager::BeginTransactionWith
   }
 
   MOZ_ASSERT(mForwarder, "ClientLayerManager::BeginTransaction without forwarder");
   if (!mForwarder->IPCOpen()) {
     gfxCriticalNote << "ClientLayerManager::BeginTransaction with IPC channel down. GPU process may have died.";
     return false;
   }
 
-  if (XRE_IsContentProcess() &&
-      mForwarder->DeviceCanReset() &&
-      mDeviceResetSequenceNumber != CompositorBridgeChild::Get()->DeviceResetSequenceNumber())
-  {
-    // The compositor has informed this process that a device reset occurred,
-    // but it has not finished informing each TabChild of its new
-    // TextureFactoryIdentifier. Until then, it's illegal to paint. Note that
-    // it is also illegal to request a new TIF synchronously, because we're
-    // not guaranteed the UI process has finished acquiring new compositors
-    // for each widget.
-    //
-    // Note that we only do this for accelerated backends, since we do not
-    // perform resets on basic compositors.
-    gfxCriticalNote << "Discarding a paint since a device reset has not yet been acknowledged.";
-    return false;
-  }
-
   mInTransaction = true;
   mTransactionStart = TimeStamp::Now();
 
 #ifdef MOZ_LAYERS_HAVE_LOG
   MOZ_LAYERS_LOG(("[----- BeginTransaction"));
   Log();
 #endif
 
@@ -686,24 +664,19 @@ void
 ClientLayerManager::WaitOnTransactionProcessed()
 {
   CompositorBridgeChild* remoteRenderer = GetCompositorBridgeChild();
   if (remoteRenderer) {
     remoteRenderer->SendWaitOnTransactionProcessed();
   }
 }
 void
-ClientLayerManager::UpdateTextureFactoryIdentifier(const TextureFactoryIdentifier& aNewIdentifier,
-                                                   uint64_t aDeviceResetSeqNo)
+ClientLayerManager::UpdateTextureFactoryIdentifier(const TextureFactoryIdentifier& aNewIdentifier)
 {
-  MOZ_ASSERT_IF(XRE_IsContentProcess(),
-                aDeviceResetSeqNo == CompositorBridgeChild::Get()->DeviceResetSequenceNumber());
-
   mForwarder->IdentifyTextureHost(aNewIdentifier);
-  mDeviceResetSequenceNumber = aDeviceResetSeqNo;
 }
 
 void
 ClientLayerManager::SendInvalidRegion(const nsIntRegion& aRegion)
 {
   if (mWidget) {
     if (CompositorBridgeChild* remoteRenderer = mWidget->GetRemoteRenderer()) {
       remoteRenderer->SendNotifyRegionInvalidated(aRegion);
--- a/gfx/layers/client/ClientLayerManager.h
+++ b/gfx/layers/client/ClientLayerManager.h
@@ -105,18 +105,17 @@ public:
   virtual already_AddRefed<ImageLayer> CreateImageLayer() override;
   virtual already_AddRefed<CanvasLayer> CreateCanvasLayer() override;
   virtual already_AddRefed<ReadbackLayer> CreateReadbackLayer() override;
   virtual already_AddRefed<ColorLayer> CreateColorLayer() override;
   virtual already_AddRefed<TextLayer> CreateTextLayer() override;
   virtual already_AddRefed<BorderLayer> CreateBorderLayer() override;
   virtual already_AddRefed<RefLayer> CreateRefLayer() override;
 
-  virtual void UpdateTextureFactoryIdentifier(const TextureFactoryIdentifier& aNewIdentifier,
-											  uint64_t aDeviceResetSeqNo) override;
+  virtual void UpdateTextureFactoryIdentifier(const TextureFactoryIdentifier& aNewIdentifier) override;
   virtual TextureFactoryIdentifier GetTextureFactoryIdentifier() override
   {
     return AsShadowForwarder()->GetTextureFactoryIdentifier();
   }
 
   virtual void FlushRendering() override;
   virtual void WaitOnTransactionProcessed() override;
   virtual void SendInvalidRegion(const nsIntRegion& aRegion) override;
@@ -352,20 +351,16 @@ private:
   bool mCompositorMightResample;
   bool mNeedsComposite;
   bool mQueuedAsyncPaints;
 
   // An incrementing sequence number for paints.
   // Incremented in BeginTransaction(), but not for repeat transactions.
   uint32_t mPaintSequenceNumber;
 
-  // A sequence number for checking whether we have not yet acknowledged
-  // a device reset.
-  uint64_t mDeviceResetSequenceNumber;
-
   APZTestData mApzTestData;
 
   RefPtr<ShadowLayerForwarder> mForwarder;
   mozilla::TimeStamp mTransactionStart;
 
   nsTArray<DidCompositeObserver*> mDidCompositeObservers;
 
   RefPtr<MemoryPressureObserver> mMemoryPressureObserver;
--- a/gfx/layers/ipc/CompositorBridgeChild.cpp
+++ b/gfx/layers/ipc/CompositorBridgeChild.cpp
@@ -82,17 +82,16 @@ Atomic<int32_t> KnowsCompositor::sSerial
 
 CompositorBridgeChild::CompositorBridgeChild(CompositorManagerChild *aManager)
   : mCompositorManager(aManager)
   , mIdNamespace(0)
   , mResourceId(0)
   , mCanSend(false)
   , mActorDestroyed(false)
   , mFwdTransactionId(0)
-  , mDeviceResetSequenceNumber(0)
   , mMessageLoop(MessageLoop::current())
   , mProcessToken(0)
   , mSectionAllocator(nullptr)
   , mPaintLock("CompositorBridgeChild.mPaintLock")
   , mOutstandingAsyncPaints(0)
   , mOutstandingAsyncEndTransaction(false)
   , mIsDelayingForAsyncPaints(false)
   , mSlowFlushCount(0)
--- a/gfx/layers/ipc/CompositorBridgeChild.h
+++ b/gfx/layers/ipc/CompositorBridgeChild.h
@@ -210,20 +210,16 @@ public:
   void WillEndTransaction();
 
   PWebRenderBridgeChild* AllocPWebRenderBridgeChild(const wr::PipelineId& aPipelineId,
                                                     const LayoutDeviceIntSize&,
                                                     TextureFactoryIdentifier*,
                                                     wr::IdNamespace*) override;
   bool DeallocPWebRenderBridgeChild(PWebRenderBridgeChild* aActor) override;
 
-  uint64_t DeviceResetSequenceNumber() const {
-    return mDeviceResetSequenceNumber;
-  }
-
   wr::MaybeExternalImageId GetNextExternalImageId() override;
 
   wr::PipelineId GetNextPipelineId();
 
   // Must only be called from the main thread. Ensures that any paints from
   // previous frames have been flushed. The main thread blocks until the
   // operation completes.
   void FlushAsyncPaints();
@@ -354,21 +350,16 @@ private:
 
   /**
    * Transaction id of ShadowLayerForwarder.
    * It is incrementaed by UpdateFwdTransactionId() in each BeginTransaction() call.
    */
   uint64_t mFwdTransactionId;
 
   /**
-   * Last sequence number recognized for a device reset.
-   */
-  uint64_t mDeviceResetSequenceNumber;
-
-  /**
    * Hold TextureClients refs until end of their usages on host side.
    * It defer calling of TextureClient recycle callback.
    */
   nsRefPtrHashtable<nsUint64HashKey, TextureClient> mTexturesWaitingRecycled;
 
   MessageLoop* mMessageLoop;
 
   AutoTArray<RefPtr<TextureClientPool>,2> mTexturePools;
--- a/gfx/layers/wr/WebRenderCommandBuilder.cpp
+++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp
@@ -76,17 +76,17 @@ WebRenderCommandBuilder::BuildWebRenderC
       CreateWebRenderCommandsFromDisplayList(aDisplayList, aDisplayListBuilder,
                                              pageRootSc, aBuilder, aResourceUpdates);
     }
 
     // Make a "root" layer data that has everything else as descendants
     mLayerScrollData.emplace_back();
     mLayerScrollData.back().InitializeRoot(mLayerScrollData.size() - 1);
     auto callback = [&aScrollData](FrameMetrics::ViewID aScrollId) -> bool {
-      return aScrollData.HasMetadataFor(aScrollId);
+      return aScrollData.HasMetadataFor(aScrollId).isSome();
     };
     if (Maybe<ScrollMetadata> rootMetadata = nsLayoutUtils::GetRootMetadata(
           aDisplayListBuilder, mManager, ContainerLayerParameters(), callback)) {
       mLayerScrollData.back().AppendScrollMetadata(aScrollData, rootMetadata.ref());
     }
     // Append the WebRenderLayerScrollData items into WebRenderScrollData
     // in reverse order, from topmost to bottommost. This is in keeping with
     // the semantics of WebRenderScrollData.
@@ -480,31 +480,30 @@ WebRenderCommandBuilder::GenerateFallbac
 
   RefPtr<WebRenderFallbackData> fallbackData = CreateOrRecycleWebRenderUserData<WebRenderFallbackData>(aItem);
 
   bool snap;
   nsRect itemBounds = aItem->GetBounds(aDisplayListBuilder, &snap);
 
   // Blob images will only draw the visible area of the blob so we don't need to clip
   // them here and can just rely on the webrender clipping.
-  bool useClipBounds = true;
   nsRect paintBounds = itemBounds;
   if (useBlobImage) {
     paintBounds = itemBounds;
-    useClipBounds = false;
   } else {
     paintBounds = aItem->GetClippedBounds(aDisplayListBuilder);
   }
 
   // nsDisplayItem::Paint() may refer the variables that come from ComputeVisibility().
-  // So we should call RecomputeVisibility() before painting. e.g.: nsDisplayBoxShadowInner
+  // So we should call ComputeVisibility() before painting. e.g.: nsDisplayBoxShadowInner
   // uses mVisibleRegion in Paint() and mVisibleRegion is computed in
   // nsDisplayBoxShadowInner::ComputeVisibility().
-  nsRegion visibleRegion(itemBounds);
-  aItem->RecomputeVisibility(aDisplayListBuilder, &visibleRegion, useClipBounds);
+  nsRegion visibleRegion(paintBounds);
+  aItem->SetVisibleRect(paintBounds, false);
+  aItem->ComputeVisibility(aDisplayListBuilder, &visibleRegion);
 
   const int32_t appUnitsPerDevPixel = aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
   LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(paintBounds, appUnitsPerDevPixel);
 
   gfx::Size scale = aSc.GetInheritedScale();
   gfx::Size oldScale = fallbackData->GetScale();
   // This scale determination should probably be done using
   // ChooseScaleAndSetTransform but for now we just fake it.
--- a/gfx/layers/wr/WebRenderLayerManager.cpp
+++ b/gfx/layers/wr/WebRenderLayerManager.cpp
@@ -525,18 +525,17 @@ WebRenderLayerManager::ClearCachedResour
 void
 WebRenderLayerManager::WrUpdated()
 {
   mWebRenderCommandBuilder.ClearCachedResources();
   DiscardLocalImages();
 }
 
 void
-WebRenderLayerManager::UpdateTextureFactoryIdentifier(const TextureFactoryIdentifier& aNewIdentifier,
-                                                      uint64_t aDeviceResetSeqNo)
+WebRenderLayerManager::UpdateTextureFactoryIdentifier(const TextureFactoryIdentifier& aNewIdentifier)
 {
   WrBridge()->IdentifyTextureHost(aNewIdentifier);
 }
 
 TextureFactoryIdentifier
 WebRenderLayerManager::GetTextureFactoryIdentifier()
 {
   return WrBridge()->GetTextureFactoryIdentifier();
--- a/gfx/layers/wr/WebRenderLayerManager.h
+++ b/gfx/layers/wr/WebRenderLayerManager.h
@@ -89,18 +89,17 @@ public:
 
   virtual void SetLayerObserverEpoch(uint64_t aLayerObserverEpoch) override;
 
   virtual void DidComposite(uint64_t aTransactionId,
                             const mozilla::TimeStamp& aCompositeStart,
                             const mozilla::TimeStamp& aCompositeEnd) override;
 
   virtual void ClearCachedResources(Layer* aSubtree = nullptr) override;
-  virtual void UpdateTextureFactoryIdentifier(const TextureFactoryIdentifier& aNewIdentifier,
-                                              uint64_t aDeviceResetSeqNo) override;
+  virtual void UpdateTextureFactoryIdentifier(const TextureFactoryIdentifier& aNewIdentifier) override;
   virtual TextureFactoryIdentifier GetTextureFactoryIdentifier() override;
 
   virtual void SetTransactionIdAllocator(TransactionIdAllocator* aAllocator) override
   { mTransactionIdAllocator = aAllocator; }
 
   virtual void AddDidCompositeObserver(DidCompositeObserver* aObserver) override;
   virtual void RemoveDidCompositeObserver(DidCompositeObserver* aObserver) override;
 
--- a/gfx/layers/wr/WebRenderScrollData.cpp
+++ b/gfx/layers/wr/WebRenderScrollData.cpp
@@ -49,21 +49,27 @@ WebRenderLayerScrollData::Initialize(Web
   mDescendantCount = aDescendantCount;
 
   MOZ_ASSERT(aItem);
   aItem->UpdateScrollData(&aOwner, this);
   for (const ActiveScrolledRoot* asr = aItem->GetActiveScrolledRoot();
        asr && asr != aStopAtAsr;
        asr = asr->mParent) {
     MOZ_ASSERT(aOwner.GetManager());
-    Maybe<ScrollMetadata> metadata = asr->mScrollableFrame->ComputeScrollMetadata(
-        nullptr, aOwner.GetManager(), aItem->ReferenceFrame(),
-        ContainerLayerParameters(), nullptr);
-    MOZ_ASSERT(metadata);
-    mScrollIds.AppendElement(aOwner.AddMetadata(metadata.ref()));
+    FrameMetrics::ViewID scrollId = nsLayoutUtils::ViewIDForASR(asr);
+    if (Maybe<size_t> index = aOwner.HasMetadataFor(scrollId)) {
+      mScrollIds.AppendElement(index.ref());
+    } else {
+      Maybe<ScrollMetadata> metadata = asr->mScrollableFrame->ComputeScrollMetadata(
+          nullptr, aOwner.GetManager(), aItem->ReferenceFrame(),
+          ContainerLayerParameters(), nullptr);
+      MOZ_ASSERT(metadata);
+      MOZ_ASSERT(metadata->GetMetrics().GetScrollId() == scrollId);
+      mScrollIds.AppendElement(aOwner.AddMetadata(metadata.ref()));
+    }
   }
 }
 
 int32_t
 WebRenderLayerScrollData::GetDescendantCount() const
 {
   MOZ_ASSERT(mDescendantCount >= 0); // check that it was set
   return mDescendantCount;
@@ -195,20 +201,21 @@ WebRenderScrollData::GetLayerData(size_t
 
 const ScrollMetadata&
 WebRenderScrollData::GetScrollMetadata(size_t aIndex) const
 {
   MOZ_ASSERT(aIndex < mScrollMetadatas.Length());
   return mScrollMetadatas[aIndex];
 }
 
-bool
+Maybe<size_t>
 WebRenderScrollData::HasMetadataFor(const FrameMetrics::ViewID& aScrollId) const
 {
-  return mScrollIdMap.find(aScrollId) != mScrollIdMap.end();
+  auto it = mScrollIdMap.find(aScrollId);
+  return (it == mScrollIdMap.end() ? Nothing() : Some(it->second));
 }
 
 void
 WebRenderScrollData::SetFocusTarget(const FocusTarget& aFocusTarget)
 {
   mFocusTarget = aFocusTarget;
 }
 
--- a/gfx/layers/wr/WebRenderScrollData.h
+++ b/gfx/layers/wr/WebRenderScrollData.h
@@ -146,17 +146,17 @@ public:
   size_t GetLayerCount() const;
 
   // Return a pointer to the scroll data at the given index. Use with caution,
   // as the pointer may be invalidated if this WebRenderScrollData is mutated.
   WebRenderLayerScrollData* GetLayerDataMutable(size_t aIndex);
   const WebRenderLayerScrollData* GetLayerData(size_t aIndex) const;
 
   const ScrollMetadata& GetScrollMetadata(size_t aIndex) const;
-  bool HasMetadataFor(const FrameMetrics::ViewID& aScrollId) const;
+  Maybe<size_t> HasMetadataFor(const FrameMetrics::ViewID& aScrollId) const;
 
   const FocusTarget& GetFocusTarget() const { return mFocusTarget; }
   void SetFocusTarget(const FocusTarget& aFocusTarget);
 
   void SetIsFirstPaint();
   bool IsFirstPaint() const;
   void SetPaintSequenceNumber(uint32_t aPaintSequenceNumber);
   uint32_t GetPaintSequenceNumber() const;
--- a/gfx/src/nsITheme.h
+++ b/gfx/src/nsITheme.h
@@ -18,16 +18,27 @@ struct nsRect;
 class gfxContext;
 class nsAttrValue;
 class nsPresContext;
 class nsDeviceContext;
 class nsIFrame;
 class nsAtom;
 class nsIWidget;
 
+namespace mozilla {
+namespace layers {
+class StackingContextHelper;
+class WebRenderLayerManager;
+}
+namespace wr {
+class DisplayListBuilder;
+class IpcResourceUpdateQueue;
+}
+}
+
 // IID for the nsITheme interface
 // {7329f760-08cb-450f-8225-dae729096dec}
  #define NS_ITHEME_IID     \
 { 0x7329f760, 0x08cb, 0x450f, \
   { 0x82, 0x25, 0xda, 0xe7, 0x29, 0x09, 0x6d, 0xec } }
 // {0ae05515-cf7a-45a8-9e02-6556de7685b1}
 #define NS_THEMERENDERER_CID \
 { 0x0ae05515, 0xcf7a, 0x45a8, \
@@ -57,16 +68,30 @@ public:
    */
   NS_IMETHOD DrawWidgetBackground(gfxContext* aContext,
                                   nsIFrame* aFrame,
                                   uint8_t aWidgetType,
                                   const nsRect& aRect,
                                   const nsRect& aDirtyRect) = 0;
 
   /**
+   * Create WebRender commands for the theme background.
+   * @return true if the theme knows how to create WebRender commands for the
+   *         given widget type, false if DrawWidgetBackground need sto be called
+   *         instead.
+   */
+  virtual bool CreateWebRenderCommandsForWidget(mozilla::wr::DisplayListBuilder& aBuilder,
+                                                mozilla::wr::IpcResourceUpdateQueue& aResources,
+                                                const mozilla::layers::StackingContextHelper& aSc,
+                                                mozilla::layers::WebRenderLayerManager* aManager,
+                                                nsIFrame* aFrame,
+                                                uint8_t aWidgetType,
+                                                const nsRect& aRect) { return false; }
+
+  /**
    * Get the computed CSS border for the widget, in pixels.
    */
   NS_IMETHOD GetWidgetBorder(nsDeviceContext* aContext, 
                              nsIFrame* aFrame,
                              uint8_t aWidgetType,
                              nsIntMargin* aResult)=0;
 
   /**
--- a/gfx/webrender/Cargo.toml
+++ b/gfx/webrender/Cargo.toml
@@ -1,11 +1,11 @@
 [package]
 name = "webrender"
-version = "0.53.1"
+version = "0.53.2"
 authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
 license = "MPL-2.0"
 repository = "https://github.com/servo/webrender"
 build = "build.rs"
 
 [features]
 default = ["freetype-lib"]
 freetype-lib = ["freetype/servo-freetype-sys"]
--- a/gfx/webrender/res/cs_text_run.glsl
+++ b/gfx/webrender/res/cs_text_run.glsl
@@ -49,11 +49,11 @@ void main(void) {
 
     gl_Position = uTransform * vec4(pos, 0.0, 1.0);
 }
 #endif
 
 #ifdef WR_FRAGMENT_SHADER
 void main(void) {
     float a = texture(sColor0, vUv).a;
-    oFragColor = vec4(vColor.rgb, vColor.a * a);
+    oFragColor = vColor * a;
 }
 #endif
--- a/gfx/webrender/res/debug_color.glsl
+++ b/gfx/webrender/res/debug_color.glsl
@@ -5,17 +5,17 @@
 #include shared,shared_other
 
 varying vec4 vColor;
 
 #ifdef WR_VERTEX_SHADER
 in vec4 aColor;
 
 void main(void) {
-    vColor = aColor;
+    vColor = vec4(aColor.rgb * aColor.a, aColor.a);
     vec4 pos = vec4(aPosition, 1.0);
     pos.xy = floor(pos.xy * uDevicePixelRatio + 0.5) / uDevicePixelRatio;
     gl_Position = uTransform * pos;
 }
 #endif
 
 #ifdef WR_FRAGMENT_SHADER
 void main(void) {
--- a/gfx/webrender/res/debug_font.glsl
+++ b/gfx/webrender/res/debug_font.glsl
@@ -18,11 +18,11 @@ void main(void) {
     pos.xy = floor(pos.xy * uDevicePixelRatio + 0.5) / uDevicePixelRatio;
     gl_Position = uTransform * pos;
 }
 #endif
 
 #ifdef WR_FRAGMENT_SHADER
 void main(void) {
     float alpha = texture(sColor0, vec3(vColorTexCoord.xy, 0.0)).r;
-    oFragColor = vec4(vColor.xyz, vColor.w * alpha);
+    oFragColor = vColor * alpha;
 }
 #endif
--- a/gfx/webrender/res/ps_blend.glsl
+++ b/gfx/webrender/res/ps_blend.glsl
@@ -3,16 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include shared,prim_shared
 
 varying vec3 vUv;
 flat varying vec4 vUvBounds;
 flat varying float vAmount;
 flat varying int vOp;
+flat varying mat4 vColorMat;
 
 #ifdef WR_VERTEX_SHADER
 void main(void) {
     CompositeInstance ci = fetch_composite_instance();
     AlphaBatchTask dest_task = fetch_alpha_batch_task(ci.render_task_index);
     AlphaBatchTask src_task = fetch_alpha_batch_task(ci.src_task_index);
 
     vec2 dest_origin = dest_task.render_target_origin -
@@ -29,118 +30,89 @@ void main(void) {
 
     vec2 uv = src_task.render_target_origin + aPosition.xy * src_task.size;
     vUv = vec3(uv / texture_size, src_task.render_target_layer_index);
     vUvBounds = vec4(st0 + 0.5, st1 - 0.5) / texture_size.xyxy;
 
     vOp = ci.user_data0;
     vAmount = float(ci.user_data1) / 65535.0;
 
+    float lumR = 0.2126;
+    float lumG = 0.7152;
+    float lumB = 0.0722;
+    float oneMinusLumR = 1.0 - lumR;
+    float oneMinusLumG = 1.0 - lumG;
+    float oneMinusLumB = 1.0 - lumB;
+    float oneMinusAmount = 1.0 - vAmount;
+
+    switch (vOp) {
+        case 2: {
+            // Grayscale
+            vColorMat = mat4(vec4(lumR + oneMinusLumR * oneMinusAmount, lumR - lumR * oneMinusAmount, lumR - lumR * oneMinusAmount, 0.0),
+                             vec4(lumG - lumG * oneMinusAmount, lumG + oneMinusLumG * oneMinusAmount, lumG - lumG * oneMinusAmount, 0.0),
+                             vec4(lumB - lumB * oneMinusAmount, lumB - lumB * oneMinusAmount, lumB + oneMinusLumB * oneMinusAmount, 0.0),
+                             vec4(0.0, 0.0, 0.0, 1.0));
+            break;
+        }
+        case 3: {
+            // HueRotate
+            float c = cos(vAmount * 0.01745329251);
+            float s = sin(vAmount * 0.01745329251);
+            vColorMat = mat4(vec4(lumR + oneMinusLumR * c - lumR * s, lumR - lumR * c + 0.143 * s, lumR - lumR * c - oneMinusLumR * s, 0.0),
+                            vec4(lumG - lumG * c - lumG * s, lumG + oneMinusLumG * c + 0.140 * s, lumG - lumG * c + lumG * s, 0.0),
+                            vec4(lumB - lumB * c + oneMinusLumB * s, lumB - lumB * c - 0.283 * s, lumB + oneMinusLumB * c + lumB * s, 0.0),
+                            vec4(0.0, 0.0, 0.0, 1.0));
+            break;
+        }
+        case 5: {
+            // Saturate
+            vColorMat = mat4(vec4(oneMinusAmount * lumR + vAmount, oneMinusAmount * lumR, oneMinusAmount * lumR, 0.0),
+                             vec4(oneMinusAmount * lumG, oneMinusAmount * lumG + vAmount, oneMinusAmount * lumG, 0.0),
+                             vec4(oneMinusAmount * lumB, oneMinusAmount * lumB, oneMinusAmount * lumB + vAmount, 0.0),
+                             vec4(0.0, 0.0, 0.0, 1.0));
+            break;
+        }
+        case 6: {
+            // Sepia
+            vColorMat = mat4(vec4(0.393 + 0.607 * oneMinusAmount, 0.349 - 0.349 * oneMinusAmount, 0.272 - 0.272 * oneMinusAmount, 0.0),
+                             vec4(0.769 - 0.769 * oneMinusAmount, 0.686 + 0.314 * oneMinusAmount, 0.534 - 0.534 * oneMinusAmount, 0.0),
+                             vec4(0.189 - 0.189 * oneMinusAmount, 0.168 - 0.168 * oneMinusAmount, 0.131 + 0.869 * oneMinusAmount, 0.0),
+                             vec4(0.0, 0.0, 0.0, 1.0));
+            break;
+        }
+    }
+
+
     gl_Position = uTransform * vec4(local_pos, ci.z, 1.0);
 }
 #endif
 
 #ifdef WR_FRAGMENT_SHADER
 /* 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/. */
 
-vec3 rgbToHsv(vec3 c) {
-    float value = max(max(c.r, c.g), c.b);
-
-    float chroma = value - min(min(c.r, c.g), c.b);
-    if (chroma == 0.0) {
-        return vec3(0.0);
-    }
-    float saturation = chroma / value;
-
-    float hue;
-    if (c.r == value)
-        hue = (c.g - c.b) / chroma;
-    else if (c.g == value)
-        hue = 2.0 + (c.b - c.r) / chroma;
-    else // if (c.b == value)
-        hue = 4.0 + (c.r - c.g) / chroma;
-
-    hue *= 1.0/6.0;
-    if (hue < 0.0)
-        hue += 1.0;
-    return vec3(hue, saturation, value);
-}
-
-vec3 hsvToRgb(vec3 c) {
-    if (c.s == 0.0) {
-        return vec3(c.z);
-    }
-
-    float hue = c.x * 6.0;
-    int sector = int(hue);
-    float residualHue = hue - float(sector);
-
-    vec3 pqt = c.z * vec3(1.0 - c.y, 1.0 - c.y * residualHue, 1.0 - c.y * (1.0 - residualHue));
-    switch (sector) {
-        case 0:
-            return vec3(c.z, pqt.z, pqt.x);
-        case 1:
-            return vec3(pqt.y, c.z, pqt.x);
-        case 2:
-            return vec3(pqt.x, c.z, pqt.z);
-        case 3:
-            return vec3(pqt.x, pqt.y, c.z);
-        case 4:
-            return vec3(pqt.z, pqt.x, c.z);
-        default:
-            return vec3(c.z, pqt.x, pqt.y);
-    }
-}
-
 vec4 Blur(float radius, vec2 direction) {
     // TODO(gw): Support blur in WR2!
     return vec4(1.0);
 }
 
 vec4 Contrast(vec4 Cs, float amount) {
     return vec4(Cs.rgb * amount - 0.5 * amount + 0.5, 1.0);
 }
 
-vec4 Grayscale(vec4 Cs, float amount) {
-    float ia = 1.0 - amount;
-    return mat4(vec4(0.2126 + 0.7874 * ia, 0.2126 - 0.2126 * ia, 0.2126 - 0.2126 * ia, 0.0),
-                vec4(0.7152 - 0.7152 * ia, 0.7152 + 0.2848 * ia, 0.7152 - 0.7152 * ia, 0.0),
-                vec4(0.0722 - 0.0722 * ia, 0.0722 - 0.0722 * ia, 0.0722 + 0.9278 * ia, 0.0),
-                vec4(0.0, 0.0, 0.0, 1.0)) * Cs;
-}
-
-vec4 HueRotate(vec4 Cs, float amount) {
-    vec3 CsHsv = rgbToHsv(Cs.rgb);
-    CsHsv.x = mod(CsHsv.x + amount / 6.283185307179586, 1.0);
-    return vec4(hsvToRgb(CsHsv), Cs.a);
-}
-
 vec4 Invert(vec4 Cs, float amount) {
     Cs.rgb /= Cs.a;
 
     vec3 color = mix(Cs.rgb, vec3(1.0) - Cs.rgb, amount);
 
     // Pre-multiply the alpha into the output value.
     return vec4(color.rgb * Cs.a, Cs.a);
 }
 
-vec4 Saturate(vec4 Cs, float amount) {
-    return vec4(hsvToRgb(min(vec3(1.0, amount, 1.0) * rgbToHsv(Cs.rgb), vec3(1.0))), Cs.a);
-}
-
-vec4 Sepia(vec4 Cs, float amount) {
-    float ia = 1.0 - amount;
-    return mat4(vec4(0.393 + 0.607 * ia, 0.349 - 0.349 * ia, 0.272 - 0.272 * ia, 0.0),
-                vec4(0.769 - 0.769 * ia, 0.686 + 0.314 * ia, 0.534 - 0.534 * ia, 0.0),
-                vec4(0.189 - 0.189 * ia, 0.168 - 0.168 * ia, 0.131 + 0.869 * ia, 0.0),
-                vec4(0.0, 0.0, 0.0, 1.0)) * Cs;
-}
-
 vec4 Brightness(vec4 Cs, float amount) {
     // Un-premultiply the input.
     Cs.rgb /= Cs.a;
 
     // Apply the brightness factor.
     // Resulting color needs to be clamped to output range
     // since we are pre-multiplying alpha in the shader.
     vec3 color = clamp(Cs.rgb * amount, vec3(0.0), vec3(1.0));
@@ -164,32 +136,22 @@ void main(void) {
     switch (vOp) {
         case 0:
             // Gaussian blur is specially handled:
             oFragColor = Cs;// Blur(vAmount, vec2(0,0));
             break;
         case 1:
             oFragColor = Contrast(Cs, vAmount);
             break;
-        case 2:
-            oFragColor = Grayscale(Cs, vAmount);
-            break;
-        case 3:
-            oFragColor = HueRotate(Cs, vAmount);
-            break;
         case 4:
             oFragColor = Invert(Cs, vAmount);
             break;
-        case 5:
-            oFragColor = Saturate(Cs, vAmount);
-            break;
-        case 6:
-            oFragColor = Sepia(Cs, vAmount);
-            break;
         case 7:
             oFragColor = Brightness(Cs, vAmount);
             break;
         case 8:
             oFragColor = Opacity(Cs, vAmount);
             break;
+        default:
+            oFragColor = vColorMat * Cs;
     }
 }
 #endif
--- a/gfx/webrender/res/ps_composite.glsl
+++ b/gfx/webrender/res/ps_composite.glsl
@@ -293,11 +293,13 @@ void main(void) {
         case MixBlendMode_Luminosity:
             result.rgb = Luminosity(Cb.rgb, Cs.rgb);
             break;
     }
 
     result.rgb = (1.0 - Cb.a) * Cs.rgb + Cb.a * result.rgb;
     result.a = Cs.a;
 
+    result.rgb *= result.a;
+
     oFragColor = result;
 }
 #endif
--- a/gfx/webrender/res/ps_line.glsl
+++ b/gfx/webrender/res/ps_line.glsl
@@ -176,17 +176,17 @@ void main(void) {
 #else
     #ifdef WR_FEATURE_TRANSFORM
         alpha = 0.0;
         vec2 local_pos = init_transform_fs(vLocalPos, alpha);
     #else
         vec2 local_pos = vLocalPos;
     #endif
 
-        alpha = min(alpha, do_clip());
+        alpha *= do_clip();
 #endif
 
     // Find the appropriate distance to apply the step over.
     float aa_range = compute_aa_range(local_pos);
 
     // Select the x/y coord, depending on which axis this edge is.
     vec2 pos = mix(local_pos.xy, local_pos.yx, vAxisSelect);
 
@@ -254,11 +254,11 @@ void main(void) {
             if (half_line_thickness <= 1.0) {
                 alpha = 1.0 - step(alpha, MAGIC_WAVY_LINE_AA_SNAP);
             }
 
             break;
         }
     }
 
-    oFragColor = vColor * vec4(1.0, 1.0, 1.0, alpha);
+    oFragColor = vColor * alpha;
 }
 #endif
--- a/gfx/webrender/res/ps_yuv_image.glsl
+++ b/gfx/webrender/res/ps_yuv_image.glsl
@@ -189,11 +189,11 @@ void main(void) {
 
     yuv_value.x = TEX_SAMPLE(sColor0, vec3(st_y, vLayers.x)).r;
     yuv_value.y = TEX_SAMPLE(sColor1, vec3(st_u, vLayers.y)).r;
     yuv_value.z = TEX_SAMPLE(sColor2, vec3(st_v, vLayers.z)).r;
 #endif
 
     // See the YuvColorMatrix definition for an explanation of where the constants come from.
     vec3 rgb = YuvColorMatrix * (yuv_value - vec3(0.06275, 0.50196, 0.50196));
-    oFragColor = vec4(rgb, alpha);
+    oFragColor = vec4(rgb * alpha, alpha);
 }
 #endif
--- a/gfx/webrender/src/border.rs
+++ b/gfx/webrender/src/border.rs
@@ -5,17 +5,16 @@
 use api::{BorderSide, BorderStyle, BorderWidths, ClipAndScrollInfo, ColorF};
 use api::{EdgeAaSegmentMask, LayerPoint, LayerRect};
 use api::{LayerPrimitiveInfo, LayerSize, NormalBorder, RepeatMode};
 use clip::ClipSource;
 use ellipse::Ellipse;
 use frame_builder::FrameBuilder;
 use gpu_cache::GpuDataRequest;
 use prim_store::{BorderPrimitiveCpu, RectangleContent, PrimitiveContainer, TexelRect};
-use tiling::PrimitiveFlags;
 use util::{lerp, pack_as_float};
 
 #[repr(u8)]
 #[derive(Debug, Copy, Clone, PartialEq)]
 pub enum BorderCornerInstance {
     None,
     Single, // Single instance needed - corner styles are same or similar.
     Double, // Different corner styles. Draw two instances, one per style.
@@ -383,56 +382,56 @@ impl FrameBuilder {
             // Add a solid rectangle for each visible edge/corner combination.
             if top_edge == BorderEdgeKind::Solid {
                 info.rect = LayerRect::new(p0, LayerSize::new(rect_width, top_len));
                 info.edge_aa_segment_mask = EdgeAaSegmentMask::BOTTOM;
                 self.add_solid_rectangle(
                     clip_and_scroll,
                     &info,
                     RectangleContent::Fill(border.top.color),
-                    PrimitiveFlags::None,
+                    None,
                 );
             }
             if left_edge == BorderEdgeKind::Solid {
                 info.rect = LayerRect::new(
                     LayerPoint::new(p0.x, p0.y + top_len),
                     LayerSize::new(left_len, rect_height - top_len - bottom_len),
                 );
                 info.edge_aa_segment_mask = EdgeAaSegmentMask::RIGHT;
                 self.add_solid_rectangle(
                     clip_and_scroll,
                     &info,
                     RectangleContent::Fill(border.left.color),
-                    PrimitiveFlags::None,
+                    None,
                 );
             }
             if right_edge == BorderEdgeKind::Solid {
                 info.rect = LayerRect::new(
                     LayerPoint::new(p1.x - right_len, p0.y + top_len),
                     LayerSize::new(right_len, rect_height - top_len - bottom_len),
                 );
                 info.edge_aa_segment_mask = EdgeAaSegmentMask::LEFT;
                 self.add_solid_rectangle(
                     clip_and_scroll,
                     &info,
                     RectangleContent::Fill(border.right.color),
-                    PrimitiveFlags::None,
+                    None,
                 );
             }
             if bottom_edge == BorderEdgeKind::Solid {
                 info.rect = LayerRect::new(
                     LayerPoint::new(p0.x, p1.y - bottom_len),
                     LayerSize::new(rect_width, bottom_len),
                 );
                 info.edge_aa_segment_mask = EdgeAaSegmentMask::TOP;
                 self.add_solid_rectangle(
                     clip_and_scroll,
                     &info,
                     RectangleContent::Fill(border.bottom.color),
-                    PrimitiveFlags::None,
+                    None,
                 );
             }
         } else {
             // Create clip masks for border corners, if required.
             let mut extra_clips = Vec::new();
             let mut corner_instances = [BorderCornerInstance::Single; 4];
 
             for (i, corner) in corners.iter().enumerate() {
--- a/gfx/webrender/src/clip.rs
+++ b/gfx/webrender/src/clip.rs
@@ -240,17 +240,18 @@ impl ClipSources {
 
         for &(ref clip, _) in &self.clips {
             if let ClipSource::Image(ref mask) = *clip {
                 resource_cache.request_image(mask.image, ImageRendering::Auto, None, gpu_cache);
             }
         }
     }
 
-    pub fn is_masking(&self) -> bool {
+    /// Whether or not this ClipSources has any clips (does any clipping).
+    pub fn has_clips(&self) -> bool {
         !self.clips.is_empty()
     }
 }
 
 /// Represents a local rect and a device space
 /// rectangles that are either outside or inside bounds.
 #[derive(Clone, Debug, PartialEq)]
 pub struct Geometry {
--- a/gfx/webrender/src/clip_scroll_node.rs
+++ b/gfx/webrender/src/clip_scroll_node.rs
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{ClipId, DeviceIntRect, LayerPixel, LayerPoint, LayerRect, LayerSize};
 use api::{LayerToScrollTransform, LayerToWorldTransform, LayerVector2D, LayoutVector2D, PipelineId};
 use api::{ScrollClamping, ScrollEventPhase, ScrollLocation, ScrollSensitivity};
 use api::{StickyOffsetBounds, WorldPoint};
-use clip::{ClipRegion, ClipSources, ClipSourcesHandle, ClipStore};
+use clip::{ClipSourcesHandle, ClipStore};
 use clip_scroll_tree::{CoordinateSystemId, TransformUpdateState};
 use euclid::SideOffsets2D;
 use geometry::ray_intersects_rect;
 use gpu_cache::GpuCache;
 use gpu_types::{ClipScrollNodeIndex, ClipScrollNodeData};
 use render_task::{ClipChain, ClipChainNode, ClipWorkItem};
 use resource_cache::ResourceCache;
 use spring::{DAMPING, STIFFNESS, Spring};
@@ -22,40 +22,16 @@ use util::{MatrixHelpers, MaxRect};
 const CAN_OVERSCROLL: bool = true;
 
 #[cfg(not(target_os = "macos"))]
 const CAN_OVERSCROLL: bool = false;
 
 const MAX_LOCAL_VIEWPORT: f32 = 1000000.0;
 
 #[derive(Debug)]
-pub struct ClipInfo {
-    /// The clips for this node.
-    pub clip_sources: ClipSourcesHandle,
-
-    /// Whether or not this clip node automatically creates a mask.
-    pub is_masking: bool,
-}
-
-impl ClipInfo {
-    pub fn new(
-        clip_region: ClipRegion,
-        clip_store: &mut ClipStore,
-    ) -> ClipInfo {
-        let clip_sources = ClipSources::from(clip_region);
-        let is_masking = clip_sources.is_masking();
-
-        ClipInfo {
-            clip_sources: clip_store.insert(clip_sources),
-            is_masking,
-        }
-    }
-}
-
-#[derive(Debug)]
 pub struct StickyFrameInfo {
     pub margins: SideOffsets2D<Option<f32>>,
     pub vertical_offset_bounds: StickyOffsetBounds,
     pub horizontal_offset_bounds: StickyOffsetBounds,
     pub previously_applied_offset: LayoutVector2D,
     pub current_offset: LayerVector2D,
 }
 
@@ -77,17 +53,17 @@ impl StickyFrameInfo {
 }
 
 #[derive(Debug)]
 pub enum NodeType {
     /// A reference frame establishes a new coordinate space in the tree.
     ReferenceFrame(ReferenceFrameInfo),
 
     /// Other nodes just do clipping, but no transformation.
-    Clip(ClipInfo),
+    Clip(ClipSourcesHandle),
 
     /// Transforms it's content, but doesn't clip it. Can also be adjusted
     /// by scroll events or setting scroll offsets.
     ScrollFrame(ScrollingState),
 
     /// A special kind of node that adjusts its position based on the position
     /// of its parent node and a given set of sticky positioning offset bounds.
     /// Sticky positioned is described in the CSS Positioned Layout Module Level 3 here:
@@ -146,17 +122,17 @@ pub struct ClipScrollNode {
     /// The intersected outer bounds of the clips for this node.
     pub combined_clip_outer_bounds: DeviceIntRect,
 
     /// The axis-aligned coordinate system id of this node.
     pub coordinate_system_id: CoordinateSystemId,
 
     /// A linear ID / index of this clip-scroll node. Used as a reference to
     /// pass to shaders, to allow them to fetch a given clip-scroll node.
-    pub id: ClipScrollNodeIndex,
+    pub node_data_index: ClipScrollNodeIndex,
 }
 
 impl ClipScrollNode {
     fn new(
         pipeline_id: PipelineId,
         parent_id: Option<ClipId>,
         rect: &LayerRect,
         node_type: NodeType
@@ -170,17 +146,17 @@ impl ClipScrollNode {
             reference_frame_relative_scroll_offset: LayerVector2D::zero(),
             parent: parent_id,
             children: Vec::new(),
             pipeline_id,
             node_type: node_type,
             clip_chain_node: None,
             combined_clip_outer_bounds: DeviceIntRect::max_rect(),
             coordinate_system_id: CoordinateSystemId(0),
-            id: ClipScrollNodeIndex(0),
+            node_data_index: ClipScrollNodeIndex(0),
         }
     }
 
     pub fn new_scroll_frame(
         pipeline_id: PipelineId,
         parent_id: ClipId,
         frame_rect: &LayerRect,
         content_size: &LayerSize,
@@ -195,20 +171,20 @@ impl ClipScrollNode {
         ));
 
         Self::new(pipeline_id, Some(parent_id), frame_rect, node_type)
     }
 
     pub fn new_clip_node(
         pipeline_id: PipelineId,
         parent_id: ClipId,
-        clip_info: ClipInfo,
+        handle: ClipSourcesHandle,
         clip_rect: LayerRect,
     ) -> Self {
-        Self::new(pipeline_id, Some(parent_id), &clip_rect, NodeType::Clip(clip_info))
+        Self::new(pipeline_id, Some(parent_id), &clip_rect, NodeType::Clip(handle))
     }
 
     pub fn new_reference_frame(
         parent_id: Option<ClipId>,
         frame_rect: &LayerRect,
         transform: &LayerToScrollTransform,
         origin_in_parent_reference_frame: LayerVector2D,
         pipeline_id: PipelineId,
@@ -284,35 +260,87 @@ impl ClipScrollNode {
         }
 
         scrolling.offset = new_offset;
         scrolling.bouncing_back = false;
         scrolling.started_bouncing_back = false;
         true
     }
 
+    pub fn update(
+        &mut self,
+        state: &mut TransformUpdateState,
+        node_data: &mut Vec<ClipScrollNodeData>,
+        device_pixel_ratio: f32,
+        clip_store: &mut ClipStore,
+        resource_cache: &mut ResourceCache,
+        gpu_cache: &mut GpuCache,
+    ) {
+        // We set this earlier so that we can use it before we have all the data necessary
+        // to populate the ClipScrollNodeData.
+        self.node_data_index = ClipScrollNodeIndex(node_data.len() as u32);
+
+        self.update_transform(state);
+        self.update_clip_work_item(
+            state,
+            device_pixel_ratio,
+            clip_store,
+            resource_cache,
+            gpu_cache,
+        );
+
+        let local_clip_rect = if self.world_content_transform.has_perspective_component() {
+            LayerRect::new(
+                LayerPoint::new(-MAX_LOCAL_VIEWPORT, -MAX_LOCAL_VIEWPORT),
+                LayerSize::new(2.0 * MAX_LOCAL_VIEWPORT, 2.0 * MAX_LOCAL_VIEWPORT)
+            )
+        } else {
+            self.combined_local_viewport_rect
+        };
+
+        let data = match self.world_content_transform.inverse() {
+            Some(inverse) => {
+                ClipScrollNodeData {
+                    transform: self.world_content_transform,
+                    inv_transform: inverse,
+                    local_clip_rect,
+                    reference_frame_relative_scroll_offset:
+                        self.reference_frame_relative_scroll_offset,
+                    scroll_offset: self.scroll_offset(),
+                }
+            }
+            None => {
+                state.combined_outer_clip_bounds = DeviceIntRect::zero();
+                ClipScrollNodeData::invalid()
+            }
+        };
+
+        // Write the data that will be made available to the GPU for this node.
+        node_data.push(data);
+    }
+
     pub fn update_clip_work_item(
         &mut self,
         state: &mut TransformUpdateState,
         device_pixel_ratio: f32,
         clip_store: &mut ClipStore,
         resource_cache: &mut ResourceCache,
         gpu_cache: &mut GpuCache,
     ) {
         let current_clip_chain = state.parent_clip_chain.clone();
-        let clip_info = match self.node_type {
-            NodeType::Clip(ref mut info) if info.is_masking => info,
+        let clip_sources_handle = match self.node_type {
+            NodeType::Clip(ref handle) => handle,
             _ => {
                 self.clip_chain_node = current_clip_chain;
                 self.combined_clip_outer_bounds = state.combined_outer_clip_bounds;
                 return;
             }
         };
 
-        let clip_sources = clip_store.get_mut(&clip_info.clip_sources);
+        let clip_sources = clip_store.get_mut(clip_sources_handle);
         clip_sources.update(
             &self.world_viewport_transform,
             gpu_cache,
             resource_cache,
             device_pixel_ratio,
         );
 
         let outer_bounds = clip_sources.bounds.outer.as_ref().map_or_else(
@@ -321,32 +349,28 @@ impl ClipScrollNode {
         );
 
         self.combined_clip_outer_bounds = outer_bounds.intersection(
             &state.combined_outer_clip_bounds).unwrap_or_else(DeviceIntRect::zero);
 
         // TODO: Combine rectangles in the same axis-aligned clip space here?
         self.clip_chain_node = Some(Rc::new(ClipChainNode {
             work_item: ClipWorkItem {
-                scroll_node_id: self.id,
-                clip_sources: clip_info.clip_sources.weak(),
+                scroll_node_data_index: self.node_data_index,
+                clip_sources: clip_sources_handle.weak(),
                 coordinate_system_id: state.current_coordinate_system_id,
             },
             prev: current_clip_chain,
         }));
 
         state.combined_outer_clip_bounds = self.combined_clip_outer_bounds;
         state.parent_clip_chain = self.clip_chain_node.clone();
     }
 
-    pub fn update_transform(
-        &mut self,
-        state: &mut TransformUpdateState,
-        node_data: &mut Vec<ClipScrollNodeData>,
-    ) {
+    pub fn update_transform(&mut self, state: &mut TransformUpdateState) {
         // We calculate this here to avoid a double-borrow later.
         let sticky_offset = self.calculate_sticky_offset(
             &state.nearest_scrolling_ancestor_offset,
             &state.nearest_scrolling_ancestor_viewport,
         );
 
         let (local_transform, accumulated_scroll_offset) = match self.node_type {
             NodeType::ReferenceFrame(ref info) => {
@@ -435,46 +459,16 @@ impl ClipScrollNode {
                 state.parent_combined_viewport_rect = self.combined_local_viewport_rect;
                 state.parent_accumulated_scroll_offset =
                     info.current_offset + state.parent_accumulated_scroll_offset;
             }
         }
 
         // Store coord system ID, and also the ID used for shaders to reference this node.
         self.coordinate_system_id = state.current_coordinate_system_id;
-        self.id = ClipScrollNodeIndex(node_data.len() as u32);
-
-        let local_clip_rect = if self.world_content_transform.has_perspective_component() {
-            LayerRect::new(
-                LayerPoint::new(-MAX_LOCAL_VIEWPORT, -MAX_LOCAL_VIEWPORT),
-                LayerSize::new(2.0 * MAX_LOCAL_VIEWPORT, 2.0 * MAX_LOCAL_VIEWPORT)
-            )
-        } else {
-            self.combined_local_viewport_rect
-        };
-
-        let data = match self.world_content_transform.inverse() {
-            Some(inverse) => {
-                ClipScrollNodeData {
-                    transform: self.world_content_transform,
-                    inv_transform: inverse,
-                    local_clip_rect,
-                    reference_frame_relative_scroll_offset: self.reference_frame_relative_scroll_offset,
-                    scroll_offset: self.scroll_offset(),
-                }
-            }
-            None => {
-                state.combined_outer_clip_bounds = DeviceIntRect::zero();
-
-                ClipScrollNodeData::invalid()
-            }
-        };
-
-        // Write the data that will be made available to the GPU for this node.
-        node_data.push(data);
     }
 
     fn calculate_sticky_offset(
         &self,
         viewport_scroll_offset: &LayerVector2D,
         viewport_rect: &LayerRect,
     ) -> LayerVector2D {
         let info = match self.node_type {
--- a/gfx/webrender/src/clip_scroll_tree.rs
+++ b/gfx/webrender/src/clip_scroll_tree.rs
@@ -175,31 +175,31 @@ impl ClipScrollTree {
             Some(inverted) => inverted.transform_point2d(&point),
             None => {
                 cache.insert(*node_id, None);
                 return false;
             }
         };
 
         let point_in_layer = transformed_point - node.local_viewport_rect.origin.to_vector();
-        let clip_info = match node.node_type {
-            NodeType::Clip(ref info) => info,
+        let clip_sources_handle = match node.node_type {
+            NodeType::Clip(ref clip_sources_handle) => clip_sources_handle,
             _ => {
                 cache.insert(*node_id, Some(point_in_layer));
                 return true;
             }
         };
 
         if !node.local_clip_rect.contains(&transformed_point) {
             cache.insert(*node_id, None);
             return false;
         }
 
         let point_in_clips = transformed_point - node.local_clip_rect.origin.to_vector();
-        for &(ref clip, _) in clip_store.get(&clip_info.clip_sources).clips() {
+        for &(ref clip, _) in clip_store.get(&clip_sources_handle).clips() {
             if !clip.contains(&point_in_clips) {
                 cache.insert(*node_id, None);
                 return false;
             }
         }
 
         cache.insert(*node_id, Some(point_in_layer));
 
@@ -320,17 +320,17 @@ impl ClipScrollTree {
         };
 
         self.nodes
             .get_mut(&clip_id)
             .unwrap()
             .scroll(scroll_location, phase)
     }
 
-    pub fn update_all_node_transforms(
+    pub fn update_tree(
         &mut self,
         screen_rect: &DeviceIntRect,
         device_pixel_ratio: f32,
         clip_store: &mut ClipStore,
         resource_cache: &mut ResourceCache,
         gpu_cache: &mut GpuCache,
         pan: LayerPoint,
         node_data: &mut Vec<ClipScrollNodeData>,
@@ -352,28 +352,28 @@ impl ClipScrollTree {
             parent_accumulated_scroll_offset: LayerVector2D::zero(),
             nearest_scrolling_ancestor_offset: LayerVector2D::zero(),
             nearest_scrolling_ancestor_viewport: LayerRect::zero(),
             parent_clip_chain: None,
             combined_outer_clip_bounds: *screen_rect,
             current_coordinate_system_id: CoordinateSystemId(0),
             next_coordinate_system_id: CoordinateSystemId(0).next(),
         };
-        self.update_node_transform(
+        self.update_node(
             root_reference_frame_id,
             &mut state,
             device_pixel_ratio,
             clip_store,
             resource_cache,
             gpu_cache,
             node_data,
         );
     }
 
-    fn update_node_transform(
+    fn update_node(
         &mut self,
         layer_id: ClipId,
         state: &mut TransformUpdateState,
         device_pixel_ratio: f32,
         clip_store: &mut ClipStore,
         resource_cache: &mut ResourceCache,
         gpu_cache: &mut GpuCache,
         node_data: &mut Vec<ClipScrollNodeData>,
@@ -382,33 +382,30 @@ impl ClipScrollTree {
         //           Restructure this to avoid the clones!
         let mut state = state.clone();
         let node_children = {
             let node = match self.nodes.get_mut(&layer_id) {
                 Some(node) => node,
                 None => return,
             };
 
-            node.update_transform(
+            node.update(
                 &mut state,
-                node_data
-            );
-            node.update_clip_work_item(
-                &mut state,
+                node_data,
                 device_pixel_ratio,
                 clip_store,
                 resource_cache,
                 gpu_cache,
             );
 
             node.children.clone()
         };
 
         for child_layer_id in node_children {
-            self.update_node_transform(
+            self.update_node(
                 child_layer_id,
                 &mut state,
                 device_pixel_ratio,
                 clip_store,
                 resource_cache,
                 gpu_cache,
                 node_data,
             );
@@ -502,21 +499,21 @@ impl ClipScrollTree {
             _ => {}
         }
     }
 
     fn print_node<T: PrintTreePrinter>(&self, id: &ClipId, pt: &mut T, clip_store: &ClipStore) {
         let node = self.nodes.get(id).unwrap();
 
         match node.node_type {
-            NodeType::Clip(ref info) => {
+            NodeType::Clip(ref clip_sources_handle) => {
                 pt.new_level("Clip".to_owned());
 
                 pt.add_item(format!("id: {:?}", id));
-                let clips = clip_store.get(&info.clip_sources).clips();
+                let clips = clip_store.get(&clip_sources_handle).clips();
                 pt.new_level(format!("Clip Sources [{}]", clips.len()));
                 for source in clips {
                     pt.add_item(format!("{:?}", source));
                 }
                 pt.end_level();
             }
             NodeType::ReferenceFrame(ref info) => {
                 pt.new_level(format!("ReferenceFrame {:?}", info.transform));
--- a/gfx/webrender/src/debug_render.rs
+++ b/gfx/webrender/src/debug_render.rs
@@ -261,17 +261,17 @@ impl DebugRenderer {
         self.add_line(p1.x, p1.y, color, p0.x, p1.y, color);
         self.add_line(p0.x, p1.y, color, p0.x, p0.y, color);
     }
 
     pub fn render(&mut self, device: &mut Device, viewport_size: &DeviceUintSize) {
         let _gm = GpuMarker::new(device.rc_gl(), "debug");
         device.disable_depth();
         device.set_blend(true);
-        device.set_blend_mode_alpha();
+        device.set_blend_mode_premultiplied_alpha();
 
         let projection = Transform3D::ortho(
             0.0,
             viewport_size.width as f32,
             viewport_size.height as f32,
             0.0,
             ORTHO_NEAR_PLANE,
             ORTHO_FAR_PLANE,
--- a/gfx/webrender/src/device.rs
+++ b/gfx/webrender/src/device.rs
@@ -1877,26 +1877,16 @@ impl Device {
         self.gl.blend_equation(gl::FUNC_ADD);
     }
 
     pub fn set_blend_mode_premultiplied_dest_out(&self) {
         self.gl.blend_func(gl::ZERO, gl::ONE_MINUS_SRC_ALPHA);
         self.gl.blend_equation(gl::FUNC_ADD);
     }
 
-    pub fn set_blend_mode_alpha(&self) {
-        self.gl.blend_func_separate(
-            gl::SRC_ALPHA,
-            gl::ONE_MINUS_SRC_ALPHA,
-            gl::ONE,
-            gl::ONE_MINUS_SRC_ALPHA,
-        );
-        self.gl.blend_equation(gl::FUNC_ADD);
-    }
-
     pub fn set_blend_mode_multiply(&self) {
         self.gl
             .blend_func_separate(gl::ZERO, gl::SRC_COLOR, gl::ZERO, gl::SRC_ALPHA);
         self.gl.blend_equation(gl::FUNC_ADD);
     }
     pub fn set_blend_mode_max(&self) {
         self.gl
             .blend_func_separate(gl::ONE, gl::ONE, gl::ONE, gl::ONE);
--- a/gfx/webrender/src/frame.rs
+++ b/gfx/webrender/src/frame.rs
@@ -10,24 +10,24 @@ use api::{LayerSize, LayerToScrollTransf
 use api::{LayoutRect, LayoutSize, LayoutTransform};
 use api::{LocalClip, PipelineId, ScrollClamping, ScrollEventPhase, ScrollLayerState};
 use api::{ScrollLocation, ScrollPolicy, ScrollSensitivity, SpecificDisplayItem, StackingContext};
 use api::{ClipMode, TileOffset, TransformStyle, WorldPoint};
 use clip::ClipRegion;
 use clip_scroll_node::StickyFrameInfo;
 use clip_scroll_tree::{ClipScrollTree, ScrollStates};
 use euclid::rect;
-use frame_builder::{FrameBuilder, FrameBuilderConfig};
+use frame_builder::{FrameBuilder, FrameBuilderConfig, ScrollbarInfo};
 use gpu_cache::GpuCache;
 use internal_types::{FastHashMap, FastHashSet, RendererFrame};
 use prim_store::RectangleContent;
 use profiler::{GpuCacheProfileCounters, TextureCacheProfileCounters};
 use resource_cache::{FontInstanceMap,ResourceCache, TiledImageMap};
 use scene::{Scene, StackingContextHelpers, ScenePipeline};
-use tiling::{CompositeOps, Frame, PrimitiveFlags};
+use tiling::{CompositeOps, Frame};
 use util::ComplexClipRegionHelpers;
 
 #[derive(Copy, Clone, PartialEq, PartialOrd, Debug, Eq, Ord)]
 pub struct FrameId(pub u32);
 
 static DEFAULT_SCROLLBAR_COLOR: ColorF = ColorF {
     r: 0.3,
     g: 0.3,
@@ -79,17 +79,19 @@ impl<'a> FlattenContext<'a> {
             .get(complex_clips)
             .collect()
     }
 
     fn flatten_root(
         &mut self,
         traversal: &mut BuiltDisplayListIter<'a>,
         pipeline_id: PipelineId,
-        content_size: &LayoutSize,
+        frame_size: &LayoutSize,
+        root_reference_frame_id: ClipId,
+        root_scroll_frame_id: ClipId,
     ) {
         self.builder.push_stacking_context(
             &LayerVector2D::zero(),
             pipeline_id,
             CompositeOps::default(),
             TransformStyle::Flat,
             true,
             true,
@@ -98,44 +100,42 @@ impl<'a> FlattenContext<'a> {
         // We do this here, rather than above because we want any of the top-level
         // stacking contexts in the display list to be treated like root stacking contexts.
         // FIXME(mrobinson): Currently only the first one will, which for the moment is
         // sufficient for all our use cases.
         self.builder.notify_waiting_for_root_stacking_context();
 
         // For the root pipeline, there's no need to add a full screen rectangle
         // here, as it's handled by the framebuffer clear.
-        let clip_id = ClipId::root_scroll_node(pipeline_id);
         if self.scene.root_pipeline_id != Some(pipeline_id) {
             if let Some(pipeline) = self.scene.pipelines.get(&pipeline_id) {
                 if let Some(bg_color) = pipeline.background_color {
-                    let root_bounds = LayerRect::new(LayerPoint::zero(), *content_size);
+                    let root_bounds = LayerRect::new(LayerPoint::zero(), *frame_size);
                     let info = LayerPrimitiveInfo::new(root_bounds);
                     self.builder.add_solid_rectangle(
-                        ClipAndScrollInfo::simple(clip_id),
+                        ClipAndScrollInfo::simple(root_reference_frame_id),
                         &info,
                         RectangleContent::Fill(bg_color),
-                        PrimitiveFlags::None,
+                        None,
                     );
                 }
             }
         }
 
 
         self.flatten_items(traversal, pipeline_id, LayerVector2D::zero());
 
         if self.builder.config.enable_scrollbars {
             let scrollbar_rect = LayerRect::new(LayerPoint::zero(), LayerSize::new(10.0, 70.0));
-            let info = LayerPrimitiveInfo::new(scrollbar_rect);
-
+            let container_rect = LayerRect::new(LayerPoint::zero(), *frame_size);
             self.builder.add_solid_rectangle(
-                ClipAndScrollInfo::simple(clip_id),
-                &info,
+                ClipAndScrollInfo::simple(root_reference_frame_id),
+                &LayerPrimitiveInfo::new(scrollbar_rect),
                 RectangleContent::Fill(DEFAULT_SCROLLBAR_COLOR),
-                PrimitiveFlags::Scrollbar(self.clip_scroll_tree.topmost_scrolling_node_id(), 4.0),
+                Some(ScrollbarInfo(root_scroll_frame_id, container_rect)),
             );
         }
 
         self.builder.pop_stacking_context();
     }
 
     fn flatten_items(
         &mut self,
@@ -366,17 +366,19 @@ impl<'a> FlattenContext<'a> {
             &pipeline.content_size,
             ScrollSensitivity::ScriptAndInputEvents,
             self.clip_scroll_tree,
         );
 
         self.flatten_root(
             &mut pipeline.display_list.iter(),
             pipeline_id,
-            &pipeline.content_size,
+            &iframe_rect.size,
+            iframe_reference_frame_id,
+            ClipId::root_scroll_node(pipeline_id),
         );
 
         self.builder.pop_reference_frame();
     }
 
     fn flatten_item<'b>(
         &'b mut self,
         item: DisplayItemRef<'a, 'b>,
@@ -454,26 +456,26 @@ impl<'a> FlattenContext<'a> {
                     &prim_info,
                     RectangleContent::Fill(info.color),
                     &clip_and_scroll,
                 ) {
                     self.builder.add_solid_rectangle(
                         clip_and_scroll,
                         &prim_info,
                         RectangleContent::Fill(info.color),
-                        PrimitiveFlags::None,
+                        None,
                     );
                 }
             }
             SpecificDisplayItem::ClearRectangle => {
                 self.builder.add_solid_rectangle(
                     clip_and_scroll,
                     &prim_info,
                     RectangleContent::Clear,
-                    PrimitiveFlags::None,
+                    None,
                 );
             }
             SpecificDisplayItem::Line(ref info) => {
                 self.builder.add_line(
                     clip_and_scroll,
                     &prim_info,
                     info.wavy_line_thickness,
                     info.orientation,
@@ -684,17 +686,17 @@ impl<'a> FlattenContext<'a> {
                 },
                 local_clip,
                 .. info.clone()
             };
             self.builder.add_solid_rectangle(
                 *clip_and_scroll,
                 &prim_info,
                 content,
-                PrimitiveFlags::None,
+                None,
             );
             has_opaque = true;
         }
 
         if !has_opaque {
             return false
         }
 
@@ -705,17 +707,17 @@ impl<'a> FlattenContext<'a> {
                     None => continue,
                 },
                 .. info.clone()
             };
             self.builder.add_solid_rectangle(
                 *clip_and_scroll,
                 &prim_info,
                 content,
-                PrimitiveFlags::None,
+                None,
             );
         }
         true
     }
 
     /// Decomposes an image display item that is repeated into an image per individual repetition.
     /// We need to do this when we are unable to perform the repetition in the shader,
     /// for example if the image is tiled.
@@ -1137,20 +1139,24 @@ impl FrameContext {
 
             roller.builder.setup_viewport_offset(
                 window_size,
                 inner_rect,
                 device_pixel_ratio,
                 roller.clip_scroll_tree,
             );
 
+            let reference_frame_id = roller.clip_scroll_tree.root_reference_frame_id;
+            let scroll_frame_id = roller.clip_scroll_tree.topmost_scrolling_node_id;
             roller.flatten_root(
                 &mut root_pipeline.display_list.iter(),
                 root_pipeline_id,
-                &root_pipeline.content_size,
+                &root_pipeline.viewport_size,
+                reference_frame_id,
+                scroll_frame_id,
             );
 
             self.pipeline_epoch_map.extend(roller.pipeline_epochs.drain(..));
             roller.builder
         };
 
         self.clip_scroll_tree
             .finalize_and_apply_pending_scroll_offsets(old_scrolling_states);
--- a/gfx/webrender/src/frame_builder.rs
+++ b/gfx/webrender/src/frame_builder.rs
@@ -1,51 +1,54 @@
 /* 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, BuiltDisplayList};
-use api::{ClipAndScrollInfo, ClipId, ColorF};
+use api::{ClipAndScrollInfo, ClipId, ColorF, PremultipliedColorF};
 use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceUintRect, DeviceUintSize};
-use api::{ExtendMode, FilterOp, FontInstance, FontRenderMode};
+use api::{ExtendMode, FilterOp, FontRenderMode};
 use api::{GlyphInstance, GlyphOptions, GradientStop, HitTestFlags, HitTestItem, HitTestResult};
 use api::{ImageKey, ImageRendering, ItemRange, ItemTag, LayerPoint, LayerPrimitiveInfo, LayerRect};
 use api::{LayerPixel, LayerSize, LayerToScrollTransform, LayerVector2D, LayoutVector2D, LineOrientation};
 use api::{LineStyle, LocalClip, PipelineId, RepeatMode};
 use api::{ScrollSensitivity, Shadow, TileOffset, TransformStyle};
 use api::{WorldPixel, WorldPoint, YuvColorSpace, YuvData, device_length};
 use app_units::Au;
 use border::ImageBorderSegment;
 use clip::{ClipRegion, ClipSource, ClipSources, ClipStore, Contains};
-use clip_scroll_node::{ClipInfo, ClipScrollNode, NodeType};
-use clip_scroll_tree::{ClipScrollTree};
+use clip_scroll_node::{ClipScrollNode, NodeType};
+use clip_scroll_tree::ClipScrollTree;
 use euclid::{SideOffsets2D, TypedTransform3D, vec2, vec3};
 use frame::FrameId;
+use glyph_rasterizer::FontInstance;
 use gpu_cache::GpuCache;
 use internal_types::{FastHashMap, FastHashSet, HardwareCompositeOp};
-use picture::{PicturePrimitive};
+use picture::{PictureKind, PicturePrimitive};
 use plane_split::{BspSplitter, Polygon, Splitter};
 use prim_store::{TexelRect, YuvImagePrimitiveCpu};
 use prim_store::{GradientPrimitiveCpu, ImagePrimitiveCpu, LinePrimitive, PrimitiveKind};
 use prim_store::{PrimitiveContainer, PrimitiveIndex, PrimitiveRun};
 use prim_store::{PrimitiveStore, RadialGradientPrimitiveCpu};
 use prim_store::{RectangleContent, RectanglePrimitive, TextRunPrimitiveCpu};
 use profiler::{FrameProfileCounters, GpuCacheProfileCounters, TextureCacheProfileCounters};
 use render_task::{AlphaRenderItem, ClearMode, RenderTask, RenderTaskId, RenderTaskLocation};
 use render_task::RenderTaskTree;
 use resource_cache::ResourceCache;
 use scene::ScenePipeline;
 use std::{mem, usize, f32, i32};
-use tiling::{CompositeOps, Frame};
-use tiling::{ContextIsolation, RenderTargetKind, StackingContextIndex};
-use tiling::{PrimitiveFlags, PrimitiveRunCmd, RenderPass};
-use tiling::{RenderTargetContext, ScrollbarPrimitive, StackingContext};
+use tiling::{CompositeOps, ContextIsolation, Frame, PrimitiveRunCmd, RenderPass};
+use tiling::{RenderTargetContext, RenderTargetKind, ScrollbarPrimitive, StackingContext};
+use tiling::StackingContextIndex;
 use util::{self, pack_as_float, RectHelpers, recycle_vec};
 use box_shadow::BLUR_SAMPLE_SCALE;
 
+#[derive(Debug)]
+pub struct ScrollbarInfo(pub ClipId, pub LayerRect);
+
 /// Construct a polygon from stacking context boundaries.
 /// `anchor` here is an index that's going to be preserved in all the
 /// splits of the polygon.
 fn make_polygon(
     stacking_context: &StackingContext,
     node: &ClipScrollNode,
     anchor: usize,
 ) -> Polygon<f64, WorldPixel> {
@@ -468,21 +471,22 @@ impl FrameBuilder {
         &mut self,
         new_node_id: ClipId,
         parent_id: ClipId,
         pipeline_id: PipelineId,
         clip_region: ClipRegion,
         clip_scroll_tree: &mut ClipScrollTree,
     ) {
         let clip_rect = clip_region.main;
-        let clip_info = ClipInfo::new(
-            clip_region,
-            &mut self.clip_store,
-        );
-        let node = ClipScrollNode::new_clip_node(pipeline_id, parent_id, clip_info, clip_rect);
+        let clip_sources = ClipSources::from(clip_region);
+        debug_assert!(clip_sources.has_clips());
+
+        let handle = self.clip_store.insert(clip_sources);
+
+        let node = ClipScrollNode::new_clip_node(pipeline_id, parent_id, handle, clip_rect);
         clip_scroll_tree.add_node(node, new_node_id);
     }
 
     pub fn add_scroll_frame(
         &mut self,
         new_node_id: ClipId,
         parent_id: ClipId,
         pipeline_id: PipelineId,
@@ -560,17 +564,17 @@ impl FrameBuilder {
         mem::replace(&mut self.shadow_prim_stack, shadows);
     }
 
     pub fn add_solid_rectangle(
         &mut self,
         clip_and_scroll: ClipAndScrollInfo,
         info: &LayerPrimitiveInfo,
         content: RectangleContent,
-        flags: PrimitiveFlags,
+        scrollbar_info: Option<ScrollbarInfo>,
     ) {
         if let RectangleContent::Fill(ColorF{a, ..}) = content {
             if a == 0.0 {
                 // Don't add transparent rectangles to the draw list, but do consider them for hit
                 // testing. This allows specifying invisible hit testing areas.
                 self.add_primitive_to_hit_testing_list(info, clip_and_scroll);
                 return;
             }
@@ -582,96 +586,97 @@ impl FrameBuilder {
 
         let prim_index = self.add_primitive(
             clip_and_scroll,
             info,
             Vec::new(),
             PrimitiveContainer::Rectangle(prim),
         );
 
-        match flags {
-            PrimitiveFlags::None => {}
-            PrimitiveFlags::Scrollbar(clip_id, border_radius) => {
-                self.scrollbar_prims.push(ScrollbarPrimitive {
-                    prim_index,
-                    clip_id,
-                    border_radius,
-                });
-            }
+        if let Some(ScrollbarInfo(clip_id, frame_rect)) = scrollbar_info {
+            self.scrollbar_prims.push(ScrollbarPrimitive {
+                prim_index,
+                clip_id,
+                frame_rect,
+            });
         }
     }
 
     pub fn add_line(
         &mut self,
         clip_and_scroll: ClipAndScrollInfo,
         info: &LayerPrimitiveInfo,
         wavy_line_thickness: f32,
         orientation: LineOrientation,
-        color: &ColorF,
+        line_color: &ColorF,
         style: LineStyle,
     ) {
         let line = LinePrimitive {
             wavy_line_thickness,
-            color: *color,
-            style: style,
-            orientation: orientation,
+            color: line_color.premultiplied(),
+            style,
+            orientation,
         };
 
         let mut fast_shadow_prims = Vec::new();
         for (idx, &(shadow_prim_index, _)) in self.shadow_prim_stack.iter().enumerate() {
             let shadow_metadata = &self.prim_store.cpu_metadata[shadow_prim_index.0];
             let picture = &self.prim_store.cpu_pictures[shadow_metadata.cpu_prim_index.0];
-            let shadow = picture.as_text_shadow();
-            if shadow.blur_radius == 0.0 {
-                fast_shadow_prims.push((idx, shadow.clone()));
+            match picture.kind {
+                PictureKind::TextShadow { offset, color, blur_radius } if blur_radius == 0.0 => {
+                    fast_shadow_prims.push((idx, offset, color));
+                }
+                _ => {}
             }
         }
 
-        for (idx, shadow) in fast_shadow_prims {
+        for (idx, shadow_offset, shadow_color) in fast_shadow_prims {
             let mut line = line.clone();
-            line.color = shadow.color;
+            line.color = shadow_color.premultiplied();
             let mut info = info.clone();
-            info.rect = info.rect.translate(&shadow.offset);
+            info.rect = info.rect.translate(&shadow_offset);
             let prim_index = self.create_primitive(
                 &info,
                 Vec::new(),
                 PrimitiveContainer::Line(line),
             );
             self.shadow_prim_stack[idx].1.push((prim_index, clip_and_scroll));
         }
 
         let prim_index = self.create_primitive(
             &info,
             Vec::new(),
             PrimitiveContainer::Line(line),
         );
 
-        if color.a > 0.0 {
+        if line_color.a > 0.0 {
             if self.shadow_prim_stack.is_empty() {
                 self.add_primitive_to_hit_testing_list(&info, clip_and_scroll);
                 self.add_primitive_to_draw_list(prim_index, clip_and_scroll);
             } else {
                 self.pending_shadow_contents.push((prim_index, clip_and_scroll, *info));
             }
         }
 
         for &(shadow_prim_index, _) in &self.shadow_prim_stack {
             let shadow_metadata = &mut self.prim_store.cpu_metadata[shadow_prim_index.0];
             debug_assert_eq!(shadow_metadata.prim_kind, PrimitiveKind::Picture);
             let picture =
                 &mut self.prim_store.cpu_pictures[shadow_metadata.cpu_prim_index.0];
-            let blur_radius = picture.as_text_shadow().blur_radius;
 
-            // Only run real blurs here (fast path zero blurs are handled above).
-            if blur_radius > 0.0 {
-                picture.add_primitive(
-                    prim_index,
-                    &info.rect,
-                    clip_and_scroll,
-                );
+            match picture.kind {
+                // Only run real blurs here (fast path zero blurs are handled above).
+                PictureKind::TextShadow { blur_radius, .. } if blur_radius > 0.0 => {
+                    picture.add_primitive(
+                        prim_index,
+                        &info.rect,
+                        clip_and_scroll,
+                    );
+                }
+                _ => {}
             }
         }
     }
 
     pub fn add_border(
         &mut self,
         clip_and_scroll: ClipAndScrollInfo,
         info: &LayerPrimitiveInfo,
@@ -1038,22 +1043,22 @@ impl FrameBuilder {
     }
 
     pub fn add_text(
         &mut self,
         clip_and_scroll: ClipAndScrollInfo,
         run_offset: LayoutVector2D,
         info: &LayerPrimitiveInfo,
         font: &FontInstance,
-        color: &ColorF,
+        text_color: &ColorF,
         glyph_range: ItemRange<GlyphInstance>,
         glyph_count: usize,
         glyph_options: Option<GlyphOptions>,
     ) {
-        let rect = info.rect;
+        let original_rect = info.rect;
         // Trivial early out checks
         if font.size.0 <= 0 {
             return;
         }
 
         // Sanity check - anything with glyphs bigger than this
         // is probably going to consume too much memory to render
         // efficiently anyway. This is specifically to work around
@@ -1088,17 +1093,17 @@ impl FrameBuilder {
                     render_mode = FontRenderMode::Alpha;
                 }
             }
         }
 
         let prim_font = FontInstance::new(
             font.font_key,
             font.size,
-            *color,
+            *text_color,
             font.bg_color,
             render_mode,
             font.subpx_dir,
             font.platform_options,
             font.variations.clone(),
             font.synthetic_italics,
         );
         let prim = TextRunPrimitiveCpu {
@@ -1117,22 +1122,24 @@ impl FrameBuilder {
         // 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
         // order. Store them in a Vec first to work around borrowck issues.
         // TODO(gw): Refactor to avoid having to store them in a Vec first.
         let mut fast_shadow_prims = Vec::new();
         for (idx, &(shadow_prim_index, _)) in self.shadow_prim_stack.iter().enumerate() {
             let shadow_metadata = &self.prim_store.cpu_metadata[shadow_prim_index.0];
             let picture_prim = &self.prim_store.cpu_pictures[shadow_metadata.cpu_prim_index.0];
-            let shadow = picture_prim.as_text_shadow();
-            if shadow.blur_radius == 0.0 {
-                let mut text_prim = prim.clone();
-                text_prim.font.color = shadow.color.into();
-                text_prim.offset += shadow.offset;
-                fast_shadow_prims.push((idx, text_prim));
+            match picture_prim.kind {
+                PictureKind::TextShadow { offset, color, blur_radius } if blur_radius == 0.0 => {
+                    let mut text_prim = prim.clone();
+                    text_prim.font.color = color.into();
+                    text_prim.offset += offset;
+                    fast_shadow_prims.push((idx, text_prim));
+                }
+                _ => {}
             }
         }
 
         for (idx, text_prim) in fast_shadow_prims {
             let rect = info.rect;
             let mut info = info.clone();
             info.rect = rect.translate(&text_prim.offset);
             let prim_index = self.create_primitive(
@@ -1147,17 +1154,17 @@ impl FrameBuilder {
         // used for both the visual element and also the shadow(s).
         let prim_index = self.create_primitive(
             info,
             Vec::new(),
             PrimitiveContainer::TextRun(prim),
         );
 
         // Only add a visual element if it can contribute to the scene.
-        if color.a > 0.0 {
+        if text_color.a > 0.0 {
             if self.shadow_prim_stack.is_empty() {
                 self.add_primitive_to_hit_testing_list(info, clip_and_scroll);
                 self.add_primitive_to_draw_list(prim_index, clip_and_scroll);
             } else {
                 self.pending_shadow_contents.push((prim_index, clip_and_scroll, *info));
             }
         }
 
@@ -1166,27 +1173,29 @@ impl FrameBuilder {
         // primitive here, they will still draw before the visual text, since
         // the shadow primitive itself has been added to the draw cmd
         // list *before* the visual element, during push_shadow. We need
         // the primitive index of the visual element here before we can add
         // the indices as sub-primitives to the shadow primitives.
         for &(shadow_prim_index, _) in &self.shadow_prim_stack {
             let shadow_metadata = &mut self.prim_store.cpu_metadata[shadow_prim_index.0];
             debug_assert_eq!(shadow_metadata.prim_kind, PrimitiveKind::Picture);
-            let picture_prim =
+            let picture =
                 &mut self.prim_store.cpu_pictures[shadow_metadata.cpu_prim_index.0];
 
-            // Only run real blurs here (fast path zero blurs are handled above).
-            let blur_radius = picture_prim.as_text_shadow().blur_radius;
-            if blur_radius > 0.0 {
-                picture_prim.add_primitive(
-                    prim_index,
-                    &rect,
-                    clip_and_scroll,
-                );
+            match picture.kind {
+                // Only run real blurs here (fast path zero blurs are handled above).
+                PictureKind::TextShadow { blur_radius, .. } if blur_radius > 0.0 => {
+                    picture.add_primitive(
+                        prim_index,
+                        &original_rect,
+                        clip_and_scroll,
+                    );
+                }
+                _ => {}
             }
         }
     }
 
     pub fn add_image(
         &mut self,
         clip_and_scroll: ClipAndScrollInfo,
         info: &LayerPrimitiveInfo,
@@ -1510,55 +1519,41 @@ impl FrameBuilder {
                 }
             }
         }
 
         mem::replace(&mut self.cmds, commands);
     }
 
     fn update_scroll_bars(&mut self, clip_scroll_tree: &ClipScrollTree, gpu_cache: &mut GpuCache) {
-        let distance_from_edge = 8.0;
+        static SCROLLBAR_PADDING: f32 = 8.0;
 
         for scrollbar_prim in &self.scrollbar_prims {
             let metadata = &mut self.prim_store.cpu_metadata[scrollbar_prim.prim_index.0];
-            let clip_scroll_node = &clip_scroll_tree.nodes[&scrollbar_prim.clip_id];
+            let scroll_frame = &clip_scroll_tree.nodes[&scrollbar_prim.clip_id];
 
             // Invalidate what's in the cache so it will get rebuilt.
             gpu_cache.invalidate(&metadata.gpu_location);
 
-            let scrollable_distance = clip_scroll_node.scrollable_size().height;
-
+            let scrollable_distance = scroll_frame.scrollable_size().height;
             if scrollable_distance <= 0.0 {
                 metadata.local_clip_rect.size = LayerSize::zero();
                 continue;
             }
-
-            let scroll_offset = clip_scroll_node.scroll_offset();
-            let f = -scroll_offset.y / scrollable_distance;
-
-            let min_y = clip_scroll_node.local_viewport_rect.origin.y - scroll_offset.y +
-                distance_from_edge;
-
-            let max_y = clip_scroll_node.local_viewport_rect.origin.y +
-                clip_scroll_node.local_viewport_rect.size.height -
-                scroll_offset.y - metadata.local_rect.size.height -
-                distance_from_edge;
+            let amount_scrolled = -scroll_frame.scroll_offset().y / scrollable_distance;
 
-            metadata.local_rect.origin.x = clip_scroll_node.local_viewport_rect.origin.x +
-                clip_scroll_node.local_viewport_rect.size.width -
-                metadata.local_rect.size.width -
-                distance_from_edge;
+            let frame_rect = scrollbar_prim.frame_rect;
+            let min_y = frame_rect.origin.y + SCROLLBAR_PADDING;
+            let max_y = frame_rect.origin.y + frame_rect.size.height -
+                (SCROLLBAR_PADDING + metadata.local_rect.size.height);
 
-            metadata.local_rect.origin.y = util::lerp(min_y, max_y, f);
+            metadata.local_rect.origin.x = frame_rect.origin.x + frame_rect.size.width -
+                (metadata.local_rect.size.width + SCROLLBAR_PADDING);
+            metadata.local_rect.origin.y = util::lerp(min_y, max_y, amount_scrolled);
             metadata.local_clip_rect = metadata.local_rect;
-
-            // TODO(gw): The code to set / update border clips on scroll bars
-            //           has been broken for a long time, so I've removed it
-            //           for now. We can re-add that code once the clips
-            //           data is moved over to the GPU cache!
         }
     }
 
     fn build_render_task(
         &mut self,
         clip_scroll_tree: &ClipScrollTree,
         gpu_cache: &mut GpuCache,
         render_tasks: &mut RenderTaskTree,
@@ -1711,17 +1706,17 @@ impl FrameBuilder {
                                             .inflate(inflate_size as i32);
                                 let blur_render_task = RenderTask::new_blur(
                                     blur_std_deviation,
                                     current_task_id,
                                     render_tasks,
                                     RenderTargetKind::Color,
                                     &[],
                                     ClearMode::Transparent,
-                                    ColorF::new(0.0, 0.0, 0.0, 0.0),
+                                    PremultipliedColorF::TRANSPARENT,
                                 );
                                 let blur_render_task_id = render_tasks.add(blur_render_task);
                                 let item = AlphaRenderItem::HardwareComposite(
                                     stacking_context_index,
                                     blur_render_task_id,
                                     HardwareCompositeOp::PremultipliedAlpha,
                                     DeviceIntPoint::new(
                                         screen_origin.x - inflate_size as i32,
@@ -1851,17 +1846,22 @@ impl FrameBuilder {
 
                     for i in 0 .. run.count {
                         let prim_index = PrimitiveIndex(run.base_prim_index.0 + i);
 
                         if self.prim_store.cpu_metadata[prim_index.0].screen_rect.is_some() {
                             self.prim_store
                                 .add_render_tasks_for_prim(prim_index, &mut current_task);
                             let item =
-                                AlphaRenderItem::Primitive(clip_node.id, scroll_node.id, prim_index, next_z);
+                                AlphaRenderItem::Primitive(
+                                    clip_node.node_data_index,
+                                    scroll_node.node_data_index,
+                                    prim_index,
+                                    next_z
+                                );
                             current_task.as_alpha_batch_mut().items.push(item);
                             next_z += 1;
                         }
                     }
                 }
             }
         }
 
@@ -1898,17 +1898,17 @@ impl FrameBuilder {
             DeviceIntSize::new(
                 self.screen_size.width as i32,
                 self.screen_size.height as i32,
             ),
         );
 
         let mut node_data = Vec::new();
 
-        clip_scroll_tree.update_all_node_transforms(
+        clip_scroll_tree.update_tree(
             &screen_rect,
             device_pixel_ratio,
             &mut self.clip_store,
             resource_cache,
             gpu_cache,
             pan,
             &mut node_data,
         );
--- a/gfx/webrender/src/glyph_cache.rs
+++ b/gfx/webrender/src/glyph_cache.rs
@@ -1,14 +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/. */
 
-use api::{DevicePoint, DeviceUintSize, FontInstance, GlyphKey};
-use glyph_rasterizer::GlyphFormat;
+use api::{DevicePoint, DeviceUintSize, GlyphKey};
+use glyph_rasterizer::{FontInstance, GlyphFormat};
 use internal_types::FastHashMap;
 use resource_cache::ResourceClassCache;
 use std::sync::Arc;
 use texture_cache::TextureCacheHandle;
 
 pub struct CachedGlyphInfo {
     pub texture_cache_handle: TextureCacheHandle,
     pub glyph_bytes: Arc<Vec<u8>>,
--- a/gfx/webrender/src/glyph_rasterizer.rs
+++ b/gfx/webrender/src/glyph_rasterizer.rs
@@ -1,33 +1,85 @@
 /* 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 api::{ColorF, ColorU, IdNamespace, LayoutPoint, SubpixelDirection};
-use api::{DevicePoint, DeviceUintSize, FontInstance, FontRenderMode};
-use api::{FontKey, FontTemplate, GlyphDimensions, GlyphKey};
+use api::{IdNamespace, LayoutPoint};
+use api::{ColorF, ColorU, DevicePoint, DeviceUintSize};
+use api::{FontInstancePlatformOptions, FontRenderMode, FontVariation};
+use api::{FontKey, FontTemplate, GlyphDimensions, GlyphKey, SubpixelDirection};
 use api::{ImageData, ImageDescriptor, ImageFormat};
-#[cfg(test)]
 use app_units::Au;
 use device::TextureFilter;
 use glyph_cache::{CachedGlyphInfo, GlyphCache};
 use gpu_cache::GpuCache;
 use internal_types::FastHashSet;
 use platform::font::FontContext;
 use profiler::TextureCacheProfileCounters;
 use rayon::ThreadPool;
 use rayon::prelude::*;
 use std::collections::hash_map::Entry;
 use std::mem;
 use std::sync::{Arc, Mutex, MutexGuard};
 use std::sync::mpsc::{channel, Receiver, Sender};
 use texture_cache::{TextureCache, TextureCacheHandle};
 
+#[derive(Clone, Hash, PartialEq, Eq, Debug, Ord, PartialOrd)]
+pub struct FontInstance {
+    pub font_key: FontKey,
+    // The font size is in *device* pixels, not logical pixels.
+    // It is stored as an Au since we need sub-pixel sizes, but
+    // can't store as a f32 due to use of this type as a hash key.
+    // TODO(gw): Perhaps consider having LogicalAu and DeviceAu
+    //           or something similar to that.
+    pub size: Au,
+    pub color: ColorU,
+    pub bg_color: ColorU,
+    pub render_mode: FontRenderMode,
+    pub subpx_dir: SubpixelDirection,
+    pub platform_options: Option<FontInstancePlatformOptions>,
+    pub variations: Vec<FontVariation>,
+    pub synthetic_italics: bool,
+}
+
+impl FontInstance {
+    pub fn new(
+        font_key: FontKey,
+        size: Au,
+        color: ColorF,
+        bg_color: ColorU,
+        render_mode: FontRenderMode,
+        subpx_dir: SubpixelDirection,
+        platform_options: Option<FontInstancePlatformOptions>,
+        variations: Vec<FontVariation>,
+        synthetic_italics: bool,
+    ) -> Self {
+        FontInstance {
+            font_key,
+            size,
+            color: color.into(),
+            bg_color,
+            render_mode,
+            subpx_dir,
+            platform_options,
+            variations,
+            synthetic_italics,
+        }
+    }
+
+    pub fn get_subpx_offset(&self, glyph: &GlyphKey) -> (f64, f64) {
+        match self.subpx_dir {
+            SubpixelDirection::None => (0.0, 0.0),
+            SubpixelDirection::Horizontal => (glyph.subpixel_offset.into(), 0.0),
+            SubpixelDirection::Vertical => (0.0, glyph.subpixel_offset.into()),
+        }
+    }
+}
+
 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
 pub enum GlyphFormat {
     Mono,
     Alpha,
     Subpixel,
     ColorBitmap,
 }
 
--- a/gfx/webrender/src/gpu_cache.rs
+++ b/gfx/webrender/src/gpu_cache.rs
@@ -19,17 +19,17 @@
 //! data is not in the cache, the user provided closure
 //! will be invoked to build the data.
 //!
 //! After ```end_frame``` has occurred, callers can
 //! use the ```get_address``` API to get the allocated
 //! address in the GPU cache of a given resource slot
 //! for this frame.
 
-use api::{ColorF, LayerRect};
+use api::{LayerRect, PremultipliedColorF};
 use device::FrameId;
 use internal_types::UvRect;
 use profiler::GpuCacheProfileCounters;
 use renderer::MAX_VERTEX_TEXTURE_WIDTH;
 use std::{mem, u16, u32};
 use std::ops::Add;
 
 pub const GPU_CACHE_INITIAL_HEIGHT: u32 = 512;
@@ -59,17 +59,17 @@ pub struct GpuBlockData {
 
 impl GpuBlockData {
     pub fn empty() -> GpuBlockData {
         GpuBlockData { data: [0.0; 4] }
     }
 }
 
 /// Conversion helpers for GpuBlockData
-impl Into<GpuBlockData> for ColorF {
+impl Into<GpuBlockData> for PremultipliedColorF {
     fn into(self) -> GpuBlockData {
         GpuBlockData {
             data: [self.r, self.g, self.b, self.a],
         }
     }
 }
 
 impl Into<GpuBlockData> for [f32; 4] {
--- a/gfx/webrender/src/gpu_types.rs
+++ b/gfx/webrender/src/gpu_types.rs
@@ -26,17 +26,17 @@ pub struct BlurInstance {
 
 /// A clipping primitive drawn into the clipping mask.
 /// Could be an image or a rectangle, which defines the
 /// way `address` is treated.
 #[derive(Debug, Copy, Clone)]
 #[repr(C)]
 pub struct ClipMaskInstance {
     pub render_task_address: RenderTaskAddress,
-    pub scroll_node_id: ClipScrollNodeIndex,
+    pub scroll_node_data_index: ClipScrollNodeIndex,
     pub segment: i32,
     pub clip_data_address: GpuCacheAddress,
     pub resource_address: GpuCacheAddress,
 }
 
 // 32 bytes per instance should be enough for anyone!
 #[derive(Debug, Clone)]
 pub struct PrimitiveInstance {
--- a/gfx/webrender/src/picture.rs
+++ b/gfx/webrender/src/picture.rs
@@ -1,14 +1,15 @@
 /* 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::{BorderRadiusKind, ColorF, ClipAndScrollInfo, device_length, DeviceIntSize};
-use api::{BoxShadowClipMode, LayerPoint, LayerRect, LayerSize, Shadow};
+use api::{BorderRadiusKind, ColorF, ClipAndScrollInfo};
+use api::{device_length, DeviceIntSize};
+use api::{BoxShadowClipMode, LayerPoint, LayerRect, LayerSize, LayerVector2D, Shadow};
 use box_shadow::BLUR_SAMPLE_SCALE;
 use frame_builder::PrimitiveContext;
 use gpu_cache::GpuDataRequest;
 use prim_store::{PrimitiveIndex, PrimitiveRun};
 use render_task::{ClearMode, RenderTask, RenderTaskId, RenderTaskTree};
 use tiling::RenderTargetKind;
 
 /*
@@ -19,17 +20,19 @@ use tiling::RenderTargetKind;
    picture into its parent.
  * A configuration describing how to draw the primitives on
    this picture (e.g. in screen space or local space).
  */
 
 #[derive(Debug)]
 pub enum PictureKind {
     TextShadow {
-        shadow: Shadow,
+        offset: LayerVector2D,
+        color: ColorF,
+        blur_radius: f32,
     },
     BoxShadow {
         blur_radius: f32,
         color: ColorF,
         blur_regions: Vec<LayerRect>,
         clip_mode: BoxShadowClipMode,
         radii_kind: BorderRadiusKind,
     },
@@ -43,55 +46,50 @@ pub struct PicturePrimitive {
     pub content_rect: LayerRect,
 
     // TODO(gw): Add a mode that specifies if this
     //           picture should be rasterized in
     //           screen-space or local-space.
 }
 
 impl PicturePrimitive {
-    pub fn new_text_shadow(shadow: Shadow) -> PicturePrimitive {
+    pub fn new_text_shadow(shadow: Shadow) -> Self {
         PicturePrimitive {
             prim_runs: Vec::new(),
             render_task_id: None,
             content_rect: LayerRect::zero(),
             kind: PictureKind::TextShadow {
-                shadow,
+                offset: shadow.offset,
+                color: shadow.color,
+                blur_radius: shadow.blur_radius,
             },
         }
     }
 
     pub fn new_box_shadow(
         blur_radius: f32,
         color: ColorF,
         blur_regions: Vec<LayerRect>,
         clip_mode: BoxShadowClipMode,
         radii_kind: BorderRadiusKind,
-    ) -> PicturePrimitive {
+    ) -> Self {
         PicturePrimitive {
             prim_runs: Vec::new(),
             render_task_id: None,
             content_rect: LayerRect::zero(),
             kind: PictureKind::BoxShadow {
                 blur_radius,
-                color: color.premultiplied(),
+                color,
                 blur_regions,
                 clip_mode,
                 radii_kind,
             },
         }
     }
 
-    pub fn as_text_shadow(&self) -> &Shadow {
-        match self.kind {
-            PictureKind::TextShadow { ref shadow } => shadow,
-            PictureKind::BoxShadow { .. } => panic!("bug: not a text shadow")
-        }
-    }
-
     pub fn add_primitive(
         &mut self,
         prim_index: PrimitiveIndex,
         local_rect: &LayerRect,
         clip_and_scroll: ClipAndScrollInfo
     ) {
         // TODO(gw): Accumulating the primitive local rect
         //           into the content rect here is fine, for now.
@@ -115,25 +113,25 @@ impl PicturePrimitive {
             base_prim_index: prim_index,
             count: 1,
             clip_and_scroll,
         });
     }
 
     pub fn build(&mut self) -> LayerRect {
         match self.kind {
-            PictureKind::TextShadow { ref shadow } => {
-                let blur_offset = shadow.blur_radius * BLUR_SAMPLE_SCALE;
+            PictureKind::TextShadow { offset, blur_radius, .. } => {
+                let blur_offset = blur_radius * BLUR_SAMPLE_SCALE;
 
                 self.content_rect = self.content_rect.inflate(
                     blur_offset,
                     blur_offset,
                 );
 
-                self.content_rect.translate(&shadow.offset)
+                self.content_rect.translate(&offset)
             }
             PictureKind::BoxShadow { blur_radius, clip_mode, radii_kind, .. } => {
                 // We need to inflate the content rect if outset.
                 match clip_mode {
                     BoxShadowClipMode::Outset => {
                         let blur_offset = blur_radius * BLUR_SAMPLE_SCALE;
 
                         // If the radii are uniform, we can render just the top
@@ -188,43 +186,43 @@ impl PicturePrimitive {
         // Gecko tests.
         let cache_width =
             (self.content_rect.size.width * prim_context.device_pixel_ratio).round() as i32;
         let cache_height =
             (self.content_rect.size.height * prim_context.device_pixel_ratio).round() as i32;
         let cache_size = DeviceIntSize::new(cache_width, cache_height);
 
         match self.kind {
-            PictureKind::TextShadow { ref shadow } => {
-                let blur_radius = device_length(shadow.blur_radius, prim_context.device_pixel_ratio);
+            PictureKind::TextShadow { blur_radius, color, .. } => {
+                let blur_radius = device_length(blur_radius, prim_context.device_pixel_ratio);
 
                 // Quote from https://drafts.csswg.org/css-backgrounds-3/#shadow-blur
                 // "the image that would be generated by applying to the shadow a
                 // Gaussian blur with a standard deviation equal to half the blur radius."
                 let blur_std_deviation = blur_radius.0 as f32 * 0.5;
 
                 let picture_task = RenderTask::new_picture(
                     cache_size,
                     prim_index,
                     RenderTargetKind::Color,
                     self.content_rect.origin,
-                    shadow.color,
+                    color.premultiplied(),
                     ClearMode::Transparent,
                 );
 
                 let picture_task_id = render_tasks.add(picture_task);
 
                 let render_task = RenderTask::new_blur(
                     blur_std_deviation,
                     picture_task_id,
                     render_tasks,
                     RenderTargetKind::Color,
                     &[],
                     ClearMode::Transparent,
-                    shadow.color,
+                    color.premultiplied(),
                 );
 
                 self.render_task_id = Some(render_tasks.add(render_task));
             }
             PictureKind::BoxShadow { blur_radius, clip_mode, ref blur_regions, color, .. } => {
                 let blur_radius = device_length(blur_radius, prim_context.device_pixel_ratio);
 
                 // Quote from https://drafts.csswg.org/css-backgrounds-3/#shadow-blur
@@ -241,30 +239,30 @@ impl PicturePrimitive {
                     }
                 };
 
                 let picture_task = RenderTask::new_picture(
                     cache_size,
                     prim_index,
                     RenderTargetKind::Alpha,
                     self.content_rect.origin,
-                    color,
+                    color.premultiplied(),
                     ClearMode::Zero,
                 );
 
                 let picture_task_id = render_tasks.add(picture_task);
 
                 let render_task = RenderTask::new_blur(
                     blur_std_deviation,
                     picture_task_id,
                     render_tasks,
                     RenderTargetKind::Alpha,
                     blur_regions,
                     blur_clear_mode,
-                    color,
+                    color.premultiplied(),
                 );
 
                 self.render_task_id = Some(render_tasks.add(render_task));
             }
         }
     }
 
     pub fn write_gpu_blocks(&self, mut _request: GpuDataRequest) {
--- a/gfx/webrender/src/platform/macos/font.rs
+++ b/gfx/webrender/src/platform/macos/font.rs
@@ -1,14 +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/. */
 
 use api::{ColorU, FontKey, FontRenderMode, GlyphDimensions};
-use api::{FontInstance, FontVariation, NativeFontHandle};
+use api::{FontVariation, NativeFontHandle};
 use api::{GlyphKey, SubpixelDirection};
 use app_units::Au;
 use core_foundation::array::{CFArray, CFArrayRef};
 use core_foundation::base::TCFType;
 use core_foundation::dictionary::{CFDictionary, CFDictionaryRef};
 use core_foundation::number::{CFNumber, CFNumberRef};
 use core_foundation::string::{CFString, CFStringRef};
 use core_graphics::base::{kCGImageAlphaNoneSkipFirst, kCGImageAlphaPremultipliedFirst};
@@ -17,17 +17,17 @@ use core_graphics::color_space::CGColorS
 use core_graphics::context::{CGContext, CGTextDrawingMode};
 use core_graphics::data_provider::CGDataProvider;
 use core_graphics::font::{CGFont, CGFontRef, CGGlyph};
 use core_graphics::geometry::{CGPoint, CGRect, CGSize};
 use core_text;
 use core_text::font::{CTFont, CTFontRef};
 use core_text::font_descriptor::{kCTFontDefaultOrientation, kCTFontColorGlyphsTrait};
 use gamma_lut::{ColorLut, GammaLut};
-use glyph_rasterizer::{GlyphFormat, RasterizedGlyph};
+use glyph_rasterizer::{FontInstance, GlyphFormat, RasterizedGlyph};
 use internal_types::FastHashMap;
 use std::collections::hash_map::Entry;
 use std::ptr;
 use std::sync::Arc;
 
 pub struct FontContext {
     cg_fonts: FastHashMap<FontKey, CGFont>,
     ct_fonts: FastHashMap<(FontKey, Au, Vec<FontVariation>), CTFont>,
--- a/gfx/webrender/src/platform/unix/font.rs
+++ b/gfx/webrender/src/platform/unix/font.rs
@@ -1,29 +1,29 @@
 /* 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::{FontInstance, FontKey, FontRenderMode, GlyphDimensions};
+use api::{ColorU, GlyphDimensions, GlyphKey, FontKey, FontRenderMode};
 use api::{FontInstancePlatformOptions, FontLCDFilter, FontHinting};
-use api::{NativeFontHandle, SubpixelDirection, GlyphKey, ColorU};
+use api::{NativeFontHandle, SubpixelDirection};
 use api::{FONT_FORCE_AUTOHINT, FONT_NO_AUTOHINT, FONT_EMBEDDED_BITMAP};
 use api::{FONT_EMBOLDEN, FONT_VERTICAL_LAYOUT, FONT_SUBPIXEL_BGR};
 use freetype::freetype::{FT_BBox, FT_Outline_Translate, FT_Pixel_Mode, FT_Render_Mode};
 use freetype::freetype::{FT_Done_Face, FT_Error, FT_Get_Char_Index, FT_Int32};
 use freetype::freetype::{FT_Done_FreeType, FT_Library_SetLcdFilter, FT_Pos};
 use freetype::freetype::{FT_F26Dot6, FT_Face, FT_Glyph_Format, FT_Long, FT_UInt};
 use freetype::freetype::{FT_GlyphSlot, FT_LcdFilter, FT_New_Face, FT_New_Memory_Face};
 use freetype::freetype::{FT_Init_FreeType, FT_Load_Glyph, FT_Render_Glyph};
 use freetype::freetype::{FT_Library, FT_Outline_Get_CBox, FT_Set_Char_Size, FT_Select_Size};
 use freetype::freetype::{FT_LOAD_COLOR, FT_LOAD_DEFAULT, FT_LOAD_FORCE_AUTOHINT};
 use freetype::freetype::{FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH, FT_LOAD_NO_AUTOHINT};
 use freetype::freetype::{FT_LOAD_NO_BITMAP, FT_LOAD_NO_HINTING, FT_LOAD_VERTICAL_LAYOUT};
 use freetype::freetype::{FT_FACE_FLAG_SCALABLE, FT_FACE_FLAG_FIXED_SIZES, FT_Err_Cannot_Render_Glyph};
-use glyph_rasterizer::{GlyphFormat, RasterizedGlyph};
+use glyph_rasterizer::{FontInstance, GlyphFormat, RasterizedGlyph};
 use internal_types::FastHashMap;
 use std::{cmp, mem, ptr, slice};
 use std::cmp::max;
 use std::ffi::CString;
 use std::sync::Arc;
 
 // These constants are not present in the freetype
 // bindings due to bindgen not handling the way
@@ -393,23 +393,23 @@ impl FontContext {
         }
         unsafe { FT_Select_Size(face, best_size) }
     }
 
     pub fn prepare_font(font: &mut FontInstance) {
         match font.render_mode {
             FontRenderMode::Mono | FontRenderMode::Bitmap => {
                 // In mono/bitmap modes the color of the font is irrelevant.
-                font.color = ColorU::new(255, 255, 255, 255);
+                font.color = ColorU::new(0xFF, 0xFF, 0xFF, 0xFF);
                 // Subpixel positioning is disabled in mono and bitmap modes.
                 font.subpx_dir = SubpixelDirection::None;
             }
             FontRenderMode::Alpha | FontRenderMode::Subpixel => {
                 // We don't do any preblending with FreeType currently, so the color is not used.
-                font.color = ColorU::new(255, 255, 255, 255);
+                font.color = ColorU::new(0xFF, 0xFF, 0xFF, 0xFF);
             }
         }
     }
 
     fn rasterize_glyph_outline(
         &mut self,
         slot: FT_GlyphSlot,
         font: &FontInstance,
--- a/gfx/webrender/src/platform/windows/font.rs
+++ b/gfx/webrender/src/platform/windows/font.rs
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-use api::{FontInstance, FontInstancePlatformOptions, FontKey, FontRenderMode};
+use api::{FontInstancePlatformOptions, FontKey, FontRenderMode};
 use api::{ColorU, GlyphDimensions, GlyphKey, SubpixelDirection};
 use dwrote;
 use gamma_lut::{ColorLut, GammaLut};
-use glyph_rasterizer::{GlyphFormat, RasterizedGlyph};
+use glyph_rasterizer::{FontInstance, GlyphFormat, RasterizedGlyph};
 use internal_types::FastHashMap;
 use std::sync::Arc;
 
 lazy_static! {
     static ref DEFAULT_FONT_DESCRIPTOR: dwrote::FontDescriptor = dwrote::FontDescriptor {
         family_name: "Arial".to_owned(),
         weight: dwrote::FontWeight::Regular,
         stretch: dwrote::FontStretch::Normal,
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -1,20 +1,22 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{BorderRadius, BuiltDisplayList, ColorF, ComplexClipRegion, DeviceIntRect};
-use api::{DevicePoint, ExtendMode, FontInstance, GlyphInstance, GlyphKey};
+use api::{DevicePoint, ExtendMode, GlyphInstance, GlyphKey};
 use api::{GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag, LayerPoint, LayerRect};
 use api::{ClipMode, LayerSize, LayerVector2D, LineOrientation, LineStyle};
-use api::{ClipAndScrollInfo, EdgeAaSegmentMask, TileOffset, YuvColorSpace, YuvFormat};
+use api::{ClipAndScrollInfo, EdgeAaSegmentMask, PremultipliedColorF, TileOffset};
+use api::{YuvColorSpace, YuvFormat};
 use border::BorderCornerInstance;
 use clip::{ClipSourcesHandle, ClipStore, Geometry};
 use frame_builder::PrimitiveContext;
+use glyph_rasterizer::FontInstance;
 use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest,
                 ToGpuBlocks};
 use picture::PicturePrimitive;
 use render_task::{ClipWorkItem, ClipChainNode, RenderTask, RenderTaskId, RenderTaskTree};
 use renderer::MAX_VERTEX_TEXTURE_WIDTH;
 use resource_cache::{ImageProperties, ResourceCache};
 use std::{mem, usize};
 use std::rc::Rc;
@@ -175,25 +177,21 @@ pub enum RectangleContent {
 #[derive(Debug)]
 pub struct RectanglePrimitive {
     pub content: RectangleContent,
     pub edge_aa_segment_mask: EdgeAaSegmentMask,
 }
 
 impl ToGpuBlocks for RectanglePrimitive {
     fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
-        match &self.content {
-            &RectangleContent::Fill(ref color) => {
-                request.push(color.premultiplied());
-            }
-            &RectangleContent::Clear => {
-                // Opaque black with operator dest out
-                request.push(ColorF::new(0.0, 0.0, 0.0, 1.0));
-            }
-        }
+        request.push(match self.content {
+            RectangleContent::Fill(color) => color.premultiplied(),
+            // Opaque black with operator dest out
+            RectangleContent::Clear => PremultipliedColorF::BLACK,
+        });
         request.extend_from_slice(&[GpuBlockData {
             data: [self.edge_aa_segment_mask.bits() as f32, 0.0, 0.0, 0.0],
         }]);
     }
 }
 
 #[derive(Debug)]
 pub enum BrushMaskKind {
@@ -249,17 +247,17 @@ impl ToGpuBlocks for BrushPrimitive {
             }
         }
     }
 }
 
 #[derive(Debug, Clone)]
 #[repr(C)]
 pub struct LinePrimitive {
-    pub color: ColorF,
+    pub color: PremultipliedColorF,
     pub wavy_line_thickness: f32,
     pub style: LineStyle,
     pub orientation: LineOrientation,
 }
 
 impl ToGpuBlocks for LinePrimitive {
     fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
         request.push(self.color);
@@ -372,44 +370,44 @@ pub const GRADIENT_DATA_TABLE_SIZE: usiz
 
 // The number of entries in a gradient data: GRADIENT_DATA_TABLE_SIZE + first stop entry + last stop entry
 pub const GRADIENT_DATA_SIZE: usize = GRADIENT_DATA_TABLE_SIZE + 2;
 
 #[derive(Debug)]
 #[repr(C)]
 // An entry in a gradient data table representing a segment of the gradient color space.
 pub struct GradientDataEntry {
-    pub start_color: ColorF,
-    pub end_color: ColorF,
+    pub start_color: PremultipliedColorF,
+    pub end_color: PremultipliedColorF,
 }
 
 struct GradientGpuBlockBuilder<'a> {
     stops_range: ItemRange<GradientStop>,
     display_list: &'a BuiltDisplayList,
 }
 
 impl<'a> GradientGpuBlockBuilder<'a> {
     fn new(
         stops_range: ItemRange<GradientStop>,
         display_list: &'a BuiltDisplayList,
-    ) -> GradientGpuBlockBuilder<'a> {
+    ) -> Self {
         GradientGpuBlockBuilder {
             stops_range,
             display_list,
         }
     }
 
     /// Generate a color ramp filling the indices in [start_idx, end_idx) and interpolating
     /// from start_color to end_color.
     fn fill_colors(
         &self,
         start_idx: usize,
         end_idx: usize,
-        start_color: &ColorF,
-        end_color: &ColorF,
+        start_color: &PremultipliedColorF,
+        end_color: &PremultipliedColorF,
         entries: &mut [GradientDataEntry; GRADIENT_DATA_SIZE],
     ) {
         // Calculate the color difference for individual steps in the ramp.
         let inv_steps = 1.0 / (end_idx - start_idx) as f32;
         let step_r = (end_color.r - start_color.r) * inv_steps;
         let step_g = (end_color.g - start_color.g) * inv_steps;
         let step_b = (end_color.b - start_color.b) * inv_steps;
         let step_a = (end_color.a - start_color.a) * inv_steps;
@@ -623,18 +621,22 @@ impl TextRunPrimitiveCpu {
                 self.glyph_gpu_blocks.push(gpu_block);
             }
         }
 
         resource_cache.request_glyphs(font, &self.glyph_keys, gpu_cache);
     }
 
     fn write_gpu_blocks(&self, request: &mut GpuDataRequest) {
+        let bg_color = ColorF::from(self.font.bg_color);
         request.push(ColorF::from(self.font.color).premultiplied());
-        request.push(ColorF::from(self.font.bg_color));
+        // this is the only case where we need to provide plain color to GPU
+        request.extend_from_slice(&[
+            GpuBlockData { data: [bg_color.r, bg_color.g, bg_color.b, 1.0] }
+        ]);
         request.push([
             self.offset.x,
             self.offset.y,
             self.font.subpx_dir.limit_by(self.font.render_mode) as u32 as f32,
             0.0,
         ]);
         request.extend_from_slice(&self.glyph_gpu_blocks);
 
@@ -1234,34 +1236,35 @@ impl PrimitiveStore {
             resource_cache,
             prim_context.device_pixel_ratio,
         );
 
         // Try to create a mask if we may need to.
         let prim_clips = clip_store.get(&metadata.clip_sources);
         let is_axis_aligned = transform.transform_kind() == TransformedRectKind::AxisAligned;
 
-        let clip_task = if prim_context.clip_node.clip_chain_node.is_some() || prim_clips.is_masking() {
+        let has_clips = prim_context.clip_node.clip_chain_node.is_some() || prim_clips.has_clips();
+        let clip_task = if has_clips {
             // Take into account the actual clip info of the primitive, and
             // mutate the current bounds accordingly.
             let mask_rect = match prim_clips.bounds.outer {
                 Some(ref outer) => match prim_screen_rect.intersection(&outer.device_rect) {
                     Some(rect) => rect,
                     None => {
                         metadata.screen_rect = None;
                         return false;
                     }
                 },
                 _ => prim_screen_rect,
             };
 
-            let extra_clip = if prim_clips.is_masking() {
+            let extra_clip = if prim_clips.has_clips() {
                 Some(Rc::new(ClipChainNode {
                     work_item: ClipWorkItem {
-                        scroll_node_id: prim_context.scroll_node.id,
+                        scroll_node_data_index: prim_context.scroll_node.node_data_index,
                         clip_sources: metadata.clip_sources.weak(),
                         coordinate_system_id: prim_context.scroll_node.coordinate_system_id,
                     },
                     prev: None,
                 }))
             } else {
                 None
             };
--- a/gfx/webrender/src/profiler.rs
+++ b/gfx/webrender/src/profiler.rs
@@ -563,18 +563,18 @@ impl ProfileGraph {
         );
 
         rect.size.width += 140.0;
         debug_renderer.add_quad(
             rect.origin.x,
             rect.origin.y,
             rect.origin.x + rect.size.width + 10.0,
             rect.origin.y + rect.size.height,
-            ColorF::new(0.1, 0.1, 0.1, 0.8).into(),
-            ColorF::new(0.2, 0.2, 0.2, 0.8).into(),
+            ColorU::new(25, 25, 25, 200),
+            ColorU::new(51, 51, 51, 200),
         );
 
         let bx0 = x + 10.0;
         let by0 = y + 10.0;
         let bx1 = bx0 + size.width - 20.0;
         let by1 = by0 + size.height - 20.0;
 
         let w = (bx1 - bx0) / self.max_samples as f32;
@@ -650,18 +650,18 @@ impl GpuFrameCollection {
         );
         let graph_rect = bounding_rect.inflate(-GRAPH_PADDING, -GRAPH_PADDING);
 
         debug_renderer.add_quad(
             bounding_rect.origin.x,
             bounding_rect.origin.y,
             bounding_rect.origin.x + bounding_rect.size.width,
             bounding_rect.origin.y + bounding_rect.size.height,
-            ColorF::new(0.1, 0.1, 0.1, 0.8).into(),
-            ColorF::new(0.2, 0.2, 0.2, 0.8).into(),
+            ColorU::new(25, 25, 25, 200),
+            ColorU::new(51, 51, 51, 200),
         );
 
         let w = graph_rect.size.width;
         let mut y0 = graph_rect.origin.y;
 
         let max_time = self.frames
             .iter()
             .max_by_key(|f| f.total_time)
--- a/gfx/webrender/src/render_backend.rs
+++ b/gfx/webrender/src/render_backend.rs
@@ -466,21 +466,23 @@ impl RenderBackend {
                 }
             };
 
             match msg {
                 ApiMsg::UpdateResources(updates) => {
                     self.resource_cache
                         .update_resources(updates, &mut profile_counters.resources);
                 }
-                ApiMsg::GetGlyphDimensions(font, glyph_keys, tx) => {
+                ApiMsg::GetGlyphDimensions(instance_key, glyph_keys, tx) => {
                     let mut glyph_dimensions = Vec::with_capacity(glyph_keys.len());
-                    for glyph_key in &glyph_keys {
-                        let glyph_dim = self.resource_cache.get_glyph_dimensions(&font, glyph_key);
-                        glyph_dimensions.push(glyph_dim);
+                    if let Some(font) = self.resource_cache.get_font_instance(instance_key) {
+                        for glyph_key in &glyph_keys {
+                            let glyph_dim = self.resource_cache.get_glyph_dimensions(&font, glyph_key);
+                            glyph_dimensions.push(glyph_dim);
+                        }
                     }
                     tx.send(glyph_dimensions).unwrap();
                 }
                 ApiMsg::GetGlyphIndices(font_key, text, tx) => {
                     let mut glyph_indices = Vec::new();
                     for ch in text.chars() {
                         let index = self.resource_cache.get_glyph_index(font_key, ch);
                         glyph_indices.push(index);
--- a/gfx/webrender/src/render_task.rs
+++ b/gfx/webrender/src/render_task.rs
@@ -1,15 +1,15 @@
 /* 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::{ClipId, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
-use api::{ColorF, FilterOp, LayerPoint, MixBlendMode};
-use api::{LayerRect, PipelineId};
+use api::{FilterOp, LayerPoint, LayerRect, MixBlendMode};
+use api::{PipelineId, PremultipliedColorF};
 use clip::{ClipSource, ClipSourcesWeakHandle, ClipStore};
 use clip_scroll_tree::CoordinateSystemId;
 use gpu_cache::GpuCacheHandle;
 use gpu_types::{ClipScrollNodeIndex};
 use internal_types::HardwareCompositeOp;
 use prim_store::PrimitiveIndex;
 use std::{cmp, usize, f32, i32};
 use std::rc::Rc;
@@ -199,17 +199,17 @@ pub enum MaskSegment {
 pub enum MaskGeometryKind {
     Default, // Draw the entire rect
     CornersOnly, // Draw the corners (simple axis aligned mask)
              // TODO(gw): Add more types here (e.g. 4 rectangles outside the inner rect)
 }
 
 #[derive(Debug, Clone)]
 pub struct ClipWorkItem {
-    pub scroll_node_id: ClipScrollNodeIndex,
+    pub scroll_node_data_index: ClipScrollNodeIndex,
     pub clip_sources: ClipSourcesWeakHandle,
     pub coordinate_system_id: CoordinateSystemId,
 }
 
 impl ClipWorkItem {
     fn get_geometry_kind(
         &self,
         clip_store: &ClipStore,
@@ -258,25 +258,25 @@ pub struct CacheMaskTask {
     pub coordinate_system_id: CoordinateSystemId,
 }
 
 #[derive(Debug)]
 pub struct PictureTask {
     pub prim_index: PrimitiveIndex,
     pub target_kind: RenderTargetKind,
     pub content_origin: LayerPoint,
-    pub color: ColorF,
+    pub color: PremultipliedColorF,
 }
 
 #[derive(Debug)]
 pub struct BlurTask {
     pub blur_std_deviation: f32,
     pub target_kind: RenderTargetKind,
     pub regions: Vec<LayerRect>,
-    pub color: ColorF,
+    pub color: PremultipliedColorF,
     pub scale_factor: f32,
 }
 
 #[derive(Debug)]
 pub struct RenderTaskData {
     pub data: [f32; FLOATS_PER_RENDER_TASK_INFO],
 }
 
@@ -311,61 +311,61 @@ pub struct RenderTask {
     pub clear_mode: ClearMode,
 }
 
 impl RenderTask {
     pub fn new_alpha_batch(
         screen_origin: DeviceIntPoint,
         location: RenderTaskLocation,
         frame_output_pipeline_id: Option<PipelineId>,
-    ) -> RenderTask {
+    ) -> Self {
         RenderTask {
             cache_key: None,
             children: Vec::new(),
             location,
             kind: RenderTaskKind::Alpha(AlphaRenderTask {
                 screen_origin,
                 items: Vec::new(),
                 frame_output_pipeline_id,
             }),
             clear_mode: ClearMode::Transparent,
         }
     }
 
     pub fn new_dynamic_alpha_batch(
         rect: &DeviceIntRect,
         frame_output_pipeline_id: Option<PipelineId>,
-    ) -> RenderTask {
+    ) -> Self {
         let location = RenderTaskLocation::Dynamic(None, rect.size);
         Self::new_alpha_batch(rect.origin, location, frame_output_pipeline_id)
     }
 
     pub fn new_picture(
         size: DeviceIntSize,
         prim_index: PrimitiveIndex,
         target_kind: RenderTargetKind,
         content_origin: LayerPoint,
-        color: ColorF,
+        color: PremultipliedColorF,
         clear_mode: ClearMode,
-    ) -> RenderTask {
+    ) -> Self {
         RenderTask {
             cache_key: None,
             children: Vec::new(),
             location: RenderTaskLocation::Dynamic(None, size),
             kind: RenderTaskKind::Picture(PictureTask {
                 prim_index,
                 target_kind,
                 content_origin,
                 color,
             }),
             clear_mode,
         }
     }
 
-    pub fn new_readback(screen_rect: DeviceIntRect) -> RenderTask {
+    pub fn new_readback(screen_rect: DeviceIntRect) -> Self {
         RenderTask {
             cache_key: None,
             children: Vec::new(),
             location: RenderTaskLocation::Dynamic(None, screen_rect.size),
             kind: RenderTaskKind::Readback(screen_rect),
             clear_mode: ClearMode::Transparent,
         }
     }
@@ -374,17 +374,17 @@ impl RenderTask {
         key: Option<ClipId>,
         task_rect: DeviceIntRect,
         raw_clips: ClipChain,
         extra_clip: ClipChain,
         prim_rect: DeviceIntRect,
         clip_store: &ClipStore,
         is_axis_aligned: bool,
         prim_coordinate_system_id: CoordinateSystemId,
-    ) -> Option<RenderTask> {
+    ) -> Option<Self> {
         // Filter out all the clip instances that don't contribute to the result
         let mut current_coordinate_system_id = prim_coordinate_system_id;
         let mut inner_rect = Some(task_rect);
         let clips: Vec<_> = ClipChainNodeIter { current: raw_clips }
             .chain(ClipChainNodeIter { current: extra_clip })
             .filter_map(|node| {
                 let work_item = node.work_item.clone();
 
@@ -394,17 +394,17 @@ impl RenderTask {
                     current_coordinate_system_id = node.work_item.coordinate_system_id;
                     inner_rect = None;
                     return Some(work_item)
                 }
 
                 let clip_info = clip_store
                     .get_opt(&node.work_item.clip_sources)
                     .expect("bug: clip item should exist");
-                debug_assert!(clip_info.is_masking());
+                debug_assert!(clip_info.has_clips());
 
                 match clip_info.bounds.inner {
                     Some(ref inner) if !inner.device_rect.is_empty() => {
                         inner_rect = inner_rect.and_then(|r| r.intersection(&inner.device_rect));
                         if inner.device_rect.contains_rect(&task_rect) {
                             return None;
                         }
                     }
@@ -448,17 +448,17 @@ impl RenderTask {
                 clips,
                 geometry_kind,
                 coordinate_system_id: prim_coordinate_system_id,
             }),
             clear_mode: ClearMode::One,
         })
     }
 
-    // Construct a render task to apply a blur to a primitive. 
+    // Construct a render task to apply a blur to a primitive.
     // The render task chain that is constructed looks like:
     //
     //    PrimitiveCacheTask: Draw the primitives.
     //           ^
     //           |
     //    DownscalingTask(s): Each downscaling task reduces the size of render target to
     //           ^            half. Also reduce the std deviation to half until the std
     //           |            deviation less than 4.0.
@@ -473,18 +473,18 @@ impl RenderTask {
     //
     pub fn new_blur(
         blur_std_deviation: f32,
         src_task_id: RenderTaskId,
         render_tasks: &mut RenderTaskTree,
         target_kind: RenderTargetKind,
         regions: &[LayerRect],
         clear_mode: ClearMode,
-        color: ColorF,
-    ) -> RenderTask {
+        color: PremultipliedColorF,
+    ) -> Self {
         // Adjust large std deviation value.
         let mut adjusted_blur_std_deviation = blur_std_deviation;
         let blur_target_size = render_tasks.get(src_task_id).get_dynamic_size();
         let mut adjusted_blur_target_size = blur_target_size;
         let mut downscaling_src_task_id = src_task_id;
         let mut scale_factor = 1.0;
         while adjusted_blur_std_deviation > MAX_BLUR_STD_DEVIATION {
             if adjusted_blur_target_size.width < MIN_DOWNSCALING_RT_SIZE ||
@@ -535,17 +535,17 @@ impl RenderTask {
 
         blur_task_h
     }
 
     pub fn new_scaling(
         target_kind: RenderTargetKind,
         src_task_id: RenderTaskId,
         target_size: DeviceIntSize,
-    ) -> RenderTask {
+    ) -> Self {
         RenderTask {
             cache_key: None,
             children: vec![src_task_id],
             location: RenderTaskLocation::Dynamic(None, target_size),
             kind: RenderTaskKind::Scaling(target_kind),
             clear_mode: match target_kind {
                 RenderTargetKind::Color => ClearMode::Transparent,
                 RenderTargetKind::Alpha => ClearMode::One,
@@ -644,33 +644,33 @@ impl RenderTask {
                         0.0,
                         task.inner_rect.origin.x as f32,
                         task.inner_rect.origin.y as f32,
                         (task.inner_rect.origin.x + task.inner_rect.size.width) as f32,
                         (task.inner_rect.origin.y + task.inner_rect.size.height) as f32,
                     ],
                 }
             }
-            RenderTaskKind::VerticalBlur(ref task_info) |
-            RenderTaskKind::HorizontalBlur(ref task_info) => {
+            RenderTaskKind::VerticalBlur(ref task) |
+            RenderTaskKind::HorizontalBlur(ref task) => {
                 let (target_rect, target_index) = self.get_target_rect();
                 RenderTaskData {
                     data: [
                         target_rect.origin.x as f32,
                         target_rect.origin.y as f32,
                         target_rect.size.width as f32,
                         target_rect.size.height as f32,
                         target_index.0 as f32,
-                        task_info.blur_std_deviation,
-                        task_info.scale_factor,
+                        task.blur_std_deviation,
+                        task.scale_factor,
                         0.0,
-                        task_info.color.r,
-                        task_info.color.g,
-                        task_info.color.b,
-                        task_info.color.a,
+                        task.color.r,
+                        task.color.g,
+                        task.color.b,
+                        task.color.a,
                     ],
                 }
             }
             RenderTaskKind::Readback(..) |
             RenderTaskKind::Scaling(..) => {
                 let (target_rect, target_index) = self.get_target_rect();
                 RenderTaskData {
                     data: [
--- a/gfx/webrender/src/renderer.rs
+++ b/gfx/webrender/src/renderer.rs
@@ -5,17 +5,17 @@
 //! The webrender API.
 //!
 //! The `webrender::renderer` module provides the interface to webrender, which
 //! is accessible through [`Renderer`][renderer]
 //!
 //! [renderer]: struct.Renderer.html
 
 use api::{channel, BlobImageRenderer, FontRenderMode};
-use api::{ColorF, ColorU, Epoch, PipelineId, RenderApiSender, RenderNotifier};
+use api::{ColorF, Epoch, PipelineId, RenderApiSender, RenderNotifier};
 use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceUintRect, DeviceUintSize};
 use api::{ExternalImageId, ExternalImageType, ImageFormat};
 use api::{YUV_COLOR_SPACES, YUV_FORMATS};
 use api::{YuvColorSpace, YuvFormat};
 #[cfg(not(feature = "debugger"))]
 use api::ApiMsg;
 use api::DebugCommand;
 #[cfg(not(feature = "debugger"))]
@@ -634,20 +634,19 @@ impl SourceTextureResolver {
         }
     }
 }
 
 #[derive(Debug, Copy, Clone, PartialEq)]
 #[allow(dead_code)] // SubpixelVariableTextColor is not used at the moment.
 pub enum BlendMode {
     None,
-    Alpha,
     PremultipliedAlpha,
     PremultipliedDestOut,
-    SubpixelConstantTextColor(ColorU),
+    SubpixelConstantTextColor(ColorF),
     SubpixelWithBgColor,
     SubpixelVariableTextColor,
 }
 
 // Tracks the state of each row in the GPU cache texture.
 struct CacheRow {
     is_dirty: bool,
 }
@@ -1008,17 +1007,16 @@ impl BrushShader {
         projection: &Transform3D<f32>,
         mode: M,
         renderer_errors: &mut Vec<RendererError>,
     ) where M: Into<ShaderMode> {
         match blend_mode {
             BlendMode::None => {
                 self.opaque.bind(device, projection, mode, renderer_errors)
             }
-            BlendMode::Alpha |
             BlendMode::PremultipliedAlpha |
             BlendMode::PremultipliedDestOut |
             BlendMode::SubpixelConstantTextColor(..) |
             BlendMode::SubpixelVariableTextColor |
             BlendMode::SubpixelWithBgColor => {
                 self.alpha.bind(device, projection, mode, renderer_errors)
             }
         }
@@ -2512,17 +2510,16 @@ impl Renderer {
                         GPU_TAG_BRUSH_IMAGE
                     }
                 }
             }
             BatchKind::Transformable(transform_kind, batch_kind) => match batch_kind {
                 TransformBatchKind::Rectangle(needs_clipping) => {
                     debug_assert!(
                         !needs_clipping || match key.blend_mode {
-                            BlendMode::Alpha |
                             BlendMode::PremultipliedAlpha |
                             BlendMode::PremultipliedDestOut |
                             BlendMode::SubpixelConstantTextColor(..) |
                             BlendMode::SubpixelVariableTextColor |
                             BlendMode::SubpixelWithBgColor => true,
                             BlendMode::None => false,
                         }
                     );
@@ -2754,17 +2751,16 @@ impl Renderer {
     ) {
         {
             let _gm = self.gpu_profile.add_marker(GPU_TAG_SETUP_TARGET);
             self.device
                 .bind_draw_target(render_target, Some(target_size));
             self.device.disable_depth();
             self.device.enable_depth_write();
             self.device.set_blend(false);
-            self.device.set_blend_mode_alpha();
             match render_target {
                 Some(..) if self.enable_clear_scissor => {
                     // TODO(gw): Applying a scissor rect and minimal clear here
                     // is a very large performance win on the Intel and nVidia
                     // GPUs that I have tested with. It's possible it may be a
                     // performance penalty on other GPU types - we should test this
                     // and consider different code paths.
                     self.device
@@ -2813,34 +2809,34 @@ impl Renderer {
         // Draw any textrun caches for this target. For now, this
         // is only used to cache text runs that are to be blurred
         // for shadow support. In the future it may be worth
         // considering using this for (some) other text runs, since
         // it removes the overhead of submitting many small glyphs
         // to multiple tiles in the normal text run case.
         if !target.text_run_cache_prims.is_empty() {
             self.device.set_blend(true);
-            self.device.set_blend_mode_alpha();
+            self.device.set_blend_mode_premultiplied_alpha();
 
             let _gm = self.gpu_profile.add_marker(GPU_TAG_CACHE_TEXT_RUN);
             self.cs_text_run
                 .bind(&mut self.device, projection, 0, &mut self.renderer_errors);
             for (texture_id, instances) in &target.text_run_cache_prims {
                 self.draw_instanced_batch(
                     instances,
                     VertexArrayKind::Primitive,
                     &BatchTextures::color(*texture_id),
                 );
             }
         }
         if !target.line_cache_prims.is_empty() {
             // TODO(gw): Technically, we don't need blend for solid
             //           lines. We could check that here?
             self.device.set_blend(true);
-            self.device.set_blend_mode_alpha();
+            self.device.set_blend_mode_premultiplied_alpha();
 
             let _gm = self.gpu_profile.add_marker(GPU_TAG_CACHE_LINE);
             self.cs_line
                 .bind(&mut self.device, projection, 0, &mut self.renderer_errors);
             self.draw_instanced_batch(
                 &target.line_cache_prims,
                 VertexArrayKind::Primitive,
                 &BatchTextures::no_texture(),
@@ -2883,17 +2879,16 @@ impl Renderer {
 
             self.device.disable_depth_write();
             self.gpu_profile.add_sampler(GPU_SAMPLER_TAG_TRANSPARENT);
 
             for batch in &target.alpha_batcher.batch_list.alpha_batch_list.batches {
                 if self.debug_flags.contains(DebugFlags::ALPHA_PRIM_DBG) {
                     let color = match batch.key.blend_mode {
                         BlendMode::None => debug_colors::BLACK,
-                        BlendMode::Alpha => debug_colors::YELLOW,
                         BlendMode::PremultipliedAlpha => debug_colors::GREY,
                         BlendMode::PremultipliedDestOut => debug_colors::SALMON,
                         BlendMode::SubpixelConstantTextColor(..) => debug_colors::GREEN,
                         BlendMode::SubpixelVariableTextColor => debug_colors::RED,
                         BlendMode::SubpixelWithBgColor => debug_colors::BLUE,
                     }.into();
                     for item_rect in &batch.item_rects {
                         self.debug.add_rect(item_rect, color);
@@ -2927,17 +2922,17 @@ impl Renderer {
 
                                 self.draw_instanced_batch(
                                     &batch.instances,
                                     VertexArrayKind::Primitive,
                                     &batch.key.textures
                                 );
                             }
                             BlendMode::SubpixelConstantTextColor(color) => {
-                                self.device.set_blend_mode_subpixel_constant_text_color(color.into());
+                                self.device.set_blend_mode_subpixel_constant_text_color(color);
 
                                 self.ps_text_run.bind(
                                     &mut self.device,
                                     transform_kind,
                                     projection,
                                     TextShaderMode::SubpixelConstantTextColor,
                                     &mut self.renderer_errors,
                                 );
@@ -3033,34 +3028,30 @@ impl Renderer {
                                     projection,
                                     TextShaderMode::SubpixelWithBgColorPass2,
                                     &mut self.renderer_errors,
                                 );
 
                                 self.device
                                     .draw_indexed_triangles_instanced_u16(6, batch.instances.len() as i32);
                             }
-                            BlendMode::Alpha | BlendMode::PremultipliedDestOut | BlendMode::None => {
+                            BlendMode::PremultipliedDestOut | BlendMode::None => {
                                 unreachable!("bug: bad blend mode for text");
                             }
                         }
 
                         prev_blend_mode = BlendMode::None;
                         self.device.set_blend(false);
                     }
                     _ => {
                         if batch.key.blend_mode != prev_blend_mode {
                             match batch.key.blend_mode {
                                 BlendMode::None => {
                                     self.device.set_blend(false);
                                 }
-                                BlendMode::Alpha => {
-                                    self.device.set_blend(true);
-                                    self.device.set_blend_mode_alpha();
-                                }
                                 BlendMode::PremultipliedAlpha => {
                                     self.device.set_blend(true);
                                     self.device.set_blend_mode_premultiplied_alpha();
                                 }
                                 BlendMode::PremultipliedDestOut => {
                                     self.device.set_blend(true);
                                     self.device.set_blend_mode_premultiplied_dest_out();
                                 }
--- a/gfx/webrender/src/resource_cache.rs
+++ b/gfx/webrender/src/resource_cache.rs
@@ -1,27 +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 api::{AddFont, BlobImageData, BlobImageResources, ResourceUpdate, ResourceUpdates};
 use api::{BlobImageDescriptor, BlobImageError, BlobImageRenderer, BlobImageRequest};
 use api::{ColorF, FontRenderMode};
 use api::{DevicePoint, DeviceUintRect, DeviceUintSize};
-use api::{Epoch, FontInstance, FontInstanceKey, FontKey, FontTemplate};
+use api::{Epoch, FontInstanceKey, FontKey, FontTemplate};
 use api::{ExternalImageData, ExternalImageType};
 use api::{FontInstanceOptions, FontInstancePlatformOptions, FontVariation};
 use api::{GlyphDimensions, GlyphKey, IdNamespace};
 use api::{ImageData, ImageDescriptor, ImageKey, ImageRendering};
 use api::{TileOffset, TileSize};
 use app_units::Au;
 use device::TextureFilter;
 use frame::FrameId;
 use glyph_cache::GlyphCache;
-use glyph_rasterizer::{GlyphFormat, GlyphRasterizer, GlyphRequest};
+use glyph_rasterizer::{FontInstance, GlyphFormat, GlyphRasterizer, GlyphRequest};
 use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle};
 use internal_types::{FastHashMap, FastHashSet, SourceTexture, TextureUpdateList};
 use profiler::{ResourceProfileCounters, TextureCacheProfileCounters};
 use rayon::ThreadPool;
 use std::collections::hash_map::Entry::{self, Occupied, Vacant};
 use std::cmp;
 use std::fmt::Debug;
 use std::hash::Hash;
@@ -384,16 +384,21 @@ impl ResourceCache {
             r.delete_font_instance(instance_key);
         }
     }
 
     pub fn get_font_instances(&self) -> FontInstanceMap {
         self.resources.font_instances.clone()
     }
 
+    pub fn get_font_instance(&self, instance_key: FontInstanceKey) -> Option<FontInstance> {
+        let instance_map = self.resources.font_instances.read().unwrap();
+        instance_map.get(&instance_key).cloned()
+    }
+
     pub fn add_image_template(
         &mut self,
         image_key: ImageKey,
         descriptor: ImageDescriptor,
         mut data: ImageData,
         mut tiling: Option<TileSize>,
     ) {
         if tiling.is_none() && Self::should_tile(self.max_texture_size(), &descriptor, &data) {
--- a/gfx/webrender/src/tiling.rs
+++ b/gfx/webrender/src/tiling.rs
@@ -57,17 +57,17 @@ impl AlphaBatchHelpers for PrimitiveStor
         match metadata.prim_kind {
             PrimitiveKind::TextRun => {
                 let font = &self.cpu_text_runs[metadata.cpu_prim_index.0].font;
                 match font.render_mode {
                     FontRenderMode::Subpixel => {
                         if font.bg_color.a != 0 {
                             BlendMode::SubpixelWithBgColor
                         } else {
-                            BlendMode::SubpixelConstantTextColor(font.color)
+                            BlendMode::SubpixelConstantTextColor(font.color.into())
                         }
                     }
                     FontRenderMode::Alpha |
                     FontRenderMode::Mono |
                     FontRenderMode::Bitmap => BlendMode::PremultipliedAlpha,
                 }
             },
             PrimitiveKind::Rectangle => {
@@ -84,56 +84,46 @@ impl AlphaBatchHelpers for PrimitiveStor
                         // with alpha == 0.0 instead of alpha == 1.0, and the RectanglePrimitive
                         // would need to know about that.
                         BlendMode::PremultipliedDestOut
                     },
                 }
             },
             PrimitiveKind::Border |
             PrimitiveKind::Image |
+            PrimitiveKind::YuvImage |
             PrimitiveKind::AlignedGradient |
             PrimitiveKind::AngleGradient |
             PrimitiveKind::RadialGradient |
+            PrimitiveKind::Line |
+            PrimitiveKind::Brush |
             PrimitiveKind::Picture => if needs_blending {
                 BlendMode::PremultipliedAlpha
             } else {
                 BlendMode::None
             },
-            PrimitiveKind::YuvImage |
-            PrimitiveKind::Line |
-            PrimitiveKind::Brush => if needs_blending {
-                BlendMode::Alpha
-            } else {
-                BlendMode::None
-            },
         }
     }
 }
 
 #[derive(Debug)]
 pub struct ScrollbarPrimitive {
     pub clip_id: ClipId,
     pub prim_index: PrimitiveIndex,
-    pub border_radius: f32,
+    pub frame_rect: LayerRect,
 }
 
 #[derive(Debug)]
 pub enum PrimitiveRunCmd {
     PushStackingContext(StackingContextIndex),
     PopStackingContext,
     PrimitiveRun(PrimitiveRun),
 }
 
 #[derive(Debug, Copy, Clone)]
-pub enum PrimitiveFlags {
-    None,
-    Scrollbar(ClipId, f32),
-}
-
-#[derive(Debug, Copy, Clone)]
 pub struct RenderTargetIndex(pub usize);
 
 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
 pub struct RenderPassIndex(isize);
 
 #[derive(Debug)]
 struct DynamicTaskInfo {
     task_id: RenderTaskId,
@@ -277,17 +267,17 @@ impl BatchList {
 
     fn get_suitable_batch(
         &mut self,
         key: BatchKey,
         item_bounding_rect: &DeviceIntRect,
     ) -> &mut Vec<PrimitiveInstance> {
         match key.blend_mode {
             BlendMode::None => self.opaque_batch_list.get_suitable_batch(key),
-            BlendMode::Alpha | BlendMode::PremultipliedAlpha |
+            BlendMode::PremultipliedAlpha |
             BlendMode::PremultipliedDestOut |
             BlendMode::SubpixelConstantTextColor(..) |
             BlendMode::SubpixelVariableTextColor |
             BlendMode::SubpixelWithBgColor => {
                 self.alpha_batch_list
                     .get_suitable_batch(key, item_bounding_rect)
             }
         }
@@ -394,17 +384,17 @@ impl AlphaRenderItem {
             AlphaRenderItem::Composite(stacking_context_index, source_id, backdrop_id, mode, z) => {
                 let stacking_context = &ctx.stacking_context_store[stacking_context_index.0];
                 let key = BatchKey::new(
                     BatchKind::Composite {
                         task_id,
                         source_id,
                         backdrop_id,
                     },
-                    BlendMode::Alpha,
+                    BlendMode::PremultipliedAlpha,
                     BatchTextures::no_texture(),
                 );
                 let batch = batch_list.get_suitable_batch(key, &stacking_context.screen_bounds);
                 let backdrop_task_address = render_tasks.get_task_address(backdrop_id);
                 let source_task_address = render_tasks.get_task_address(source_id);
 
                 let instance = CompositePrimitiveInstance::new(
                     task_address,
@@ -876,17 +866,17 @@ impl ClipBatcher {
         gpu_cache: &GpuCache,
         geometry_kind: MaskGeometryKind,
         clip_store: &ClipStore,
     ) {
         let mut coordinate_system_id = coordinate_system_id;
         for work_item in clips.iter() {
             let instance = ClipMaskInstance {
                 render_task_address: task_address,
-                scroll_node_id: work_item.scroll_node_id,
+                scroll_node_data_index: work_item.scroll_node_data_index,
                 segment: 0,
                 clip_data_address: GpuCacheAddress::invalid(),
                 resource_address: GpuCacheAddress::invalid(),
             };
             let info = clip_store
                 .get_opt(&work_item.clip_sources)
                 .expect("bug: clip handle should be valid");
 
--- a/gfx/webrender_api/Cargo.toml
+++ b/gfx/webrender_api/Cargo.toml
@@ -1,11 +1,11 @@
 [package]
 name = "webrender_api"
-version = "0.53.1"
+version = "0.53.2"
 authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
 license = "MPL-2.0"
 repository = "https://github.com/servo/webrender"
 
 [features]
 nightly = ["euclid/unstable", "serde/unstable"]
 ipc = ["ipc-channel"]
 
--- a/gfx/webrender_api/src/api.rs
+++ b/gfx/webrender_api/src/api.rs
@@ -1,14 +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/. */
 
 use {BuiltDisplayList, BuiltDisplayListDescriptor, ClipId, ColorF, DeviceIntPoint, DeviceUintRect};
-use {DeviceUintSize, FontInstance, FontInstanceKey, FontInstanceOptions};
+use {DeviceUintSize, FontInstanceKey, FontInstanceOptions};
 use {FontInstancePlatformOptions, FontKey, FontVariation, GlyphDimensions, GlyphKey, ImageData};
 use {ImageDescriptor, ImageKey, ItemTag, LayoutPoint, LayoutSize, LayoutTransform, LayoutVector2D};
 use {NativeFontHandle, WorldPoint};
 use app_units::Au;
 use channel::{self, MsgSender, Payload, PayloadSender, PayloadSenderHelperMethods};
 use std::cell::Cell;
 use std::fmt;
 use std::marker::PhantomData;
@@ -260,17 +260,17 @@ pub enum DebugCommand {
 }
 
 #[derive(Clone, Deserialize, Serialize)]
 pub enum ApiMsg {
     /// Add/remove/update images and fonts.
     UpdateResources(ResourceUpdates),
     /// Gets the glyph dimensions
     GetGlyphDimensions(
-        FontInstance,
+        FontInstanceKey,
         Vec<GlyphKey>,
         MsgSender<Vec<Option<GlyphDimensions>>>,
     ),
     /// Gets the glyph indices from a string
     GetGlyphIndices(FontKey, String, MsgSender<Vec<Option<u32>>>),
     /// Adds a new document namespace.
     CloneApi(MsgSender<IdNamespace>),
     /// Adds a new document with given initial size.
@@ -382,23 +382,24 @@ impl RenderApiSender {
         RenderApiSender {
             api_sender,
             payload_sender,
         }
     }
 
     /// Creates a new resource API object with a dedicated namespace.
     pub fn create_api(&self) -> RenderApi {
-        let (sync_tx, sync_rx) = channel::msg_channel().unwrap();
+        let (sync_tx, sync_rx) =
+            channel::msg_channel().expect("Failed to create channel");
         let msg = ApiMsg::CloneApi(sync_tx);
-        self.api_sender.send(msg).unwrap();
+        self.api_sender.send(msg).expect("Failed to send CloneApi message");
         RenderApi {
             api_sender: self.api_sender.clone(),
             payload_sender: self.payload_sender.clone(),
-            namespace_id: sync_rx.recv().unwrap(),
+            namespace_id: sync_rx.recv().expect("Failed to receive API response"),
             next_id: Cell::new(ResourceId(0)),
         }
     }
 }
 
 pub struct RenderApi {
     api_sender: MsgSender<ApiMsg>,
     payload_sender: PayloadSender,
@@ -442,17 +443,17 @@ impl RenderApi {
 
     /// Gets the dimensions for the supplied glyph keys
     ///
     /// Note: Internally, the internal texture cache doesn't store
     /// 'empty' textures (height or width = 0)
     /// This means that glyph dimensions e.g. for spaces (' ') will mostly be None.
     pub fn get_glyph_dimensions(
         &self,
-        font: FontInstance,
+        font: FontInstanceKey,
         glyph_keys: Vec<GlyphKey>,
     ) -> Vec<Option<GlyphDimensions>> {
         let (tx, rx) = channel::msg_channel().unwrap();
         let msg = ApiMsg::GetGlyphDimensions(font, glyph_keys, tx);
         self.api_sender.send(msg).unwrap();
         rx.recv().unwrap()
     }
 
--- a/gfx/webrender_api/src/color.rs
+++ b/gfx/webrender_api/src/color.rs
@@ -1,14 +1,38 @@
 /* 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 std::cmp;
 use std::hash::{Hash, Hasher};
 
+/// Represents pre-multiplied RGBA colors with floating point numbers.
+///
+/// All components must be between 0.0 and 1.0.
+/// An alpha value of 1.0 is opaque while 0.0 is fully transparent.
+///
+/// In premultiplied colors transitions to transparent always look "nice"
+/// therefore they are used in CSS gradients.
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
+pub struct PremultipliedColorF {
+    pub r: f32,
+    pub g: f32,
+    pub b: f32,
+    pub a: f32,
+}
+
+impl PremultipliedColorF {
+    ///
+    pub const BLACK: Self = PremultipliedColorF { r: 0.0, g: 0.0, b: 0.0, a: 1.0 };
+    ///
+    pub const TRANSPARENT: Self = PremultipliedColorF { r: 0.0, g: 0.0, b: 0.0, a: 0.0 };
+}
+
 /// Represents RGBA screen colors with floating point numbers.
 ///
 /// All components must be between 0.0 and 1.0.
 /// An alpha value of 1.0 is opaque while 0.0 is fully transparent.
 #[repr(C)]
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct ColorF {
     pub r: f32,
@@ -33,43 +57,36 @@ impl ColorF {
         }
     }
 
     pub fn to_array(&self) -> [f32; 4] {
         [self.r, self.g, self.b, self.a]
     }
 
     /// Multiply the RGB components with the alpha channel.
-    ///
-    /// In premultiplied colors transistions to transparent always look "nice"
-    /// therefore they are used in CSS gradients.
-    pub fn premultiplied(&self) -> ColorF {
-        self.scale_rgb(self.a)
+    pub fn premultiplied(&self) -> PremultipliedColorF {
+        let c = self.scale_rgb(self.a);
+        PremultipliedColorF { r: c.r, g: c.g, b: c.b, a: c.a }
     }
 }
 
-// Floats don't impl Hash/Eq...
-impl Eq for ColorF {}
-impl Hash for ColorF {
+// Floats don't impl Hash/Eq/Ord...
+impl Eq for PremultipliedColorF {}
+impl Ord for PremultipliedColorF {
+    fn cmp(&self, other: &Self) -> cmp::Ordering {
+        self.partial_cmp(other).unwrap_or(cmp::Ordering::Equal)
+    }
+}
+impl Hash for PremultipliedColorF {
     fn hash<H: Hasher>(&self, state: &mut H) {
         // Note: this is inconsistent with the Eq impl for -0.0 (don't care).
-        self.r._to_bits().hash(state);
-        self.g._to_bits().hash(state);
-        self.b._to_bits().hash(state);
-        self.a._to_bits().hash(state);
-    }
-}
-
-// FIXME: remove this when Rust 1.21 is stable (float_bits_conv)
-pub trait ToBits {
-    fn _to_bits(self) -> u32;
-}
-impl ToBits for f32 {
-    fn _to_bits(self) -> u32 {
-        unsafe { ::std::mem::transmute(self) }
+        self.r.to_bits().hash(state);
+        self.g.to_bits().hash(state);
+        self.b.to_bits().hash(state);
+        self.a.to_bits().hash(state);
     }
 }
 
 /// Represents RGBA screen colors with one byte per channel.
 ///
 /// If the alpha value `a` is 255 the color is opaque.
 #[repr(C)]
 #[derive(Clone, Copy, Hash, Eq, Debug, Deserialize, PartialEq, PartialOrd, Ord, Serialize)]
@@ -90,29 +107,32 @@ impl ColorU {
 fn round_to_int(x: f32) -> u8 {
     debug_assert!((0.0 <= x) && (x <= 1.0));
     let f = (255.0 * x) + 0.5;
     let val = f.floor();
     debug_assert!(val <= 255.0);
     val as u8
 }
 
+// TODO: We shouldn't really convert back to `ColorU` ever,
+// since it's lossy. One of the blockers is that all of our debug colors
+// are specified in `ColorF`. Changing it to `ColorU` would be nice.
 impl From<ColorF> for ColorU {
-    fn from(color: ColorF) -> ColorU {
+    fn from(color: ColorF) -> Self {
         ColorU {
             r: round_to_int(color.r),
             g: round_to_int(color.g),
             b: round_to_int(color.b),
             a: round_to_int(color.a),
         }
     }
 }
 
 impl From<ColorU> for ColorF {
-    fn from(color: ColorU) -> ColorF {
+    fn from(color: ColorU) -> Self {
         ColorF {
             r: color.r as f32 / 255.0,
             g: color.g as f32 / 255.0,
             b: color.b as f32 / 255.0,
             a: color.a as f32 / 255.0,
         }
     }
 }
--- a/gfx/webrender_api/src/display_list.rs
+++ b/gfx/webrender_api/src/display_list.rs
@@ -528,32 +528,86 @@ impl<'a> Write for SizeCounter {
         self.0 += buf.len();
         Ok(())
     }
 
     #[inline(always)]
     fn flush(&mut self) -> io::Result<()> { Ok(()) }
 }
 
+/// Serializes a value assuming the Serialize impl has a stable size across two 
+/// invocations.
+///
+/// If this assumption is incorrect, the result will be Undefined Behaviour. This
+/// assumption should hold for all derived Serialize impls, which is all we currently
+/// use.
 fn serialize_fast<T: Serialize>(vec: &mut Vec<u8>, e: &T) {
     // manually counting the size is faster than vec.reserve(bincode::serialized_size(&e) as usize) for some reason
     let mut size = SizeCounter(0);
     bincode::serialize_into(&mut size,e , bincode::Infinite).unwrap();
     vec.reserve(size.0);
 
     let old_len = vec.len();
     let ptr = unsafe { vec.as_mut_ptr().offset(old_len as isize) };
     let mut w = UnsafeVecWriter(ptr);
     bincode::serialize_into(&mut w, e, bincode::Infinite).unwrap();
 
     // fix up the length
     unsafe { vec.set_len(old_len + size.0); }
 
     // make sure we wrote the right amount
-    debug_assert!(((w.0 as usize) - (vec.as_ptr() as usize)) == vec.len());
+    debug_assert_eq!(((w.0 as usize) - (vec.as_ptr() as usize)), vec.len());
+}
+
+/// Serializes an iterator, assuming: 
+///
+/// * The Clone impl is trivial (e.g. we're just memcopying a slice iterator)
+/// * The ExactSizeIterator impl is stable and correct across a Clone
+/// * The Serialize impl has a stable size across two invocations
+///
+/// If the first is incorrect, webrender will be very slow. If the other two are
+/// incorrect, the result will be Undefined Behaviour! The ExactSizeIterator
+/// bound would ideally be replaced with a TrustedLen bound to protect us a bit
+/// better, but that trait isn't stable (and won't be for a good while, if ever).
+///
+/// Debug asserts are included that should catch all Undefined Behaviour, but
+/// we can't afford to include these in release builds.
+fn serialize_iter_fast<I>(vec: &mut Vec<u8>, iter: I) -> usize
+where I: ExactSizeIterator + Clone,
+      I::Item: Serialize,
+{
+    // manually counting the size is faster than vec.reserve(bincode::serialized_size(&e) as usize) for some reason
+    let mut size = SizeCounter(0);
+    let mut count1 = 0;
+
+    for e in iter.clone() {
+        bincode::serialize_into(&mut size, &e, bincode::Infinite).unwrap();
+        count1 += 1;
+    }
+
+    vec.reserve(size.0);
+
+    let old_len = vec.len();
+    let ptr = unsafe { vec.as_mut_ptr().offset(old_len as isize) };
+    let mut w = UnsafeVecWriter(ptr);
+    let mut count2 = 0;
+
+    for e in iter {
+        bincode::serialize_into(&mut w, &e, bincode::Infinite).unwrap();
+        count2 += 1;
+    }
+
+    // fix up the length
+    unsafe { vec.set_len(old_len + size.0); }
+
+    // make sure we wrote the right amount
+    debug_assert_eq!(((w.0 as usize) - (vec.as_ptr() as usize)), vec.len());
+    debug_assert_eq!(count1, count2);
+
+    count1
 }
 
 // This uses a (start, end) representation instead of (start, len) so that
 // only need to update a single field as we read through it. This
 // makes it easier for llvm to understand what's going on. (https://github.com/rust-lang/rust/issues/45068)
 // We update the slice only once we're done reading
 struct UnsafeReader<'a: 'b, 'b> {
     start: *const u8,
@@ -749,36 +803,32 @@ impl DisplayListBuilder {
                 info,
             }
         )
     }
 
     fn push_iter<I>(&mut self, iter: I)
     where
         I: IntoIterator,
-        I::IntoIter: ExactSizeIterator,
+        I::IntoIter: ExactSizeIterator + Clone,
         I::Item: Serialize,
     {
         let iter = iter.into_iter();
         let len = iter.len();
-        let mut count = 0;
 
         // Format:
         // payload_byte_size: usize, item_count: usize, [I; item_count]
 
         // We write a dummy value so there's room for later
         let byte_size_offset = self.data.len();
         serialize_fast(&mut self.data, &0usize);
         serialize_fast(&mut self.data, &len);
         let payload_offset = self.data.len();
 
-        for elem in iter {
-            count += 1;
-            serialize_fast(&mut self.data, &elem);
-        }
+        let count = serialize_iter_fast(&mut self.data, iter.into_iter());
 
         // Now write the actual byte_size
         let final_offset = self.data.len();
         let byte_size = final_offset - payload_offset;
 
         // Note we don't use serialize_fast because we don't want to change the Vec's len
         bincode::serialize_into(&mut &mut self.data[byte_size_offset..],
                                 &byte_size,
@@ -1160,17 +1210,17 @@ impl DisplayListBuilder {
         content_rect: LayoutRect,
         clip_rect: LayoutRect,
         complex_clips: I,
         image_mask: Option<ImageMask>,
         scroll_sensitivity: ScrollSensitivity,
     ) -> ClipId
     where
         I: IntoIterator<Item = ComplexClipRegion>,
-        I::IntoIter: ExactSizeIterator,
+        I::IntoIter: ExactSizeIterator + Clone,
     {
         let parent = self.clip_stack.last().unwrap().scroll_node_id;
         self.define_scroll_frame_with_parent(
             id,
             parent,
             content_rect,
             clip_rect,
             complex_clips,
@@ -1185,17 +1235,17 @@ impl DisplayListBuilder {
         content_rect: LayoutRect,
         clip_rect: LayoutRect,
         complex_clips: I,
         image_mask: Option<ImageMask>,
         scroll_sensitivity: ScrollSensitivity,
     ) -> ClipId
     where
         I: IntoIterator<Item = ComplexClipRegion>,
-        I::IntoIter: ExactSizeIterator,
+        I::IntoIter: ExactSizeIterator + Clone,
     {
         let id = self.generate_clip_id(id);
         let item = SpecificDisplayItem::ScrollFrame(ScrollFrameDisplayItem {
             id: id,
             image_mask: image_mask,
             scroll_sensitivity,
         });
         let info = LayoutPrimitiveInfo::with_clip_rect(content_rect, clip_rect);
@@ -1210,17 +1260,17 @@ impl DisplayListBuilder {
         &mut self,
         id: Option<ClipId>,
         clip_rect: LayoutRect,
         complex_clips: I,
         image_mask: Option<ImageMask>,
     ) -> ClipId
     where
         I: IntoIterator<Item = ComplexClipRegion>,
-        I::IntoIter: ExactSizeIterator,
+        I::IntoIter: ExactSizeIterator + Clone,
     {
         let parent = self.clip_stack.last().unwrap().scroll_node_id;
         self.define_clip_with_parent(
             id,
             parent,
             clip_rect,
             complex_clips,
             image_mask)
@@ -1231,17 +1281,17 @@ impl DisplayListBuilder {
         id: Option<ClipId>,
         parent: ClipId,
         clip_rect: LayoutRect,
         complex_clips: I,
         image_mask: Option<ImageMask>,
     ) -> ClipId
     where
         I: IntoIterator<Item = ComplexClipRegion>,
-        I::IntoIter: ExactSizeIterator,
+        I::IntoIter: ExactSizeIterator + Clone,
     {
         let id = self.generate_clip_id(id);
         let item = SpecificDisplayItem::Clip(ClipDisplayItem {
             id,
             image_mask: image_mask,
         });
 
         let info = LayoutPrimitiveInfo::new(clip_rect);
--- a/gfx/webrender_api/src/font.rs
+++ b/gfx/webrender_api/src/font.rs
@@ -1,14 +1,13 @@
 /* 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 {ColorF, ColorU, IdNamespace, LayoutPoint, ToBits};
-use app_units::Au;
+use {ColorU, IdNamespace, LayoutPoint};
 #[cfg(target_os = "macos")]
 use core_foundation::string::CFString;
 #[cfg(target_os = "macos")]
 use core_graphics::font::CGFont;
 #[cfg(target_os = "windows")]
 use dwrote::FontDescriptor;
 #[cfg(target_os = "macos")]
 use serde::de::{self, Deserialize, Deserializer};
@@ -173,33 +172,33 @@ impl Into<f64> for SubpixelOffset {
 pub struct FontVariation {
     pub tag: u32,
     pub value: f32,
 }
 
 impl Ord for FontVariation {
     fn cmp(&self, other: &FontVariation) -> Ordering {
         self.tag.cmp(&other.tag)
-            .then(self.value._to_bits().cmp(&other.value._to_bits()))
+            .then(self.value.to_bits().cmp(&other.value.to_bits()))
     }
 }
 
 impl PartialEq for FontVariation {
     fn eq(&self, other: &FontVariation) -> bool {
         self.tag == other.tag &&
-        self.value._to_bits() == other.value._to_bits()
+        self.value.to_bits() == other.value.to_bits()
     }
 }
 
 impl Eq for FontVariation {}
 
 impl Hash for FontVariation {
     fn hash<H: Hasher>(&self, state: &mut H) {
         self.tag.hash(state);
-        self.value._to_bits().hash(state);
+        self.value.to_bits().hash(state);
     }
 }
 
 #[repr(C)]
 #[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)]
 pub struct GlyphOptions {
     pub render_mode: FontRenderMode,
 }
@@ -304,68 +303,16 @@ impl Default for FontInstancePlatformOpt
         FontInstancePlatformOptions {
             flags: 0,
             lcd_filter: FontLCDFilter::Default,
             hinting: FontHinting::LCD,
         }
     }
 }
 
-#[derive(Clone, Hash, PartialEq, Eq, Debug, Deserialize, Serialize, Ord, PartialOrd)]
-pub struct FontInstance {
-    pub font_key: FontKey,
-    // The font size is in *device* pixels, not logical pixels.
-    // It is stored as an Au since we need sub-pixel sizes, but
-    // can't store as a f32 due to use of this type as a hash key.
-    // TODO(gw): Perhaps consider having LogicalAu and DeviceAu
-    //           or something similar to that.
-    pub size: Au,
-    pub color: ColorU,
-    pub bg_color: ColorU,
-    pub render_mode: FontRenderMode,
-    pub subpx_dir: SubpixelDirection,
-    pub platform_options: Option<FontInstancePlatformOptions>,
-    pub variations: Vec<FontVariation>,
-    pub synthetic_italics: bool,
-}
-
-impl FontInstance {
-    pub fn new(
-        font_key: FontKey,
-        size: Au,
-        color: ColorF,
-        bg_color: ColorU,
-        render_mode: FontRenderMode,
-        subpx_dir: SubpixelDirection,
-        platform_options: Option<FontInstancePlatformOptions>,
-        variations: Vec<FontVariation>,
-        synthetic_italics: bool,
-    ) -> FontInstance {
-        FontInstance {
-            font_key,
-            size,
-            color: color.into(),
-            bg_color,
-            render_mode,
-            subpx_dir,
-            platform_options,
-            variations,
-            synthetic_italics,
-        }
-    }
-
-    pub fn get_subpx_offset(&self, glyph: &GlyphKey) -> (f64, f64) {
-        match self.subpx_dir {
-            SubpixelDirection::None => (0.0, 0.0),
-            SubpixelDirection::Horizontal => (glyph.subpixel_offset.into(), 0.0),
-            SubpixelDirection::Vertical => (0.0, glyph.subpixel_offset.into()),
-        }
-    }
-}
-
 #[repr(C)]
 #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Ord, PartialOrd)]
 pub struct FontInstanceKey(pub IdNamespace, pub u32);
 
 impl FontInstanceKey {
     pub fn new(namespace: IdNamespace, key: u32) -> FontInstanceKey {
         FontInstanceKey(namespace, key)
     }
--- a/gfx/webrender_bindings/Cargo.toml
+++ b/gfx/webrender_bindings/Cargo.toml
@@ -1,26 +1,26 @@
 [package]
 name = "webrender_bindings"
 version = "0.1.0"
 authors = ["The Mozilla Project Developers"]
 license = "MPL-2.0"
 
 [dependencies]
-webrender_api = {path = "../webrender_api", version = "0.53.1"}
+webrender_api = {path = "../webrender_api", version = "0.53.2"}
 rayon = "0.8"
 thread_profiler = "0.1.1"
 euclid = "0.15"
 app_units = "0.5.6"
 gleam = "0.4"
 log = "0.3"
 
 [dependencies.webrender]
 path = "../webrender"
-version = "0.53.1"
+version = "0.53.2"
 default-features = false
 
 [target.'cfg(target_os = "windows")'.dependencies]
 dwrote = "0.4"
 
 [target.'cfg(target_os = "macos")'.dependencies]
 core-foundation = "0.4"
 core-graphics = "0.9"
--- a/intl/locale/windows/OSPreferences_win.cpp
+++ b/intl/locale/windows/OSPreferences_win.cpp
@@ -171,18 +171,21 @@ OSPreferences::ReadDateTimePattern(DateT
     // but in a CLDR/ICU-style pattern these should be "EEE" and "EEEE".
     //   http://userguide.icu-project.org/formatparse/datetime
     // So we fix that up here.
     nsAString::const_iterator start, pos, end;
     start = str->BeginReading(pos);
     str->EndReading(end);
     if (FindInReadable(NS_LITERAL_STRING("dddd"), pos, end)) {
       str->ReplaceLiteral(pos - start, 4, u"EEEE");
-    } else if (FindInReadable(NS_LITERAL_STRING("ddd"), pos, end)) {
-      str->ReplaceLiteral(pos - start, 3, u"EEE");
+    } else {
+      pos = start;
+      if (FindInReadable(NS_LITERAL_STRING("ddd"), pos, end)) {
+        str->ReplaceLiteral(pos - start, 3, u"EEE");
+      }
     }
 
     // Also, Windows uses lowercase "g" or "gg" for era, but ICU wants uppercase "G"
     // (it would interpret "g" as "modified Julian day"!). So fix that.
     int32_t index = str->FindChar('g');
     if (index >= 0) {
       str->Replace(index, 1, 'G');
       // If it was a double "gg", just drop the second one.
--- a/js/src/gc/Verifier.cpp
+++ b/js/src/gc/Verifier.cpp
@@ -685,20 +685,22 @@ CheckGrayMarkingTracer::checkCell(Cell* 
     if (parent->isMarkedBlack() && cell->isMarkedGray()) {
         failures++;
 
         fprintf(stderr, "Found black to gray edge to ");
         dumpCellInfo(cell);
         fprintf(stderr, "\n");
         dumpCellPath();
 
+#ifdef DEBUG
         if (cell->getTraceKind() == JS::TraceKind::Object) {
             fprintf(stderr, "\n");
             DumpObject(static_cast<JSObject*>(cell), stderr);
         }
+#endif
     }
 }
 
 bool
 CheckGrayMarkingTracer::check(AutoLockForExclusiveAccess& lock)
 {
     if (!traceHeap(lock))
         return true; // Ignore failure.
--- a/js/src/old-configure.in
+++ b/js/src/old-configure.in
@@ -554,17 +554,22 @@ case "$host" in
     ;;
 esac
 
 MOZ_DOING_LTO(lto_is_enabled)
 
 dnl ========================================================
 dnl Add optional and non-optional hardening flags
 dnl ========================================================
-if test "$GNU_CC" -o test -n "${CLANG_CC}${CLANG_CL}"; then
+
+dnl Note that in the top-level old-configure.in, we don't enable
+dnl FORTIFY_SOURCE on Android. But in js/ we *can* enable it on
+dnl Android, so we do.
+
+if test "$GNU_CC" -o -n "${CLANG_CC}${CLANG_CL}"; then
    CFLAGS="$CFLAGS -D_FORTIFY_SOURCE=2"
    CPPFLAGS="$CPPFLAGS -D_FORTIFY_SOURCE=2"
    CXXFLAGS="$CXXFLAGS -D_FORTIFY_SOURCE=2"
 fi
 
 dnl ========================================================
 dnl System overrides of the defaults for target
 dnl ========================================================
@@ -1954,17 +1959,17 @@ dnl =
 dnl = Maintainer debug option (no --enable equivalent)
 dnl =
 dnl ========================================================
 
 AC_SUBST(AR)
 AC_SUBST(AR_FLAGS)
 AC_SUBST(AR_EXTRACT)
 AC_SUBST(AS)
-AC_SUBST(ASFLAGS)
+AC_SUBST_LIST(ASFLAGS)
 AC_SUBST(AS_DASH_C_FLAG)
 AC_SUBST(RC)
 AC_SUBST(RCFLAGS)
 AC_SUBST(WINDRES)
 AC_SUBST(IMPLIB)
 AC_SUBST(FILTER)
 AC_SUBST_LIST(MOZ_DEBUG_LDFLAGS)
 AC_SUBST(WARNINGS_AS_ERRORS)
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -3090,16 +3090,24 @@ nsPresContext::GfxUnitsToAppUnits(gfxFlo
 }
 
 gfxFloat
 nsPresContext::AppUnitsToGfxUnits(nscoord aAppUnits) const
 {
   return mDeviceContext->AppUnitsToGfxUnits(aAppUnits);
 }
 
+nscoord
+nsPresContext::PhysicalMillimetersToAppUnits(float aMM) const
+{
+  float inches = aMM / MM_PER_INCH_FLOAT;
+  return NSToCoordFloorClamped(
+      inches * float(DeviceContext()->AppUnitsPerPhysicalInch()));
+}
+
 bool
 nsPresContext::IsDeviceSizePageSize()
 {
   bool isDeviceSizePageSize = false;
   nsCOMPtr<nsIDocShell> docShell(mContainer);
   if (docShell) {
     isDeviceSizePageSize = docShell->GetDeviceSizeIsPageSize();
   }
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -712,16 +712,18 @@ public:
                     CSSTwipsToAppUnits(float(marginInTwips.right)),
                     CSSTwipsToAppUnits(float(marginInTwips.bottom)),
                     CSSTwipsToAppUnits(float(marginInTwips.left))); }
 
   static nscoord CSSPointsToAppUnits(float aPoints)
   { return NSToCoordRound(aPoints * mozilla::AppUnitsPerCSSInch() /
                           POINTS_PER_INCH_FLOAT); }
 
+  nscoord PhysicalMillimetersToAppUnits(float aMM) const;
+
   nscoord RoundAppUnitsToNearestDevPixels(nscoord aAppUnits) const
   { return DevPixelsToAppUnits(AppUnitsToDevPixels(aAppUnits)); }
 
   /**
    * This checks the root element and the HTML BODY, if any, for an "overflow"
    * property that should be applied to the viewport. If one is found then we
    * return the element that we took the overflow from (which should then be
    * treated as "overflow: visible"), and we store the overflow style here.
--- a/layout/base/tests/bug1093686_inner.html
+++ b/layout/base/tests/bug1093686_inner.html
@@ -6,17 +6,16 @@
   <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <style>
   .target { position:absolute; left:200px; top:200px; width:200px; height:200px; background:blue; }
   </style>
 </head>
 <body id="body" onload="setTimeout(runTest, 0)" style="margin:0; width:100%; height:100%; overflow:hidden">
 <div id="content">
-  <div id="ruler" style="position:absolute; left:0; top:0; width:1mozmm; height:0;"></div>
   <div class="target" id="t"></div>
 </div>
 <pre id="test">
 <script type="application/javascript">
 var eventTarget;
 window.onmousedown = function(event) { eventTarget = event.target; };
 
 // Make sure the target div is "clickable" by adding a click listener on it.
@@ -47,17 +46,17 @@ function testWithAndWithoutBodyListener(
   testMouseClick(aX, aY, aExpectedId, aMsg + " with listener on body");
   document.body.removeEventListener("click", func);
 }
 
 // Main tests
 
 var mm;
 function runTest() {
-  mm = document.getElementById("ruler").getBoundingClientRect().width;
+  mm = SpecialPowers.getDOMWindowUtils(parent).physicalMillimeterInCSSPixels;
   parent.ok(4*mm >= 10, "WARNING: mm " + mm + " too small in this configuration. Test results will be bogus");
 
   // Test near the target, check it hits the target
   testWithAndWithoutBodyListener(200 - 2*mm, 200 - 2*mm, "t", "basic click retargeting");
   // Test on the target, check it hits the target
   testWithAndWithoutBodyListener(200 + 2*mm, 200 + 2*mm, "t", "direct click");
   // Test outside the target, check it hits the root
   testWithAndWithoutBodyListener(40, 40, "body", "click way outside target");
--- a/layout/base/tests/test_event_target_radius.html
+++ b/layout/base/tests/test_event_target_radius.html
@@ -10,18 +10,16 @@ https://bugzilla.mozilla.org/show_bug.cg
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <style>
   .target { position:absolute; left:100px; top:100px; width:100px; height:100px; background:blue; }
   </style>
 </head>
 <body id="body" onload="setTimeout(startTest, 0)" style="margin:0; width:100%; height:100%; overflow:hidden">
 <p id="display"></p>
 <div id="content">
-  <div id="ruler" style="position:absolute; left:0; top:0; width:1mozmm; height:0;"></div>
-
   <!-- XXX Though B2G isn't supported anymore, we probably want to keep this
        test's B2G special-cases around, and just make them apply to Android.
        (We may need to do so, for this test to be runnable on Android...?)
        See bug 1355206 for more.
   -->
   <!-- the iframe holding this test is only 300px tall on B2G, so we need to
        make the t target shorter than normal to test the bottom edge fluffing
   -->
@@ -108,17 +106,17 @@ function runTest() {
   if (SpecialPowers.Services.appinfo.name == "B2G") {
     // This test runs on B2G as well, zoomed out. Therefore we need to account
     // for the resolution as well, because the fluff area is relative to screen
     // pixels rather than CSS pixels.
     let out = {};
     SpecialPowers.getDOMWindowUtils(window.top).getResolution(out);
     resolution = 1.0 / out.value;
   }
-  mm = document.getElementById("ruler").getBoundingClientRect().width * resolution;
+  mm = SpecialPowers.getDOMWindowUtils(window.top).physicalMillimeterInCSSPixels * resolution;
   ok(4*mm >= 10, "WARNING: mm " + mm + " too small in this configuration. Test results will be bogus");
 
   // Test basic functionality: clicks sufficiently close to the element
   // should be allowed to hit the element. We test points just inside and
   // just outside the edges we set up in the prefs.
   testMouseClick("t", 100 + 13*mm, 10, "body", "basic functionality");
   testMouseClick("t", 100 + 11*mm, 10, "t", "basic functionality");
   testMouseClick("t", 10, 80 + 9*mm, "body", "basic functionality");
--- a/layout/mathml/nsMathMLFrame.cpp
+++ b/layout/mathml/nsMathMLFrame.cpp
@@ -214,19 +214,16 @@ nsMathMLFrame::GetAxisHeight(DrawTarget*
 /* static */ nscoord
 nsMathMLFrame::CalcLength(nsPresContext*   aPresContext,
                           nsStyleContext*   aStyleContext,
                           const nsCSSValue& aCSSValue,
                           float             aFontSizeInflation)
 {
   NS_ASSERTION(aCSSValue.IsLengthUnit(), "not a length unit");
 
-  if (aCSSValue.IsFixedLengthUnit()) {
-    return aCSSValue.GetFixedLength(aPresContext);
-  }
   if (aCSSValue.IsPixelLengthUnit()) {
     return aCSSValue.GetPixelLength();
   }
 
   nsCSSUnit unit = aCSSValue.GetUnit();
 
   if (eCSSUnit_EM == unit) {
     const nsStyleFont* font = aStyleContext->StyleFont();
--- a/layout/painting/nsCSSRenderingBorders.cpp
+++ b/layout/painting/nsCSSRenderingBorders.cpp
@@ -3583,17 +3583,20 @@ nsCSSBorderRenderer::DrawBorders()
     }
   }
 }
 
 bool
 nsCSSBorderRenderer::CanCreateWebRenderCommands()
 {
   NS_FOR_CSS_SIDES(i) {
-    if (mCompositeColors[i] != nullptr) {
+    if (mCompositeColors[i] != nullptr &&
+        mBorderWidths[i] > 0.0f &&
+        mBorderStyles[i] != NS_STYLE_BORDER_STYLE_HIDDEN &&
+        mBorderStyles[i] != NS_STYLE_BORDER_STYLE_NONE) {
       return false;
     }
   }
 
   return true;
 }
 
 void
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -4304,16 +4304,29 @@ nsDisplayThemedBackground::PaintInternal
   nsITheme *theme = presContext->GetTheme();
   nsRect drawing(mBackgroundRect);
   theme->GetWidgetOverflow(presContext->DeviceContext(), mFrame, mAppearance,
                            &drawing);
   drawing.IntersectRect(drawing, aBounds);
   theme->DrawWidgetBackground(aCtx, mFrame, mAppearance, mBackgroundRect, drawing);
 }
 
+
+bool
+nsDisplayThemedBackground::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
+                                                   mozilla::wr::IpcResourceUpdateQueue& aResources,
+                                                   const StackingContextHelper& aSc,
+                                                   mozilla::layers::WebRenderLayerManager* aManager,
+                                                   nsDisplayListBuilder* aDisplayListBuilder)
+{
+  nsITheme *theme = mFrame->PresContext()->GetTheme();
+  return theme->CreateWebRenderCommandsForWidget(aBuilder, aResources, aSc, aManager,
+                                                 mFrame, mAppearance, mBackgroundRect);
+}
+
 bool
 nsDisplayThemedBackground::IsWindowActive() const
 {
   EventStates docState = mFrame->GetContent()->OwnerDoc()->GetDocumentState();
   return !docState.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE);
 }
 
 void
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -3891,16 +3891,21 @@ public:
     nsDisplayItem::Destroy(aBuilder);
   }
 
   virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                        HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) override;
   virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                                    bool* aSnap) const override;
   virtual mozilla::Maybe<nscolor> IsUniform(nsDisplayListBuilder* aBuilder) const override;
+  virtual bool CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
+                                       mozilla::wr::IpcResourceUpdateQueue& aResources,
+                                       const StackingContextHelper& aSc,
+                                       mozilla::layers::WebRenderLayerManager* aManager,
+                                       nsDisplayListBuilder* aDisplayListBuilder) override;
   virtual bool MustPaintOnContentSide() const override { return true; }
 
   /**
    * GetBounds() returns the background painting area.
    */
   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
                            bool* aSnap) const override;
   virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -7356,17 +7356,16 @@ const UnitInfo UnitData[] = {
   { STR_WITH_LEN("em"), eCSSUnit_EM, VARIANT_LENGTH },
   { STR_WITH_LEN("ex"), eCSSUnit_XHeight, VARIANT_LENGTH },
   { STR_WITH_LEN("pt"), eCSSUnit_Point, VARIANT_LENGTH },
   { STR_WITH_LEN("in"), eCSSUnit_Inch, VARIANT_LENGTH },
   { STR_WITH_LEN("cm"), eCSSUnit_Centimeter, VARIANT_LENGTH },
   { STR_WITH_LEN("ch"), eCSSUnit_Char, VARIANT_LENGTH },
   { STR_WITH_LEN("rem"), eCSSUnit_RootEM, VARIANT_LENGTH },
   { STR_WITH_LEN("mm"), eCSSUnit_Millimeter, VARIANT_LENGTH },
-  { STR_WITH_LEN("mozmm"), eCSSUnit_PhysicalMillimeter, VARIANT_LENGTH },
   { STR_WITH_LEN("vw"), eCSSUnit_ViewportWidth, VARIANT_LENGTH },
   { STR_WITH_LEN("vh"), eCSSUnit_ViewportHeight, VARIANT_LENGTH },
   { STR_WITH_LEN("vmin"), eCSSUnit_ViewportMin, VARIANT_LENGTH },
   { STR_WITH_LEN("vmax"), eCSSUnit_ViewportMax, VARIANT_LENGTH },
   { STR_WITH_LEN("pc"), eCSSUnit_Pica, VARIANT_LENGTH },
   { STR_WITH_LEN("q"), eCSSUnit_Quarter, VARIANT_LENGTH },
   { STR_WITH_LEN("deg"), eCSSUnit_Degree, VARIANT_ANGLE },
   { STR_WITH_LEN("grad"), eCSSUnit_Grad, VARIANT_ANGLE },
--- a/layout/style/nsCSSValue.cpp
+++ b/layout/style/nsCSSValue.cpp
@@ -379,26 +379,16 @@ nsCSSValue::GetPossiblyStaticImageValue(
 {
   imgRequestProxy* req = GetImageValue(aDocument);
   if (aPresContext->IsDynamic()) {
     return do_AddRef(req);
   }
   return nsContentUtils::GetStaticRequest(aDocument, req);
 }
 
-nscoord nsCSSValue::GetFixedLength(nsPresContext* aPresContext) const
-{
-  MOZ_ASSERT(mUnit == eCSSUnit_PhysicalMillimeter,
-             "not a fixed length unit");
-
-  float inches = mValue.mFloat / MM_PER_INCH_FLOAT;
-  return NSToCoordFloorClamped(inches *
-    float(aPresContext->DeviceContext()->AppUnitsPerPhysicalInch()));
-}
-
 nscoord nsCSSValue::GetPixelLength() const
 {
   MOZ_ASSERT(IsPixelLengthUnit(), "not a fixed length unit");
 
   double scaleFactor;
   switch (mUnit) {
   case eCSSUnit_Pixel: return nsPresContext::CSSPixelsToAppUnits(mValue.mFloat);
   case eCSSUnit_Pica: scaleFactor = 16.0; break;
@@ -2059,17 +2049,16 @@ nsCSSValue::AppendToString(nsCSSProperty
     case eCSSUnit_ListDep:      break;
     case eCSSUnit_SharedList:   break;
     case eCSSUnit_PairList:     break;
     case eCSSUnit_PairListDep:  break;
     case eCSSUnit_GridTemplateAreas:     break;
 
     case eCSSUnit_Inch:         aResult.AppendLiteral("in");   break;
     case eCSSUnit_Millimeter:   aResult.AppendLiteral("mm");   break;
-    case eCSSUnit_PhysicalMillimeter: aResult.AppendLiteral("mozmm");   break;
     case eCSSUnit_Centimeter:   aResult.AppendLiteral("cm");   break;
     case eCSSUnit_Point:        aResult.AppendLiteral("pt");   break;
     case eCSSUnit_Pica:         aResult.AppendLiteral("pc");   break;
     case eCSSUnit_Quarter:      aResult.AppendLiteral("q");    break;
 
     case eCSSUnit_ViewportWidth:  aResult.AppendLiteral("vw");   break;
     case eCSSUnit_ViewportHeight: aResult.AppendLiteral("vh");   break;
     case eCSSUnit_ViewportMin:    aResult.AppendLiteral("vmin"); break;
@@ -2244,17 +2233,16 @@ nsCSSValue::SizeOfExcludingThis(mozilla:
     // Complex Color
     case eCSSUnit_ComplexColor:
       n += mValue.mComplexColor->SizeOfIncludingThis(aMallocSizeOf);
       break;
 
     // Float: nothing extra to measure.
     case eCSSUnit_Percent:
     case eCSSUnit_Number:
-    case eCSSUnit_PhysicalMillimeter:
     case eCSSUnit_ViewportWidth:
     case eCSSUnit_ViewportHeight:
     case eCSSUnit_ViewportMin:
     case eCSSUnit_ViewportMax:
     case eCSSUnit_EM:
     case eCSSUnit_XHeight:
     case eCSSUnit_Char:
     case eCSSUnit_RootEM:
--- a/layout/style/nsCSSValue.h
+++ b/layout/style/nsCSSValue.h
@@ -529,19 +529,16 @@ enum nsCSSUnit {
                                        // allowed.
   eCSSUnit_HSLColor            = 89,   // (nsCSSValueFloatColor*)
   eCSSUnit_HSLAColor           = 90,   // (nsCSSValueFloatColor*)
   eCSSUnit_ComplexColor        = 91,   // (ComplexColorValue*)
 
   eCSSUnit_Percent      = 100,     // (float) 1.0 == 100%) value is percentage of something
   eCSSUnit_Number       = 101,     // (float) value is numeric (usually multiplier, different behavior than percent)
 
-  // Physical length units
-  eCSSUnit_PhysicalMillimeter = 200,   // (float) 1/25.4 inch
-
   // Length units - relative
   // Viewport relative measure
   eCSSUnit_ViewportWidth  = 700,    // (float) 1% of the width of the initial containing block
   eCSSUnit_ViewportHeight = 701,    // (float) 1% of the height of the initial containing block
   eCSSUnit_ViewportMin    = 702,    // (float) smaller of ViewportWidth and ViewportHeight
   eCSSUnit_ViewportMax    = 703,    // (float) larger of ViewportWidth and ViewportHeight
 
   // Font relative measure
@@ -639,27 +636,20 @@ public:
   /**
    * Serialize |this| as a specified value for |aProperty| and append
    * it to |aResult|.
    */
   void AppendToString(nsCSSPropertyID aProperty, nsAString& aResult) const;
 
   nsCSSUnit GetUnit() const { return mUnit; }
   bool      IsLengthUnit() const
-    { return eCSSUnit_PhysicalMillimeter <= mUnit && mUnit <= eCSSUnit_Pixel; }
+    { return eCSSUnit_ViewportWidth <= mUnit && mUnit <= eCSSUnit_Pixel; }
   bool      IsLengthPercentCalcUnit() const
     { return IsLengthUnit() || mUnit == eCSSUnit_Percent || IsCalcUnit(); }
   /**
-   * A "fixed" length unit is one that means a specific physical length
-   * which we try to match based on the physical characteristics of an
-   * output device.
-   */
-  bool      IsFixedLengthUnit() const
-    { return mUnit == eCSSUnit_PhysicalMillimeter; }
-  /**
    * What the spec calls relative length units is, for us, split
    * between relative length units and pixel length units.
    *
    * A "relative" length unit is a multiple of some derived metric,
    * such as a font em-size, which itself was controlled by an input CSS
    * length. Relative length units should not be scaled by zooming, since
    * the underlying CSS length would already have been scaled.
    */
@@ -880,17 +870,16 @@ public:
   // all over.
   imgRequestProxy* GetImageValue(nsIDocument* aDocument) const;
 
   // Like GetImageValue, but additionally will pass the imgRequestProxy
   // through nsContentUtils::GetStaticRequest if aPresContent is static.
   already_AddRefed<imgRequestProxy> GetPossiblyStaticImageValue(
       nsIDocument* aDocument, nsPresContext* aPresContext) const;
 
-  nscoord GetFixedLength(nsPresContext* aPresContext) const;
   nscoord GetPixelLength() const;
 
   nsCSSValueFloatColor* GetFloatColorValue() const
   {
     MOZ_ASSERT(IsFloatColorUnit(), "not a float color value");
     return mValue.mFloatColor;
   }
 
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -540,19 +540,16 @@ static nscoord CalcLengthWith(const nsCS
   NS_ASSERTION(aValue.IsLengthUnit() || aValue.IsCalcUnit(),
                "not a length or calc unit");
   NS_ASSERTION(aStyleFont || aStyleContext,
                "Must have style data");
   NS_ASSERTION(aStyleContext || aUseProvidedRootEmSize,
                "Must have style context or specify aUseProvidedRootEmSize");
   NS_ASSERTION(aPresContext, "Must have prescontext");
 
-  if (aValue.IsFixedLengthUnit()) {
-    return aValue.GetFixedLength(aPresContext);
-  }
   if (aValue.IsPixelLengthUnit()) {
     return aValue.GetPixelLength();
   }
   if (aValue.IsCalcUnit()) {
     // For properties for which lengths are the *only* units accepted in
     // calc(), we can handle calc() here and just compute a final
     // result.  We ensure that we don't get to this code for other
     // properties by not calling CalcLength in those cases:  SetCoord
--- a/layout/style/test/test_pixel_lengths.html
+++ b/layout/style/test/test_pixel_lengths.html
@@ -10,18 +10,16 @@
 
 <div id="pt" style="width:90pt; height:90pt; background:lime;">pt</div>
 <div id="pc" style="width:5pc; height:5pc; background:yellow;">pc</div>
 <div id="mm" style="width:25.4mm; height:25.4mm; background:orange;">mm</div>
 <div id="cm" style="width:2.54cm; height:2.54cm; background:purple;">cm</div>
 <div id="in" style="width:1in; height:1in; background:magenta;">in</div>
 <div id="q" style="width:101.6q; height:101.6q; background:blue;">q</div>
 
-<div id="mozmm" style="width:25.4mozmm; height:25.4mozmm; background:cyan;">mozmm</div>
-
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 var oldDPI = SpecialPowers.getIntPref("layout.css.dpi");
 var dpi = oldDPI;
 
 function check(id, val) {
@@ -42,34 +40,22 @@ function checkPixelRelativeUnits() {
 }
 
 checkPixelRelativeUnits();
 
 SimpleTest.waitForExplicitFinish();
 
 SpecialPowers.pushPrefEnv({'set': [['layout.css.dpi', dpi=96]]}, test1);
 
-var mozmmSize;
 function test1() {
-  var mozmm = document.getElementById("mozmm");
-  mozmmSize = mozmm.getBoundingClientRect().width;
-  is(Math.round(mozmmSize), Math.round(mozmm.getBoundingClientRect().height),
-     "mozmm div should be square");
-
   checkPixelRelativeUnits();
-
   SpecialPowers.pushPrefEnv({'set': [['layout.css.dpi', dpi=192]]}, test2);
 }
 
 function test2() {
-  // At 192 dpi, a one-inch box should be twice the number of device pixels,
-  // and since we haven't changed the device-pixels-per-CSS-pixel ratio, the
-  // mozmm box should be twice the size in CSS pixels.
-  check("mozmm", mozmmSize*2);
   checkPixelRelativeUnits();
-
   SimpleTest.finish();
 }
 
 </script>
 </pre>
 </body>
 </html>
--- a/memory/build/mozjemalloc.cpp
+++ b/memory/build/mozjemalloc.cpp
@@ -537,17 +537,17 @@ static void*
 base_alloc(size_t aSize);
 
 // Mutexes based on spinlocks.  We can't use normal pthread spinlocks in all
 // places, because they require malloc()ed memory, which causes bootstrapping
 // issues in some cases.
 struct Mutex
 {
 #if defined(XP_WIN)
-  CRITICAL_SECTION mMutex;
+  SRWLOCK mMutex;
 #elif defined(XP_DARWIN)
   OSSpinLock mMutex;
 #else
   pthread_mutex_t mMutex;
 #endif
 
   inline bool Init();
 
@@ -571,17 +571,17 @@ private:
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER;
   Mutex& mMutex;
 };
 
 // Set to true once the allocator has been initialized.
 static Atomic<bool> malloc_initialized(false);
 
 #if defined(XP_WIN)
-// No init lock for Windows.
+static Mutex gInitLock = { SRWLOCK_INIT };
 #elif defined(XP_DARWIN)
 static Mutex gInitLock = { OS_SPINLOCK_INIT };
 #elif defined(XP_LINUX) && !defined(ANDROID)
 static Mutex gInitLock = { PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP };
 #else
 static Mutex gInitLock = { PTHREAD_MUTEX_INITIALIZER };
 #endif
 
@@ -1253,23 +1253,18 @@ chunk_ensure_zero(void* aPtr, size_t aSi
 static void*
 huge_malloc(size_t size, bool zero, arena_t* aArena);
 static void*
 huge_palloc(size_t aSize, size_t aAlignment, bool aZero, arena_t* aArena);
 static void*
 huge_ralloc(void* aPtr, size_t aSize, size_t aOldSize, arena_t* aArena);
 static void
 huge_dalloc(void* aPtr, arena_t* aArena);
-#ifdef XP_WIN
-extern "C"
-#else
-static
-#endif
-  bool
-  malloc_init_hard();
+static bool
+malloc_init_hard();
 
 #ifdef XP_DARWIN
 #define FORK_HOOK extern "C"
 #else
 #define FORK_HOOK static
 #endif
 FORK_HOOK void
 _malloc_prefork(void);
@@ -1279,31 +1274,27 @@ FORK_HOOK void
 _malloc_postfork_child(void);
 
 // End forward declarations.
 // ***************************************************************************
 
 // FreeBSD's pthreads implementation calls malloc(3), so the malloc
 // implementation has to take pains to avoid infinite recursion during
 // initialization.
-#if defined(XP_WIN)
-#define malloc_init() true
-#else
 // Returns whether the allocator was successfully initialized.
 static inline bool
 malloc_init()
 {
 
   if (malloc_initialized == false) {
     return malloc_init_hard();
   }
 
   return true;
 }
-#endif
 
 static void
 _malloc_message(const char* p)
 {
 #if !defined(XP_WIN)
 #define _write write
 #endif
   // Pretend to check _write() errors to suppress gcc warnings about
@@ -1333,19 +1324,17 @@ pthread_atfork(void (*)(void), void (*)(
 // cases. We also can't use constructors, because for statics, they would fire
 // after the first use of malloc, resetting the locks.
 
 // Initializes a mutex. Returns whether initialization succeeded.
 bool
 Mutex::Init()
 {
 #if defined(XP_WIN)
-  if (!InitializeCriticalSectionAndSpinCount(&mMutex, 5000)) {
-    return false;
-  }
+  InitializeSRWLock(&mMutex);
 #elif defined(XP_DARWIN)
   mMutex = OS_SPINLOCK_INIT;
 #elif defined(XP_LINUX) && !defined(ANDROID)
   pthread_mutexattr_t attr;
   if (pthread_mutexattr_init(&attr) != 0) {
     return false;
   }
   pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP);
@@ -1361,29 +1350,29 @@ Mutex::Init()
 #endif
   return true;
 }
 
 void
 Mutex::Lock()
 {
 #if defined(XP_WIN)
-  EnterCriticalSection(&mMutex);
+  AcquireSRWLockExclusive(&mMutex);
 #elif defined(XP_DARWIN)
   OSSpinLockLock(&mMutex);
 #else
   pthread_mutex_lock(&mMutex);
 #endif
 }
 
 void
 Mutex::Unlock()
 {
 #if defined(XP_WIN)
-  LeaveCriticalSection(&mMutex);
+  ReleaseSRWLockExclusive(&mMutex);
 #elif defined(XP_DARWIN)
   OSSpinLockUnlock(&mMutex);
 #else
   pthread_mutex_unlock(&mMutex);
 #endif
 }
 
 // End mutex.
@@ -4066,29 +4055,24 @@ GetKernelPageSize()
     MOZ_ASSERT(result != -1);
     return result;
 #endif
   })();
   return kernel_page_size;
 }
 
 // Returns whether the allocator was successfully initialized.
-#if !defined(XP_WIN)
-static
-#endif
-  bool
-  malloc_init_hard()
+static bool
+malloc_init_hard()
 {
   unsigned i;
   const char* opts;
   long result;
 
-#ifndef XP_WIN
   MutexAutoLock lock(gInitLock);
-#endif
 
   if (malloc_initialized) {
     // Another thread initialized the allocator before this one
     // acquired gInitLock.
     return true;
   }
 
   if (!thread_arena.init()) {
@@ -5073,32 +5057,9 @@ void*
   return nullptr;
 }
 
 size_t
 _msize(void* aPtr)
 {
   return DefaultMalloc::malloc_usable_size(aPtr);
 }
-
-// In the new style jemalloc integration jemalloc is built as a separate
-// shared library.  Since we're no longer hooking into the CRT binary,
-// we need to initialize the heap at the first opportunity we get.
-// DLL_PROCESS_ATTACH in DllMain is that opportunity.
-BOOL APIENTRY
-DllMain(HINSTANCE hModule, DWORD reason, LPVOID lpReserved)
-{
-  switch (reason) {
-    case DLL_PROCESS_ATTACH:
-      // Don't force the system to page DllMain back in every time
-      // we create/destroy a thread
-      DisableThreadLibraryCalls(hModule);
-      // Initialize the heap
-      malloc_init_hard();
-      break;
-
-    case DLL_PROCESS_DETACH:
-      break;
-  }
-
-  return TRUE;
-}
 #endif
--- a/memory/replace/logalloc/replay/Replay.cpp
+++ b/memory/replace/logalloc/replay/Replay.cpp
@@ -279,22 +279,16 @@ MOZ_BEGIN_EXTERN_C
 #define MALLOC_FUNCS MALLOC_FUNCS_MALLOC
 #include "malloc_decls.h"
 
 #define MALLOC_DECL(name, return_type, ...) \
   return_type name(__VA_ARGS__);
 #define MALLOC_FUNCS MALLOC_FUNCS_JEMALLOC
 #include "malloc_decls.h"
 
-/* mozjemalloc relies on DllMain to initialize, but DllMain is not invoked
- * for executables, so manually invoke mozjemalloc initialization. */
-#if defined(_WIN32)
-void malloc_init_hard(void);
-#endif
-
 #ifdef ANDROID
 /* mozjemalloc uses MozTagAnonymousMemory, which doesn't have an inline
  * implementation on Android */
 void
 MozTagAnonymousMemory(const void* aPtr, size_t aLength, const char* aTag) {}
 
 /* mozjemalloc and jemalloc use pthread_atfork, which Android doesn't have.
  * While gecko has one in libmozglue, the replay program can't use that.
@@ -470,20 +464,16 @@ private:
 
 int
 main()
 {
   size_t first_pid = 0;
   FdReader reader(0);
   Replay replay;
 
-#if defined(_WIN32)
-  malloc_init_hard();
-#endif
-
   /* Read log from stdin and dispatch function calls to the Replay instance.
    * The log format is essentially:
    *   <pid> <function>([<args>])[=<result>]
    * <args> is a comma separated list of arguments.
    *
    * The logs are expected to be preprocessed so that allocations are
    * attributed a tracking slot. The input is trusted not to have crazy
    * values for these slot numbers.
--- a/mfbt/moz.build
+++ b/mfbt/moz.build
@@ -4,21 +4,16 @@
 # 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/.
 
 with Files("**"):
     BUG_COMPONENT = ("Core", "MFBT")
 
 TEST_DIRS += ['tests']
 
-# On win we build two mfbt libs - mfbt linked to crt dlls here and in
-# staticruntime we build a statically linked mfbt lib.
-if CONFIG['OS_ARCH'] == 'WINNT':
-    DIRS += ['staticruntime']
-
 Library('mfbt')
 
 EXPORTS.mozilla = [
     'Alignment.h',
     'AllocPolicy.h',
     'AlreadyAddRefed.h',
     'Array.h',
     'ArrayUtils.h',
@@ -122,35 +117,54 @@ if CONFIG['OS_ARCH'] == 'WINNT':
     EXPORTS.mozilla += [
         'WindowsVersion.h',
     ]
 elif CONFIG['OS_ARCH'] == 'Linux':
     EXPORTS.mozilla += [
         'LinuxSignal.h',
     ]
 
-include('objs.mozbuild')
-
-UNIFIED_SOURCES += mfbt_src_cppsrcs
+UNIFIED_SOURCES += [
+    'Assertions.cpp',
+    'ChaosMode.cpp',
+    'double-conversion/double-conversion/bignum-dtoa.cc',
+    'double-conversion/double-conversion/bignum.cc',
+    'double-conversion/double-conversion/cached-powers.cc',
+    'double-conversion/double-conversion/diy-fp.cc',
+    'double-conversion/double-conversion/double-conversion.cc',
+    'double-conversion/double-conversion/fast-dtoa.cc',
+    'double-conversion/double-conversion/fixed-dtoa.cc',
+    'double-conversion/double-conversion/strtod.cc',
+    'FloatingPoint.cpp',
+    'HashFunctions.cpp',
+    'JSONWriter.cpp',
+    'Poison.cpp',
+    'SHA1.cpp',
+    'TaggedAnonymousMemory.cpp',
+    'Unused.cpp',
+]
 
 DEFINES['IMPL_MFBT'] = True
 
-SOURCES += mfbt_nonunified_src_cppsrcs
+SOURCES += [
+    'Compression.cpp',
+    'decimal/Decimal.cpp',
+]
 
 DisableStlWrapping()
 
 # Suppress warnings in third-party LZ4 code.
 # TODO: Remove these suppressions after bug 993267 is fixed.
 
 if CONFIG['GNU_CXX']:
-    SOURCES['/mfbt/Compression.cpp'].flags += ['-Wno-unused-function']
+    SOURCES['Compression.cpp'].flags += ['-Wno-unused-function']
     CXXFLAGS += ['-Wno-error=shadow']
 
 if CONFIG['CLANG_CXX']:
     # Suppress warnings from third-party V8 Decimal code.
-    SOURCES['/mfbt/decimal/Decimal.cpp'].flags += ['-Wno-implicit-fallthrough']
+    SOURCES['decimal/Decimal.cpp'].flags += ['-Wno-implicit-fallthrough']
 
 if CONFIG['_MSC_VER']:
     # Error 4804 is "'>' : unsafe use of type 'bool' in operation"
-    SOURCES['/mfbt/Compression.cpp'].flags += ['-wd4804']
+    SOURCES['Compression.cpp'].flags += ['-wd4804']
 
 if CONFIG['MOZ_NEEDS_LIBATOMIC']:
     OS_LIBS += ['atomic']
deleted file mode 100644
--- a/mfbt/objs.mozbuild
+++ /dev/null
@@ -1,40 +0,0 @@
-# -*- 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/.
-
-mfbt_src_lcppsrcs = [
-    'Assertions.cpp',
-    'ChaosMode.cpp',
-    'double-conversion/double-conversion/bignum-dtoa.cc',
-    'double-conversion/double-conversion/bignum.cc',
-    'double-conversion/double-conversion/cached-powers.cc',
-    'double-conversion/double-conversion/diy-fp.cc',
-    'double-conversion/double-conversion/double-conversion.cc',
-    'double-conversion/double-conversion/fast-dtoa.cc',
-    'double-conversion/double-conversion/fixed-dtoa.cc',
-    'double-conversion/double-conversion/strtod.cc',
-    'FloatingPoint.cpp',
-    'HashFunctions.cpp',
-    'JSONWriter.cpp',
-    'Poison.cpp',
-    'SHA1.cpp',
-    'TaggedAnonymousMemory.cpp',
-    'Unused.cpp',
-]
-
-mfbt_src_cppsrcs = [
-    '/mfbt/%s' % s for s in mfbt_src_lcppsrcs
-]
-
-# Compression.cpp cannot be built in unified mode because it pulls in Windows system headers.
-# Decimal.cpp doesn't build in unified mode with gcc.
-mfbt_nonunified_src_lcppsrcs = [
-    'Compression.cpp',
-    'decimal/Decimal.cpp',
-]
-
-mfbt_nonunified_src_cppsrcs = [
-    '/mfbt/%s' % s for s in mfbt_nonunified_src_lcppsrcs
-]
deleted file mode 100644
--- a/mfbt/staticruntime/moz.build
+++ /dev/null
@@ -1,33 +0,0 @@
-# -*- 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/.
-
-Library('mfbt_staticruntime')
-
-LOCAL_INCLUDES += [
-    '/mfbt/double-conversion',
-]
-
-include('../objs.mozbuild')
-
-UNIFIED_SOURCES += mfbt_src_cppsrcs
-
-DEFINES['IMPL_MFBT'] = True
-
-SOURCES += mfbt_nonunified_src_cppsrcs
-
-USE_STATIC_LIBS = True
-
-DisableStlWrapping()
-
-# Suppress warnings in third-party LZ4 code.
-# TODO: Remove these suppressions after bug 993267 is fixed.
-
-if CONFIG['GNU_CXX']:
-    SOURCES['/mfbt/Compression.cpp'].flags += ['-Wno-unused-function']
-
-if CONFIG['_MSC_VER']:
-    # Error 4804 is "'>' : unsafe use of type 'bool' in operation"
-    SOURCES['/mfbt/Compression.cpp'].flags += ['-wd4804']
--- a/mobile/android/app/build.gradle
+++ b/mobile/android/app/build.gradle
@@ -308,21 +308,17 @@ task checkstyle(type: Checkstyle) {
     // TODO: This ignores our pre-processed resources.
     include '**/*.java'
     // TODO: classpath should probably be something.
     classpath = files()
 }
 
 task syncPreprocessedCode(type: Sync, dependsOn: rootProject.generateCodeAndResources) {
     into("${project.buildDir}/generated/source/preprocessed_code")
-    from("${topobjdir}/mobile/android/base/generated/preprocessed") {
-        // All other preprocessed code is included in the geckoview project.
-        include '**/AdjustConstants.java'
-        include '**/MmaConstants.java'
-    }
+    from("${topobjdir}/mobile/android/base/generated/preprocessed")
 }
 
 // The localization system uses the moz.build preprocessor to interpolate a .dtd
 // file of XML entity definitions into an XML file of elements referencing those
 // entities.  (Each locale produces its own .dtd file, backstopped by the en-US
 // .dtd file in tree.)  Android Studio (and IntelliJ) don't handle these inline
 // entities smoothly.  This filter merely expands the entities in place, making
 // them appear properly throughout the IDE.  Be aware that this assumes that the
--- a/mobile/android/base/android-services.mozbuild
+++ b/mobile/android/base/android-services.mozbuild
@@ -902,17 +902,16 @@ sync_java_files = [TOPSRCDIR + '/mobile/
     'sync/MetaGlobal.java',
     'sync/MetaGlobalException.java',
     'sync/MetaGlobalMissingEnginesException.java',
     'sync/MetaGlobalNotSetException.java',
     'sync/middleware/BufferingMiddlewareRepository.java',
     'sync/middleware/BufferingMiddlewareRepositorySession.java',
     'sync/middleware/Crypto5MiddlewareRepository.java',
     'sync/middleware/Crypto5MiddlewareRepositorySession.java',
-    'sync/middleware/MiddlewareRepository.java',
     'sync/middleware/MiddlewareRepositorySession.java',
     'sync/middleware/storage/BufferStorage.java',
     'sync/middleware/storage/MemoryBufferStorage.java',
     'sync/net/AbstractBearerTokenAuthHeaderProvider.java',
     'sync/net/AuthHeaderProvider.java',
     'sync/net/BaseResource.java',
     'sync/net/BaseResourceDelegate.java',
     'sync/net/BasicAuthHeaderProvider.java',
@@ -964,29 +963,23 @@ sync_java_files = [TOPSRCDIR + '/mobile/
     'sync/repositories/android/FormHistoryRepositorySession.java',
     'sync/repositories/android/HistoryDataAccessor.java',
     'sync/repositories/android/HistoryRepository.java',
     'sync/repositories/android/HistoryRepositorySession.java',
     'sync/repositories/android/HistorySessionHelper.java',
     'sync/repositories/android/PasswordsRepositorySession.java',
     'sync/repositories/android/RepoUtils.java',
     'sync/repositories/android/SessionHelper.java',
-    'sync/repositories/android/ThreadedRepository.java',
     'sync/repositories/android/VisitsHelper.java',
     'sync/repositories/BookmarkNeedsReparentingException.java',
-    'sync/repositories/BookmarksRepository.java',
     'sync/repositories/ConfigurableServer15Repository.java',
-    'sync/repositories/delegates/DeferrableRepositorySessionCreationDelegate.java',
-    'sync/repositories/delegates/DeferredRepositorySessionBeginDelegate.java',
     'sync/repositories/delegates/DeferredRepositorySessionFetchRecordsDelegate.java',
     'sync/repositories/delegates/DeferredRepositorySessionFinishDelegate.java',
     'sync/repositories/delegates/DeferredRepositorySessionStoreDelegate.java',
-    'sync/repositories/delegates/RepositorySessionBeginDelegate.java',
     'sync/repositories/delegates/RepositorySessionCleanDelegate.java',
-    'sync/repositories/delegates/RepositorySessionCreationDelegate.java',
     'sync/repositories/delegates/RepositorySessionFetchRecordsDelegate.java',
     'sync/repositories/delegates/RepositorySessionFinishDelegate.java',
     'sync/repositories/delegates/RepositorySessionStoreDelegate.java',
     'sync/repositories/delegates/RepositorySessionWipeDelegate.java',
     'sync/repositories/domain/BookmarkRecord.java',
     'sync/repositories/domain/BookmarkRecordFactory.java',
     'sync/repositories/domain/ClientRecord.java',
     'sync/repositories/domain/ClientRecordFactory.java',
@@ -1000,17 +993,16 @@ sync_java_files = [TOPSRCDIR + '/mobile/
     'sync/repositories/domain/TabsRecord.java',
     'sync/repositories/domain/TabsRecordFactory.java',
     'sync/repositories/domain/VersionConstants.java',
     'sync/repositories/downloaders/BatchingDownloader.java',
     'sync/repositories/downloaders/BatchingDownloaderController.java',
     'sync/repositories/downloaders/BatchingDownloaderDelegate.java',
     'sync/repositories/FetchFailedException.java',
     'sync/repositories/HashSetStoreTracker.java',
-    'sync/repositories/HistoryRepository.java',
     'sync/repositories/IdentityRecordFactory.java',
     'sync/repositories/InactiveSessionException.java',
     'sync/repositories/InvalidBookmarkTypeException.java',
     'sync/repositories/InvalidRequestException.java',
     'sync/repositories/InvalidSessionTransitionException.java',
     'sync/repositories/MultipleRecordsForGuidException.java',
     'sync/repositories/NoContentProviderException.java',
     'sync/repositories/NoGuidForIdException.java',
@@ -1038,16 +1030,17 @@ sync_java_files = [TOPSRCDIR + '/mobile/
     'sync/repositories/uploaders/Payload.java',
     'sync/repositories/uploaders/PayloadDispatcher.java',
     'sync/repositories/uploaders/PayloadUploadDelegate.java',
     'sync/repositories/uploaders/RecordUploadRunnable.java',
     'sync/repositories/uploaders/UploaderMeta.java',
     'sync/repositories/VersioningDelegateHelper.java',
     'sync/Server15PreviousPostFailedException.java',
     'sync/Server15RecordPostFailedException.java',
+    'sync/SessionCreateException.java',
     'sync/setup/activities/ActivityUtils.java',
     'sync/setup/activities/WebURLFinder.java',
     'sync/setup/Constants.java',
     'sync/setup/InvalidSyncKeyException.java',
     'sync/SharedPreferencesClientsDataDelegate.java',
     'sync/stage/AbstractNonRepositorySyncStage.java',
     'sync/stage/AbstractSessionManagingSyncStage.java',
     'sync/stage/BookmarksServerSyncStage.java',
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
@@ -849,20 +849,16 @@ public abstract class GeckoApp extends G
                 inSampleSize = Math.round((float)height / idealHeight);
             } else {
                 inSampleSize = Math.round((float)width / idealWidth);
             }
         }
         return inSampleSize;
     }
 
-    public void requestRender() {
-        mLayerView.requestRender();
-    }
-
     @Override // GeckoSession.ContentListener
     public void onTitleChange(final GeckoSession session, final String title) {
     }
 
     @Override // GeckoSession.ContentListener
     public void onFullScreen(final GeckoSession session, final boolean fullScreen) {
         if (fullScreen) {
             SnackbarBuilder.builder(this)
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -392,21 +392,23 @@ gvjar.sources += [geckoview_source_dir +
     'GeckoView.java',
     'GeckoVRManager.java',
     'gfx/BitmapUtils.java',
     'gfx/BufferedImage.java',
     'gfx/BufferedImageGLInfo.java',
     'gfx/DynamicToolbarAnimator.java',
     'gfx/FloatSize.java',
     'gfx/FullScreenState.java',
+    'gfx/GeckoDisplay.java',
     'gfx/GeckoLayerClient.java',
     'gfx/GeckoSurface.java',
     'gfx/GeckoSurfaceTexture.java',
     'gfx/ImmutableViewportMetrics.java',
     'gfx/IntSize.java',
+    'gfx/LayerSession.java',
     'gfx/LayerView.java',
     'gfx/NativePanZoomController.java',
     'gfx/Overscroll.java',
     'gfx/OverscrollEdgeEffect.java',
     'gfx/PanningPerfAPI.java',
     'gfx/PanZoomController.java',
     'gfx/PointUtils.java',
     'gfx/RenderTask.java',
--- a/mobile/android/chrome/content/aboutAddons.js
+++ b/mobile/android/chrome/content/aboutAddons.js
@@ -419,22 +419,16 @@ var Addons = {
       case AddonManager.OPTIONS_TYPE_TAB:
         // Keep the usual layout for any options related the legacy (or system) add-ons
         // when the options are opened in a new tab from a single button in the addon
         // details page.
         optionsBox.classList.add("inner");
 
         this.createOptionsInTabButton(optionsBox, addon, addonItem);
         break;
-      case AddonManager.OPTIONS_TYPE_INLINE:
-        // Keep the usual layout for any options related the legacy (or system) add-ons.
-        optionsBox.classList.add("inner");
-
-        this.createInlineOptions(optionsBox, optionsURL, aListItem);
-        break;
     }
 
     showAddonOptions();
   },
 
   createOptionsInTabButton: function(destination, addon, detailItem) {
     let frame = destination.querySelector("iframe#addon-options");
     let button = destination.querySelector("button#open-addon-options");
@@ -515,72 +509,16 @@ var Addons = {
     // button from applying to the iframe.
     frame.contentWindow.location.replace(optionsURL);
 
     // Ensure that the Addon Options are visible (the options box will be hidden if the optionsURL
     // attribute is an empty string, which happens when a WebExtensions is still loading).
     detailItem.removeAttribute("optionsURL");
   },
 
-  createInlineOptions(destination, optionsURL, aListItem) {
-    // This function removes and returns the text content of aNode without
-    // removing any child elements. Removing the text nodes ensures any XBL
-    // bindings apply properly.
-    function stripTextNodes(aNode) {
-      var text = "";
-      for (var i = 0; i < aNode.childNodes.length; i++) {
-        if (aNode.childNodes[i].nodeType != document.ELEMENT_NODE) {
-          text += aNode.childNodes[i].textContent;
-          aNode.removeChild(aNode.childNodes[i--]);
-        } else {
-          text += stripTextNodes(aNode.childNodes[i]);
-        }
-      }
-      return text;
-    }
-
-    try {
-      let xhr = new XMLHttpRequest();
-      xhr.open("GET", optionsURL, true);
-      xhr.onload = function(e) {
-        if (xhr.responseXML) {
-          // Only allow <setting> for now
-          let settings = xhr.responseXML.querySelectorAll(":root > setting");
-          if (settings.length > 0) {
-            for (let i = 0; i < settings.length; i++) {
-              var setting = settings[i];
-              var desc = stripTextNodes(setting).trim();
-              if (!setting.hasAttribute("desc")) {
-                setting.setAttribute("desc", desc);
-              }
-              destination.appendChild(setting);
-            }
-            // Send an event so add-ons can prepopulate any non-preference based
-            // settings
-            let event = document.createEvent("Events");
-            event.initEvent("AddonOptionsLoad", true, false);
-            window.dispatchEvent(event);
-          } else {
-            // Reset the options URL to hide the options header if there are no
-            // valid settings to show.
-            let detailItem = document.querySelector("#addons-details > .addon-item");
-            detailItem.setAttribute("optionsURL", "");
-          }
-
-          // Also send a notification to match the behavior of desktop Firefox
-          let id = aListItem.getAttribute("addonID");
-          Services.obs.notifyObservers(document, AddonManager.OPTIONS_NOTIFICATION_DISPLAYED, id);
-        }
-      };
-      xhr.send(null);
-    } catch (e) {
-      Cu.reportError(e);
-    }
-  },
-
   setEnabled: function setEnabled(aValue, aAddon) {
     let detailItem = document.querySelector("#addons-details > .addon-item");
     let addon = aAddon || detailItem.addon;
     if (!addon)
       return;
 
     let listItem = this._getElementForAddon(addon.id);
 
deleted file mode 100644
--- a/mobile/android/chrome/content/bindings/checkbox.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0"?>
-
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-<bindings
-    xmlns="http://www.mozilla.org/xbl"
-    xmlns:xbl="http://www.mozilla.org/xbl"
-    xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
-
-  <binding id="checkbox-with-spacing"
-           extends="chrome://global/content/bindings/checkbox.xml#checkbox">
-
-    <content>
-      <xul:hbox class="checkbox-spacer-box">
-        <xul:image class="checkbox-check" xbl:inherits="checked,disabled"/>
-      </xul:hbox>
-      <xul:hbox class="checkbox-label-center-box" flex="1">
-        <xul:hbox class="checkbox-label-box" flex="1">
-          <xul:image class="checkbox-icon" xbl:inherits="src"/>
-          <xul:label class="checkbox-label" xbl:inherits="xbl:text=label,accesskey,crop" flex="1"/>
-        </xul:hbox>
-      </xul:hbox>
-    </content>
-  </binding>
-
-</bindings>
deleted file mode 100644
--- a/mobile/android/chrome/content/bindings/settings.xml
+++ /dev/null
@@ -1,66 +0,0 @@
-<?xml version="1.0"?>
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-
-<bindings
-    xmlns="http://www.mozilla.org/xbl"
-    xmlns:xbl="http://www.mozilla.org/xbl"
-    xmlns:html="http://www.w3.org/1999/xhtml"
-    xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
-
-  <binding id="setting-fulltoggle-bool" extends="chrome://mozapps/content/extensions/setting.xml#setting-bool">
-    <handlers>
-      <handler event="command" button="0" phase="capturing">
-        <![CDATA[
-        event.stopPropagation();
-        ]]>
-      </handler>
-      <handler event="click" button="0" phase="capturing">
-        <![CDATA[
-        event.stopPropagation();
-        this.input.checked = !this.input.checked;
-        this.inputChanged();
-        this.fireEvent("oncommand");
-        ]]>
-      </handler>
-    </handlers>
-  </binding>
-
-  <binding id="setting-fulltoggle-boolint" extends="chrome://mozapps/content/extensions/setting.xml#setting-boolint">
-    <handlers>
-      <handler event="command" button="0" phase="capturing">
-        <![CDATA[
-        event.stopPropagation();
-        ]]>
-      </handler>
-      <handler event="click" button="0" phase="capturing">
-        <![CDATA[
-        event.stopPropagation();
-        this.input.checked = !this.input.checked;
-        this.inputChanged();
-        this.fireEvent("oncommand");
-        ]]>
-      </handler>
-    </handlers>
-  </binding>
-
-  <binding id="setting-fulltoggle-localized-bool" extends="chrome://mozapps/content/extensions/setting.xml#setting-localized-bool">
-    <handlers>
-      <handler event="command" button="0" phase="capturing">
-        <![CDATA[
-        event.stopPropagation();
-        ]]>
-      </handler>
-      <handler event="click" button="0" phase="capturing">
-        <![CDATA[
-        event.stopPropagation();
-        this.input.checked = !this.input.checked;
-        this.inputChanged();
-        this.fireEvent("oncommand");
-        ]]>
-      </handler>
-    </handlers>
-  </binding>
-</bindings>
--- a/mobile/android/chrome/jar.mn
+++ b/mobile/android/chrome/jar.mn
@@ -23,18 +23,16 @@ chrome.jar:
   content/aboutRights.xhtml            (content/aboutRights.xhtml)
   content/blockedSite.xhtml            (content/blockedSite.xhtml)
   content/languages.properties         (content/languages.properties)
   content/browser.xul                  (content/browser.xul)
   content/browser.css                  (content/browser.css)
   content/browser.js                   (content/browser.js)
   content/PresentationView.xul         (content/PresentationView.xul)
   content/PresentationView.js          (content/PresentationView.js)
-  content/bindings/checkbox.xml        (content/bindings/checkbox.xml)
-  content/bindings/settings.xml        (content/bindings/settings.xml)
   content/netError.xhtml               (content/netError.xhtml)
   content/EmbedRT.js                   (content/EmbedRT.js)
   content/MemoryObserver.js            (content/MemoryObserver.js)
   content/ConsoleAPI.js                (content/ConsoleAPI.js)
   content/PluginHelper.js              (content/PluginHelper.js)
   content/PrintHelper.js               (content/PrintHelper.js)
   content/OfflineApps.js               (content/OfflineApps.js)
   content/MasterPassword.js            (content/MasterPassword.js)
--- a/mobile/android/geckoview/BuildConfig.java.in
+++ b/mobile/android/geckoview/BuildConfig.java.in
@@ -49,9 +49,58 @@ public class BuildConfig {
         MOZ_APP_VERSION + ") Gecko/" +
         MOZ_APP_VERSION + " GeckoView/" +
         MOZ_APP_VERSION;
 
     /**
      * Target CPU architecture: "armeabi-v7a", "arm64-v8a", "x86", "mips", ..
      */
     public static final String ANDROID_CPU_ARCH = "@ANDROID_CPU_ARCH@";
+
+    public static final String MOZ_UPDATE_CHANNEL = "@MOZ_UPDATE_CHANNEL@";
+
+    public static final int MIN_SDK_VERSION = @MOZ_ANDROID_MIN_SDK_VERSION@;
+
+    // Is the underlying compiled C/C++ code compiled with --enable-debug?
+    public static final boolean DEBUG_BUILD =
+//#ifdef MOZ_DEBUG
+    true;
+//#else
+    false;
+//#endif
+
+    // See this wiki page for more details about channel specific build defines:
+    // https://wiki.mozilla.org/Platform/Channel-specific_build_defines
+    // This makes no sense for GeckoView and should be removed as soon as possible.
+    public static final boolean RELEASE_OR_BETA =
+//#ifdef RELEASE_OR_BETA
+    true;
+//#else
+    false;
+//#endif
+
+    // This makes no sense for GeckoView and should be removed as soon as possible.
+    public static final boolean NIGHTLY_BUILD =
+//#ifdef NIGHTLY_BUILD
+    true;
+//#else
+    false;
+//#endif
+
+    // This makes no sense for GeckoView and should be removed as soon as possible.
+    public static final boolean MOZ_CRASHREPORTER =
+//#ifdef MOZ_CRASHREPORTER
+    true;
+//#else
+    false;
+//#endif
+
+    // Official corresponds, roughly, to whether this build is performed on
+    // Mozilla's continuous integration infrastructure. You should disable
+    // developer-only functionality when this flag is set.
+    // This makes no sense for GeckoView and should be removed as soon as possible.
+    public static final boolean MOZILLA_OFFICIAL =
+//#ifdef MOZILLA_OFFICIAL
+    true;
+//#else
+    false;
+//#endif
 }
--- a/mobile/android/geckoview/build.gradle
+++ b/mobile/android/geckoview/build.gradle
@@ -76,16 +76,36 @@ android {
         // MOZILLA_VERSION is oddly quoted from autoconf, but we don't have to handle it specially in Gradle.
         buildConfigField 'String', "MOZILLA_VERSION", "\"${mozconfig.substs.MOZILLA_VERSION}\"";
         buildConfigField 'String', "OMNIJAR_NAME", "\"${mozconfig.substs.OMNIJAR_NAME}\"";
 
         buildConfigField 'String', "USER_AGENT_GECKOVIEW_MOBILE", "\"Mozilla/5.0 (Android \" + android.os.Build.VERSION.RELEASE + \"; Mobile; rv: ${mozconfig.substs.MOZ_APP_VERSION}) Gecko/${mozconfig.substs.MOZ_APP_VERSION} GeckoView/${mozconfig.substs.MOZ_APP_VERSION}\"";
         buildConfigField 'String', "USER_AGENT_GECKOVIEW_TABLET", "\"Mozilla/5.0 (Android \" + android.os.Build.VERSION.RELEASE + \"; Tablet; rv: ${mozconfig.substs.MOZ_APP_VERSION}) Gecko/${mozconfig.substs.MOZ_APP_VERSION} GeckoView/${mozconfig.substs.MOZ_APP_VERSION}\"";
 
         buildConfigField 'String', "ANDROID_CPU_ARCH", "\"${mozconfig.substs.ANDROID_CPU_ARCH}\"";
+
+        buildConfigField 'int', 'MIN_SDK_VERSION', mozconfig.substs.MOZ_ANDROID_MIN_SDK_VERSION;
+
+        // Is the underlying compiled C/C++ code compiled with --enable-debug?
+        buildConfigField 'boolean', 'DEBUG_BUILD', mozconfig.substs.DEBUG_BUILD ? 'true' : 'false';
+
+        // See this wiki page for more details about channel specific build defines:
+        // https://wiki.mozilla.org/Platform/Channel-specific_build_defines
+        // This makes no sense for GeckoView and should be removed as soon as possible.
+        buildConfigField 'boolean', 'RELEASE_OR_BETA', mozconfig.substs.RELEASE_OR_BETA ? 'true' : 'false';
+        // This makes no sense for GeckoView and should be removed as soon as possible.
+        buildConfigField 'boolean', 'NIGHTLY_BUILD', mozconfig.substs.NIGHTLY_BUILD ? 'true' : 'false';
+        // This makes no sense for GeckoView and should be removed as soon as possible.
+        buildConfigField 'boolean', 'MOZ_CRASHREPORTER', mozconfig.substs.MOZ_CRASH_REPORTER ? 'true' : 'false';
+
+        // Official corresponds, roughly, to whether this build is performed on
+        // Mozilla's continuous integration infrastructure. You should disable
+        // developer-only functionality when this flag is set.
+        // This makes no sense for GeckoView and should be removed as soon as possible.
+        buildConfigField 'boolean', 'MOZILLA_OFFICIAL', mozconfig.substs.MOZILLA_OFFICIAL ? 'true' : 'false';
     }
 
     buildTypes {
         withGeckoBinaries {
             initWith release
         }
         withoutGeckoBinaries { // For clarity and consistency throughout the tree.
             initWith release
@@ -119,46 +139,32 @@ android {
                     exclude 'org/mozilla/gecko/media/Utils.java'
                 }
 
                 if (mozconfig.substs.MOZ_WEBRTC) {
                     srcDir "${topsrcdir}/media/webrtc/trunk/webrtc/base/java/src"
                     srcDir "${topsrcdir}/media/webrtc/trunk/webrtc/modules/audio_device/android/java/src"
                     srcDir "${topsrcdir}/media/webrtc/trunk/webrtc/modules/video_capture/android/java/src"
                 }
-
-                // TODO: don't use AppConstants.
-                srcDir "${project.buildDir}/generated/source/preprocessed_code" // See syncPreprocessedCode.
             }
 
             assets {
             }
         }
     }
 }
 
 dependencies {
     compile "com.android.support:support-v4:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
     compile "com.android.support:palette-v7:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
 }
 
-task syncPreprocessedCode(type: Sync, dependsOn: rootProject.generateCodeAndResources) {
-    into("${project.buildDir}/generated/source/preprocessed_code")
-    from("${topobjdir}/mobile/android/base/generated/preprocessed") {
-        // These constants files are included in the main app project.
-        exclude '**/AdjustConstants.java'
-        exclude '**/MmaConstants.java'
-    }
-}
-
 apply from: "${topsrcdir}/mobile/android/gradle/with_gecko_binaries.gradle"
 
 android.libraryVariants.all { variant ->
-    variant.preBuild.dependsOn syncPreprocessedCode
-
     // Like 'debug', 'release', or 'withGeckoBinaries'.
     def buildType = variant.buildType.name
 
     // It would be most natural for :geckoview to always include the Gecko
     // binaries, but that's difficult; see the notes in
     // mobile/android/gradle/with_gecko_binaries.gradle.  Instead :app uses
     // :geckoview:release and handles it's own Gecko binary inclusion.
     if (buildType.equals('withGeckoBinaries')) {
--- a/mobile/android/geckoview/proguard-rules.txt
+++ b/mobile/android/geckoview/proguard-rules.txt
@@ -81,23 +81,16 @@
 # through introspection.
 
 -keepclassmembers class **.R$* {
   public static <fields>;
 }
 
 # GeckoView specific rules.
 
-# Keep classes, and all their contents, compiled before annotation.*.
--keep class org.mozilla.gecko.AppConstants {
-    *;
-}
--keep class org.mozilla.gecko.AppConstants$Versions {
-    *;
-}
 -keep class org.mozilla.gecko.SysInfo {
     *;
 }
 
 # Keep the annotation.
 -keep @interface org.mozilla.gecko.annotation.JNITarget
 
 # Keep classes tagged with the annotation.
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/CrashHandler.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/CrashHandler.java
@@ -22,16 +22,18 @@ import android.content.Intent;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Process;
 import android.util.Log;
 
+import org.mozilla.geckoview.BuildConfig;
+
 public class CrashHandler implements Thread.UncaughtExceptionHandler {
 
     private static final String LOGTAG = "GeckoCrashHandler";
     private static final Thread MAIN_THREAD = Thread.currentThread();
     private static final String DEFAULT_SERVER_URL =
         "https://crash-reports.mozilla.com/submit?id=%1$s&version=%2$s&buildid=%3$s";
 
     // Context for getting device information
@@ -303,18 +305,16 @@ public class CrashHandler implements Thr
                 final Intent intent = new Intent(action);
                 intent.setComponent(new ComponentName(pkg, component));
                 intent.putExtra("minidumpPath", dumpFile);
                 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                 context.startActivity(intent);
                 return true;
             }
 
-            // Avoid AppConstants dependency for SDK version constants,
-            // because CrashHandler could be used outside of Fennec code.
             if (Build.VERSION.SDK_INT < 17) {
                 pb = new ProcessBuilder(
                     "/system/bin/am", "start",
                     "-a", action,
                     "-n", pkg + '/' + component,
                     "--es", "minidumpPath", dumpFile);
             } else {
                 pb = new ProcessBuilder(
@@ -451,28 +451,28 @@ public class CrashHandler implements Thr
     }
 
     public static CrashHandler createDefaultCrashHandler(final Context context) {
         return new CrashHandler(context) {
             @Override
             protected Bundle getCrashExtras(final Thread thread, final Throwable exc) {
                 final Bundle extras = super.getCrashExtras(thread, exc);
 
-                extras.putString("ProductName", AppConstants.MOZ_APP_BASENAME);
-                extras.putString("ProductID", AppConstants.MOZ_APP_ID);
-                extras.putString("Version", AppConstants.MOZ_APP_VERSION);
-                extras.putString("BuildID", AppConstants.MOZ_APP_BUILDID);
-                extras.putString("Vendor", AppConstants.MOZ_APP_VENDOR);
-                extras.putString("ReleaseChannel", AppConstants.MOZ_UPDATE_CHANNEL);
+                extras.putString("ProductName", BuildConfig.MOZ_APP_BASENAME);
+                extras.putString("ProductID", BuildConfig.MOZ_APP_ID);
+                extras.putString("Version", BuildConfig.MOZ_APP_VERSION);
+                extras.putString("BuildID", BuildConfig.MOZ_APP_BUILDID);
+                extras.putString("Vendor", BuildConfig.MOZ_APP_VENDOR);
+                extras.putString("ReleaseChannel", BuildConfig.MOZ_UPDATE_CHANNEL);
                 return extras;
             }
 
             @Override
             public boolean reportException(final Thread thread, final Throwable exc) {
-                if (AppConstants.MOZ_CRASHREPORTER && AppConstants.MOZILLA_OFFICIAL) {
+                if (BuildConfig.MOZ_CRASHREPORTER && BuildConfig.MOZILLA_OFFICIAL) {
                     // Only use Java crash reporter if enabled on official build.
                     return super.reportException(thread, exc);
                 }
                 return false;
             }
         };
     }
 }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/EventDispatcher.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/EventDispatcher.java
@@ -9,16 +9,17 @@ package org.mozilla.gecko;
 import org.mozilla.gecko.annotation.ReflectionTarget;
 import org.mozilla.gecko.annotation.RobocopTarget;
 import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.mozglue.JNIObject;
 import org.mozilla.gecko.util.BundleEventListener;
 import org.mozilla.gecko.util.EventCallback;
 import org.mozilla.gecko.util.GeckoBundle;
 import org.mozilla.gecko.util.ThreadUtils;
+import org.mozilla.geckoview.BuildConfig;
 
 import org.json.JSONException;
 import org.json.JSONObject;
 
 import android.os.Bundle;
 import android.os.Handler;
 import android.util.Log;
 
@@ -111,30 +112,30 @@ public final class EventDispatcher exten
                     List<T> listeners = listenersMap.get(event);
                     if (listeners == null) {
                         // Java doesn't let us put Class<? extends List<T>> as the type for listType.
                         @SuppressWarnings("unchecked")
                         final Class<? extends List<T>> type = (Class) listType;
                         listeners = type.newInstance();
                         listenersMap.put(event, listeners);
                     }
-                    if (!AppConstants.RELEASE_OR_BETA && listeners.contains(listener)) {
+                    if (!BuildConfig.RELEASE_OR_BETA && listeners.contains(listener)) {
                         throw new IllegalStateException("Already registered " + event);
                     }
                     listeners.add(listener);
                 }
             }
         } catch (final IllegalAccessException | InstantiationException e) {
             throw new IllegalArgumentException("Invalid new list type", e);
         }
     }
 
     private void checkNotRegisteredElsewhere(final Map<String, ?> allowedMap,
                                              final String[] events) {
-        if (AppConstants.RELEASE_OR_BETA) {
+        if (BuildConfig.RELEASE_OR_BETA) {
             // for performance reasons, we only check for
             // already-registered listeners in non-release builds.
             return;
         }
         for (final Map<String, ?> listenersMap : Arrays.asList(mGeckoThreadListeners,
                                                                mUiThreadListeners,
                                                                mBackgroundThreadListeners)) {
             if (listenersMap == allowedMap) {
@@ -156,17 +157,17 @@ public final class EventDispatcher exten
                                         final String[] events) {
         synchronized (listenersMap) {
             for (final String event : events) {
                 if (event == null) {
                     continue;
                 }
                 List<T> listeners = listenersMap.get(event);
                 if ((listeners == null ||
-                     !listeners.remove(listener)) && !AppConstants.RELEASE_OR_BETA) {
+                     !listeners.remove(listener)) && !BuildConfig.RELEASE_OR_BETA) {
                     throw new IllegalArgumentException(event + " was not registered");
                 }
             }
         }
     }
 
     public void registerGeckoThreadListener(final BundleEventListener listener,
                                             final String... events) {
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
@@ -32,16 +32,17 @@ import org.mozilla.gecko.gfx.BitmapUtils
 import org.mozilla.gecko.permissions.Permissions;
 import org.mozilla.gecko.process.GeckoProcessManager;
 import org.mozilla.gecko.SysInfo;
 import org.mozilla.gecko.util.HardwareCodecCapabilityUtils;
 import org.mozilla.gecko.util.HardwareUtils;
 import org.mozilla.gecko.util.IOUtils;
 import org.mozilla.gecko.util.ProxySelector;
 import org.mozilla.gecko.util.ThreadUtils;
+import org.mozilla.geckoview.BuildConfig;
 
 import android.Manifest;
 import android.annotation.SuppressLint;
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
@@ -70,17 +71,16 @@ import android.hardware.SensorManager;
 import android.location.Criteria;
 import android.location.Location;
 import android.location.LocationListener;
 import android.location.LocationManager;
 import android.media.AudioManager;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 import android.net.Uri;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.Looper;
 import android.os.ParcelFileDescriptor;
 import android.os.PowerManager;
 import android.os.SystemClock;
 import android.os.Vibrator;
 import android.provider.Settings;
@@ -108,34 +108,38 @@ public class GeckoAppShell
     private static final String LOGTAG = "GeckoAppShell";
 
     // We have static members only.
     private GeckoAppShell() { }
 
     private static final CrashHandler CRASH_HANDLER = new CrashHandler() {
         @Override
         protected String getAppPackageName() {
-            return AppConstants.ANDROID_PACKAGE_NAME;
+            final Context appContext = getAppContext();
+            if (appContext == null) {
+                return "<unknown>";
+            }
+            return appContext.getPackageName();
         }
 
         @Override
         protected Context getAppContext() {
             return getApplicationContext();
         }
 
         @Override
         protected Bundle getCrashExtras(final Thread thread, final Throwable exc) {
             final Bundle extras = super.getCrashExtras(thread, exc);
 
-            extras.putString("ProductName", AppConstants.MOZ_APP_BASENAME);
-            extras.putString("ProductID", AppConstants.MOZ_APP_ID);
-            extras.putString("Version", AppConstants.MOZ_APP_VERSION);
-            extras.putString("BuildID", AppConstants.MOZ_APP_BUILDID);
-            extras.putString("Vendor", AppConstants.MOZ_APP_VENDOR);
-            extras.putString("ReleaseChannel", AppConstants.MOZ_UPDATE_CHANNEL);
+            extras.putString("ProductName", BuildConfig.MOZ_APP_BASENAME);
+            extras.putString("ProductID", BuildConfig.MOZ_APP_ID);
+            extras.putString("Version", BuildConfig.MOZ_APP_VERSION);
+            extras.putString("BuildID", BuildConfig.MOZ_APP_BUILDID);
+            extras.putString("Vendor", BuildConfig.MOZ_APP_VENDOR);
+            extras.putString("ReleaseChannel", BuildConfig.MOZ_UPDATE_CHANNEL);
 
             final String appNotes = getAppNotes();
             if (appNotes != null) {
                 extras.putString("Notes", appNotes);
             }
             return extras;
         }
 
@@ -155,17 +159,17 @@ public class GeckoAppShell
 
                 reportJavaCrash(exc, getExceptionStackTrace(exc));
 
             } catch (final Throwable e) {
             }
 
             // reportJavaCrash should have caused us to hard crash. If we're still here,
             // it probably means Gecko is not loaded, and we should do something else.
-            if (AppConstants.MOZ_CRASHREPORTER && AppConstants.MOZILLA_OFFICIAL) {
+            if (BuildConfig.MOZ_CRASHREPORTER && BuildConfig.MOZILLA_OFFICIAL) {
                 // Only use Java crash reporter if enabled on official build.
                 return super.reportException(thread, exc);
             }
             return false;
         }
     };
 
     private static String sAppNotes;
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoInputConnection.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoInputConnection.java
@@ -4,17 +4,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko;
 
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
 
-import org.mozilla.gecko.gfx.DynamicToolbarAnimator;
 import org.mozilla.gecko.util.ActivityUtils;
 import org.mozilla.gecko.util.GamepadUtils;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.annotation.SuppressLint;
 import android.annotation.TargetApi;
 import android.app.Activity;
 import android.content.Context;
@@ -361,30 +360,23 @@ class GeckoInputConnection
         mCursorAnchorInfoBuilder.reset();
 
         // Calculate Gecko logical coords to screen coords
         final GeckoView view = getView();
         if (view == null) {
             return;
         }
 
-        int[] viewCoords = new int[2];
-        view.getLocationOnScreen(viewCoords);
-
-        DynamicToolbarAnimator animator = view.getDynamicToolbarAnimator();
-        float toolbarHeight = (float) animator.getCurrentToolbarHeight();
-
         Matrix matrix = view.getMatrixForLayerRectToViewRect();
         if (matrix == null) {
             if (DEBUG) {
                 Log.d(LOGTAG, "Cannot get Matrix to convert from Gecko coords to layer view coords");
             }
             return;
         }
-        matrix.postTranslate(viewCoords[0], viewCoords[1] + toolbarHeight);
         mCursorAnchorInfoBuilder.setMatrix(matrix);
 
         final Editable content = getEditable();
         if (content == null) {
             return;
         }
         int composingStart = getComposingSpanStart(content);
         int composingEnd = getComposingSpanEnd(content);
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoSession.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoSession.java
@@ -6,16 +6,17 @@
 
 package org.mozilla.gecko;
 
 import java.net.URLConnection;
 import java.util.ArrayList;
 import java.util.Arrays;
 
 import org.mozilla.gecko.annotation.WrapForJNI;
+import org.mozilla.gecko.gfx.LayerSession;
 import org.mozilla.gecko.mozglue.JNIObject;
 import org.mozilla.gecko.util.BundleEventListener;
 import org.mozilla.gecko.util.EventCallback;
 import org.mozilla.gecko.util.GeckoBundle;
 
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.Resources;
@@ -26,17 +27,18 @@ import android.os.IBinder;
 import android.os.IInterface;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.Log;
 
-public class GeckoSession implements Parcelable {
+public class GeckoSession extends LayerSession
+                          implements Parcelable {
     private static final String LOGTAG = "GeckoSession";
     private static final boolean DEBUG = false;
 
     /* package */ enum State implements NativeQueue.State {
         INITIAL(0),
         READY(1);
 
         private final int mRank;
@@ -294,40 +296,42 @@ public class GeckoSession implements Par
             if (mBinder == null) {
                 mBinder = new Binder();
                 mBinder.attachInterface(this, Window.class.getName());
             }
             return mBinder;
         }
 
         @WrapForJNI(dispatchTo = "proxy")
-        static native void open(Window instance, EventDispatcher dispatcher,
-                                GeckoBundle settings, String chromeUri,
-                                int screenId, boolean privateMode);
+        public static native void open(Window instance, Compositor compositor,
+                                       EventDispatcher dispatcher,
+                                       GeckoBundle settings, String chromeUri,
+                                       int screenId, boolean privateMode);
 
         @WrapForJNI(dispatchTo = "proxy")
         @Override protected native void disposeNative();
 
         @WrapForJNI(dispatchTo = "proxy")
-        native void close();
+        public native void close();
 
         @WrapForJNI(dispatchTo = "proxy")
-        native void transfer(EventDispatcher dispatcher, GeckoBundle settings);
+        public native void transfer(Compositor compositor, EventDispatcher dispatcher,
+                                    GeckoBundle settings);
 
         @WrapForJNI(calledFrom = "gecko")
         private synchronized void onTransfer(final EventDispatcher dispatcher) {
             final NativeQueue nativeQueue = dispatcher.getNativeQueue();
             if (mNativeQueue != nativeQueue) {
                 nativeQueue.setState(mNativeQueue.getState());
                 mNativeQueue = nativeQueue;
             }
         }
 
         @WrapForJNI(dispatchTo = "proxy")
-        native void attach(GeckoView view, Object compositor);
+        public native void attach(GeckoView view);
 
         @WrapForJNI(calledFrom = "gecko")
         private synchronized void onReady() {
             if (mNativeQueue.checkAndSetState(State.INITIAL, State.READY)) {
                 Log.i(LOGTAG, "zerdatime " + SystemClock.elapsedRealtime() +
                       " - chrome startup finished");
             }
         }
@@ -375,20 +379,21 @@ public class GeckoSession implements Par
             throw new IllegalStateException("Session is open");
         }
 
         mWindow = window;
         mSettings = new GeckoSessionSettings(settings, this);
 
         if (mWindow != null) {
             if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
-                mWindow.transfer(mEventDispatcher, mSettings.asBundle());
+                mWindow.transfer(mCompositor, mEventDispatcher, mSettings.asBundle());
             } else {
                 GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY,
                         mWindow, "transfer",
+                        Compositor.class, mCompositor,
                         EventDispatcher.class, mEventDispatcher,
                         GeckoBundle.class, mSettings.asBundle());
             }
         }
     }
 
     /* package */ void transferFrom(final GeckoSession session) {
         transferFrom(session.mWindow, session.mSettings);
@@ -478,42 +483,42 @@ public class GeckoSession implements Par
 
         final String chromeUri = mSettings.getString(GeckoSessionSettings.CHROME_URI);
         final int screenId = mSettings.getInt(GeckoSessionSettings.SCREEN_ID);
         final boolean isPrivate = mSettings.getBoolean(GeckoSessionSettings.USE_PRIVATE_MODE);
 
         mWindow = new Window(mNativeQueue);
 
         if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
-            Window.open(mWindow, mEventDispatcher, mSettings.asBundle(),
-                        chromeUri, screenId, isPrivate);
+            Window.open(mWindow, mCompositor, mEventDispatcher,
+                        mSettings.asBundle(), chromeUri, screenId, isPrivate);
         } else {
             GeckoThread.queueNativeCallUntil(
                 GeckoThread.State.PROFILE_READY,
                 Window.class, "open",
                 Window.class, mWindow,
+                Compositor.class, mCompositor,
                 EventDispatcher.class, mEventDispatcher,
                 GeckoBundle.class, mSettings.asBundle(),
                 String.class, chromeUri,
                 screenId, isPrivate);
         }
     }
 
     public void attachView(final GeckoView view) {
         if (view == null) {
             return;
         }
 
         if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
-            mWindow.attach(view, view.getCompositor());
+            mWindow.attach(view);
         } else {
             GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY,
                     mWindow, "attach",
-                    GeckoView.class, view,
-                    Object.class, view.getCompositor());
+                    GeckoView.class, view);
         }
     }
 
     public void closeWindow() {
         if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
             mWindow.close();
             mWindow.disposeNative();
         } else {
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
@@ -1,36 +1,45 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * vim: ts=4 sw=4 expandtab:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko;
 
+import org.mozilla.gecko.gfx.GeckoDisplay;
 import org.mozilla.gecko.gfx.LayerView;
 
 import android.content.Context;
+import android.graphics.Color;
 import android.graphics.Rect;
+import android.graphics.Region;
 import android.os.Handler;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.AttributeSet;
 import android.view.KeyEvent;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.ViewGroup;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
 
 public class GeckoView extends LayerView {
     private static final String LOGTAG = "GeckoView";
     private static final boolean DEBUG = false;
 
+    private final Display mDisplay = new Display();
     protected GeckoSession mSession;
     private boolean mStateSaved;
 
+    protected SurfaceView mSurfaceView;
+
     private InputConnectionListener mInputConnectionListener;
     private boolean mIsResettingFocus;
 
     private static class SavedState extends BaseSavedState {
         public final GeckoSession session;
 
         public SavedState(final Parcelable superState, final GeckoSession session) {
             super(superState);
@@ -56,59 +65,161 @@ public class GeckoView extends LayerView
 
             @Override
             public SavedState[] newArray(final int size) {
                 return new SavedState[size];
             }
         };
     }
 
-    public GeckoView(Context context) {
-        super(context);
-        initializeView();
+    private class Display implements GeckoDisplay,
+                                     SurfaceHolder.Callback {
+        private final int[] mOrigin = new int[2];
+
+        private Listener mListener;
+        private boolean mValid;
+
+        @Override // GeckoDisplay
+        public Listener getListener() {
+            return mListener;
+        }
+
+        @Override // GeckoDisplay
+        public void setListener(final Listener listener) {
+            if (mValid && mListener != null) {
+                // Tell old listener the surface is gone.
+                mListener.surfaceDestroyed();
+            }
+
+            mListener = listener;
+
+            if (!mValid || listener == null) {
+                return;
+            }
+
+            // Tell new listener there is already a surface.
+            onGlobalLayout();
+            if (GeckoView.this.mSurfaceView != null) {
+                final SurfaceHolder holder = GeckoView.this.mSurfaceView.getHolder();
+                final Rect frame = holder.getSurfaceFrame();
+                listener.surfaceChanged(holder.getSurface(), frame.right, frame.bottom);
+            }
+        }
+
+        @Override // SurfaceHolder.Callback
+        public void surfaceCreated(final SurfaceHolder holder) {
+        }
+
+        @Override // SurfaceHolder.Callback
+        public void surfaceChanged(final SurfaceHolder holder, final int format,
+                                   final int width, final int height) {
+            if (mListener != null) {
+                mListener.surfaceChanged(holder.getSurface(), width, height);
+            }
+            mValid = true;
+        }
+
+        @Override // SurfaceHolder.Callback
+        public void surfaceDestroyed(final SurfaceHolder holder) {
+            if (mListener != null) {
+                mListener.surfaceDestroyed();
+            }
+            mValid = false;
+        }
+
+        public void onGlobalLayout() {
+            if (mListener == null) {
+                return;
+            }
+            if (GeckoView.this.mSurfaceView != null) {
+                GeckoView.this.mSurfaceView.getLocationOnScreen(mOrigin);
+                mListener.screenOriginChanged(mOrigin[0], mOrigin[1]);
+            }
+        }
     }
 
-    public GeckoView(Context context, AttributeSet attrs) {
+    public GeckoView(final Context context) {
+        super(context);
+        init();
+    }
+
+    public GeckoView(final Context context, final AttributeSet attrs) {
         super(context, attrs);
+        init();
+    }
+
+    private void init() {
         initializeView();
+
+        setFocusable(true);
+        setFocusableInTouchMode(true);
+
+        // We are adding descendants to this LayerView, but we don't want the
+        // descendants to affect the way LayerView retains its focus.
+        setDescendantFocusability(FOCUS_BLOCK_DESCENDANTS);
+
+        // This will stop PropertyAnimator from creating a drawing cache (i.e. a
+        // bitmap) from a SurfaceView, which is just not possible (the bitmap will be
+        // transparent).
+        setWillNotCacheDrawing(false);
+
+        mSurfaceView = new SurfaceView(getContext());
+        mSurfaceView.setBackgroundColor(Color.WHITE);
+        addView(mSurfaceView,
+                new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                                           ViewGroup.LayoutParams.MATCH_PARENT));
+
+        mSurfaceView.getHolder().addCallback(mDisplay);
+    }
+
+    @Override
+    public void setSurfaceBackgroundColor(final int newColor) {
+        if (mSurfaceView != null) {
+            mSurfaceView.setBackgroundColor(newColor);
+        }
     }
 
     public void setSession(final GeckoSession session) {
         if (mSession != null && mSession.isOpen()) {
             throw new IllegalStateException("Current session is open");
         }
+
+        if (mSession != null) {
+            mSession.removeDisplay(mDisplay);
+        }
+        if (session != null) {
+            session.addDisplay(mDisplay);
+        }
+
         mSession = session;
     }
 
     public GeckoSession getSession() {
         return mSession;
     }
 
     public EventDispatcher getEventDispatcher() {
         return mSession.getEventDispatcher();
     }
 
     public GeckoSessionSettings getSettings() {
         return mSession.getSettings();
     }
 
-    protected Object getCompositor() {
-        return super.getCompositor();
-    }
-
     @Override
     public void onAttachedToWindow() {
         if (mSession == null) {
-            mSession = new GeckoSession();
+            setSession(new GeckoSession());
         }
 
         if (!mSession.isOpen()) {
             mSession.openWindow(getContext().getApplicationContext());
         }
         mSession.attachView(this);
+        attachCompositor(mSession);
 
         super.onAttachedToWindow();
     }
 
     @Override
     public void onDetachedFromWindow() {
         super.onDetachedFromWindow();
         super.destroy();
@@ -119,16 +230,27 @@ public class GeckoView extends LayerView
         }
 
         if (mSession != null && mSession.isOpen()) {
             mSession.closeWindow();
         }
     }
 
     @Override
+    public boolean gatherTransparentRegion(final Region region) {
+        // For detecting changes in SurfaceView layout, we take a shortcut here and
+        // override gatherTransparentRegion, instead of registering a layout listener,
+        // which is more expensive.
+        if (mSurfaceView != null) {
+            mDisplay.onGlobalLayout();
+        }
+        return super.gatherTransparentRegion(region);
+    }
+
+    @Override
     protected Parcelable onSaveInstanceState() {
         mStateSaved = true;
         return new SavedState(super.onSaveInstanceState(), mSession);
     }
 
     @Override
     protected void onRestoreInstanceState(final Parcelable state) {
         mStateSaved = false;
@@ -137,17 +259,17 @@ public class GeckoView extends LayerView
             super.onRestoreInstanceState(state);
             return;
         }
 
         final SavedState ss = (SavedState) state;
         super.onRestoreInstanceState(ss.getSuperState());
 
         if (mSession == null) {
-            mSession = ss.session;
+            setSession(ss.session);
         } else if (ss.session != null) {
             mSession.transferFrom(ss.session);
         }
     }
 
     /* package */ void setInputConnectionListener(final InputConnectionListener icl) {
         mInputConnectionListener = icl;
     }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java
@@ -44,17 +44,17 @@ public class DynamicToolbarAnimator {
         public Bitmap getBitmapOfToolbarChrome();
         public boolean isToolbarChromeVisible();
         public void toggleToolbarChrome(boolean aShow);
     }
 
     private final Set<PinReason> mPinFlags = Collections.synchronizedSet(EnumSet.noneOf(PinReason.class));
 
     private final GeckoLayerClient mTarget;
-    private LayerView.Compositor mCompositor;
+    private LayerSession.Compositor mCompositor;
     private final List<MetricsListener> mListeners;
     private ToolbarChromeProxy mToolbarChromeProxy;
     private int mMaxToolbarHeight;
 
     public DynamicToolbarAnimator(GeckoLayerClient aTarget) {
         mTarget = aTarget;
         mListeners = new ArrayList<MetricsListener>();
     }
@@ -176,17 +176,17 @@ public class DynamicToolbarAnimator {
                 mCompositor.sendToolbarAnimatorMessage(LayerView.REQUEST_HIDE_TOOLBAR_IMMEDIATELY);
             }
             for (PinReason reason : PinReason.values()) {
                 mCompositor.setPinned(mPinFlags.contains(reason), reason.value);
             }
         }
     }
 
-    /* package-private */ void notifyCompositorCreated(LayerView.Compositor aCompositor) {
+    /* package-private */ void notifyCompositorCreated(LayerSession.Compositor aCompositor) {
         ThreadUtils.assertOnUiThread();
         mCompositor = aCompositor;
     }
 
     private boolean isCompositorReady() {
         return ((mCompositor != null) && (mCompositor.isReady()));
     }
 
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoDisplay.java
@@ -0,0 +1,73 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * vim: ts=4 sw=4 expandtab:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko.gfx;
+
+import android.view.Surface;
+
+/**
+ * Displays implement this interface to provide GeckoSession
+ * with a Surface for displaying content.
+ */
+public interface GeckoDisplay {
+    /**
+     * Displays notify GeckoSession of changes to its Surface through this interface that
+     * GeckoSession implements. To ensure drawing only happens on a valid Surface,
+     * GeckoSession will only use the provided Surface after
+     * {@link #surfaceChanged(Surface, int, int) surfaceChanged} is called and
+     * before {@link #surfaceDestroyed() surfaceDestroyed} returns.
+     */
+    interface Listener {
+        /**
+         * The display's Surface has been created or changed. Must be called on the
+         * application main thread. GeckoSession may block this call to ensure the Surface
+         * is valid while resuming drawing.
+         *
+         * @param surface The new Surface.
+         * @param width New width of the Surface.
+         * @param height New height of the Surface.
+         */
+        void surfaceChanged(Surface surface, int width, int height);
+
+        /**
+         * The display's Surface has been destroyed. Must be called on the application
+         * main thread. GeckoSession may block this call to ensure the Surface is valid
+         * while pausing drawing.
+         */
+        void surfaceDestroyed();
+
+        /**
+         * The display's coordinates on the screen has changed. Must be called on the
+         * application main thread.
+         *
+         * @param left The X coordinate of the display on the screen, in screen pixels.
+         * @param top The Y coordinate of the display on the screen, in screen pixels.
+         */
+        void screenOriginChanged(int left, int top);
+    }
+
+    /**
+     * Get the current listener attached to this display. Must be called on the
+     * application main thread.
+     *
+     * @return Current listener or null if there is no listener.
+     */
+    Listener getListener();
+
+    /**
+     * Set a new listener attached to this display. Must be called on the
+     * application main thread. When attaching a new listener, and there is an
+     * existing valid Surface, the display must call {@link
+     * Listener#screenOriginChanged(int, int) screenOriginChanged} and {@link
+     * Listener#surfaceChanged(Surface, int, int) surfaceChanged} on the new
+     * listener. Similarly, when detaching a previous listener, and there is an
+     * existing valid Surface, the display must call {@link
+     * Listener#surfaceDestroyed() surfaceDestroyed} on the previous listener.
+     *
+     * @param listener New listener or null if detaching previous listener.
+     */
+    void setListener(Listener listener);
+}
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoLayerClient.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoLayerClient.java
@@ -5,35 +5,31 @@
 
 package org.mozilla.gecko.gfx;
 
 import org.mozilla.gecko.annotation.RobocopTarget;
 import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.EventDispatcher;
 import org.mozilla.gecko.util.GeckoBundle;
 
-import android.content.Context;
 import android.graphics.Color;
 import android.graphics.Matrix;
 import android.graphics.PointF;
 import android.graphics.RectF;
 import android.os.SystemClock;
-import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.InputDevice;
 import android.view.MotionEvent;
 
 import java.util.ArrayList;
 
 class GeckoLayerClient implements LayerView.Listener
 {
     private static final String LOGTAG = "GeckoLayerClient";
 
-    private final Context mContext;
-    private IntSize mScreenSize;
     private IntSize mWindowSize;
 
     private boolean mForceRedraw;
     private boolean mImeWasEnabledOnLastResize;
 
     /* The current viewport metrics.
      * This is volatile so that we can read and write to it from different threads.
      * We avoid synchronization to make getting the viewport metrics from
@@ -43,87 +39,53 @@ class GeckoLayerClient implements LayerV
      * 1) reading mViewportMetrics from any thread is fine without synchronization
      * 2) writing to mViewportMetrics requires synchronizing on the layer controller object
      * 3) whenever reading multiple fields from mViewportMetrics without synchronization (i.e. in
      *    case 1 above) you should always first grab a local copy of the reference, and then use
      *    that because mViewportMetrics might get reassigned in between reading the different
      *    fields. */
     private volatile ImmutableViewportMetrics mViewportMetrics;
 
-    private volatile boolean mGeckoIsReady;
-
     private final PanZoomController mPanZoomController;
     private final DynamicToolbarAnimator mToolbarAnimator;
     private final LayerView mView;
 
     /* This flag is true from the time that browser.js detects a first-paint is about to start,
      * to the time that we receive the first-paint composite notification from the compositor.
      * Note that there is a small race condition with this; if there are two paints that both
      * have the first-paint flag set, and the second paint happens concurrently with the
      * composite for the first paint, then this flag may be set to true prematurely. Fixing this
      * is possible but risky; see https://bugzilla.mozilla.org/show_bug.cgi?id=797615#c751
      */
     private volatile boolean mContentDocumentIsDisplayed;
 
     private SynthesizedEventState mPointerState;
 
-    public GeckoLayerClient(Context context, LayerView view) {
+    public GeckoLayerClient(LayerView view) {
         // we can fill these in with dummy values because they are always written
         // to before being read
-        mContext = context;
-        mScreenSize = new IntSize(0, 0);
         mWindowSize = new IntSize(0, 0);
 
         mForceRedraw = true;
-        DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
-        mViewportMetrics = new ImmutableViewportMetrics(displayMetrics)
+        mViewportMetrics = new ImmutableViewportMetrics()
                            .setViewportSize(view.getWidth(), view.getHeight());
 
         mToolbarAnimator = new DynamicToolbarAnimator(this);
         mPanZoomController = PanZoomController.Factory.create(view);
         mView = view;
         mView.setListener(this);
         mContentDocumentIsDisplayed = true;
     }
 
     public void setOverscrollHandler(final Overscroll listener) {
         mPanZoomController.setOverscrollHandler(listener);
     }
 
-    public void setGeckoReady(boolean ready) {
-        mGeckoIsReady = ready;
-    }
-
-    public boolean isGeckoReady() {
-        return mGeckoIsReady;
-    }
-
-    /** Attaches to root layer so that Gecko appears. */
-    @WrapForJNI(calledFrom = "gecko")
-    private void onGeckoReady() {
-        mGeckoIsReady = true;
-
-        sendResizeEventIfNecessary(true);
-
-        // Gecko being ready is one of the two conditions (along with having an available
-        // surface) that cause us to create the compositor. So here, now that we know gecko
-        // is ready, call updateCompositor() to see if we can actually do the creation.
-        // This needs to run on the UI thread so that the surface validity can't change on
-        // us while we're in the middle of creating the compositor.
-        mView.post(new Runnable() {
-            @Override
-            public void run() {
-                getView().updateCompositor();
-            }
-        });
-    }
-
     public void destroy() {
         mPanZoomController.destroy();
-        mGeckoIsReady = false;
     }
 
     public LayerView getView() {
         return mView;
     }
 
     public FloatSize getViewportSize() {
         return mViewportMetrics.getSize();
@@ -139,22 +101,17 @@ class GeckoLayerClient implements LayerV
      */
     boolean setViewportSize(int width, int height) {
         if (mViewportMetrics.viewportRectWidth == width &&
             mViewportMetrics.viewportRectHeight == height) {
             return false;
         }
         mViewportMetrics = mViewportMetrics.setViewportSize(width, height);
 
-        if (mGeckoIsReady) {
-            // here we send gecko a resize message. The code in browser.js is responsible for
-            // picking up on that resize event, modifying the viewport as necessary, and informing
-            // us of the new viewport.
-            sendResizeEventIfNecessary(true);
-
+        if (mView.isCompositorReady()) {
             // the following call also sends gecko a message, which will be processed after the resize
             // message above has updated the viewport. this message ensures that if we have just put
             // focus in a text field, we scroll the content so that the text field is in view.
             final boolean imeIsEnabled = mView.isIMEEnabled();
             if (imeIsEnabled && !mImeWasEnabledOnLastResize) {
                 // The IME just came up after not being up, so let's scroll
                 // to the focused input.
                 EventDispatcher.getInstance().dispatch("ScrollTo:FocusedInput", null);
@@ -167,48 +124,16 @@ class GeckoLayerClient implements LayerV
     PanZoomController getPanZoomController() {
         return mPanZoomController;
     }
 
     DynamicToolbarAnimator getDynamicToolbarAnimator() {
         return mToolbarAnimator;
     }
 
-    /* Informs Gecko that the screen size has changed. */
-    private void sendResizeEventIfNecessary(boolean force) {
-        DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();
-
-        IntSize newScreenSize = new IntSize(metrics.widthPixels, metrics.heightPixels);
-        IntSize newWindowSize = new IntSize(mViewportMetrics.viewportRectWidth,
-                                            mViewportMetrics.viewportRectHeight);
-
-        boolean screenSizeChanged = !mScreenSize.equals(newScreenSize);
-        boolean windowSizeChanged = !mWindowSize.equals(newWindowSize);
-
-        if (!force && !screenSizeChanged && !windowSizeChanged) {
-            return;
-        }
-
-        mScreenSize = newScreenSize;
-        mWindowSize = newWindowSize;
-
-        if (screenSizeChanged) {
-            Log.d(LOGTAG, "Screen-size changed to " + mScreenSize);
-        }
-
-        if (windowSizeChanged) {
-            Log.d(LOGTAG, "Window-size changed to " + mWindowSize);
-        }
-
-        if (mView != null) {
-            mView.notifySizeChanged(mWindowSize.width, mWindowSize.height,
-                                    mScreenSize.width, mScreenSize.height);
-        }
-    }
-
     @WrapForJNI(calledFrom = "gecko")
     void contentDocumentChanged() {
         mContentDocumentIsDisplayed = false;
     }
 
     @WrapForJNI(calledFrom = "gecko")
     boolean isContentDocumentDisplayed() {
         return mContentDocumentIsDisplayed;
@@ -223,17 +148,17 @@ class GeckoLayerClient implements LayerV
     public void updateRootFrameMetrics(float scrollX, float scrollY, float zoom) {
         mViewportMetrics = mViewportMetrics.setViewportOrigin(scrollX, scrollY)
             .setZoomFactor(zoom);
 
         mToolbarAnimator.onMetricsChanged(mViewportMetrics);
         mContentDocumentIsDisplayed = true;
     }
 
-    class PointerInfo {
+    private static class PointerInfo {
         // We reserve one pointer ID for the mouse, so that tests don't have
         // to worry about tracking pointer IDs if they just want to test mouse
         // event synthesization. If somebody tries to use this ID for a
         // synthesized touch event we'll throw an exception.
         public static final int RESERVED_MOUSE_POINTER_ID = 100000;
 
         public int pointerId;
         public int source;
@@ -247,17 +172,17 @@ class GeckoLayerClient implements LayerV
             coords.orientation = orientation;
             coords.pressure = (float)pressure;
             coords.x = screenX;
             coords.y = screenY;
             return coords;
         }
     }
 
-    class SynthesizedEventState {
+    private static class SynthesizedEventState {
         public final ArrayList<PointerInfo> pointers;
         public long downTime;
 
         SynthesizedEventState() {
             pointers = new ArrayList<PointerInfo>();
         }
 
         int getPointerIndex(int pointerId) {
@@ -321,16 +246,21 @@ class GeckoLayerClient implements LayerV
     }
 
     private void synthesizeNativePointer(int source, int pointerId,
             int eventType, int screenX, int screenY, double pressure,
             int orientation)
     {
         Log.d(LOGTAG, "Synthesizing pointer from " + source + " id " + pointerId + " at " + screenX + ", " + screenY);
 
+        final int[] origin = new int[2];
+        mView.getLocationOnScreen(origin);
+        screenX -= origin[0];
+        screenY -= origin[1];
+
         if (mPointerState == null) {
             mPointerState = new SynthesizedEventState();
         }
 
         // Find the pointer if it already exists
         int pointerIndex = mPointerState.getPointerIndex(pointerId);
 
         // Event-specific handling
@@ -378,19 +308,16 @@ class GeckoLayerClient implements LayerV
                 }
                 break;
         }
 
         // Update the pointer with the new info
         PointerInfo info = mPointerState.pointers.get(pointerIndex);
         info.screenX = screenX;
         info.screenY = screenY;
-        if (mView != null) {
-            info.screenY += mView.getCurrentToolbarHeight();
-        }
         info.pressure = pressure;
         info.orientation = orientation;
 
         // Dispatch the event
         int action = 0;
         if (eventType == MotionEvent.ACTION_POINTER_DOWN ||
             eventType == MotionEvent.ACTION_POINTER_UP) {
             // for pointer-down and pointer-up events we need to add the
@@ -457,17 +384,17 @@ class GeckoLayerClient implements LayerV
         setViewportSize(viewportSize.width, viewportSize.height);
     }
 
     ImmutableViewportMetrics getViewportMetrics() {
         return mViewportMetrics;
     }
 
     Matrix getMatrixForLayerRectToViewRect() {
-        if (!mGeckoIsReady) {
+        if (!mView.isCompositorReady()) {
             return null;
         }
 
         ImmutableViewportMetrics viewportMetrics = mViewportMetrics;
         PointF origin = viewportMetrics.getOrigin();
         float zoom = viewportMetrics.zoomFactor;
         ImmutableViewportMetrics geckoViewport = mViewportMetrics;
         PointF geckoOrigin = geckoViewport.getOrigin();
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoSurface.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoSurface.java
@@ -10,17 +10,16 @@ import android.graphics.SurfaceTexture;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.view.Surface;
 import android.util.Log;
 
 import java.util.HashMap;
 
 import org.mozilla.gecko.annotation.WrapForJNI;
-import org.mozilla.gecko.AppConstants.Versions;
 
 public final class GeckoSurface extends Surface {
     private static final String LOGTAG = "GeckoSurface";
 
     private static HashMap<Integer, GeckoSurfaceTexture> sSurfaceTextures = new HashMap<Integer, GeckoSurfaceTexture>();
 
     private int mHandle;
     private boolean mIsSingleBuffer;
@@ -85,9 +84,9 @@ public final class GeckoSurface extends 
     public boolean getAvailable() {
         return mIsAvailable;
     }
 
     @WrapForJNI
     public void setAvailable(boolean available) {
         mIsAvailable = available;
     }
-}
\ No newline at end of file
+}
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoSurfaceTexture.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoSurfaceTexture.java
@@ -1,23 +1,23 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.gfx;
 
 import android.graphics.SurfaceTexture;
+import android.os.Build;
 import android.util.Log;
 
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.HashMap;
 
 import org.mozilla.gecko.annotation.WrapForJNI;
-import org.mozilla.gecko.AppConstants.Versions;
 
 public final class GeckoSurfaceTexture extends SurfaceTexture {
     private static final String LOGTAG = "GeckoSurfaceTexture";
     private static volatile int sNextHandle = 1;
     private static HashMap<Integer, GeckoSurfaceTexture> sSurfaceTextures = new HashMap<Integer, GeckoSurfaceTexture>();
 
     private int mHandle;
     private boolean mIsSingleBuffer;
@@ -118,17 +118,17 @@ public final class GeckoSurfaceTexture e
     }
 
     public synchronized void setListener(GeckoSurfaceTexture.Callbacks listener) {
         mListener = listener;
     }
 
     @WrapForJNI
     public static boolean isSingleBufferSupported() {
-        return Versions.feature19Plus;
+        return Build.VERSION.SDK_INT >= 19;
     }
 
     @WrapForJNI
     public void incrementUse() {
         mUseCount.incrementAndGet();
     }
 
     @WrapForJNI
@@ -137,17 +137,17 @@ public final class GeckoSurfaceTexture e
 
         if (useCount == 0) {
             synchronized (sSurfaceTextures) {
                 sSurfaceTextures.remove(mHandle);
             }
 
             setListener(null);
 
-            if (Versions.feature16Plus) {
+            if (Build.VERSION.SDK_INT >= 16) {
                 try {
                     detachFromGLContext();
                 } catch (Exception e) {
                     // This can throw if the EGL context is not current
                     // but we can't do anything about that now.
                 }
             }
 
@@ -188,9 +188,9 @@ public final class GeckoSurfaceTexture e
             return sSurfaceTextures.get(handle);
         }
     }
 
     public interface Callbacks {
         void onUpdateTexImage();
         void onReleaseTexImage();
     }
-}
\ No newline at end of file
+}
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/ImmutableViewportMetrics.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/ImmutableViewportMetrics.java
@@ -2,17 +2,16 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.gfx;
 
 import android.graphics.PointF;
 import android.graphics.RectF;
-import android.util.DisplayMetrics;
 
 /**
  * ImmutableViewportMetrics are used to store the viewport metrics
  * in way that we can access a version of them from multiple threads
  * without having to take a lock
  */
 public class ImmutableViewportMetrics {
 
@@ -20,21 +19,21 @@ public class ImmutableViewportMetrics {
     // because Java doesn't have the concept of const classes
     public final float viewportRectLeft;
     public final float viewportRectTop;
     public final int viewportRectWidth;
     public final int viewportRectHeight;
 
     public final float zoomFactor;
 
-    public ImmutableViewportMetrics(DisplayMetrics metrics) {
-        viewportRectLeft   = 0;
-        viewportRectTop    = 0;
-        viewportRectWidth = metrics.widthPixels;
-        viewportRectHeight = metrics.heightPixels;
+    public ImmutableViewportMetrics() {
+        viewportRectLeft = 0;
+        viewportRectTop = 0;
+        viewportRectWidth = 0;
+        viewportRectHeight = 0;
         zoomFactor = 1.0f;
     }
 
     private ImmutableViewportMetrics(
         float aViewportRectLeft, float aViewportRectTop, int aViewportRectWidth,
         int aViewportRectHeight, float aZoomFactor)
     {
         viewportRectLeft = aViewportRectLeft;
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerSession.java
@@ -0,0 +1,304 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko.gfx;
+
+import org.mozilla.gecko.annotation.WrapForJNI;
+import org.mozilla.gecko.mozglue.JNIObject;
+import org.mozilla.gecko.util.ThreadUtils;
+
+import android.content.Context;
+import android.util.Log;
+import android.view.Surface;
+
+public class LayerSession {
+    private static final String LOGTAG = "GeckoLayerSession";
+    private static final boolean DEBUG = false;
+
+    // Sent from compositor when the static toolbar image has been updated and
+    // is ready to animate.
+    /* package */ final static int STATIC_TOOLBAR_READY = 1;
+    // Sent from compositor when the static toolbar has been made visible so
+    // the real toolbar should be shown.
+    /* package */ final static int TOOLBAR_SHOW = 4;
+    // Special message sent from UiCompositorControllerChild once it is open.
+    /* package */ final static int COMPOSITOR_CONTROLLER_OPEN = 20;
+    // Special message sent from controller to query if the compositor controller is open.
+    /* package */ final static int IS_COMPOSITOR_CONTROLLER_OPEN = 21;
+
+    protected class Compositor extends JNIObject {
+        public LayerView layerView;
+
+        public boolean isReady() {
+            return LayerSession.this.isCompositorReady();
+        }
+
+        @WrapForJNI(calledFrom = "ui")
+        private void onCompositorAttached() {
+            LayerSession.this.onCompositorAttached();
+        }
+
+        @WrapForJNI(calledFrom = "ui")
+        private void onCompositorDetached() {
+            if (layerView != null) {
+                layerView.clearDrawListeners();
+                layerView = null;
+            }
+            // Clear out any pending calls on the UI thread.
+            LayerSession.this.onCompositorDetached();
+            disposeNative();
+        }
+
+        @WrapForJNI(calledFrom = "ui", dispatchTo = "gecko")
+        @Override protected native void disposeNative();
+
+        @WrapForJNI(calledFrom = "any", dispatchTo = "gecko")
+        public native void attachToJava(GeckoLayerClient layerClient,
+                                        NativePanZoomController npzc);
+
+        @WrapForJNI(calledFrom = "ui", dispatchTo = "gecko")
+        public native void onBoundsChanged(int left, int top, int width, int height);
+
+        // Gecko thread creates compositor; blocks UI thread.
+        @WrapForJNI(calledFrom = "ui", dispatchTo = "proxy")
+        public native void createCompositor(int width, int height, Object surface);
+
+        // Gecko thread pauses compositor; blocks UI thread.
+        @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
+        public native void syncPauseCompositor();
+
+        // UI thread resumes compositor and notifies Gecko thread; does not block UI thread.
+        @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
+        public native void syncResumeResizeCompositor(int width, int height, Object surface);
+
+        @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
+        public native void setMaxToolbarHeight(int height);
+
+        @WrapForJNI(calledFrom = "any", dispatchTo = "current")
+        public native void setPinned(boolean pinned, int reason);
+
+        @WrapForJNI(calledFrom = "any", dispatchTo = "current")
+        public native void sendToolbarAnimatorMessage(int message);
+
+        @WrapForJNI(calledFrom = "ui")
+        private void recvToolbarAnimatorMessage(int message) {
+            if (message == COMPOSITOR_CONTROLLER_OPEN) {
+                if (LayerSession.this.isCompositorReady()) {
+                    return;
+                }
+
+                // Delay calling onCompositorReady to avoid deadlock due
+                // to synchronous call to the compositor.
+                ThreadUtils.postToUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        LayerSession.this.onCompositorReady();
+                    }
+                });
+            }
+
+            if (layerView != null) {
+                layerView.handleToolbarAnimatorMessage(message);
+            }
+
+            if (message == STATIC_TOOLBAR_READY || message == TOOLBAR_SHOW) {
+                LayerSession.this.onWindowBoundsChanged();
+            }
+        }
+
+        @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
+        public native void setDefaultClearColor(int color);
+
+        @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
+        public native void requestScreenPixels();
+
+        @WrapForJNI(calledFrom = "ui")
+        private void recvScreenPixels(int width, int height, int[] pixels) {
+            if (layerView != null) {
+                layerView.recvScreenPixels(width, height, pixels);
+            }
+        }
+
+        @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
+        public native void enableLayerUpdateNotifications(boolean enable);
+
+        @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
+        public native void sendToolbarPixelsToCompositor(final int width, final int height,
+                                                         final int[] pixels);
+    }
+
+    protected final Compositor mCompositor = new Compositor();
+
+    // Following fields are accessed on UI thread.
+    private GeckoDisplay mDisplay;
+    private boolean mAttachedCompositor;
+    private boolean mCalledCreateCompositor;
+    private boolean mCompositorReady;
+    private Surface mSurface;
+    private int mLeft;
+    private int mTop;
+    private int mWidth;
+    private int mHeight;
+
+    /* package */ GeckoDisplay getDisplay() {
+        if (DEBUG) {
+            ThreadUtils.assertOnUiThread();
+        }
+        return mDisplay;
+    }
+
+    /* package */ void onCompositorAttached() {
+        if (DEBUG) {
+            ThreadUtils.assertOnUiThread();
+        }
+
+        mAttachedCompositor = true;
+
+        if (mSurface != null) {
+            // If we have a valid surface, create the compositor now that we're attached.
+            // Leave mSurface alone because we'll need it later for onCompositorReady.
+            onSurfaceChanged(mSurface, mWidth, mHeight);
+        }
+    }
+
+    /* package */ void onCompositorDetached() {
+        if (DEBUG) {
+            ThreadUtils.assertOnUiThread();
+        }
+
+        mAttachedCompositor = false;
+        mCalledCreateCompositor = false;
+        mCompositorReady = false;
+    }
+
+    /* package */ boolean isCompositorReady() {
+        return mCompositorReady;
+    }
+
+    /* package */ void onCompositorReady() {
+        if (DEBUG) {
+            ThreadUtils.assertOnUiThread();
+        }
+
+        mCompositorReady = true;
+
+        if (mSurface != null) {
+            // If we have a valid surface, resume the
+            // compositor now that the compositor is ready.
+            onSurfaceChanged(mSurface, mWidth, mHeight);
+            mSurface = null;
+        }
+    }
+
+    /* protected */ void onWindowBoundsChanged() {
+        if (DEBUG) {
+            ThreadUtils.assertOnUiThread();
+        }
+
+        if (mAttachedCompositor) {
+            final int toolbarHeight;
+            if (mCompositor.layerView != null) {
+                toolbarHeight = mCompositor.layerView.getCurrentToolbarHeight();
+            } else {
+                toolbarHeight = 0;
+            }
+            mCompositor.onBoundsChanged(mLeft, mTop + toolbarHeight,
+                                        mWidth, mHeight - toolbarHeight);
+
+            if (mCompositor.layerView != null) {
+                mCompositor.layerView.onSizeChanged(mWidth, mHeight);
+            }
+        }
+    }
+
+    /* package */ void onSurfaceChanged(final Surface surface, final int width,
+                                        final int height) {
+        ThreadUtils.assertOnUiThread();
+
+        mWidth = width;
+        mHeight = height;
+
+        if (mCompositorReady) {
+            mCompositor.syncResumeResizeCompositor(width, height, surface);
+            onWindowBoundsChanged();
+            return;
+        }
+
+        if (mAttachedCompositor && !mCalledCreateCompositor) {
+            mCompositor.createCompositor(width, height, surface);
+            mCompositor.sendToolbarAnimatorMessage(IS_COMPOSITOR_CONTROLLER_OPEN);
+            mCalledCreateCompositor = true;
+        }
+
+        // We have a valid surface but we're not attached or the compositor
+        // is not ready; save the surface for later when we're ready.
+        mSurface = surface;
+    }
+
+    /* package */ void onSurfaceDestroyed() {
+        ThreadUtils.assertOnUiThread();
+
+        if (mCompositorReady) {
+            mCompositor.syncPauseCompositor();
+            return;
+        }
+
+        // While the surface was valid, we never became attached or the
+        // compositor never became ready; clear the saved surface.
+        mSurface = null;
+    }
+
+    /* package */ void onScreenOriginChanged(final int left, final int top) {
+        ThreadUtils.assertOnUiThread();
+
+        if (mLeft == left && mTop == top) {
+            return;
+        }
+
+        mLeft = left;
+        mTop = top;
+        onWindowBoundsChanged();
+    }
+
+    public void addDisplay(final GeckoDisplay display) {
+        ThreadUtils.assertOnUiThread();
+
+        if (display.getListener() != null) {
+            throw new IllegalArgumentException("Display already attached");
+        } else if (mDisplay != null) {
+            throw new IllegalArgumentException("Only one display supported");
+        }
+
+        mDisplay = display;
+        display.setListener(new GeckoDisplay.Listener() {
+            @Override
+            public void surfaceChanged(final Surface surface, final int width,
+                                       final int height) {
+                onSurfaceChanged(surface, width, height);
+            }
+
+            @Override
+            public void surfaceDestroyed() {
+                onSurfaceDestroyed();
+            }
+
+            @Override
+            public void screenOriginChanged(final int left, final int top) {
+                onScreenOriginChanged(left, top);
+            }
+        });
+    }
+
+    public void removeDisplay(final GeckoDisplay display) {
+        ThreadUtils.assertOnUiThread();
+
+        if (mDisplay != display) {
+            throw new IllegalArgumentException("Display not attached");
+        }
+
+        display.setListener(null);
+        mDisplay = null;
+    }
+}
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerView.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerView.java
@@ -49,35 +49,25 @@ public class LayerView extends FrameLayo
 
     private static AccessibilityManager sAccessibilityManager;
 
     private GeckoLayerClient mLayerClient;
     private PanZoomController mPanZoomController;
     private DynamicToolbarAnimator mToolbarAnimator;
     private FullScreenState mFullScreenState;
 
-    private SurfaceView mSurfaceView;
-    private TextureView mTextureView;
-
     private Listener mListener;
 
     /* This should only be modified on the Java UI thread. */
     private final Overscroll mOverscroll;
 
-    private boolean mServerSurfaceValid;
-    private int mWidth, mHeight;
-
     private int mDefaultClearColor = Color.WHITE;
     /* package */ GetPixelsResult mGetPixelsResult;
     private final List<DrawListener> mDrawListeners;
 
-    /* This is written by the Gecko thread and the UI thread, and read by the UI thread. */
-    /* package */ volatile boolean mCompositorCreated;
-    /* package */ volatile boolean mCompositorControllerOpen;
-
     //
     // NOTE: These values are also defined in gfx/layers/ipc/UiCompositorControllerMessageTypes.h
     //       and must be kept in sync. Any new AnimatorMessageType added here must also be added there.
     //
     /* package */ final static int STATIC_TOOLBAR_NEEDS_UPDATE      = 0;  // Sent from compositor when the static toolbar wants to hide.
     /* package */ final static int STATIC_TOOLBAR_READY             = 1;  // Sent from compositor when the static toolbar image has been updated and is ready to animate.
     /* package */ final static int TOOLBAR_HIDDEN                   = 2;  // Sent to compositor when the real toolbar has been hidden.
     /* package */ final static int TOOLBAR_VISIBLE                  = 3;  // Sent to compositor when the real toolbar is visible.
@@ -85,128 +75,29 @@ public class LayerView extends FrameLayo
     /* package */ final static int FIRST_PAINT                      = 5;  // Sent from compositor after first paint
     /* package */ final static int REQUEST_SHOW_TOOLBAR_IMMEDIATELY = 6;  // Sent to compositor requesting toolbar be shown immediately
     /* package */ final static int REQUEST_SHOW_TOOLBAR_ANIMATED    = 7;  // Sent to compositor requesting toolbar be shown animated
     /* package */ final static int REQUEST_HIDE_TOOLBAR_IMMEDIATELY = 8;  // Sent to compositor requesting toolbar be hidden immediately
     /* package */ final static int REQUEST_HIDE_TOOLBAR_ANIMATED    = 9;  // Sent to compositor requesting toolbar be hidden animated
     /* package */ final static int LAYERS_UPDATED                   = 10; // Sent from compositor when a layer has been updated
     /* package */ final static int TOOLBAR_SNAPSHOT_FAILED          = 11; // Sent to compositor when the toolbar snapshot fails.
     /* package */ final static int COMPOSITOR_CONTROLLER_OPEN       = 20; // Special message sent from UiCompositorControllerChild once it is open
-    /* package */ final static int IS_COMPOSITOR_CONTROLLER_OPEN    = 21; // Special message sent from controller to query if the compositor controller is open
 
     private void postCompositorMessage(final int message) {
         ThreadUtils.postToUiThread(new Runnable() {
             @Override
             public void run() {
                 mCompositor.sendToolbarAnimatorMessage(message);
             }
         });
     }
 
-    @WrapForJNI(calledFrom = "ui")
     /* package */ boolean isCompositorReady() {
         ThreadUtils.assertOnUiThread();
-        return mCompositorCreated && mCompositorControllerOpen;
-    }
-
-    /* package */ class Compositor extends JNIObject {
-        public Compositor() {
-        }
-
-        /* package */ boolean isReady() {
-            return isCompositorReady();
-        }
-        @WrapForJNI(calledFrom = "ui", dispatchTo = "gecko")
-        @Override protected native void disposeNative();
-
-        // Gecko thread sets its Java instances; does not block UI thread.
-        @WrapForJNI(calledFrom = "any", dispatchTo = "gecko")
-        /* package */ native void attachToJava(GeckoLayerClient layerClient,
-                                               NativePanZoomController npzc);
-
-        @WrapForJNI(calledFrom = "any", dispatchTo = "gecko")
-        /* package */ native void onSizeChanged(int windowWidth, int windowHeight,
-                                                int screenWidth, int screenHeight);
-
-        // Gecko thread creates compositor; blocks UI thread.
-        @WrapForJNI(calledFrom = "ui", dispatchTo = "proxy")
-        /* package */ native void createCompositor(int width, int height, Object surface);
-
-        // Gecko thread pauses compositor; blocks UI thread.
-        @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
-        /* package */ native void syncPauseCompositor();
-
-        // UI thread resumes compositor and notifies Gecko thread; does not block UI thread.
-        @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
-        /* package */ native void syncResumeResizeCompositor(int width, int height, Object surface);
-
-        @WrapForJNI(calledFrom = "any", dispatchTo = "current")
-        /* package */ native void syncInvalidateAndScheduleComposite();
-
-        @WrapForJNI(calledFrom = "any", dispatchTo = "current")
-        /* package */ native void setMaxToolbarHeight(int height);
-
-        @WrapForJNI(calledFrom = "any", dispatchTo = "current")
-        /* package */ native void setPinned(boolean pinned, int reason);
-
-        @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
-        /* package */ native void sendToolbarAnimatorMessage(int message);
-
-        @WrapForJNI(calledFrom = "ui")
-        /* package */ void recvToolbarAnimatorMessage(int message) {
-            handleToolbarAnimatorMessage(message);
-        }
-
-        @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
-        /* package */ native void setDefaultClearColor(int color);
-
-        @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
-        /* package */ native void requestScreenPixels();
-
-        @WrapForJNI(calledFrom = "ui")
-        /* package */ void recvScreenPixels(int width, int height, int[] pixels) {
-            if (mGetPixelsResult != null) {
-                mGetPixelsResult.onPixelsResult(width, height, IntBuffer.wrap(pixels));
-                mGetPixelsResult = null;
-            }
-        }
-
-        @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
-        /* package */ native void enableLayerUpdateNotifications(boolean enable);
-
-        @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
-        /* package */ native void sendToolbarPixelsToCompositor(final int width, final int height, final int[] pixels);
-
-        @WrapForJNI(calledFrom = "gecko")
-        private void reattach() {
-            ThreadUtils.postToUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    updateCompositor();
-                }
-            });
-        }
-
-        @WrapForJNI(calledFrom = "gecko")
-        private void destroy() {
-            // The nsWindow has been closed. First mark our compositor as destroyed.
-            LayerView.this.mCompositorCreated = false;
-            LayerView.this.mCompositorControllerOpen = false;
-
-            LayerView.this.mLayerClient.setGeckoReady(false);
-
-            // Then clear out any pending calls on the UI thread by disposing on the UI thread.
-            ThreadUtils.postToUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    LayerView.this.mDrawListeners.clear();
-                    disposeNative();
-                }
-            });
-        }
+        return mCompositor != null && mCompositor.isReady();
     }
 
     /* package */ void handleToolbarAnimatorMessage(int message) {
         switch (message) {
             case STATIC_TOOLBAR_NEEDS_UPDATE:
                 // Send updated toolbar image to compositor.
                 Bitmap bm = mToolbarAnimator.getBitmapOfToolbarChrome();
                 if (bm == null) {
@@ -239,88 +130,54 @@ public class LayerView extends FrameLayo
                 setSurfaceBackgroundColor(Color.TRANSPARENT);
                 break;
             case LAYERS_UPDATED:
                 for (DrawListener listener : mDrawListeners) {
                     listener.drawFinished();
                 }
                 break;
             case COMPOSITOR_CONTROLLER_OPEN:
-                // It is possible to get this message multiple times. Only act on it if we didn't know the compositor controller was open
-                if (mCompositorControllerOpen) {
-                    break;
-                }
-                mCompositorControllerOpen = true;
-                // updateCompositor makes a synchronous call to the compositor which will dead lock if called directly from here
                 ThreadUtils.postToUiThread(new Runnable() {
                     @Override
                     public void run() {
                         mCompositor.setDefaultClearColor(mDefaultClearColor);
                         mCompositor.enableLayerUpdateNotifications(!mDrawListeners.isEmpty());
                         mToolbarAnimator.updateCompositor();
-                        updateCompositor();
                     }
                 });
                 break;
             default:
                 Log.e(LOGTAG, "Unhandled Toolbar Animator Message: " + message);
                 break;
         }
     }
 
-    private final Compositor mCompositor = new Compositor();
-
-    public boolean shouldUseTextureView() {
-        // Disable TextureView support for now as it causes panning/zooming
-        // performance regressions (see bug 792259). Uncomment the code below
-        // once this bug is fixed.
-        return false;
-
-        /*
-        // we can only use TextureView on ICS or higher
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
-            Log.i(LOGTAG, "Not using TextureView: not on ICS+");
-            return false;
-        }
-
-        try {
-            // and then we can only use it if we have a hardware accelerated window
-            Method m = View.class.getMethod("isHardwareAccelerated", (Class[]) null);
-            return (Boolean) m.invoke(this);
-        } catch (Exception e) {
-            Log.i(LOGTAG, "Not using TextureView: caught exception checking for hw accel: " + e.toString());
-            return false;
-        } */
-    }
+    private LayerSession.Compositor mCompositor;
 
     public LayerView(Context context, AttributeSet attrs) {
         super(context, attrs);
 
         mFullScreenState = FullScreenState.NONE;
 
         mOverscroll = new OverscrollEdgeEffect(this);
         mDrawListeners = new ArrayList<DrawListener>();
     }
 
     public LayerView(Context context) {
         this(context, null);
     }
 
     public void initializeView() {
-        mLayerClient = new GeckoLayerClient(getContext(), this);
+        mLayerClient = new GeckoLayerClient(this);
         if (mOverscroll != null) {
             mLayerClient.setOverscrollHandler(mOverscroll);
         }
 
         mPanZoomController = mLayerClient.getPanZoomController();
         mToolbarAnimator = mLayerClient.getDynamicToolbarAnimator();
-        mToolbarAnimator.notifyCompositorCreated(mCompositor);
-
-        setFocusable(true);
-        setFocusableInTouchMode(true);
     }
 
     /**
      * MotionEventHelper dragAsync() robocop tests can instruct
      * PanZoomController not to generate longpress events.
      * This call comes in from a thread other than the UI thread.
      * So dispatch to UI thread first to prevent assert in nsWindow.
      */
@@ -333,26 +190,16 @@ public class LayerView extends FrameLayo
         });
     }
 
     private static Point getEventRadius(MotionEvent event) {
         return new Point((int)event.getToolMajor() / 2,
                          (int)event.getToolMinor() / 2);
     }
 
-    public void showSurface() {
-        // Fix this if TextureView support is turned back on above
-        mSurfaceView.setVisibility(View.VISIBLE);
-    }
-
-    public void hideSurface() {
-        // Fix this if TextureView support is turned back on above
-        mSurfaceView.setVisibility(View.INVISIBLE);
-    }
-
     public void destroy() {
         if (mLayerClient != null) {
             mLayerClient.destroy();
         }
     }
 
     @Override
     public void dispatchDraw(final Canvas canvas) {
@@ -365,17 +212,17 @@ public class LayerView extends FrameLayo
     }
 
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
             requestFocus();
         }
 
-        if (!mLayerClient.isGeckoReady()) {
+        if (!isCompositorReady()) {<