merge mozilla-central to mozilla-inbound. r=merge a=merge on a CLOSED TREE
authorSebastian Hengst <archaeopteryx@coole-files.de>
Thu, 05 Oct 2017 11:47:25 +0200
changeset 384677 b25870b6590be5e881987ec7092be735e668ee80
parent 384676 9602570c97b40a66f35156a1101ffb9296f7470a (current diff)
parent 384650 53bbdaaa2b8c1819061be26101b075c081b23260 (diff)
child 384678 e1c12b8d548ee7447106511ab2d14400463d5c3d
push id32634
push userkwierso@gmail.com
push dateFri, 06 Oct 2017 19:55:44 +0000
treeherdermozilla-central@2d7b8b5dd174 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone58.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 on a CLOSED TREE
dom/interfaces/html/nsIDOMHTMLFrameElement.idl
toolkit/mozapps/extensions/test/xpcshell/test_system_update.js
xpcom/io/moz.build
--- a/browser/app/Makefile.in
+++ b/browser/app/Makefile.in
@@ -78,17 +78,17 @@ endif
 LPROJ := Contents/Resources/$(LPROJ_ROOT).lproj
 
 clean clobber repackage::
 	$(RM) -r $(dist_dest)
 
 MAC_BUNDLE_VERSION = $(shell $(PYTHON) $(srcdir)/macversion.py --version=$(MOZ_APP_VERSION) --buildid=$(DEPTH)/buildid.h)
 
 .PHONY: repackage
-tools repackage:: $(DIST)/bin/$(MOZ_APP_NAME)
+tools repackage:: $(DIST)/bin/$(MOZ_APP_NAME) features
 	rm -rf $(dist_dest)
 	$(MKDIR) -p $(dist_dest)/Contents/MacOS
 	$(MKDIR) -p $(dist_dest)/$(LPROJ)
 	rsync -a --exclude '*.in' $(srcdir)/macbuild/Contents $(dist_dest) --exclude English.lproj
 	rsync -a --exclude '*.in' $(srcdir)/macbuild/Contents/Resources/English.lproj/ $(dist_dest)/$(LPROJ)
 	sed -e 's/%APP_VERSION%/$(MOZ_APP_VERSION)/' -e 's/%MAC_APP_NAME%/$(MAC_APP_NAME)/' -e 's/%MOZ_MACBUNDLE_ID%/$(MOZ_MACBUNDLE_ID)/' -e 's/%MAC_BUNDLE_VERSION%/$(MAC_BUNDLE_VERSION)/' -e 's|%MOZ_DEVELOPER_REPO_PATH%|$(topsrcdir)|' -e 's|%MOZ_DEVELOPER_OBJ_PATH%|$(topobjdir)|' $(srcdir)/macbuild/Contents/Info.plist.in > $(dist_dest)/Contents/Info.plist
 	sed -e 's/%MAC_APP_NAME%/$(MAC_APP_NAME)/' $(srcdir)/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in | iconv -f UTF-8 -t UTF-16 > $(dist_dest)/$(LPROJ)/InfoPlist.strings
 	rsync -a --exclude-from='$(srcdir)/macbuild/Contents/MacOS-files.in' $(DIST)/bin/ $(dist_dest)/Contents/Resources
@@ -99,8 +99,12 @@ tools repackage:: $(DIST)/bin/$(MOZ_APP_
 	cp -RL $(DIST)/branding/document.icns $(dist_dest)/Contents/Resources/document.icns
 	$(MKDIR) -p $(dist_dest)/Contents/Library/LaunchServices
 ifdef MOZ_UPDATER
 	mv -f $(dist_dest)/Contents/MacOS/updater.app/Contents/MacOS/org.mozilla.updater $(dist_dest)/Contents/Library/LaunchServices
 	ln -s ../../../../Library/LaunchServices/org.mozilla.updater $(dist_dest)/Contents/MacOS/updater.app/Contents/MacOS/org.mozilla.updater
 endif
 	printf APPLMOZB > $(dist_dest)/Contents/PkgInfo
 endif
+
+.PHONY: features
+tools features::
+	$(PYTHON) -c 'import os, json; listing = {"system": os.listdir("$(DIST)/bin/browser/features")}; print json.dumps(listing)' > $(DIST)/bin/browser/chrome/browser/content/browser/built_in_addons.json
--- a/browser/app/nsBrowserApp.cpp
+++ b/browser/app/nsBrowserApp.cpp
@@ -227,19 +227,19 @@ static int do_main(int argc, char* argv[
   if (getenv("LIBFUZZER"))
     gBootstrap->XRE_LibFuzzerSetDriver(fuzzer::FuzzerDriver);
 #endif
 
   return gBootstrap->XRE_main(argc, argv, config);
 }
 
 static nsresult
-InitXPCOMGlue(const char *argv0)
+InitXPCOMGlue()
 {
-  UniqueFreePtr<char> exePath = BinaryPath::Get(argv0);
+  UniqueFreePtr<char> exePath = BinaryPath::Get();
   if (!exePath) {
     Output("Couldn't find the application directory.\n");
     return NS_ERROR_FAILURE;
   }
 
   gBootstrap = mozilla::GetBootstrap(exePath.get());
   if (!gBootstrap) {
     Output("Couldn't load XPCOM.\n");
@@ -267,17 +267,17 @@ int main(int argc, char* argv[], char* e
     // We need to initialize the sandbox TargetServices before InitXPCOMGlue
     // because we might need the sandbox broker to give access to some files.
     if (IsSandboxedProcess() && !sandboxing::GetInitializedTargetServices()) {
       Output("Failed to initialize the sandbox target services.");
       return 255;
     }
 #endif
 
-    nsresult rv = InitXPCOMGlue(argv[0]);
+    nsresult rv = InitXPCOMGlue();
     if (NS_FAILED(rv)) {
       return 255;
     }
 
     int result = content_process_main(gBootstrap.get(), argc, argv);
 
     // InitXPCOMGlue calls NS_LogInit, so we need to balance it here.
     gBootstrap->NS_LogTerm();
@@ -285,17 +285,17 @@ int main(int argc, char* argv[], char* e
     return result;
   }
 #endif
 
 #ifdef HAS_DLL_BLOCKLIST
   DllBlocklist_Initialize();
 #endif
 
-  nsresult rv = InitXPCOMGlue(argv[0]);
+  nsresult rv = InitXPCOMGlue();
   if (NS_FAILED(rv)) {
     return 255;
   }
 
   gBootstrap->XRE_StartupTimelineRecord(mozilla::StartupTimeline::START, start);
 
 #ifdef MOZ_BROWSER_CAN_BE_CONTENTPROC
   gBootstrap->XRE_EnableSameExecutableForContentProc();
--- a/browser/base/content/browser-sync.js
+++ b/browser/base/content/browser-sync.js
@@ -519,16 +519,17 @@ var gSync = {
   // doSync forces a sync - it *does not* return a promise as it is called
   // via the various UI components.
   doSync() {
     if (!UIState.isReady()) {
       return;
     }
     const state = UIState.get();
     if (state.status == UIState.STATUS_SIGNED_IN) {
+      this.updateSyncStatus({ syncing: true });
       setTimeout(() => Weave.Service.errorHandler.syncAndReportErrors(), 0);
     }
   },
 
   openPrefs(entryPoint = "syncbutton", origin = undefined) {
     window.openPreferences("paneSync", { origin, urlParams: { entrypoint: entryPoint } });
   },
 
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -110,19 +110,19 @@ tabbrowser {
   -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser");
 }
 
 .tabbrowser-tabs {
   -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-tabs");
 }
 
 #tabbrowser-tabs:not([overflow="true"]) ~ #alltabs-button,
-#tabbrowser-tabs:not([overflow="true"]) + #new-tab-button,
+#tabbrowser-tabs[hasadjacentnewtabbutton]:not([overflow="true"]) ~ #new-tab-button,
 #tabbrowser-tabs[overflow="true"] > .tabbrowser-arrowscrollbox > .tabs-newtab-button,
-#TabsToolbar[currentset]:not([currentset*="tabbrowser-tabs,new-tab-button"]) > #tabbrowser-tabs > .tabbrowser-arrowscrollbox > .tabs-newtab-button,
+#tabbrowser-tabs:not([hasadjacentnewtabbutton]) > .tabbrowser-arrowscrollbox > .tabs-newtab-button,
 #TabsToolbar[customizing="true"] > #tabbrowser-tabs > .tabbrowser-arrowscrollbox > .tabs-newtab-button {
   visibility: collapse;
 }
 
 #tabbrowser-tabs:not([overflow="true"])[using-closing-tabs-spacer] ~ #alltabs-button {
   visibility: hidden; /* temporary space to keep a tab's close button under the cursor */
 }
 
@@ -1245,29 +1245,29 @@ toolbarpaletteitem[place="palette"] > #d
 }
 
 #customization-panelHolder {
   padding-top: 10px;
   padding-bottom: 10px;
 }
 
 #customization-panelHolder > #widget-overflow-fixed-list {
-  flex: 0 1 auto; /* Size to content, but allow ourselves to shrink */
+  flex: 1 1 auto; /* Grow within the available space, and allow ourselves to shrink */
   display: flex;
   flex-direction: column;
   overflow-y: auto;
   overflow-x: hidden;
 }
 
 #customization-panelWrapper,
 #customization-panelWrapper > .panel-arrowcontent,
 #customization-panelHolder {
   flex-direction: column;
   display: flex;
-  min-height: 0;
+  min-height: calc(174px + 8em);
 }
 
 #customization-panelWrapper {
   flex: 1 1 auto;
   height: 0; /* Don't let my contents affect ancestors' content-based sizing */
   align-items: end; /* align to the end on the cross-axis (affects arrow) */
 }
 
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -24,16 +24,23 @@ function setContextMenuContentData(data)
   gContextMenuContentData = data;
 }
 
 function openContextMenu(aMessage) {
   let data = aMessage.data;
   let browser = aMessage.target;
   let spellInfo = data.spellInfo;
 
+  // ContextMenu.jsm sends us the target as a CPOW only so that
+  // we can send that CPOW back down to the content process and
+  // have it resolve to a DOM node. The parent should not attempt
+  // to access any properties on this CPOW (in fact, doing so
+  // will throw an exception).
+  data.context.targetAsCPOW = aMessage.objects.targetAsCPOW;
+
   if (spellInfo) {
     spellInfo.target = aMessage.target.messageManager;
   }
 
   let documentURIObject = makeURI(data.docLocation,
                                   data.charSet,
                                   makeURI(data.baseURI));
   gContextMenuContentData = { context: data.context,
@@ -199,16 +206,17 @@ nsContextMenu.prototype = {
     this.onMozExtLink        = context.onMozExtLink;
     this.onNumeric           = context.onNumeric;
     this.onPassword          = context.onPassword;
     this.onSaveableLink      = context.onSaveableLink;
     this.onTextInput         = context.onTextInput;
     this.onVideo             = context.onVideo;
 
     this.target = this.isRemote ? context.target : document.popupNode;
+    this.targetAsCPOW = context.targetAsCPOW;
 
     this.principal = context.principal;
     this.frameOuterWindowID = context.frameOuterWindowID;
 
     this.inSyntheticDoc = context.inSyntheticDoc;
 
     // Everything after this isn't sent directly from ContextMenu
     this.ownerDoc = this.target.ownerDocument;
@@ -712,17 +720,17 @@ nsContextMenu.prototype = {
       fillMenu.setAttribute("label", fillMenu.getAttribute("label-login"));
       fillMenu.setAttribute("accesskey", fillMenu.getAttribute("accesskey-login"));
     }
 
     if (!showFill || disableFill) {
       return;
     }
     let documentURI = gContextMenuContentData.documentURIObject;
-    let fragment = LoginManagerContextMenu.addLoginsToMenu(this.target, this.browser, documentURI);
+    let fragment = LoginManagerContextMenu.addLoginsToMenu(this.targetAsCPOW, this.browser, documentURI);
 
     this.showItem("fill-login-no-logins", !fragment);
 
     if (!fragment) {
       return;
     }
     let popup = document.getElementById("fill-login-popup");
     let insertBeforeElement = document.getElementById("fill-login-no-logins");
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -5822,30 +5822,77 @@
               // This is the only pref observed.
               this._findAsYouType = Services.prefs.getBoolPref("accessibility.typeaheadfind");
               break;
             }
           }
         ]]></body>
       </method>
 
+      <method name="_updateNewTabVisibility">
+        <body><![CDATA[
+          let sib = this.tabContainer.nextElementSibling;
+          while (sib && sib.hidden) {
+            sib = sib.nextElementSibling;
+          }
+          const kAttr = "hasadjacentnewtabbutton";
+          if (sib && sib.id == "new-tab-button") {
+            this.tabContainer.setAttribute(kAttr, "true");
+          } else {
+            this.tabContainer.removeAttribute(kAttr);
+          }
+        ]]></body>
+      </method>
+
+      <method name="onWidgetAfterDOMChange">
+        <parameter name="aNode"/>
+        <parameter name="aNextNode"/>
+        <parameter name="aContainer"/>
+        <body><![CDATA[
+          if (aContainer.ownerDocument == document &&
+              aContainer.id == "TabsToolbar") {
+            this._updateNewTabVisibility();
+          }
+        ]]></body>
+      </method>
+      <method name="onAreaNodeRegistered">
+        <parameter name="aArea"/>
+        <parameter name="aContainer"/>
+        <body><![CDATA[
+          if (aContainer.ownerDocument == document &&
+              aArea == "TabsToolbar") {
+            this._updateNewTabVisibility();
+          }
+        ]]></body>
+      </method>
+      <method name="onAreaReset">
+        <parameter name="aArea"/>
+        <parameter name="aContainer"/>
+        <body><![CDATA[
+          this.onAreaNodeRegistered(aArea, aContainer);
+        ]]></body>
+      </method>
+
       <field name="_tabMinWidthLimit">50</field>
       <property name="tabMinWidth">
         <setter><![CDATA[
           let root = document.documentElement;
           root.style.setProperty("--tab-min-width", val + "px");
           return val;
         ]]></setter>
       </property>
 
       <constructor>
         <![CDATA[
           this.mCurrentBrowser = document.getAnonymousElementByAttribute(this, "anonid", "initialBrowser");
           this.mCurrentBrowser.permanentKey = {};
 
+          CustomizableUI.addListener(this);
+          this._updateNewTabVisibility();
+
           Services.obs.addObserver(this, "contextual-identity-updated");
 
           this.mCurrentTab = this.tabContainer.firstChild;
           const nsIEventListenerService =
             Components.interfaces.nsIEventListenerService;
           let els = Components.classes["@mozilla.org/eventlistenerservice;1"]
                               .getService(nsIEventListenerService);
           els.addSystemEventListener(document, "keydown", this, false);
@@ -5952,16 +5999,18 @@
           return "panel-" + outerID + "-" + (++this._uniquePanelIDCounter);
         ]]></body>
       </method>
 
       <destructor>
         <![CDATA[
           Services.obs.removeObserver(this, "contextual-identity-updated");
 
+          CustomizableUI.removeListener(this);
+
           for (let tab of this.tabs) {
             let browser = tab.linkedBrowser;
             if (browser.registeredOpenURI) {
               this._unifiedComplete.unregisterOpenPage(browser.registeredOpenURI,
                                                        browser.getAttribute("usercontextid") || 0);
               delete browser.registeredOpenURI;
             }
 
--- a/browser/components/customizableui/content/customizeMode.inc.xul
+++ b/browser/components/customizableui/content/customizeMode.inc.xul
@@ -22,17 +22,17 @@
     </box>
     <vbox id="customization-panel-container">
       <vbox id="customization-panelWrapper">
         <box class="panel-arrowbox">
           <image class="panel-arrow" side="top"/>
         </box>
         <box class="panel-arrowcontent" side="top" flex="1">
           <vbox id="customization-panelHolder">
-            <description id="customization-panelHeader">&customizeMode.overflowList.title;</description>
+            <description id="customization-panelHeader">&customizeMode.overflowList.title2;</description>
             <description id="customization-panelDescription">&customizeMode.overflowList.description;</description>
           </vbox>
           <box class="panel-inner-arrowcontentfooter" hidden="true"/>
         </box>
       </vbox>
     </vbox>
   </box>
   <hbox id="customization-footer">
--- a/browser/components/customizableui/test/browser_synced_tabs_menu.js
+++ b/browser/components/customizableui/test/browser_synced_tabs_menu.js
@@ -2,16 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 requestLongerTimeout(2);
 
 let {SyncedTabs} = Cu.import("resource://services-sync/SyncedTabs.jsm", {});
+let {UIState} = Cu.import("resource://services-sync/UIState.jsm", {});
 
 XPCOMUtils.defineLazyModuleGetter(this, "UITour", "resource:///modules/UITour.jsm");
 
 // These are available on the widget implementation, but it seems impossible
 // to grab that impl at runtime.
 const DECKINDEX_TABS = 0;
 const DECKINDEX_TABSDISABLED = 1;
 const DECKINDEX_FETCHING = 2;
@@ -37,28 +38,26 @@ let mockedInternal = {
   hasSyncedThisSession: false,
 };
 
 
 add_task(async function setup() {
   let oldInternal = SyncedTabs._internal;
   SyncedTabs._internal = mockedInternal;
 
-  // This test hacks some observer states to simulate a user being signed
-  // in to Sync - restore them when the test completes.
-  let initialObserverStates = {};
-  for (let id of ["sync-reauth-state", "sync-setup-state", "sync-syncnow-state"]) {
-    initialObserverStates[id] = document.getElementById(id).hidden;
-  }
+  let origNotifyStateUpdated = UIState._internal.notifyStateUpdated;
+  // Sync start-up will interfere with our tests, don't let UIState send UI updates.
+  UIState._internal.notifyStateUpdated = () => {};
+
+  // Force gSync initialization
+  gSync.init();
 
   registerCleanupFunction(() => {
+    UIState._internal.notifyStateUpdated = origNotifyStateUpdated;
     SyncedTabs._internal = oldInternal;
-    for (let [id, initial] of Object.entries(initialObserverStates)) {
-      document.getElementById(id).hidden = initial;
-    }
   });
 });
 
 // The test expects the about:preferences#sync page to open in the current tab
 async function openPrefsFromMenuPanel(expectedPanelId, entryPoint) {
   info("Check Sync button functionality");
   Services.prefs.setCharPref("identity.fxaccounts.remote.signup.uri", "http://example.com/");
   CustomizableUI.addWidgetToArea("sync-button", CustomizableUI.AREA_FIXED_OVERFLOW_PANEL);
@@ -130,41 +129,34 @@ async function asyncCleanup() {
   // restore the tabs
   BrowserTestUtils.addTab(gBrowser, initialLocation);
   gBrowser.removeTab(newTab);
   UITour.tourBrowsersByWindow.delete(window);
 }
 
 // When Sync is not setup.
 add_task(async function() {
-  document.getElementById("sync-reauth-state").hidden = true;
-  document.getElementById("sync-setup-state").hidden = false;
-  document.getElementById("sync-syncnow-state").hidden = true;
-  await openPrefsFromMenuPanel("PanelUI-remotetabs-setupsync", "synced-tabs")
+  gSync.updateAllUI({ status: UIState.STATUS_NOT_CONFIGURED });
+  await openPrefsFromMenuPanel("PanelUI-remotetabs-setupsync", "synced-tabs");
 });
 add_task(asyncCleanup);
 
 // When Sync is configured in a "needs reauthentication" state.
 add_task(async function() {
-  // configure our broadcasters so we are in the right state.
-  document.getElementById("sync-reauth-state").hidden = false;
-  document.getElementById("sync-setup-state").hidden = true;
-  document.getElementById("sync-syncnow-state").hidden = true;
+  gSync.updateAllUI({ status: UIState.STATUS_LOGIN_FAILED, email: "foo@bar.com" });
   await openPrefsFromMenuPanel("PanelUI-remotetabs-reauthsync", "synced-tabs")
 });
 
 // Test the mobile promo links
 add_task(async function() {
   // change the preferences for the mobile links.
   Services.prefs.setCharPref("identity.mobilepromo.android", "http://example.com/?os=android&tail=");
   Services.prefs.setCharPref("identity.mobilepromo.ios", "http://example.com/?os=ios&tail=");
 
-  document.getElementById("sync-reauth-state").hidden = true;
-  document.getElementById("sync-setup-state").hidden = true;
-  document.getElementById("sync-syncnow-state").hidden = false;
+  gSync.updateAllUI({ status: UIState.STATUS_SIGNED_IN, email: "foo@bar.com" });
 
   let syncPanel = document.getElementById("PanelUI-remotetabs");
   let links = syncPanel.querySelectorAll(".remotetabs-promo-link");
 
   is(links.length, 2, "found 2 links as expected");
 
   // test each link and left and middle mouse buttons
   for (let link of links) {
@@ -205,20 +197,17 @@ add_task(async function() {
   await hideOverflow();
 
   Services.prefs.clearUserPref("identity.mobilepromo.android");
   Services.prefs.clearUserPref("identity.mobilepromo.ios");
 });
 
 // Test the "Sync Now" button
 add_task(async function() {
-  // configure our broadcasters so we are in the right state.
-  document.getElementById("sync-reauth-state").hidden = true;
-  document.getElementById("sync-setup-state").hidden = true;
-  document.getElementById("sync-syncnow-state").hidden = false;
+  gSync.updateAllUI({ status: UIState.STATUS_SIGNED_IN, email: "foo@bar.com" });
 
   await document.getElementById("nav-bar").overflowable.show();
   let tabsUpdatedPromise = promiseObserverNotified("synced-tabs-menu:test:tabs-updated");
   let syncPanel = document.getElementById("PanelUI-remotetabs");
   let viewShownPromise = BrowserTestUtils.waitForEvent(syncPanel, "ViewShown");
   let syncButton = document.getElementById("sync-button");
   syncButton.click();
   await Promise.all([tabsUpdatedPromise, viewShownPromise]);
@@ -227,32 +216,21 @@ add_task(async function() {
   let subpanel = document.getElementById("PanelUI-remotetabs-main")
   ok(!subpanel.hidden, "main pane is visible");
   let deck = document.getElementById("PanelUI-remotetabs-deck");
 
   // The widget is still fetching tabs, as we've neutered everything that
   // provides them
   is(deck.selectedIndex, DECKINDEX_FETCHING, "first deck entry is visible");
 
-  let syncNowButton = document.getElementById("PanelUI-remotetabs-syncnow");
-
-  let didSync = false;
-  let oldDoSync = gSync.doSync;
-  gSync.doSync = function() {
-    didSync = true;
-    mockedInternal.hasSyncedThisSession = true;
-    gSync.doSync = oldDoSync;
-  }
-  syncNowButton.click();
-  ok(didSync, "clicking the button called the correct function");
-
   // Tell the widget there are tabs available, but with zero clients.
   mockedInternal.getTabClients = () => {
     return Promise.resolve([]);
   }
+  mockedInternal.hasSyncedThisSession = true;
   await updateTabsPanel();
   // The UI should be showing the "no clients" pane.
   is(deck.selectedIndex, DECKINDEX_NOCLIENTS, "no-clients deck entry is visible");
 
   // Tell the widget there are tabs available - we have 3 clients, one with no
   // tabs.
   mockedInternal.getTabClients = () => {
     return Promise.resolve([
@@ -344,16 +322,28 @@ add_task(async function() {
   // There is a single node saying there's no tabs for the client.
   node = node.nextSibling;
   is(node.nodeName, "label", "node is a label");
   is(node.getAttribute("itemtype"), "", "node is neither a tab nor a client");
 
   node = node.nextSibling;
   is(node, null, "no more entries");
 
+  let didSync = false;
+  let oldDoSync = gSync.doSync;
+  gSync.doSync = function() {
+    didSync = true;
+    gSync.doSync = oldDoSync;
+  }
+
+  let syncNowButton = document.getElementById("PanelUI-remotetabs-syncnow");
+  is(syncNowButton.disabled, false);
+  syncNowButton.click();
+  ok(didSync, "clicking the button called the correct function");
+
   await hideOverflow();
 });
 
 // Test the pagination capabilities (Show More/All tabs)
 add_task(async function() {
   mockedInternal.getTabClients = () => {
     return Promise.resolve([
       {
@@ -370,20 +360,17 @@ add_task(async function() {
             allTabsDesktop.push({ title: "Tab #" + i });
           }
           return allTabsDesktop;
         }(),
       }
     ]);
   };
 
-  // configure our broadcasters so we are in the right state.
-  document.getElementById("sync-reauth-state").hidden = true;
-  document.getElementById("sync-setup-state").hidden = true;
-  document.getElementById("sync-syncnow-state").hidden = false;
+  gSync.updateAllUI({ status: UIState.STATUS_SIGNED_IN, email: "foo@bar.com" });
 
   await document.getElementById("nav-bar").overflowable.show();
   let tabsUpdatedPromise = promiseObserverNotified("synced-tabs-menu:test:tabs-updated");
   let syncPanel = document.getElementById("PanelUI-remotetabs");
   let viewShownPromise = BrowserTestUtils.waitForEvent(syncPanel, "ViewShown");
   let syncButton = document.getElementById("sync-button");
   syncButton.click();
   await Promise.all([tabsUpdatedPromise, viewShownPromise]);
--- a/browser/extensions/formautofill/ProfileStorage.jsm
+++ b/browser/extensions/formautofill/ProfileStorage.jsm
@@ -336,25 +336,24 @@ class AutofillRecords {
       recordToSave = {
         guid: record.guid,
         timeLastModified: record.timeLastModified || Date.now(),
         deleted: true,
       };
     } else {
       this._ensureMatchingVersion(record);
       recordToSave = record;
+      this._computeFields(recordToSave);
     }
 
     if (sourceSync) {
       let sync = this._getSyncMetaData(recordToSave, true);
       sync.changeCounter = 0;
     }
 
-    this._computeFields(recordToSave);
-
     this._store.data[this._collectionName].push(recordToSave);
 
     this._store.saveSoon();
 
     Services.obs.notifyObservers({wrappedJSObject: {sourceSync}}, "formautofill-storage-changed", "add");
     return recordToSave.guid;
   }
 
--- a/browser/extensions/formautofill/test/browser/head.js
+++ b/browser/extensions/formautofill/test/browser/head.js
@@ -206,16 +206,17 @@ function getNotification(index = 0) {
  */
 async function clickDoorhangerButton(button, index) {
   let popuphidden = BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popuphidden");
 
   if (button == MAIN_BUTTON || button == SECONDARY_BUTTON) {
     EventUtils.synthesizeMouseAtCenter(getNotification()[button], {});
   } else if (button == MENU_BUTTON) {
     // Click the dropmarker arrow and wait for the menu to show up.
+    await BrowserTestUtils.waitForCondition(() => getNotification().menubutton);
     await sleep(); // menubutton needs extra time for binding
     let notification = getNotification();
     ok(notification.menubutton, "notification menupopup displayed");
     let dropdownPromise =
       BrowserTestUtils.waitForEvent(notification.menupopup, "popupshown");
     await EventUtils.synthesizeMouseAtCenter(notification.menubutton, {});
     await dropdownPromise;
 
--- a/browser/extensions/formautofill/test/mochitest/mochitest.ini
+++ b/browser/extensions/formautofill/test/mochitest/mochitest.ini
@@ -6,12 +6,11 @@ support-files =
   ../../../../../toolkit/components/satchel/test/parent_utils.js
   formautofill_common.js
   formautofill_parent_utils.js
 
 [test_autofocus_form.html]
 [test_basic_autocomplete_form.html]
 [test_basic_creditcard_autocomplete_form.html]
 scheme=https
-skip-if = debug # Bug 1401454
 [test_formautofill_preview_highlight.html]
 [test_multiple_forms.html]
 [test_on_address_submission.html]
--- a/browser/extensions/formautofill/test/unit/test_storage_tombstones.js
+++ b/browser/extensions/formautofill/test/unit/test_storage_tombstones.js
@@ -87,32 +87,44 @@ add_storage_task(async function test_sim
   // and getAll should also not return it.
   Assert.equal(storage.getAll().length, 0);
 
   // but getAll allows us to access deleted items.
   let all = storage.getAll({includeDeleted: true});
   Assert.equal(all.length, 1);
 
   do_check_tombstone_record(all[0]);
+
+  // a tombstone got from API should look exactly the same as it got from the
+  // disk (besides "_sync").
+  let tombstoneInDisk = Object.assign({}, storage._store.data[storage._collectionName][0]);
+  delete tombstoneInDisk._sync;
+  do_check_tombstone_record(tombstoneInDisk);
 });
 
 add_storage_task(async function test_add_tombstone(storage, record) {
   do_print("Should be able to add a new tombstone");
   let guid = storage.add({guid: "test-guid-1", deleted: true});
 
   // should be unable to get it normally.
   Assert.equal(storage.get(guid), null);
   // and getAll should also not return it.
   Assert.equal(storage.getAll().length, 0);
 
   // but getAll allows us to access deleted items.
   let all = storage.getAll({rawData: true, includeDeleted: true});
   Assert.equal(all.length, 1);
 
   do_check_tombstone_record(all[0]);
+
+  // a tombstone got from API should look exactly the same as it got from the
+  // disk (besides "_sync").
+  let tombstoneInDisk = Object.assign({}, storage._store.data[storage._collectionName][0]);
+  delete tombstoneInDisk._sync;
+  do_check_tombstone_record(tombstoneInDisk);
 });
 
 add_storage_task(async function test_add_tombstone_without_guid(storage, record) {
   do_print("Should not be able to add a new tombstone without specifying the guid");
   Assert.throws(() => { storage.add({deleted: true}); });
   Assert.equal(storage.getAll({includeDeleted: true}).length, 0);
 });
 
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -850,17 +850,17 @@ you can use these alternative items. Oth
 <!ENTITY customizeMode.toolbars2 "Toolbars">
 <!ENTITY customizeMode.lwthemes "Themes">
 <!ENTITY customizeMode.lwthemes.myThemes "My Themes">
 <!ENTITY customizeMode.lwthemes.recommended "Recommended">
 <!ENTITY customizeMode.lwthemes.menuManage "Manage">
 <!ENTITY customizeMode.lwthemes.menuManage.accessKey "M">
 <!ENTITY customizeMode.lwthemes.menuGetMore "Get More Themes">
 <!ENTITY customizeMode.lwthemes.menuGetMore.accessKey "G">
-<!ENTITY customizeMode.overflowList.title "Overflow Panel">
+<!ENTITY customizeMode.overflowList.title2 "Overflow Menu">
 <!ENTITY customizeMode.overflowList.description "Drag and drop items here to keep them within reach but out of your toolbar…">
 <!ENTITY customizeMode.uidensity "Density">
 <!-- LOCALIZATION NOTE (customizeMode.uidensity.menuNormal.*):
      “Normal” is displayed in the Customize screen, under the Density menu. -->
 <!ENTITY customizeMode.uidensity.menuNormal.label "Normal">
 <!ENTITY customizeMode.uidensity.menuNormal.tooltip "Normal">
 <!ENTITY customizeMode.uidensity.menuNormal.accessKey "N">
 <!-- LOCALIZATION NOTE (customizeMode.uidensity.menuCompact.*):
--- a/browser/locales/l10n-changesets.json
+++ b/browser/locales/l10n-changesets.json
@@ -454,16 +454,26 @@
             "linux", 
             "linux64", 
             "macosx64", 
             "win32", 
             "win64"
         ], 
         "revision": "default"
     }, 
+    "ia": {
+        "platforms": [
+            "linux", 
+            "linux64", 
+            "macosx64", 
+            "win32", 
+            "win64"
+        ], 
+        "revision": "default"
+    }, 
     "id": {
         "platforms": [
             "linux", 
             "linux64", 
             "macosx64", 
             "win32", 
             "win64"
         ], 
--- a/browser/modules/ContextMenu.jsm
+++ b/browser/modules/ContextMenu.jsm
@@ -567,17 +567,18 @@ class ContextMenu {
     let context = this.context;
     this.target = context.target;
 
     let spellInfo = null;
     let editFlags = null;
     let principal = null;
     let customMenuItems = null;
 
-    if (context.target) {
+    let targetAsCPOW = context.target;
+    if (targetAsCPOW) {
       this._cleanContext();
     }
 
     let isRemote = Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT;
 
     if (isRemote) {
       editFlags = SpellCheckHelper.isEditable(aEvent.target, this.content);
 
@@ -616,25 +617,29 @@ class ContextMenu {
       contentDisposition,
       frameOuterWindowID,
       popupNodeSelectors,
       disableSetDesktopBg,
       parentAllowsMixedContent,
     };
 
     if (isRemote) {
-      this.global.sendAsyncMessage("contextmenu", data);
+      this.global.sendAsyncMessage("contextmenu", data, {
+        targetAsCPOW,
+      });
     } else {
       let browser = this.global.docShell.chromeEventHandler;
       let mainWin = browser.ownerGlobal;
 
       data.documentURIObject = doc.documentURIObject;
       data.disableSetDesktopBackground = data.disableSetDesktopBg;
       delete data.disableSetDesktopBg;
 
+      data.context.targetAsCPOW = targetAsCPOW;
+
       mainWin.setContextMenuContentData(data);
     }
   }
 
   /**
    * Some things are not serializable, so we either have to only send
    * their needed data or regenerate them in nsContextMenu.js
    * - target and target.ownerDocument
--- a/browser/themes/shared/customizableui/customizeMode.inc.css
+++ b/browser/themes/shared/customizableui/customizeMode.inc.css
@@ -506,17 +506,16 @@ toolbarpaletteitem[place=toolbar] > tool
   font-size: 1.3em;
   font-weight: 500;
   padding: 2px 12px;
   margin: 0;
 }
 
 #customization-panelHolder > #widget-overflow-fixed-list {
   padding-top: 10px;
-  min-height: 225px;
 }
 
 /**
  * We create a ::before pseudoelement that contains a background image to show the
  * drop dragon. This element fades in and out depending on whether the containing
  * panel list is empty and unhovered, or not.
  */
 #customization-panelHolder > #widget-overflow-fixed-list:not(:empty) {
--- a/build/automation.py.in
+++ b/build/automation.py.in
@@ -194,17 +194,16 @@ class Automation(object):
     preloadEnvVar = None
     if self.UNIXISH or self.IS_MAC:
       envVar = "LD_LIBRARY_PATH"
       preloadEnvVar = "LD_PRELOAD"
       if self.IS_MAC:
         envVar = "DYLD_LIBRARY_PATH"
         dmdLibrary = "libdmd.dylib"
       else: # unixish
-        env['MOZILLA_FIVE_HOME'] = xrePath
         dmdLibrary = "libdmd.so"
       if envVar in env:
         ldLibraryPath = ldLibraryPath + ":" + env[envVar]
       env[envVar] = ldLibraryPath
     elif self.IS_WIN32:
       env["PATH"] = env["PATH"] + ";" + str(ldLibraryPath)
       dmdLibrary = "dmd.dll"
       preloadEnvVar = "MOZ_REPLACE_MALLOC_LIB"
--- a/build/moz.configure/old.configure
+++ b/build/moz.configure/old.configure
@@ -238,17 +238,16 @@ def old_configure_options(*options):
     '--with-android-min-sdk',
     '--with-android-sdk',
     '--with-app-basename',
     '--with-app-name',
     '--with-arch',
     '--with-branding',
     '--with-cross-lib',
     '--with-debug-label',
-    '--with-default-mozilla-five-home',
     '--with-distribution-id',
     '--with-doc-include-dirs',
     '--with-doc-input-dirs',
     '--with-doc-output-dir',
     '--with-float-abi',
     '--with-fpu',
     '--with-intl-api',
     '--with-ios-sdk',
--- a/build/unix/run-mozilla.sh
+++ b/build/unix/run-mozilla.sh
@@ -232,23 +232,18 @@ fi
 ##
 ## Make sure the program is executable
 ##
 if [ ! -x "$MOZ_PROGRAM" ]
 then
 	moz_bail "Cannot execute $MOZ_PROGRAM."
 fi
 #
-##
-## Set MOZILLA_FIVE_HOME
-##
-MOZILLA_FIVE_HOME=$MOZ_DIST_BIN
-
 if [ -z "$MRE_HOME" ]; then
-    MRE_HOME=$MOZILLA_FIVE_HOME
+    MRE_HOME=$MOZ_DIST_BIN"
 fi
 ##
 ## Set LD_LIBRARY_PATH
 ##
 ## On Solaris we use $ORIGIN (set in RUNPATH) instead of LD_LIBRARY_PATH 
 ## to locate shared libraries. 
 ##
 ## When a shared library is a symbolic link, $ORIGIN will be replaced with
@@ -310,17 +305,16 @@ then
 fi
 
 # Disable Gnome crash dialog
 GNOME_DISABLE_CRASH_DIALOG=1
 export GNOME_DISABLE_CRASH_DIALOG
 
 if [ "$moz_debug" -eq 1 ]
 then
-  echo "MOZILLA_FIVE_HOME=$MOZILLA_FIVE_HOME"
   echo "  LD_LIBRARY_PATH=$LD_LIBRARY_PATH"
   if [ -n "$LD_LIBRARYN32_PATH" ]
   then
   	echo "LD_LIBRARYN32_PATH=$LD_LIBRARYN32_PATH"
   fi
   if [ -n "$LD_LIBRARYN64_PATH" ]
   then
   	echo "LD_LIBRARYN64_PATH=$LD_LIBRARYN64_PATH"
@@ -344,17 +338,17 @@ then
   echo "       ADDON_PATH=$ADDON_PATH"
   echo "      MOZ_PROGRAM=$MOZ_PROGRAM"
   echo "      MOZ_TOOLKIT=$MOZ_TOOLKIT"
   echo "        moz_debug=$moz_debug"
   echo "     moz_debugger=$moz_debugger"
   echo "moz_debugger_args=$moz_debugger_args"
 fi
 #
-export MOZILLA_FIVE_HOME LD_LIBRARY_PATH
+export LD_LIBRARY_PATH
 export SHLIB_PATH LIBPATH LIBRARY_PATH ADDON_PATH DYLD_LIBRARY_PATH
 
 if [ $moz_debug -eq 1 ]
 then
 	moz_debug_program ${1+"$@"}
 else
 	moz_run_program ${1+"$@"}
 fi
--- a/config/faster/rules.mk
+++ b/config/faster/rules.mk
@@ -105,8 +105,14 @@ ACDEFINES += -DBUILD_FASTER
 ifndef FASTER_RECURSIVE_MAKE
 $(TOPOBJDIR)/config/makefiles/xpidl/xpidl: $(TOPOBJDIR)/install-dist_idl
 endif
 # It also requires all the install manifests for dist/bin to have been processed
 # because it adds interfaces.manifest references with buildlist.py.
 $(TOPOBJDIR)/config/makefiles/xpidl/xpidl: $(addprefix install-,$(filter dist/bin%,$(INSTALL_MANIFESTS)))
 
 $(TOPOBJDIR)/build/application.ini: $(TOPOBJDIR)/buildid.h $(TOPOBJDIR)/source-repo.h
+
+# The manifest of allowed system add-ons should be re-built when using
+# "build faster".
+ifeq ($(MOZ_BUILD_APP),browser/app)
+default: $(TOPOBJDIR)/browser/app/features
+endif
--- a/devtools/shared/layout/utils.js
+++ b/devtools/shared/layout/utils.js
@@ -463,17 +463,17 @@ function getElementFromPoint(document, x
       y -= rect.top + offsetTop;
 
       if (x < 0 || y < 0) {
         // Didn't reach the content document, still over the frame.
         return node;
       }
     }
     if (node instanceof Ci.nsIDOMHTMLIFrameElement ||
-        node instanceof Ci.nsIDOMHTMLFrameElement) {
+        ChromeUtils.getClassName(node) === "HTMLFrameElement") {
       let subnode = getElementFromPoint(node.contentDocument, x, y);
       if (subnode) {
         node = subnode;
       }
     }
   }
   return node;
 }
--- a/docshell/base/nsIContentViewer.idl
+++ b/docshell/base/nsIContentViewer.idl
@@ -208,16 +208,28 @@ interface nsIContentViewer : nsISupports
 
   /** The actual text zoom in effect, as modified by the system font scale. */
   readonly attribute float effectiveTextZoom;
 
   /** The amount by which to scale all lengths. Default is 1.0. */
   attribute float fullZoom;
 
   /**
+   * The actual full zoom in effect, as modified by the device context.
+   * For a requested full zoom, the device context may choose a slightly
+   * different effectiveFullZoom to accomodate integer rounding of app units
+   * per dev pixel. This property returns the actual zoom amount in use,
+   * though it may not be good user experience to report that a requested zoom
+   * of 90% is actually 89.1%, for example. This value is provided primarily to
+   * support media queries of dppx values, because those queries are matched
+   * against the actual native device pixel ratio and the actual full zoom.
+   */
+  readonly attribute float deviceFullZoom;
+
+  /**
    * The value used to override devicePixelRatio and media queries dppx.
    * Default is 0.0, that means no overriding is done (only a positive value
    * is applied).
    */
   attribute float overrideDPPX;
 
   /** Disable entire author style level (including HTML presentation hints) */
   attribute boolean authorStyleDisabled;
--- a/dom/animation/Animation.cpp
+++ b/dom/animation/Animation.cpp
@@ -1183,19 +1183,19 @@ Animation::PauseNoUpdate(ErrorResult& aR
 void
 Animation::ResumeAt(const TimeDuration& aReadyTime)
 {
   // This method is only expected to be called for an animation that is
   // waiting to play. We can easily adapt it to handle other states
   // but it's currently not necessary.
   MOZ_ASSERT(mPendingState == PendingState::PlayPending,
              "Expected to resume a play-pending animation");
-  MOZ_ASSERT(mHoldTime.IsNull() != mStartTime.IsNull(),
+  MOZ_ASSERT(!mHoldTime.IsNull() || !mStartTime.IsNull(),
              "An animation in the play-pending state should have either a"
-             " resolved hold time or resolved start time (but not both)");
+             " resolved hold time or resolved start time");
 
   // If we aborted a pending pause operation we will already have a start time
   // we should use. In all other cases, we resolve it from the ready time.
   if (mStartTime.IsNull()) {
     mStartTime = StartTimeFromReadyTime(aReadyTime);
     if (mPlaybackRate != 0) {
       mHoldTime.SetNull();
     }
new file mode 100644
--- /dev/null
+++ b/dom/animation/test/crashtests/1291413-1.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html class=reftest-wait>
+<script>
+window.onload = () => {
+  const div = document.createElement('div');
+
+  document.documentElement.appendChild(div);
+  const anim = div.animate([], 1000);
+
+  anim.ready.then(() => {
+    anim.pause();
+    anim.reverse();
+    anim.playbackRate = 0;
+    anim.ready.then(() => {
+      document.documentElement.className = '';
+    });
+  });
+};
+</script>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/animation/test/crashtests/1291413-2.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<html class=reftest-wait>
+<script>
+window.onload = () => {
+  const div = document.createElement('div');
+
+  document.documentElement.appendChild(div);
+  const anim = div.animate([], 1000);
+
+  anim.ready.then(() => {
+    anim.pause();
+    anim.reverse();
+    anim.playbackRate = 0;
+    anim.playbackRate = 1;
+    anim.ready.then(() => {
+      document.documentElement.className = '';
+    });
+  });
+};
+</script>
+</html>
--- a/dom/animation/test/crashtests/crashtests.list
+++ b/dom/animation/test/crashtests/crashtests.list
@@ -5,16 +5,18 @@ pref(dom.animations-api.core.enabled,tru
 pref(dom.animations-api.core.enabled,true) load 1216842-3.html
 pref(dom.animations-api.core.enabled,true) load 1216842-4.html
 pref(dom.animations-api.core.enabled,true) load 1216842-5.html
 pref(dom.animations-api.core.enabled,true) load 1216842-6.html
 pref(dom.animations-api.core.enabled,true) load 1272475-1.html
 pref(dom.animations-api.core.enabled,true) load 1272475-2.html
 pref(dom.animations-api.core.enabled,true) load 1278485-1.html
 pref(dom.animations-api.core.enabled,true) load 1277272-1.html
+pref(dom.animations-api.core.enabled,true) load 1291413-1.html
+pref(dom.animations-api.core.enabled,true) load 1291413-2.html
 pref(dom.animations-api.core.enabled,true) load 1304886-1.html
 pref(dom.animations-api.core.enabled,true) load 1322382-1.html
 pref(dom.animations-api.core.enabled,true) load 1322291-1.html
 pref(dom.animations-api.core.enabled,true) load 1322291-2.html
 pref(dom.animations-api.core.enabled,true) load 1323114-1.html
 pref(dom.animations-api.core.enabled,true) load 1323114-2.html
 pref(dom.animations-api.core.enabled,true) load 1324554-1.html
 pref(dom.animations-api.core.enabled,true) load 1325193-1.html
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -9560,17 +9560,16 @@ nsDocument::OnPageHide(bool aPersisted,
       PageUnloadingEventTimeStamp timeStamp(this);
       DispatchPageTransition(target, NS_LITERAL_STRING("pagehide"), aPersisted);
     }
   }
 
   mVisible = false;
 
   UpdateVisibilityState();
-
   EnumerateExternalResources(NotifyPageHide, &aPersisted);
   EnumerateActivityObservers(NotifyActivityChanged, nullptr);
 
   ClearPendingFullscreenRequests(this);
   if (GetFullscreenElement()) {
     // If this document was fullscreen, we should exit fullscreen in this
     // doctree branch. This ensures that if the user navigates while in
     // fullscreen mode we don't leave its still visible ancestor documents
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -10,17 +10,16 @@
  */
 
 #include "base/basictypes.h"
 
 #include "prenv.h"
 
 #include "nsDocShell.h"
 #include "nsIDOMHTMLIFrameElement.h"
-#include "nsIDOMHTMLFrameElement.h"
 #include "nsIDOMMozBrowserFrame.h"
 #include "nsIDOMWindow.h"
 #include "nsIPresShell.h"
 #include "nsIContentInlines.h"
 #include "nsIContentViewer.h"
 #include "nsIDocument.h"
 #include "nsIDOMDocument.h"
 #include "nsPIDOMWindow.h"
--- a/dom/html/HTMLContentElement.cpp
+++ b/dom/html/HTMLContentElement.cpp
@@ -51,21 +51,18 @@ HTMLContentElement::HTMLContentElement(a
 HTMLContentElement::~HTMLContentElement()
 {
 }
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLContentElement,
                                    nsGenericHTMLElement,
                                    mMatchedNodes)
 
-NS_IMPL_ADDREF_INHERITED(HTMLContentElement, Element)
-NS_IMPL_RELEASE_INHERITED(HTMLContentElement, Element)
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(HTMLContentElement)
-NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)
+NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(HTMLContentElement,
+                                               nsGenericHTMLElement)
 
 NS_IMPL_ELEMENT_CLONE(HTMLContentElement)
 
 JSObject*
 HTMLContentElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return HTMLContentElementBinding::Wrap(aCx, this, aGivenProto);
 }
--- a/dom/html/HTMLDataListElement.cpp
+++ b/dom/html/HTMLDataListElement.cpp
@@ -17,25 +17,22 @@ HTMLDataListElement::~HTMLDataListElemen
 }
 
 JSObject*
 HTMLDataListElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return HTMLDataListElementBinding::Wrap(aCx, this, aGivenProto);
 }
 
-NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLDataListElement, nsGenericHTMLElement,
+NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLDataListElement,
+                                   nsGenericHTMLElement,
                                    mOptions)
 
-NS_IMPL_ADDREF_INHERITED(HTMLDataListElement, Element)
-NS_IMPL_RELEASE_INHERITED(HTMLDataListElement, Element)
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(HTMLDataListElement)
-NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)
-
+NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(HTMLDataListElement,
+                                               nsGenericHTMLElement)
 
 NS_IMPL_ELEMENT_CLONE(HTMLDataListElement)
 
 bool
 HTMLDataListElement::MatchOptions(Element* aElement, int32_t aNamespaceID,
                                   nsIAtom* aAtom, void* aData)
 {
   return aElement->NodeInfo()->Equals(nsGkAtoms::option, kNameSpaceID_XHTML) &&
--- a/dom/html/HTMLFrameElement.cpp
+++ b/dom/html/HTMLFrameElement.cpp
@@ -20,38 +20,20 @@ HTMLFrameElement::HTMLFrameElement(alrea
 {
 }
 
 HTMLFrameElement::~HTMLFrameElement()
 {
 }
 
 
-NS_IMPL_ISUPPORTS_INHERITED(HTMLFrameElement, nsGenericHTMLFrameElement,
-                            nsIDOMHTMLFrameElement)
+NS_IMPL_ISUPPORTS_INHERITED0(HTMLFrameElement, nsGenericHTMLFrameElement)
 
 NS_IMPL_ELEMENT_CLONE(HTMLFrameElement)
 
-
-NS_IMPL_STRING_ATTR(HTMLFrameElement, FrameBorder, frameborder)
-NS_IMPL_URI_ATTR(HTMLFrameElement, LongDesc, longdesc)
-NS_IMPL_STRING_ATTR(HTMLFrameElement, MarginHeight, marginheight)
-NS_IMPL_STRING_ATTR(HTMLFrameElement, MarginWidth, marginwidth)
-NS_IMPL_STRING_ATTR(HTMLFrameElement, Name, name)
-NS_IMPL_BOOL_ATTR(HTMLFrameElement, NoResize, noresize)
-NS_IMPL_STRING_ATTR(HTMLFrameElement, Scrolling, scrolling)
-NS_IMPL_URI_ATTR(HTMLFrameElement, Src, src)
-
-
-NS_IMETHODIMP
-HTMLFrameElement::GetContentDocument(nsIDOMDocument** aContentDocument)
-{
-  return nsGenericHTMLFrameElement::GetContentDocument(aContentDocument);
-}
-
 bool
 HTMLFrameElement::ParseAttribute(int32_t aNamespaceID,
                                  nsIAtom* aAttribute,
                                  const nsAString& aValue,
                                  nsAttrValue& aResult)
 {
   if (aNamespaceID == kNameSpaceID_None) {
     if (aAttribute == nsGkAtoms::bordercolor) {
--- a/dom/html/HTMLFrameElement.h
+++ b/dom/html/HTMLFrameElement.h
@@ -3,93 +3,111 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_HTMLFrameElement_h
 #define mozilla_dom_HTMLFrameElement_h
 
 #include "mozilla/Attributes.h"
-#include "nsIDOMHTMLFrameElement.h"
 #include "nsGenericHTMLFrameElement.h"
 #include "nsGkAtoms.h"
 
 namespace mozilla {
 namespace dom {
 
-class HTMLFrameElement final : public nsGenericHTMLFrameElement,
-                               public nsIDOMHTMLFrameElement
+class HTMLFrameElement final : public nsGenericHTMLFrameElement
 {
 public:
   using nsGenericHTMLFrameElement::SwapFrameLoaders;
 
   explicit HTMLFrameElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo,
                             FromParser aFromParser = NOT_FROM_PARSER);
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
-  // nsIDOMHTMLFrameElement
-  NS_DECL_NSIDOMHTMLFRAMEELEMENT
+  NS_IMPL_FROMCONTENT_HTML_WITH_TAG(HTMLFrameElement, frame)
 
   // nsIContent
   virtual bool ParseAttribute(int32_t aNamespaceID,
                               nsIAtom* aAttribute,
                               const nsAString& aValue,
                               nsAttrValue& aResult) override;
   virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,
                          bool aPreallocateChildren) const override;
 
   // WebIDL API
-  // The XPCOM GetFrameBorder is OK for us
+  void GetFrameBorder(DOMString& aFrameBorder) const
+  {
+    GetHTMLAttr(nsGkAtoms::frameborder, aFrameBorder);
+  }
   void SetFrameBorder(const nsAString& aFrameBorder, ErrorResult& aError)
   {
     SetHTMLAttr(nsGkAtoms::frameborder, aFrameBorder, aError);
   }
 
-  // The XPCOM GetLongDesc is OK for us
+  void GetLongDesc(nsAString& aLongDesc) const
+  {
+    GetURIAttr(nsGkAtoms::longdesc, nullptr, aLongDesc);
+  }
   void SetLongDesc(const nsAString& aLongDesc, ErrorResult& aError)
   {
     SetAttrHelper(nsGkAtoms::longdesc, aLongDesc);
   }
 
-  // The XPCOM GetMarginHeight is OK for us
+  void GetMarginHeight(DOMString& aMarginHeight) const
+  {
+    GetHTMLAttr(nsGkAtoms::marginheight, aMarginHeight);
+  }
   void SetMarginHeight(const nsAString& aMarginHeight, ErrorResult& aError)
   {
     SetHTMLAttr(nsGkAtoms::marginheight, aMarginHeight, aError);
   }
 
-  // The XPCOM GetMarginWidth is OK for us
+  void GetMarginWidth(DOMString& aMarginWidth) const
+  {
+    GetHTMLAttr(nsGkAtoms::marginwidth, aMarginWidth);
+  }
   void SetMarginWidth(const nsAString& aMarginWidth, ErrorResult& aError)
   {
     SetHTMLAttr(nsGkAtoms::marginwidth, aMarginWidth, aError);
   }
 
-  // The XPCOM GetName is OK for us
+  void GetName(DOMString& aName) const
+  {
+    GetHTMLAttr(nsGkAtoms::name, aName);
+  }
   void SetName(const nsAString& aName, ErrorResult& aError)
   {
     SetHTMLAttr(nsGkAtoms::name, aName, aError);
   }
 
   bool NoResize() const
   {
    return GetBoolAttr(nsGkAtoms::noresize);
   }
   void SetNoResize(bool& aNoResize, ErrorResult& aError)
   {
     SetHTMLBoolAttr(nsGkAtoms::noresize, aNoResize, aError);
   }
 
-  // The XPCOM GetScrolling is OK for us
+  void GetScrolling(DOMString& aScrolling) const
+  {
+    GetHTMLAttr(nsGkAtoms::scrolling, aScrolling);
+  }
   void SetScrolling(const nsAString& aScrolling, ErrorResult& aError)
   {
     SetHTMLAttr(nsGkAtoms::scrolling, aScrolling, aError);
   }
 
-  // The XPCOM GetSrc is OK for us
+  void GetSrc(nsAString& aSrc) const
+  {
+    GetURIAttr(nsGkAtoms::src, nullptr, aSrc);
+  }
   void SetSrc(const nsAString& aSrc, ErrorResult& aError)
   {
     SetAttrHelper(nsGkAtoms::src, aSrc);
   }
 
   using nsGenericHTMLFrameElement::GetContentDocument;
   using nsGenericHTMLFrameElement::GetContentWindow;
 
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -16,16 +16,17 @@
 #include "mozilla/EMEUtils.h"
 
 #include "base/basictypes.h"
 #include "nsIDOMHTMLMediaElement.h"
 #include "nsIDOMHTMLSourceElement.h"
 #include "TimeRanges.h"
 #include "nsGenericHTMLElement.h"
 #include "nsAttrValueInlines.h"
+#include "nsDocShellLoadTypes.h"
 #include "nsPresContext.h"
 #include "nsIClassOfService.h"
 #include "nsIPresShell.h"
 #include "nsGkAtoms.h"
 #include "nsSize.h"
 #include "nsIFrame.h"
 #include "nsIDocument.h"
 #include "nsIDOMDocument.h"
@@ -2839,16 +2840,27 @@ HTMLMediaElement::Seek(double aTime,
   //       actual seek target before beginning the synchronous section, but
   //       that requires changing all MediaDecoderReaders to support telling
   //       us the fastSeek target, and it's currently not possible to get
   //       this information as we don't yet control the demuxer for all
   //       MediaDecoderReaders.
 
   mPlayingBeforeSeek = IsPotentiallyPlaying();
 
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+  if (!mDecoder->IsMetadataLoaded()) {
+    // This is for debugging bug 1402584.
+    // We should reach here only after metadata loaded by the decoder.
+    MOZ_CRASH_UNSAFE_PRINTF(
+      "Metadata not loaded! readyState=%d networkState=%d",
+      static_cast<int>(mReadyState.Ref()),
+      static_cast<int>(mNetworkState));
+  }
+#endif
+
   // The media backend is responsible for dispatching the timeupdate
   // event if it changes the playback position as a result of the seek.
   LOG(LogLevel::Debug, ("%p SetCurrentTime(%f) starting seek", this, aTime));
   nsresult rv = mDecoder->Seek(aTime, aSeekType);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
@@ -3733,32 +3745,36 @@ HTMLMediaElement::AddMediaElementToURITa
   }
   MediaElementSetForURI* entry = gElementTable->PutEntry(mLoadingSrc);
   entry->mElements.AppendElement(this);
   NS_ASSERTION(MediaElementTableCount(this, mLoadingSrc) == 1,
     "Should have a single entry for element in element table after addition");
 }
 
 void
-HTMLMediaElement::RemoveMediaElementFromURITable()
+HTMLMediaElement::RemoveMediaElementFromURITable(bool aFroceClearEntry)
 {
   if (!mDecoder || !mLoadingSrc || !gElementTable) {
     return;
   }
   MediaElementSetForURI* entry = gElementTable->GetEntry(mLoadingSrc);
   if (!entry) {
     return;
   }
-  entry->mElements.RemoveElement(this);
-  if (entry->mElements.IsEmpty()) {
+  if (aFroceClearEntry) {
     gElementTable->RemoveEntry(entry);
-    if (gElementTable->Count() == 0) {
-      delete gElementTable;
-      gElementTable = nullptr;
-    }
+  } else {
+    entry->mElements.RemoveElement(this);
+    if (entry->mElements.IsEmpty()) {
+      gElementTable->RemoveEntry(entry);
+    }
+  }
+  if (gElementTable->Count() == 0) {
+    delete gElementTable;
+    gElementTable = nullptr;
   }
   NS_ASSERTION(MediaElementTableCount(this, mLoadingSrc) == 0,
     "After remove, should no longer have an entry in element table");
 }
 
 HTMLMediaElement*
 HTMLMediaElement::LookupMediaElementURITable(nsIURI* aURI)
 {
@@ -3840,16 +3856,116 @@ private:
   }
   // Guaranteed to be valid by HTMLMediaElement.
   HTMLMediaElement* mWeak = nullptr;
   Phase mPhase = Phase::Init;
 };
 
 NS_IMPL_ISUPPORTS(HTMLMediaElement::ShutdownObserver, nsIObserver)
 
+class HTMLMediaElement::ForceReloadListener : public nsIWebProgressListener
+                                            , public nsSupportsWeakReference
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  void Subscribe(HTMLMediaElement* aPtr, nsIWebProgress* aWebProgress)
+  {
+    MOZ_DIAGNOSTIC_ASSERT(!mWeak);
+    MOZ_DIAGNOSTIC_ASSERT(aWebProgress);
+    mWeak = aPtr;
+    aWebProgress->AddProgressListener(this,
+                                      nsIWebProgress::NOTIFY_STATE_NETWORK);
+  }
+  void Unsubscribe(nsIWebProgress* aWebProgress)
+  {
+    MOZ_DIAGNOSTIC_ASSERT(mWeak);
+    mWeak = nullptr;
+    if (aWebProgress) {
+      aWebProgress->RemoveProgressListener(this);
+    }
+  }
+
+  NS_IMETHODIMP OnStateChange(nsIWebProgress* aWebProgress,
+                              nsIRequest* aRequest,
+                              uint32_t aProgressStateFlags,
+                              nsresult aStatus) override
+  {
+    MOZ_DIAGNOSTIC_ASSERT(mWeak);
+    if ((aProgressStateFlags & STATE_IS_NETWORK) &&
+        (aProgressStateFlags & STATE_START)) {
+      // Query the LoadType to see if it's a ctrl+F5.
+      nsCOMPtr<nsIDocShell> shell(do_QueryInterface(aWebProgress));
+      if (shell) {
+        uint32_t loadType;
+        shell->GetLoadType(&loadType);
+        if (LOAD_RELOAD_BYPASS_PROXY_AND_CACHE == loadType && mWeak->mDecoder) {
+          mWeak->RemoveMediaElementFromURITable(true);
+          mWeak->ShutdownDecoder();
+        }
+      }
+    }
+    return NS_OK;
+  }
+
+  NS_IMETHODIMP
+  OnProgressChange(nsIWebProgress* aProgress,
+                   nsIRequest* aRequest,
+                   int32_t aCurSelfProgress,
+                   int32_t aMaxSelfProgress,
+                   int32_t aCurTotalProgress,
+                   int32_t aMaxTotalProgress) override
+  {
+    NS_NOTREACHED("notification excluded in AddProgressListener(...)");
+    return NS_OK;
+  }
+
+  NS_IMETHODIMP
+  OnLocationChange(nsIWebProgress* aWebProgress,
+                   nsIRequest* aRequest,
+                   nsIURI* aLocation,
+                   uint32_t aFlags) override
+  {
+    NS_NOTREACHED("notification excluded in AddProgressListener(...)");
+    return NS_OK;
+  }
+
+  NS_IMETHODIMP
+  OnStatusChange(nsIWebProgress* aWebProgress,
+                 nsIRequest* aRequest,
+                 nsresult aStatus,
+                 const char16_t* aMessage) override
+  {
+    NS_NOTREACHED("notification excluded in AddProgressListener(...)");
+    return NS_OK;
+  }
+
+  NS_IMETHODIMP
+  OnSecurityChange(nsIWebProgress* aWebProgress,
+                   nsIRequest* aRequest,
+                   uint32_t aState) override
+  {
+    NS_NOTREACHED("notification excluded in AddProgressListener(...)");
+    return NS_OK;
+  }
+
+protected:
+  virtual ~ForceReloadListener()
+  {
+    MOZ_DIAGNOSTIC_ASSERT(!mWeak);
+  }
+
+private:
+  HTMLMediaElement* mWeak = nullptr;
+};
+
+NS_IMPL_ISUPPORTS(HTMLMediaElement::ForceReloadListener,
+                  nsIWebProgressListener,
+                  nsISupportsWeakReference)
+
 HTMLMediaElement::HTMLMediaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
   : nsGenericHTMLElement(aNodeInfo),
     mMainThreadEventTarget(OwnerDoc()->EventTargetFor(TaskCategory::Other)),
     mAbstractMainThread(OwnerDoc()->AbstractMainThreadFor(TaskCategory::Other)),
     mWatchManager(this, mAbstractMainThread),
     mSrcStreamTracksAvailable(false),
     mSrcStreamPausedCurrentTime(-1),
     mShutdownObserver(new ShutdownObserver),
@@ -3923,24 +4039,43 @@ HTMLMediaElement::HTMLMediaElement(alrea
 
   MOZ_ASSERT(NS_IsMainThread());
   mWatchManager.Watch(mDownloadSuspendedByCache, &HTMLMediaElement::UpdateReadyStateInternal);
   // Paradoxically, there is a self-edge whereby UpdateReadyStateInternal refuses
   // to run until mReadyState reaches at least HAVE_METADATA by some other means.
   mWatchManager.Watch(mReadyState, &HTMLMediaElement::UpdateReadyStateInternal);
 
   mShutdownObserver->Subscribe(this);
+  nsIDocShell* docShell = OwnerDoc()->GetDocShell();
+  if (docShell) {
+    nsCOMPtr<nsIDocShellTreeItem> root;
+    docShell->GetSameTypeRootTreeItem(getter_AddRefs(root));
+    nsCOMPtr<nsIWebProgress> webProgress = do_GetInterface(root);
+    if (webProgress) {
+      mForceReloadListener = new ForceReloadListener();
+      mForceReloadListener->Subscribe(this, webProgress);
+    }
+  }
 }
 
 HTMLMediaElement::~HTMLMediaElement()
 {
   NS_ASSERTION(!mHasSelfReference,
                "How can we be destroyed if we're still holding a self reference?");
-
   mShutdownObserver->Unsubscribe();
+  nsIDocShell* docShell = OwnerDoc()->GetDocShell();
+  nsCOMPtr<nsIWebProgress> webProgress;
+  if (docShell) {
+    nsCOMPtr<nsIDocShellTreeItem> root;
+    docShell->GetSameTypeRootTreeItem(getter_AddRefs(root));
+    webProgress = do_GetInterface(root);
+  }
+  if (mForceReloadListener) {
+    mForceReloadListener->Unsubscribe(webProgress);
+  }
 
   if (mVideoFrameContainer) {
     mVideoFrameContainer->ForgetElement();
   }
   UnregisterActivityObserver();
   if (mDecoder) {
     ShutdownDecoder();
   }
@@ -5913,16 +6048,26 @@ void HTMLMediaElement::ChangeReadyState(
   }
 
   CheckAutoplayDataReady();
 
   if (oldState < nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA &&
       mReadyState >= nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA) {
     DispatchAsyncEvent(NS_LITERAL_STRING("canplaythrough"));
   }
+
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+  if (mReadyState >= nsIDOMHTMLMediaElement::HAVE_METADATA && mDecoder &&
+      !mDecoder->IsMetadataLoaded()) {
+    MOZ_CRASH_UNSAFE_PRINTF(
+      "Metadata not loaded! readyState=%d networkState=%d",
+      static_cast<int>(mReadyState.Ref()),
+      static_cast<int>(mNetworkState));
+  }
+#endif
 }
 
 static const char* const gNetworkStateToString[] = {
   "EMPTY",
   "IDLE",
   "LOADING",
   "NO_SOURCE"
  };
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -805,16 +805,17 @@ protected:
   class ChannelLoader;
   class ErrorSink;
   class MediaLoadListener;
   class MediaStreamTracksAvailableCallback;
   class MediaStreamTrackListener;
   class StreamListener;
   class StreamSizeListener;
   class ShutdownObserver;
+  class ForceReloadListener;
 
   MediaDecoderOwner::NextFrameStatus NextFrameStatus();
 
   void SetDecoder(MediaDecoder* aDecoder);
 
   class WakeLockBoolWrapper {
   public:
     WakeLockBoolWrapper(bool aVal, HTMLMediaElement& aOuter)
@@ -981,17 +982,17 @@ protected:
 
   /**
    * Call this after setting up mLoadingSrc and mDecoder.
    */
   void AddMediaElementToURITable();
   /**
    * Call this before modifying mLoadingSrc.
    */
-  void RemoveMediaElementFromURITable();
+  void RemoveMediaElementFromURITable(bool aFroceClearEntry = false);
   /**
    * Call this to find a media element with the same NodePrincipal and mLoadingSrc
    * set to aURI, and with a decoder on which Load() has been called.
    */
   HTMLMediaElement* LookupMediaElementURITable(nsIURI* aURI);
 
   /**
    * Shutdown and clear mDecoder and maintain associated invariants.
@@ -1386,16 +1387,17 @@ protected:
   RefPtr<StreamListener> mMediaStreamListener;
   // Holds a reference to the size-getting MediaStreamListener attached to
   // mSrcStream.
   RefPtr<StreamSizeListener> mMediaStreamSizeListener;
   // The selected video stream track which contained mMediaStreamSizeListener.
   RefPtr<VideoStreamTrack> mSelectedVideoStreamTrack;
 
   const RefPtr<ShutdownObserver> mShutdownObserver;
+  RefPtr<ForceReloadListener> mForceReloadListener;
 
   // Holds a reference to the MediaSource, if any, referenced by the src
   // attribute on the media element.
   RefPtr<MediaSource> mSrcMediaSource;
 
   // Holds a reference to the MediaSource supplying data for playback.  This
   // may either match mSrcMediaSource or come from Source element children.
   // This is set when and only when mLoadingSrc corresponds to an object url
--- a/dom/html/HTMLSharedElement.cpp
+++ b/dom/html/HTMLSharedElement.cpp
@@ -26,18 +26,18 @@ namespace mozilla {
 namespace dom {
 
 extern nsAttrValue::EnumTable kListTypeTable[];
 
 HTMLSharedElement::~HTMLSharedElement()
 {
 }
 
-NS_IMPL_ADDREF_INHERITED(HTMLSharedElement, Element)
-NS_IMPL_RELEASE_INHERITED(HTMLSharedElement, Element)
+NS_IMPL_ADDREF_INHERITED(HTMLSharedElement, nsGenericHTMLElement)
+NS_IMPL_RELEASE_INHERITED(HTMLSharedElement, nsGenericHTMLElement)
 
 // QueryInterface implementation for HTMLSharedElement
 NS_INTERFACE_MAP_BEGIN(HTMLSharedElement)
   NS_INTERFACE_MAP_ENTRY_IF_TAG(nsIDOMHTMLBaseElement, base)
   NS_INTERFACE_MAP_ENTRY_IF_TAG(nsIDOMHTMLHtmlElement, html)
 NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)
 
 
--- a/dom/html/HTMLSharedListElement.cpp
+++ b/dom/html/HTMLSharedListElement.cpp
@@ -20,23 +20,18 @@ NS_IMPL_NS_NEW_HTML_ELEMENT(SharedList)
 
 namespace mozilla {
 namespace dom {
 
 HTMLSharedListElement::~HTMLSharedListElement()
 {
 }
 
-NS_IMPL_ADDREF_INHERITED(HTMLSharedListElement, Element)
-NS_IMPL_RELEASE_INHERITED(HTMLSharedListElement, Element)
-
-// QueryInterface implementation for nsHTMLSharedListElement
-NS_INTERFACE_MAP_BEGIN(HTMLSharedListElement)
-NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)
-
+NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(HTMLSharedListElement,
+                                               nsGenericHTMLElement)
 
 NS_IMPL_ELEMENT_CLONE(HTMLSharedListElement)
 
 
 // Shared with nsHTMLSharedElement.cpp
 nsAttrValue::EnumTable kListTypeTable[] = {
   { "none", NS_STYLE_LIST_STYLE_NONE },
   { "disc", NS_STYLE_LIST_STYLE_DISC },
--- a/dom/html/HTMLTableElement.cpp
+++ b/dom/html/HTMLTableElement.cpp
@@ -605,23 +605,18 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_IN
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mRows)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLTableElement,
                                                   nsGenericHTMLElement)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTBodies)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRows)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
-NS_IMPL_ADDREF_INHERITED(HTMLTableElement, Element)
-NS_IMPL_RELEASE_INHERITED(HTMLTableElement, Element)
-
-// QueryInterface implementation for HTMLTableElement
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(HTMLTableElement)
-NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)
-
+NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(HTMLTableElement,
+                                               nsGenericHTMLElement)
 
 NS_IMPL_ELEMENT_CLONE(HTMLTableElement)
 
 
 // the DOM spec says border, cellpadding, cellSpacing are all "wstring"
 // in fact, they are integers or they are meaningless.  so we store them
 // here as ints.
 
--- a/dom/html/HTMLTableRowElement.cpp
+++ b/dom/html/HTMLTableRowElement.cpp
@@ -31,23 +31,18 @@ HTMLTableRowElement::WrapNode(JSContext 
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLTableRowElement)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLTableRowElement,
                                                   nsGenericHTMLElement)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCells)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
-NS_IMPL_ADDREF_INHERITED(HTMLTableRowElement, Element)
-NS_IMPL_RELEASE_INHERITED(HTMLTableRowElement, Element)
-
-// QueryInterface implementation for HTMLTableRowElement
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(HTMLTableRowElement)
-NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)
-
+NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(HTMLTableRowElement,
+                                               nsGenericHTMLElement)
 
 NS_IMPL_ELEMENT_CLONE(HTMLTableRowElement)
 
 
 // protected method
 HTMLTableSectionElement*
 HTMLTableRowElement::GetSection() const
 {
--- a/dom/html/HTMLTableSectionElement.cpp
+++ b/dom/html/HTMLTableSectionElement.cpp
@@ -31,23 +31,18 @@ HTMLTableSectionElement::WrapNode(JSCont
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLTableSectionElement)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLTableSectionElement,
                                                   nsGenericHTMLElement)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRows)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
-NS_IMPL_ADDREF_INHERITED(HTMLTableSectionElement, Element)
-NS_IMPL_RELEASE_INHERITED(HTMLTableSectionElement, Element)
-
-// QueryInterface implementation for HTMLTableSectionElement
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(HTMLTableSectionElement)
-NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)
-
+NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(HTMLTableSectionElement,
+                                               nsGenericHTMLElement)
 
 NS_IMPL_ELEMENT_CLONE(HTMLTableSectionElement)
 
 nsIHTMLCollection*
 HTMLTableSectionElement::Rows()
 {
   if (!mRows) {
     mRows = new nsContentList(this,
--- a/dom/html/HTMLTemplateElement.cpp
+++ b/dom/html/HTMLTemplateElement.cpp
@@ -33,38 +33,34 @@ HTMLTemplateElement::HTMLTemplateElement
 
 HTMLTemplateElement::~HTMLTemplateElement()
 {
   if (mContent) {
     mContent->SetHost(nullptr);
   }
 }
 
-NS_IMPL_ADDREF_INHERITED(HTMLTemplateElement, Element)
-NS_IMPL_RELEASE_INHERITED(HTMLTemplateElement, Element)
+NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(HTMLTemplateElement,
+                                               nsGenericHTMLElement)
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLTemplateElement)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLTemplateElement,
                                                 nsGenericHTMLElement)
   if (tmp->mContent) {
     tmp->mContent->SetHost(nullptr);
     tmp->mContent = nullptr;
   }
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLTemplateElement,
                                                   nsGenericHTMLElement)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContent)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
-// QueryInterface implementation for HTMLTemplateElement
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(HTMLTemplateElement)
-NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)
-
 NS_IMPL_ELEMENT_CLONE(HTMLTemplateElement)
 
 JSObject*
 HTMLTemplateElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return HTMLTemplateElementBinding::Wrap(aCx, this, aGivenProto);
 }
 
--- a/dom/html/HTMLTrackElement.cpp
+++ b/dom/html/HTMLTrackElement.cpp
@@ -141,24 +141,21 @@ HTMLTrackElement::~HTMLTrackElement()
   if (mWindowDestroyObserver) {
     mWindowDestroyObserver->UnRegisterWindowDestroyObserver();
   }
   NotifyShutdown();
 }
 
 NS_IMPL_ELEMENT_CLONE(HTMLTrackElement)
 
-NS_IMPL_ADDREF_INHERITED(HTMLTrackElement, Element)
-NS_IMPL_RELEASE_INHERITED(HTMLTrackElement, Element)
-
 NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLTrackElement, nsGenericHTMLElement,
                                    mTrack, mMediaParent, mListener)
 
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(HTMLTrackElement)
-NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)
+NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(HTMLTrackElement,
+                                               nsGenericHTMLElement)
 
 void
 HTMLTrackElement::GetKind(DOMString& aKind) const
 {
   GetEnumAttr(nsGkAtoms::kind, kKindTable[0].tag, aKind);
 }
 
 void
--- a/dom/html/moz.build
+++ b/dom/html/moz.build
@@ -27,16 +27,17 @@ XPIDL_SOURCES += [
     'nsIImageDocument.idl',
     'nsIMenuBuilder.idl',
 ]
 
 XPIDL_MODULE = 'content_html'
 
 EXPORTS += [
     'nsGenericHTMLElement.h',
+    'nsGenericHTMLFrameElement.h',
     'nsHTMLDNSPrefetch.h',
     'nsIConstraintValidation.h',
     'nsIForm.h',
     'nsIFormControl.h',
     'nsIFormProcessor.h',
     'nsIHTMLCollection.h',
     'nsIHTMLDocument.h',
     'nsIRadioGroupContainer.h',
--- a/dom/interfaces/html/moz.build
+++ b/dom/interfaces/html/moz.build
@@ -9,17 +9,16 @@ with Files("**"):
 
 XPIDL_SOURCES += [
     'nsIDOMHTMLBaseElement.idl',
     'nsIDOMHTMLCanvasElement.idl',
     'nsIDOMHTMLCollection.idl',
     'nsIDOMHTMLDocument.idl',
     'nsIDOMHTMLElement.idl',
     'nsIDOMHTMLFormElement.idl',
-    'nsIDOMHTMLFrameElement.idl',
     'nsIDOMHTMLHtmlElement.idl',
     'nsIDOMHTMLIFrameElement.idl',
     'nsIDOMHTMLImageElement.idl',
     'nsIDOMHTMLInputElement.idl',
     'nsIDOMHTMLLinkElement.idl',
     'nsIDOMHTMLMediaElement.idl',
     'nsIDOMHTMLMenuItemElement.idl',
     'nsIDOMHTMLOptionElement.idl',
deleted file mode 100644
--- a/dom/interfaces/html/nsIDOMHTMLFrameElement.idl
+++ /dev/null
@@ -1,32 +0,0 @@
-/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsIDOMHTMLElement.idl"
-
-/**
- * The nsIDOMHTMLFrameElement interface is the interface to a [X]HTML
- * frame element.
- *
- * This interface is trying to follow the DOM Level 2 HTML specification:
- * http://www.w3.org/TR/DOM-Level-2-HTML/
- *
- * with changes from the work-in-progress WHATWG HTML specification:
- * http://www.whatwg.org/specs/web-apps/current-work/
- */
-
-[uuid(012a8982-c9d3-4614-91e2-18ee51c97c06)]
-interface nsIDOMHTMLFrameElement : nsISupports
-{
-           attribute DOMString        frameBorder;
-           attribute DOMString        longDesc;
-           attribute DOMString        marginHeight;
-           attribute DOMString        marginWidth;
-           attribute DOMString        name;
-           attribute boolean          noResize;
-           attribute DOMString        scrolling;
-           attribute DOMString        src;
-  // Introduced in DOM Level 2:
-  readonly attribute nsIDOMDocument   contentDocument;
-};
--- a/dom/media/ADTSDemuxer.cpp
+++ b/dom/media/ADTSDemuxer.cpp
@@ -325,22 +325,16 @@ ADTSDemuxer::Init()
     return InitPromise::CreateAndReject(
       NS_ERROR_DOM_MEDIA_METADATA_ERR, __func__);
   }
 
   ADTSLOG("Init() successful");
   return InitPromise::CreateAndResolve(NS_OK, __func__);
 }
 
-bool
-ADTSDemuxer::HasTrackType(TrackInfo::TrackType aType) const
-{
-  return aType == TrackInfo::kAudioTrack;
-}
-
 uint32_t
 ADTSDemuxer::GetNumberTracks(TrackInfo::TrackType aType) const
 {
   return (aType == TrackInfo::kAudioTrack) ? 1 : 0;
 }
 
 already_AddRefed<MediaTrackDemuxer>
 ADTSDemuxer::GetTrackDemuxer(TrackInfo::TrackType aType, uint32_t aTrackNumber)
--- a/dom/media/ADTSDemuxer.h
+++ b/dom/media/ADTSDemuxer.h
@@ -23,17 +23,16 @@ class FrameParser;
 class ADTSTrackDemuxer;
 
 class ADTSDemuxer : public MediaDataDemuxer
 {
 public:
   // MediaDataDemuxer interface.
   explicit ADTSDemuxer(MediaResource* aSource);
   RefPtr<InitPromise> Init() override;
-  bool HasTrackType(TrackInfo::TrackType aType) const override;
   uint32_t GetNumberTracks(TrackInfo::TrackType aType) const override;
   already_AddRefed<MediaTrackDemuxer>
   GetTrackDemuxer(TrackInfo::TrackType aType, uint32_t aTrackNumber) override;
   bool IsSeekable() const override;
 
   // Return true if a valid ADTS frame header could be found.
   static bool ADTSSniffer(const uint8_t* aData, const uint32_t aLength);
 
--- a/dom/media/MediaData.cpp
+++ b/dom/media/MediaData.cpp
@@ -248,16 +248,17 @@ ConstructPlanarYCbCrData(const VideoInfo
   data.mCbCrStride = Cb.mStride;
   data.mCbSkip = Cb.mSkip;
   data.mCrSkip = Cr.mSkip;
   data.mPicX = aPicture.x;
   data.mPicY = aPicture.y;
   data.mPicSize = aPicture.Size();
   data.mStereoMode = aInfo.mStereoMode;
   data.mYUVColorSpace = aBuffer.mYUVColorSpace;
+  data.mBitDepth = aBuffer.mBitDepth;
   return data;
 }
 
 /* static */ bool
 VideoData::SetVideoDataToImage(PlanarYCbCrImage* aVideoImage,
                                const VideoInfo& aInfo,
                                const YCbCrBuffer &aBuffer,
                                const IntRect& aPicture,
--- a/dom/media/MediaData.h
+++ b/dom/media/MediaData.h
@@ -461,16 +461,17 @@ public:
       uint32_t mHeight;
       uint32_t mStride;
       uint32_t mOffset;
       uint32_t mSkip;
     };
 
     Plane mPlanes[3];
     YUVColorSpace mYUVColorSpace = YUVColorSpace::BT601;
+    uint32_t mBitDepth = 8;
   };
 
   class Listener
   {
   public:
     virtual void OnSentToCompositor() = 0;
     virtual ~Listener() { }
   };
--- a/dom/media/MediaDataDemuxer.h
+++ b/dom/media/MediaDataDemuxer.h
@@ -39,19 +39,16 @@ public:
   // initialization has completed and succeeded.
   // Typically a demuxer will wait to parse the metadata before resolving the
   // promise. The promise must not be resolved until sufficient data is
   // supplied. For example, an incomplete metadata would cause the promise to be
   // rejected should no more data be coming, while the demuxer would wait
   // otherwise.
   virtual RefPtr<InitPromise> Init() = 0;
 
-  // Returns true if a aType track type is available.
-  virtual bool HasTrackType(TrackInfo::TrackType aType) const = 0;
-
   // Returns the number of tracks of aType type available. A value of
   // 0 indicates that no such type is available.
   virtual uint32_t GetNumberTracks(TrackInfo::TrackType aType) const = 0;
 
   // Returns the MediaTrackDemuxer associated with aTrackNumber aType track.
   // aTrackNumber is not to be confused with the Track ID.
   // aTrackNumber must be constrained between  0 and  GetNumberTracks(aType) - 1
   // The actual Track ID is to be retrieved by calling
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -234,16 +234,19 @@ public:
 
   MediaDecoderStateMachine* GetStateMachine() const;
   void SetStateMachine(MediaDecoderStateMachine* aStateMachine);
 
   // Constructs the time ranges representing what segments of the media
   // are buffered and playable.
   virtual media::TimeIntervals GetBuffered();
 
+  // For debugging bug 1402584.
+  bool IsMetadataLoaded() const { return !!mInfo; }
+
   // Returns the size, in bytes, of the heap memory used by the currently
   // queued decoded video and audio data.
   size_t SizeOfVideoQueue();
   size_t SizeOfAudioQueue();
 
   // Helper struct for accumulating resource sizes that need to be measured
   // asynchronously. Once all references are dropped the callback will be
   // invoked.
--- a/dom/media/flac/FlacDemuxer.cpp
+++ b/dom/media/flac/FlacDemuxer.cpp
@@ -573,22 +573,16 @@ FlacDemuxer::Init()
     return InitPromise::CreateAndReject(
       NS_ERROR_DOM_MEDIA_DEMUXER_ERR, __func__);
   }
 
   LOG("Init() successful");
   return InitPromise::CreateAndResolve(NS_OK, __func__);
 }
 
-bool
-FlacDemuxer::HasTrackType(TrackInfo::TrackType aType) const
-{
-  return aType == TrackInfo::kAudioTrack;
-}
-
 uint32_t
 FlacDemuxer::GetNumberTracks(TrackInfo::TrackType aType) const
 {
   return (aType == TrackInfo::kAudioTrack) ? 1 : 0;
 }
 
 already_AddRefed<MediaTrackDemuxer>
 FlacDemuxer::GetTrackDemuxer(TrackInfo::TrackType aType, uint32_t aTrackNumber)
--- a/dom/media/flac/FlacDemuxer.h
+++ b/dom/media/flac/FlacDemuxer.h
@@ -20,17 +20,16 @@ class FlacTrackDemuxer;
 
 
 class FlacDemuxer : public MediaDataDemuxer
 {
 public:
   // MediaDataDemuxer interface.
   explicit FlacDemuxer(MediaResource* aSource);
   RefPtr<InitPromise> Init() override;
-  bool HasTrackType(TrackInfo::TrackType aType) const override;
   uint32_t GetNumberTracks(TrackInfo::TrackType aType) const override;
   already_AddRefed<MediaTrackDemuxer> GetTrackDemuxer(
     TrackInfo::TrackType aType, uint32_t aTrackNumber) override;
   bool IsSeekable() const override;
 
   // Return true if a valid flac frame header could be found.
   static bool FlacSniffer(const uint8_t* aData, const uint32_t aLength);
 
--- a/dom/media/fmp4/MP4Demuxer.cpp
+++ b/dom/media/fmp4/MP4Demuxer.cpp
@@ -269,22 +269,16 @@ MP4Demuxer::Init()
     }
   }
 
   mIsSeekable = metadata.CanSeek();
 
   return InitPromise::CreateAndResolve(result, __func__);
 }
 
-bool
-MP4Demuxer::HasTrackType(TrackInfo::TrackType aType) const
-{
-  return GetNumberTracks(aType) != 0;
-}
-
 uint32_t
 MP4Demuxer::GetNumberTracks(TrackInfo::TrackType aType) const
 {
   switch (aType) {
     case TrackInfo::kAudioTrack: return uint32_t(mAudioDemuxers.Length());
     case TrackInfo::kVideoTrack: return uint32_t(mVideoDemuxers.Length());
     default: return 0;
   }
--- a/dom/media/fmp4/MP4Demuxer.h
+++ b/dom/media/fmp4/MP4Demuxer.h
@@ -24,18 +24,16 @@ class MP4TrackDemuxer;
 
 class MP4Demuxer : public MediaDataDemuxer
 {
 public:
   explicit MP4Demuxer(MediaResource* aResource);
 
   RefPtr<InitPromise> Init() override;
 
-  bool HasTrackType(TrackInfo::TrackType aType) const override;
-
   uint32_t GetNumberTracks(TrackInfo::TrackType aType) const override;
 
   already_AddRefed<MediaTrackDemuxer>
   GetTrackDemuxer(TrackInfo::TrackType aType, uint32_t aTrackNumber) override;
 
   bool IsSeekable() const override;
 
   UniquePtr<EncryptionInfo> GetCrypto() override;
--- a/dom/media/hls/HLSDemuxer.cpp
+++ b/dom/media/hls/HLSDemuxer.cpp
@@ -179,29 +179,16 @@ HLSDemuxer::Init()
     });
 }
 
 void HLSDemuxer::NotifyDataArrived()
 {
   HLS_DEBUG("HLSDemuxer", "NotifyDataArrived");
 }
 
-bool
-HLSDemuxer::HasTrackType(TrackType aType) const
-{
-  HLS_DEBUG("HLSDemuxer", "HasTrackType(%d)", aType);
-  if (mAudioDemuxer && aType == TrackType::kAudioTrack) {
-    return mAudioDemuxer->IsTrackValid();
-  }
-  if (mVideoDemuxer && aType == TrackType::kVideoTrack) {
-    return mVideoDemuxer->IsTrackValid();
-  }
-  return false;
-}
-
 uint32_t
 HLSDemuxer::GetNumberTracks(TrackType aType) const
 {
   switch (aType) {
     case TrackType::kAudioTrack:
       return mHLSDemuxerWrapper->GetNumberOfTracks(TrackType::kAudioTrack);
     case TrackType::kVideoTrack:
       return mHLSDemuxerWrapper->GetNumberOfTracks(TrackType::kVideoTrack);
--- a/dom/media/hls/HLSDemuxer.h
+++ b/dom/media/hls/HLSDemuxer.h
@@ -28,18 +28,16 @@ class HLSTrackDemuxer;
 class HLSDemuxer final : public MediaDataDemuxer
 {
   class HLSDemuxerCallbacksSupport;
 public:
   explicit HLSDemuxer(int aPlayerId);
 
   RefPtr<InitPromise> Init() override;
 
-  bool HasTrackType(TrackInfo::TrackType aType) const override;
-
   uint32_t GetNumberTracks(TrackInfo::TrackType aType) const override;
 
   already_AddRefed<MediaTrackDemuxer>
   GetTrackDemuxer(TrackInfo::TrackType aType, uint32_t aTrackNumber) override;
 
   bool IsSeekable() const override;
 
   UniquePtr<EncryptionInfo> GetCrypto() override;
--- a/dom/media/mediasource/MediaSourceDemuxer.cpp
+++ b/dom/media/mediasource/MediaSourceDemuxer.cpp
@@ -112,37 +112,31 @@ MediaSourceDemuxer::ScanSourceBuffersFor
   }
   if (mInfo.HasAudio() && mInfo.HasVideo()) {
     // We have both audio and video. We can ignore non-ready source buffer.
     return true;
   }
   return !haveEmptySourceBuffer;
 }
 
-bool
-MediaSourceDemuxer::HasTrackType(TrackType aType) const
+uint32_t
+MediaSourceDemuxer::GetNumberTracks(TrackType aType) const
 {
   MonitorAutoLock mon(mMonitor);
 
   switch (aType) {
     case TrackType::kAudioTrack:
-      return mInfo.HasAudio();
+      return mInfo.HasAudio() ? 1u : 0;
     case TrackType::kVideoTrack:
-      return mInfo.HasVideo();
+      return mInfo.HasVideo() ? 1u : 0;
     default:
-      return false;
+      return 0;
   }
 }
 
-uint32_t
-MediaSourceDemuxer::GetNumberTracks(TrackType aType) const
-{
-  return HasTrackType(aType) ? 1u : 0;
-}
-
 already_AddRefed<MediaTrackDemuxer>
 MediaSourceDemuxer::GetTrackDemuxer(TrackType aType, uint32_t aTrackNumber)
 {
   RefPtr<TrackBuffersManager> manager = GetManager(aType);
   if (!manager) {
     return nullptr;
   }
   RefPtr<MediaSourceTrackDemuxer> e =
--- a/dom/media/mediasource/MediaSourceDemuxer.h
+++ b/dom/media/mediasource/MediaSourceDemuxer.h
@@ -25,18 +25,16 @@ class MediaSourceTrackDemuxer;
 
 class MediaSourceDemuxer : public MediaDataDemuxer
 {
 public:
   explicit MediaSourceDemuxer(AbstractThread* aAbstractMainThread);
 
   RefPtr<InitPromise> Init() override;
 
-  bool HasTrackType(TrackInfo::TrackType aType) const override;
-
   uint32_t GetNumberTracks(TrackInfo::TrackType aType) const override;
 
   already_AddRefed<MediaTrackDemuxer>
   GetTrackDemuxer(TrackInfo::TrackType aType, uint32_t aTrackNumber) override;
 
   bool IsSeekable() const override;
 
   UniquePtr<EncryptionInfo> GetCrypto() override;
--- a/dom/media/mp3/MP3Demuxer.cpp
+++ b/dom/media/mp3/MP3Demuxer.cpp
@@ -50,22 +50,16 @@ MP3Demuxer::Init()
     return InitPromise::CreateAndReject(
       NS_ERROR_DOM_MEDIA_METADATA_ERR, __func__);
   }
 
   MP3LOG("MP3Demuxer::Init() successful");
   return InitPromise::CreateAndResolve(NS_OK, __func__);
 }
 
-bool
-MP3Demuxer::HasTrackType(TrackInfo::TrackType aType) const
-{
-  return aType == TrackInfo::kAudioTrack;
-}
-
 uint32_t
 MP3Demuxer::GetNumberTracks(TrackInfo::TrackType aType) const
 {
   return aType == TrackInfo::kAudioTrack ? 1u : 0u;
 }
 
 already_AddRefed<MediaTrackDemuxer>
 MP3Demuxer::GetTrackDemuxer(TrackInfo::TrackType aType, uint32_t aTrackNumber)
--- a/dom/media/mp3/MP3Demuxer.h
+++ b/dom/media/mp3/MP3Demuxer.h
@@ -14,17 +14,16 @@ namespace mozilla {
 class MP3TrackDemuxer;
 
 class MP3Demuxer : public MediaDataDemuxer
 {
 public:
   // MediaDataDemuxer interface.
   explicit MP3Demuxer(MediaResource* aSource);
   RefPtr<InitPromise> Init() override;
-  bool HasTrackType(TrackInfo::TrackType aType) const override;
   uint32_t GetNumberTracks(TrackInfo::TrackType aType) const override;
   already_AddRefed<MediaTrackDemuxer> GetTrackDemuxer(
       TrackInfo::TrackType aType, uint32_t aTrackNumber) override;
   bool IsSeekable() const override;
   void NotifyDataArrived() override;
   void NotifyDataRemoved() override;
 
 private:
--- a/dom/media/ogg/OggDemuxer.cpp
+++ b/dom/media/ogg/OggDemuxer.cpp
@@ -212,22 +212,16 @@ OggDemuxer::Init()
   if (!GetNumberTracks(TrackInfo::kAudioTrack) &&
       !GetNumberTracks(TrackInfo::kVideoTrack)) {
     return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_METADATA_ERR, __func__);
   }
 
   return InitPromise::CreateAndResolve(NS_OK, __func__);
 }
 
-bool
-OggDemuxer::HasTrackType(TrackInfo::TrackType aType) const
-{
-  return !!GetNumberTracks(aType);
-}
-
 OggCodecState*
 OggDemuxer::GetTrackCodecState(TrackInfo::TrackType aType) const
 {
   switch(aType) {
     case TrackInfo::kAudioTrack:
       if (mVorbisState) {
         return mVorbisState;
       } else if (mOpusState) {
--- a/dom/media/ogg/OggDemuxer.h
+++ b/dom/media/ogg/OggDemuxer.h
@@ -18,18 +18,16 @@ class OggTrackDemuxer;
 
 class OggDemuxer : public MediaDataDemuxer
 {
 public:
   explicit OggDemuxer(MediaResource* aResource);
 
   RefPtr<InitPromise> Init() override;
 
-  bool HasTrackType(TrackInfo::TrackType aType) const override;
-
   uint32_t GetNumberTracks(TrackInfo::TrackType aType) const override;
 
   already_AddRefed<MediaTrackDemuxer> GetTrackDemuxer(TrackInfo::TrackType aType,
                                                       uint32_t aTrackNumber) override;
 
   bool IsSeekable() const override;
 
   UniquePtr<EncryptionInfo> GetCrypto() override;
--- a/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp
@@ -12,17 +12,20 @@
 #include "VPXDecoder.h"
 #include "mozilla/layers/KnowsCompositor.h"
 
 #include "libavutil/pixfmt.h"
 #if LIBAVCODEC_VERSION_MAJOR < 54
 #define AVPixelFormat PixelFormat
 #define AV_PIX_FMT_YUV420P PIX_FMT_YUV420P
 #define AV_PIX_FMT_YUVJ420P PIX_FMT_YUVJ420P
+#define AV_PIX_FMT_YUV422P PIX_FMT_YUV422P
 #define AV_PIX_FMT_YUV444P PIX_FMT_YUV444P
+#define AV_PIX_FMT_YUV420P10LE PIX_FMT_YUV420P10LE
+#define AV_PIX_FMT_YUV444P10LE PIX_FMT_YUV444P10LE
 #define AV_PIX_FMT_NONE PIX_FMT_NONE
 #endif
 #include "mozilla/PodOperations.h"
 #include "mozilla/TaskQueue.h"
 #include "nsThreadUtils.h"
 #include "prsystem.h"
 
 
@@ -43,22 +46,36 @@ static AVPixelFormat
 ChoosePixelFormat(AVCodecContext* aCodecContext, const AVPixelFormat* aFormats)
 {
   FFMPEG_LOG("Choosing FFmpeg pixel format for video decoding.");
   for (; *aFormats > -1; aFormats++) {
     switch (*aFormats) {
       case AV_PIX_FMT_YUV444P:
         FFMPEG_LOG("Requesting pixel format YUV444P.");
         return AV_PIX_FMT_YUV444P;
+      case AV_PIX_FMT_YUV422P:
+        FFMPEG_LOG("Requesting pixel format YUV422P.");
+        return AV_PIX_FMT_YUV422P;
       case AV_PIX_FMT_YUV420P:
         FFMPEG_LOG("Requesting pixel format YUV420P.");
         return AV_PIX_FMT_YUV420P;
       case AV_PIX_FMT_YUVJ420P:
         FFMPEG_LOG("Requesting pixel format YUVJ420P.");
         return AV_PIX_FMT_YUVJ420P;
+      case AV_PIX_FMT_YUV420P10LE:
+        FFMPEG_LOG("Requesting pixel format YUV420P10LE.");
+        return AV_PIX_FMT_YUV420P10LE;
+      case AV_PIX_FMT_YUV444P10LE:
+        FFMPEG_LOG("Requesting pixel format YUV444P10LE.");
+        return AV_PIX_FMT_YUV444P10LE;
+#if LIBAVCODEC_VERSION_MAJOR >= 57
+      case AV_PIX_FMT_YUV444P12LE:
+        FFMPEG_LOG("Requesting pixel format YUV444P12LE.");
+        return AV_PIX_FMT_YUV444P12LE;
+#endif
       default:
         break;
     }
   }
 
   NS_WARNING("FFmpeg does not share any supported pixel formats.");
   return AV_PIX_FMT_NONE;
 }
@@ -277,16 +294,30 @@ FFmpegVideoDecoder<LIBAV_VER>::DoDecode(
 
   if (!decoded) {
     if (aGotFrame) {
       *aGotFrame = false;
     }
     return NS_OK;
   }
 
+  if ((mCodecContext->pix_fmt == AV_PIX_FMT_YUV420P10LE ||
+       mCodecContext->pix_fmt == AV_PIX_FMT_YUV444P10LE
+#if LIBAVCODEC_VERSION_MAJOR >= 57
+       || mCodecContext->pix_fmt == AV_PIX_FMT_YUV444P12LE
+#endif
+       ) &&
+      (!mImageAllocator || (mImageAllocator->GetCompositorBackendType()
+                            != layers::LayersBackend::LAYERS_BASIC &&
+                            mImageAllocator->GetCompositorBackendType()
+                            != layers::LayersBackend::LAYERS_OPENGL))) {
+    return MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
+                       RESULT_DETAIL("unsupported format type (hdr)"));
+  }
+
   // If we've decoded a frame then we need to output it
   int64_t pts = mPtsContext.GuessCorrectPts(mFrame->pkt_pts, mFrame->pkt_dts);
   // Retrieve duration from dts.
   // We use the first entry found matching this dts (this is done to
   // handle damaged file with multiple frames with the same dts)
 
   int64_t duration;
   if (!mDurationMap.Find(mFrame->pkt_dts, duration)) {
@@ -312,22 +343,42 @@ FFmpegVideoDecoder<LIBAV_VER>::DoDecode(
   b.mPlanes[2].mStride = mFrame->linesize[2];
 
   b.mPlanes[0].mOffset = b.mPlanes[0].mSkip = 0;
   b.mPlanes[1].mOffset = b.mPlanes[1].mSkip = 0;
   b.mPlanes[2].mOffset = b.mPlanes[2].mSkip = 0;
 
   b.mPlanes[0].mWidth = mFrame->width;
   b.mPlanes[0].mHeight = mFrame->height;
-  if (mCodecContext->pix_fmt == AV_PIX_FMT_YUV444P) {
+  if (mCodecContext->pix_fmt == AV_PIX_FMT_YUV444P ||
+      mCodecContext->pix_fmt == AV_PIX_FMT_YUV444P10LE
+#if LIBAVCODEC_VERSION_MAJOR >= 57
+      ||
+      mCodecContext->pix_fmt == AV_PIX_FMT_YUV444P12LE
+#endif
+      ) {
     b.mPlanes[1].mWidth = b.mPlanes[2].mWidth = mFrame->width;
     b.mPlanes[1].mHeight = b.mPlanes[2].mHeight = mFrame->height;
+    if (mCodecContext->pix_fmt == AV_PIX_FMT_YUV444P10LE) {
+      b.mBitDepth = 10;
+    }
+#if LIBAVCODEC_VERSION_MAJOR >= 57
+    else if (mCodecContext->pix_fmt == AV_PIX_FMT_YUV444P12LE) {
+      b.mBitDepth = 12;
+    }
+#endif
+  } else if (mCodecContext->pix_fmt == AV_PIX_FMT_YUV422P) {
+    b.mPlanes[1].mWidth = b.mPlanes[2].mWidth = (mFrame->width + 1) >> 1;
+    b.mPlanes[1].mHeight = b.mPlanes[2].mHeight = mFrame->height;
   } else {
     b.mPlanes[1].mWidth = b.mPlanes[2].mWidth = (mFrame->width + 1) >> 1;
     b.mPlanes[1].mHeight = b.mPlanes[2].mHeight = (mFrame->height + 1) >> 1;
+    if (mCodecContext->pix_fmt == AV_PIX_FMT_YUV420P10LE) {
+      b.mBitDepth = 10;
+    }
   }
   if (mLib->av_frame_get_colorspace) {
     switch (mLib->av_frame_get_colorspace(mFrame)) {
       case AVCOL_SPC_BT709:
         b.mYUVColorSpace = YUVColorSpace::BT709;
         break;
       case AVCOL_SPC_SMPTE170M:
       case AVCOL_SPC_BT470BG:
--- a/dom/media/wave/WaveDemuxer.cpp
+++ b/dom/media/wave/WaveDemuxer.cpp
@@ -42,22 +42,16 @@ WAVDemuxer::Init()
 {
   if (!InitInternal()) {
     return InitPromise::CreateAndReject(
       NS_ERROR_DOM_MEDIA_METADATA_ERR, __func__);
   }
   return InitPromise::CreateAndResolve(NS_OK, __func__);
 }
 
-bool
-WAVDemuxer::HasTrackType(TrackInfo::TrackType aType) const
-{
-  return aType == TrackInfo::kAudioTrack;
-}
-
 uint32_t
 WAVDemuxer::GetNumberTracks(TrackInfo::TrackType aType) const
 {
   return aType == TrackInfo::kAudioTrack ? 1u : 0u;
 }
 
 already_AddRefed<MediaTrackDemuxer>
 WAVDemuxer::GetTrackDemuxer(TrackInfo::TrackType aType, uint32_t aTrackNumber)
--- a/dom/media/wave/WaveDemuxer.h
+++ b/dom/media/wave/WaveDemuxer.h
@@ -31,17 +31,16 @@ static const uint16_t DATA_CHUNK_SIZE = 
 class WAVTrackDemuxer;
 
 class WAVDemuxer : public MediaDataDemuxer
 {
 public:
   // MediaDataDemuxer interface.
   explicit WAVDemuxer(MediaResource* aSource);
   RefPtr<InitPromise> Init() override;
-  bool HasTrackType(TrackInfo::TrackType aType) const override;
   uint32_t GetNumberTracks(TrackInfo::TrackType aType) const override;
   already_AddRefed<MediaTrackDemuxer> GetTrackDemuxer(
     TrackInfo::TrackType aType, uint32_t aTrackNumber) override;
   bool IsSeekable() const override;
 
 private:
   // Synchronous Initialization.
   bool InitInternal();
--- a/dom/media/webm/WebMDemuxer.cpp
+++ b/dom/media/webm/WebMDemuxer.cpp
@@ -207,22 +207,16 @@ WebMDemuxer::Init()
 
 void
 WebMDemuxer::InitBufferedState()
 {
   MOZ_ASSERT(!mBufferedState);
   mBufferedState = new WebMBufferedState;
 }
 
-bool
-WebMDemuxer::HasTrackType(TrackInfo::TrackType aType) const
-{
-  return !!GetNumberTracks(aType);
-}
-
 uint32_t
 WebMDemuxer::GetNumberTracks(TrackInfo::TrackType aType) const
 {
   switch(aType) {
     case TrackInfo::kAudioTrack:
       return mHasAudio ? 1 : 0;
     case TrackInfo::kVideoTrack:
       return mHasVideo ? 1 : 0;
--- a/dom/media/webm/WebMDemuxer.h
+++ b/dom/media/webm/WebMDemuxer.h
@@ -127,18 +127,16 @@ class WebMDemuxer : public MediaDataDemu
 public:
   explicit WebMDemuxer(MediaResource* aResource);
   // Indicate if the WebMDemuxer is to be used with MediaSource. In which
   // case the demuxer will stop reads to the last known complete block.
   WebMDemuxer(MediaResource* aResource, bool aIsMediaSource);
 
   RefPtr<InitPromise> Init() override;
 
-  bool HasTrackType(TrackInfo::TrackType aType) const override;
-
   uint32_t GetNumberTracks(TrackInfo::TrackType aType) const override;
 
   UniquePtr<TrackInfo> GetTrackInfo(TrackInfo::TrackType aType,
                                     size_t aTrackNumber) const;
 
   already_AddRefed<MediaTrackDemuxer>
   GetTrackDemuxer(TrackInfo::TrackType aType, uint32_t aTrackNumber) override;
 
--- a/dom/plugins/base/nsNPAPIPlugin.cpp
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -5,17 +5,16 @@
 
 #include "base/basictypes.h"
 
 /* This must occur *after* layers/PLayerTransaction.h to avoid typedefs conflicts. */
 #include "mozilla/ArrayUtils.h"
 
 #include "pratom.h"
 #include "prenv.h"
-#include "prclist.h"
 
 #include "jsfriendapi.h"
 
 #include "nsPluginHost.h"
 #include "nsNPAPIPlugin.h"
 #include "nsNPAPIPluginInstance.h"
 #include "nsNPAPIPluginStreamListener.h"
 #include "nsPluginStreamListenerPeer.h"
--- a/dom/smil/nsSMILAnimationFunction.cpp
+++ b/dom/smil/nsSMILAnimationFunction.cpp
@@ -465,16 +465,40 @@ nsSMILAnimationFunction::InterpolateResu
       // are the same as <set> animations.  Instead, we treat it as a
       // discrete animation with two values (the underlying value and
       // the to="" value), and honor keyTimes="" as well.
       uint32_t index = (uint32_t)floor(scaledSimpleProgress * 2);
       aResult = index == 0 ? aBaseValue : aValues[0];
     } else {
       uint32_t index = (uint32_t)floor(scaledSimpleProgress * aValues.Length());
       aResult = aValues[index];
+
+      // For animation of CSS properties, normally when interpolating we perform
+      // a zero-value fixup which means that empty values (values with type
+      // nsSMILCSSValueType but a null pointer value) are converted into
+      // a suitable zero value based on whatever they're being interpolated
+      // with. For discrete animation, however, since we don't interpolate,
+      // that never happens. In some rare cases, such as discrete non-additive
+      // by-animation, we can arrive here with |aResult| being such an empty
+      // value so we need to manually perform the fixup.
+      //
+      // We could define a generic method for this on nsSMILValue but its faster
+      // and simpler to just special case nsSMILCSSValueType.
+      if (aResult.mType == &nsSMILCSSValueType::sSingleton) {
+        // We have currently only ever encountered this case for the first
+        // value of a by-animation (which has two values) and since we have no
+        // way of testing other cases we just skip them (but assert if we
+        // ever do encounter them so that we can add code to handle them).
+        if (index + 1 >= aValues.Length()) {
+          MOZ_ASSERT(aResult.mU.mPtr, "The last value should not be empty");
+        } else {
+          // Base the type of the zero value on the next element in the series.
+          nsSMILCSSValueType::FinalizeValue(aResult, aValues[index + 1]);
+        }
+      }
     }
     rv = NS_OK;
   }
   return rv;
 }
 
 nsresult
 nsSMILAnimationFunction::AccumulateResult(const nsSMILValueArray& aValues,
--- a/dom/smil/nsSMILCSSValueType.cpp
+++ b/dom/smil/nsSMILCSSValueType.cpp
@@ -867,8 +867,56 @@ nsSMILCSSValueType::PropertyFromValue(co
 
   const ValueWrapper* wrapper = ExtractValueWrapper(aValue);
   if (!wrapper) {
     return eCSSProperty_UNKNOWN;
   }
 
   return wrapper->mPropID;
 }
+
+// static
+void
+nsSMILCSSValueType::FinalizeValue(nsSMILValue& aValue,
+                                  const nsSMILValue& aValueToMatch)
+{
+  MOZ_ASSERT(aValue.mType == aValueToMatch.mType, "Incompatible SMIL types");
+  MOZ_ASSERT(aValue.mType == &nsSMILCSSValueType::sSingleton,
+             "Unexpected SMIL value type");
+
+  ValueWrapper* valueWrapper = ExtractValueWrapper(aValue);
+  // If |aValue| already has a value, there's nothing to do here.
+  if (valueWrapper) {
+    return;
+  }
+
+  const ValueWrapper* valueToMatchWrapper = ExtractValueWrapper(aValueToMatch);
+  if (!valueToMatchWrapper) {
+    MOZ_ASSERT_UNREACHABLE("Value to match is empty");
+    return;
+  }
+
+  bool isServo = !valueToMatchWrapper->mServoValues.IsEmpty();
+
+  if (isServo) {
+    ServoAnimationValues zeroValues;
+    zeroValues.SetCapacity(valueToMatchWrapper->mServoValues.Length());
+
+    for (auto& valueToMatch : valueToMatchWrapper->mServoValues) {
+      RefPtr<RawServoAnimationValue> zeroValue =
+        Servo_AnimationValues_GetZeroValue(valueToMatch).Consume();
+      if (!zeroValue) {
+        return;
+      }
+      zeroValues.AppendElement(Move(zeroValue));
+    }
+    aValue.mU.mPtr = new ValueWrapper(valueToMatchWrapper->mPropID,
+                                      Move(zeroValues));
+  } else {
+    const StyleAnimationValue* zeroValue =
+      GetZeroValueForUnit(valueToMatchWrapper->mGeckoValue.GetUnit());
+    if (!zeroValue) {
+      return;
+    }
+    aValue.mU.mPtr = new ValueWrapper(valueToMatchWrapper->mPropID,
+                                      *zeroValue);
+  }
+}
--- a/dom/smil/nsSMILCSSValueType.h
+++ b/dom/smil/nsSMILCSSValueType.h
@@ -119,14 +119,30 @@ public:
    *
    * @param   aValue   The nsSMILValue to examine.
    * @return           The nsCSSPropertyID enum value of the property animated
    *                   by |aValue|, or eCSSProperty_UNKNOWN if the type of
    *                   |aValue| is not nsSMILCSSValueType.
    */
   static nsCSSPropertyID PropertyFromValue(const nsSMILValue& aValue);
 
+  /**
+   * If |aValue| is an empty value, converts it to a suitable zero value by
+   * matching the type of value stored in |aValueToMatch|.
+   *
+   * There is no indication if this method fails. If a suitable zero value could
+   * not be created, |aValue| is simply unmodified.
+   *
+   * @param aValue        The nsSMILValue (of type nsSMILCSSValueType) to
+   *                      possibly update.
+   * @param aValueToMatch A nsSMILValue (of type nsSMILCSSValueType) for which
+   *                      a corresponding zero value will be created if |aValue|
+   *                      is empty.
+   */
+  static void FinalizeValue(nsSMILValue& aValue,
+                            const nsSMILValue& aValueToMatch);
+
 private:
   // Private constructor: prevent instances beyond my singleton.
   constexpr nsSMILCSSValueType() {}
 };
 
 #endif // NS_SMILCSSVALUETYPE_H_
--- a/dom/smil/test/mochitest.ini
+++ b/dom/smil/test/mochitest.ini
@@ -8,16 +8,17 @@ support-files =
   db_smilMappedAttrList.js
   file_smilWithTransition.html
   smilAnimateMotionValueLists.js
   smilExtDoc_helper.svg
   smilTestUtils.js
   smilXHR_helper.svg
 
 [test_smilAccessKey.xhtml]
+[test_smilAdditionFallback.html]
 [test_smilAnimateMotion.xhtml]
 [test_smilAnimateMotionInvalidValues.xhtml]
 [test_smilAnimateMotionOverrideRules.xhtml]
 [test_smilBackwardsSeeking.xhtml]
 [test_smilCSSFontStretchRelative.xhtml]
 [test_smilCSSFromBy.xhtml]
 [test_smilCSSFromTo.xhtml]
 [test_smilCSSInherit.xhtml]
new file mode 100644
--- /dev/null
+++ b/dom/smil/test/test_smilAdditionFallback.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<meta charset=utf-8>
+<head>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<p id=display></p>
+<div id=content>
+<svg id=svg>
+<!-- These two animations will have a default duration of indefinite which means
+     they will keep producing their first value forever -->
+<animate calcMode="discrete" attributeName="height" by="10" dur="1s"
+  fill="freeze"/>
+<animate calcMode="discrete" attributeName="height" by="10" dur="1s"
+  fill="freeze"/>
+</svg>
+</div>
+<pre id="test">
+<script>
+'use strict';
+
+SimpleTest.waitForExplicitFinish();
+
+window.addEventListener('load', () => {
+  const svg = document.getElementById('svg');
+  is(getComputedStyle(svg).height, '0px', 'Computed height should be zero');
+  SimpleTest.finish();
+});
+</script>
+</pre>
new file mode 100644
--- /dev/null
+++ b/dom/svg/crashtests/1343147.svg
@@ -0,0 +1,13 @@
+<svg xmlns="http://www.w3.org/2000/svg">
+<style>
+<![CDATA[
+  svg {
+    background-image: linear-gradient(lime, lime);
+    background-clip: text;
+  }
+  text { transform: skewy(30grad); }
+  g { perspective: 0; }
+]]>
+</style>
+  <g><text>hello</text></g>
+</svg>
--- a/dom/svg/crashtests/crashtests.list
+++ b/dom/svg/crashtests/crashtests.list
@@ -85,12 +85,13 @@ load 1322286.html
 load 1329849-1.svg
 load 1329849-2.svg
 load 1329849-3.svg
 load 1329849-4.svg
 load 1329849-5.svg
 load 1329849-6.svg
 load 1329093-1.html
 load 1329093-2.html
+load 1343147.svg
 load 1347617-1.svg
 load 1347617-2.svg
 load 1347617-3.svg
 load 1402798.html
--- a/dom/tests/mochitest/general/mochitest.ini
+++ b/dom/tests/mochitest/general/mochitest.ini
@@ -97,16 +97,17 @@ subsuite = clipboard
 skip-if = toolkit == 'android' #TIMED_OUT
 [test_for_of.html]
 [test_framedhistoryframes.html]
 [test_frameElementWrapping.html]
 [test_img_mutations.html]
 [test_interfaces.html]
 [test_interfaces_secureContext.html]
 scheme = https
+[test_media_queries_with_zoom.html]
 [test_navigation_timing.html]
 [test_network_events.html]
 skip-if = true
 # Disable this test until bug 795711 is fixed.
 [test_offsets.html]
 support-files = test_offsets.js
 [test_outerHTML.html]
 [test_outerHTML.xhtml]
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/general/test_media_queries_with_zoom.html
@@ -0,0 +1,52 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Media Queries with Zoom</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+</head>
+
+<body>
+
+<div>Testing media queries with different zoom levels</div>
+
+<script type="application/javascript">
+
+const originalDPR = window.devicePixelRatio;
+const originalZoom = SpecialPowers.getFullZoom(window);
+
+const zoomsToTest = [
+300,
+240,
+200,
+170,
+150,
+133,
+120,
+110,
+100,
+90,
+80,
+67,
+50,
+30,
+];
+
+for (let i = 0; i < zoomsToTest.length; ++i) {
+  let zoomPercent = zoomsToTest[i];
+
+  let relativeZoom = originalZoom * zoomPercent / 100;
+  SpecialPowers.setFullZoom(window, relativeZoom);
+  let actualZoom = SpecialPowers.getDeviceFullZoom(window);
+  let targetDPR = (originalDPR * actualZoom);
+  let actualDPR = window.devicePixelRatio;
+  let mql = window.matchMedia(`(resolution: ${targetDPR}dppx)`);
+  ok(mql.matches, `At ${zoomPercent}% zoom, target ${targetDPR}dppx matches ${actualDPR}dppx.`);
+}
+
+// Reset the zoom when the test is done.
+SpecialPowers.setFullZoom(window, originalZoom);
+</script>
+
+</body>
+</html>
--- a/dom/webauthn/tests/u2futil.js
+++ b/dom/webauthn/tests/u2futil.js
@@ -278,19 +278,19 @@ function sanitizeSigArray(arr) {
     arr = arr.slice(arr.length - 32)
   }
   let ret = new Uint8Array(32);
   ret.set(arr, ret.length - arr.length);
   return ret;
 }
 
 function verifySignature(key, data, derSig) {
-  if (derSig.byteLength < 70) {
-    console.log("bad sig: " + hexEncode(new Uint8Array(derSig)))
-    return Promise.reject("Invalid signature length: " + derSig.byteLength);
+  if (derSig.byteLength < 68) {
+    return Promise.reject("Invalid signature (length=" + derSig.byteLength +
+                          "): " + hexEncode(new Uint8Array(derSig)));
   }
 
   let sigAsn1 = org.pkijs.fromBER(derSig);
   let sigR = new Uint8Array(sigAsn1.result.value_block.value[0].value_block.value_hex);
   let sigS = new Uint8Array(sigAsn1.result.value_block.value[1].value_block.value_hex);
 
   // The resulting R and S values from the ASN.1 Sequence must be fit into 32
   // bytes. Sometimes they have leading zeros, sometimes they're too short, it
--- a/dom/webbrowserpersist/WebBrowserPersistLocalDocument.cpp
+++ b/dom/webbrowserpersist/WebBrowserPersistLocalDocument.cpp
@@ -20,17 +20,16 @@
 #include "nsIComponentRegistrar.h"
 #include "nsIContent.h"
 #include "nsIDOMAttr.h"
 #include "nsIDOMComment.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMHTMLBaseElement.h"
 #include "nsIDOMHTMLCollection.h"
 #include "nsIDOMHTMLDocument.h"
-#include "nsIDOMHTMLFrameElement.h"
 #include "nsIDOMHTMLIFrameElement.h"
 #include "nsIDOMHTMLImageElement.h"
 #include "nsIDOMHTMLInputElement.h"
 #include "nsIDOMHTMLLinkElement.h"
 #include "nsIDOMHTMLMediaElement.h"
 #include "nsIDOMHTMLOptionElement.h"
 #include "nsIDOMHTMLScriptElement.h"
 #include "nsIDOMHTMLSourceElement.h"
@@ -570,18 +569,17 @@ ResourceReader::OnWalkDOMNode(nsIDOMNode
                 if (current == end) {
                     break;
                 }
             }
         }
         return NS_OK;
     }
 
-    nsCOMPtr<nsIDOMHTMLFrameElement> nodeAsFrame = do_QueryInterface(aNode);
-    if (nodeAsFrame) {
+    if (content->IsHTMLElement(nsGkAtoms::frame)) {
         return OnWalkSubframe(aNode);
     }
 
     nsCOMPtr<nsIDOMHTMLIFrameElement> nodeAsIFrame = do_QueryInterface(aNode);
     if (nodeAsIFrame && !(mPersistFlags &
                           IWBP::PERSIST_FLAGS_IGNORE_IFRAMES)) {
         return OnWalkSubframe(aNode);
     }
@@ -1084,18 +1082,17 @@ PersistNodeFixup::FixupNode(nsIDOMNode *
                 FixupAnchor(*aNodeOut);
             }
             // TODO if "type" attribute == "text/css"
             //        fixup stylesheet
         }
         return rv;
     }
 
-    nsCOMPtr<nsIDOMHTMLFrameElement> nodeAsFrame = do_QueryInterface(aNodeIn);
-    if (nodeAsFrame) {
+    if (content->IsHTMLElement(nsGkAtoms::frame)) {
         rv = GetNodeToFixup(aNodeIn, aNodeOut);
         if (NS_SUCCEEDED(rv) && *aNodeOut) {
             FixupAttribute(*aNodeOut, "src");
         }
         return rv;
     }
 
     nsCOMPtr<nsIDOMHTMLIFrameElement> nodeAsIFrame = do_QueryInterface(aNodeIn);
--- a/editor/libeditor/HTMLEditorDataTransfer.cpp
+++ b/editor/libeditor/HTMLEditorDataTransfer.cpp
@@ -34,17 +34,16 @@
 #include "nsGkAtoms.h"
 #include "nsIClipboard.h"
 #include "nsIContent.h"
 #include "nsIContentFilter.h"
 #include "nsIDOMComment.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMDocumentFragment.h"
 #include "nsIDOMElement.h"
-#include "nsIDOMHTMLFrameElement.h"
 #include "nsIDOMHTMLIFrameElement.h"
 #include "nsIDOMHTMLImageElement.h"
 #include "nsIDOMHTMLInputElement.h"
 #include "nsIDOMHTMLLinkElement.h"
 #include "nsIDOMHTMLScriptElement.h"
 #include "nsIDOMNode.h"
 #include "nsIDocument.h"
 #include "nsIEditRules.h"
--- a/gfx/2d/Tools.h
+++ b/gfx/2d/Tools.h
@@ -87,30 +87,44 @@ Distance(Point aA, Point aB)
 
 static inline int
 BytesPerPixel(SurfaceFormat aFormat)
 {
   switch (aFormat) {
   case SurfaceFormat::A8:
     return 1;
   case SurfaceFormat::R5G6B5_UINT16:
+  case SurfaceFormat::A16:
     return 2;
   case SurfaceFormat::R8G8B8:
   case SurfaceFormat::B8G8R8:
     return 3;
   case SurfaceFormat::HSV:
   case SurfaceFormat::Lab:
     return 3 * sizeof(float);
   case SurfaceFormat::Depth:
     return sizeof(uint16_t);
   default:
     return 4;
   }
 }
 
+static inline SurfaceFormat
+SurfaceFormatForAlphaBitDepth(uint32_t aBitDepth)
+{
+  if (aBitDepth == 8) {
+    return SurfaceFormat::A8;
+  } else if (aBitDepth == 10 ||
+             aBitDepth == 12) {
+    return SurfaceFormat::A16;
+  }
+  MOZ_ASSERT_UNREACHABLE("Unsupported alpha bit depth");
+  return SurfaceFormat::UNKNOWN;
+}
+
 static inline bool
 IsOpaqueFormat(SurfaceFormat aFormat) {
   switch (aFormat) {
     case SurfaceFormat::B8G8R8X8:
     case SurfaceFormat::R8G8B8X8:
     case SurfaceFormat::X8R8G8B8:
     case SurfaceFormat::YUV:
     case SurfaceFormat::NV12:
--- a/gfx/2d/Types.h
+++ b/gfx/2d/Types.h
@@ -51,16 +51,17 @@ enum class SurfaceFormat : int8_t {
 
   // The _UINT16 suffix here indicates that the name reflects the layout when
   // viewed as a uint16_t value. In memory these values are stored using native
   // endianness.
   R5G6B5_UINT16,                    // 0bRRRRRGGGGGGBBBBB
 
   // This one is a single-byte, so endianness isn't an issue.
   A8,
+  A16,
 
   R8G8,
 
   // These ones are their own special cases.
   YUV,
   NV12,
   YUV422,
   HSV,
--- a/gfx/doc/README.webrender
+++ b/gfx/doc/README.webrender
@@ -74,9 +74,9 @@ there is another crate in m-c called moz
 the same folder to store its rust dependencies. If one of the libraries that is
 required by both mozjs_sys and webrender is updated without updating the other
 project's Cargo.lock file, that results in build bustage.
 This means that any time you do this sort of manual update of packages, you need
 to make sure that mozjs_sys also has its Cargo.lock file updated if needed, hence
 the need to run the cargo update command in js/src as well. Hopefully this will
 be resolved soon.
 
-Latest Commit: aa81aebba2c1b8ff8af5d40796154d66349fd131
+Latest Commit: a884e676449e5b41669cd6de51af14e70cbe3512
--- a/gfx/gl/GLBlitHelper.cpp
+++ b/gfx/gl/GLBlitHelper.cpp
@@ -696,25 +696,23 @@ GLBlitHelper::BlitImage(layers::PlanarYC
                            << yuvData->mYSize.height << ", "
                            << yuvData->mCbCrSize.width << ","
                            << yuvData->mCbCrSize.height << ", "
                            << yuvData->mYStride << ","
                            << yuvData->mCbCrStride;
         return false;
     }
 
-    const gfx::IntSize yTexSize(yuvData->mYStride, yuvData->mYSize.height);
-    const gfx::IntSize uvTexSize(yuvData->mCbCrStride, yuvData->mCbCrSize.height);
     gfx::IntSize divisors;
-    if (!GuessDivisors(yTexSize, uvTexSize, &divisors)) {
+    if (!GuessDivisors(yuvData->mYSize, yuvData->mCbCrSize, &divisors)) {
         gfxCriticalError() << "GuessDivisors failed:"
-                           << yTexSize.width << ","
-                           << yTexSize.height << ", "
-                           << uvTexSize.width << ","
-                           << uvTexSize.height;
+                           << yuvData->mYSize.width << ","
+                           << yuvData->mYSize.height << ", "
+                           << yuvData->mCbCrSize.width << ","
+                           << yuvData->mCbCrSize.height;
         return false;
     }
 
     // --
 
     // RED textures aren't valid in GLES2, and ALPHA textures are not valid in desktop GL Core Profiles.
     // So use R8 textures on GL3.0+ and GLES3.0+, but LUMINANCE/LUMINANCE/UNSIGNED_BYTE otherwise.
     GLenum internalFormat;
@@ -728,16 +726,18 @@ GLBlitHelper::BlitImage(layers::PlanarYC
         internalFormat = LOCAL_GL_LUMINANCE;
         unpackFormat = LOCAL_GL_LUMINANCE;
     }
 
     // --
 
     const ScopedSaveMultiTex saveTex(mGL, 3, LOCAL_GL_TEXTURE_2D);
     const ResetUnpackState reset(mGL);
+    const gfx::IntSize yTexSize(yuvData->mYStride, yuvData->mYSize.height);
+    const gfx::IntSize uvTexSize(yuvData->mCbCrStride, yuvData->mCbCrSize.height);
 
     if (yTexSize != mYuvUploads_YSize ||
         uvTexSize != mYuvUploads_UVSize)
     {
         mYuvUploads_YSize = yTexSize;
         mYuvUploads_UVSize = uvTexSize;
 
         mGL->fActiveTexture(LOCAL_GL_TEXTURE0);
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -3012,17 +3012,18 @@ GetBytesPerTexel(GLenum format, GLenum t
             case LOCAL_GL_RGBA:
             case LOCAL_GL_BGRA_EXT:
                 return 4 * multiplier;
             default:
                 break;
         }
     } else if (type == LOCAL_GL_UNSIGNED_SHORT_4_4_4_4 ||
                type == LOCAL_GL_UNSIGNED_SHORT_5_5_5_1 ||
-               type == LOCAL_GL_UNSIGNED_SHORT_5_6_5)
+               type == LOCAL_GL_UNSIGNED_SHORT_5_6_5 ||
+               type == LOCAL_GL_UNSIGNED_SHORT)
     {
         return 2;
     }
 
     gfxCriticalError() << "Unknown texture type " << type << " or format " << format;
     return 0;
 }
 
--- a/gfx/gl/GLUploadHelpers.cpp
+++ b/gfx/gl/GLUploadHelpers.cpp
@@ -459,16 +459,24 @@ UploadImageDataToTexture(GLContext* gl,
             surfaceFormat = SurfaceFormat::R5G6B5_UINT16;
             break;
         case SurfaceFormat::A8:
             internalFormat = format = LOCAL_GL_LUMINANCE;
             type = LOCAL_GL_UNSIGNED_BYTE;
             // We don't have a specific luminance shader
             surfaceFormat = SurfaceFormat::A8;
             break;
+        case SurfaceFormat::A16:
+            format = LOCAL_GL_LUMINANCE;
+            internalFormat = LOCAL_GL_LUMINANCE16;
+            type = LOCAL_GL_UNSIGNED_SHORT;
+            // We don't have a specific luminance shader
+            surfaceFormat = SurfaceFormat::A8;
+            pixelSize = 2;
+            break;
         default:
             NS_ASSERTION(false, "Unhandled image surface format!");
     }
 
     if (aOutUploadSize) {
         *aOutUploadSize = 0;
     }
 
--- a/gfx/layers/BufferTexture.cpp
+++ b/gfx/layers/BufferTexture.cpp
@@ -154,67 +154,83 @@ BufferTextureData::CreateInternal(Layers
     return new ShmemTextureData(aDesc, aMoz2DBackend, shm);
   }
 }
 
 BufferTextureData*
 BufferTextureData::CreateForYCbCrWithBufferSize(KnowsCompositor* aAllocator,
                                                 int32_t aBufferSize,
                                                 YUVColorSpace aYUVColorSpace,
+                                                uint32_t aBitDepth,
                                                 TextureFlags aTextureFlags)
 {
   if (aBufferSize == 0 || !gfx::Factory::CheckBufferSize(aBufferSize)) {
     return nullptr;
   }
 
   bool hasIntermediateBuffer = aAllocator ? ComputeHasIntermediateBuffer(gfx::SurfaceFormat::YUV,
                                                                          aAllocator->GetCompositorBackendType())
                                           : true;
 
   // Initialize the metadata with something, even if it will have to be rewritten
   // afterwards since we don't know the dimensions of the texture at this point.
-  BufferDescriptor desc = YCbCrDescriptor(gfx::IntSize(), gfx::IntSize(),
+  BufferDescriptor desc = YCbCrDescriptor(gfx::IntSize(), 0, gfx::IntSize(), 0,
                                           0, 0, 0, StereoMode::MONO,
                                           aYUVColorSpace,
+                                          aBitDepth,
                                           hasIntermediateBuffer);
 
   return CreateInternal(aAllocator ? aAllocator->GetTextureForwarder() : nullptr,
                        desc, gfx::BackendType::NONE, aBufferSize, aTextureFlags);
 }
 
 BufferTextureData*
 BufferTextureData::CreateForYCbCr(KnowsCompositor* aAllocator,
                                   gfx::IntSize aYSize,
+                                  uint32_t aYStride,
                                   gfx::IntSize aCbCrSize,
+                                  uint32_t aCbCrStride,
                                   StereoMode aStereoMode,
                                   YUVColorSpace aYUVColorSpace,
+                                  uint32_t aBitDepth,
                                   TextureFlags aTextureFlags)
 {
-  uint32_t bufSize = ImageDataSerializer::ComputeYCbCrBufferSize(aYSize, aCbCrSize);
+  uint32_t bufSize = ImageDataSerializer::ComputeYCbCrBufferSize(
+    aYSize, aYStride, aCbCrSize, aCbCrStride);
   if (bufSize == 0) {
     return nullptr;
   }
 
   uint32_t yOffset;
   uint32_t cbOffset;
   uint32_t crOffset;
-  ImageDataSerializer::ComputeYCbCrOffsets(aYSize.width, aYSize.height,
-                                          aCbCrSize.width, aCbCrSize.height,
-                                          yOffset, cbOffset, crOffset);
+  ImageDataSerializer::ComputeYCbCrOffsets(aYStride, aYSize.height,
+                                           aCbCrStride, aCbCrSize.height,
+                                           yOffset, cbOffset, crOffset);
 
-  bool hasIntermediateBuffer = aAllocator ? ComputeHasIntermediateBuffer(gfx::SurfaceFormat::YUV,
-                                                                         aAllocator->GetCompositorBackendType())
-                                          : true;
+  bool hasIntermediateBuffer =
+    aAllocator
+      ? ComputeHasIntermediateBuffer(gfx::SurfaceFormat::YUV,
+                                     aAllocator->GetCompositorBackendType())
+      : true;
 
-  YCbCrDescriptor descriptor = YCbCrDescriptor(aYSize, aCbCrSize, yOffset, cbOffset,
-                                               crOffset, aStereoMode, aYUVColorSpace,
+  YCbCrDescriptor descriptor = YCbCrDescriptor(aYSize, aYStride,
+                                               aCbCrSize, aCbCrStride,
+                                               yOffset, cbOffset, crOffset,
+                                               aStereoMode,
+                                               aYUVColorSpace,
+                                               aBitDepth,
                                                hasIntermediateBuffer);
 
- return CreateInternal(aAllocator ? aAllocator->GetTextureForwarder() : nullptr, descriptor,
-                       gfx::BackendType::NONE, bufSize, aTextureFlags);
+  return CreateInternal(aAllocator ? aAllocator->GetTextureForwarder()
+                                   : nullptr,
+                        descriptor,
+                        gfx::BackendType::NONE,
+                        bufSize,
+                        aTextureFlags);
 }
 
 void
 BufferTextureData::FillInfo(TextureData::Info& aInfo) const
 {
   aInfo.size = GetSize();
   aInfo.format = GetFormat();
   aInfo.hasSynchronization = false;
@@ -249,16 +265,22 @@ BufferTextureData::GetCbCrSize() const
 }
 
 Maybe<YUVColorSpace>
 BufferTextureData::GetYUVColorSpace() const
 {
   return ImageDataSerializer::YUVColorSpaceFromBufferDescriptor(mDescriptor);
 }
 
+Maybe<uint32_t>
+BufferTextureData::GetBitDepth() const
+{
+  return ImageDataSerializer::BitDepthFromBufferDescriptor(mDescriptor);
+}
+
 Maybe<StereoMode>
 BufferTextureData::GetStereoMode() const
 {
   return ImageDataSerializer::StereoModeFromBufferDescriptor(mDescriptor);
 }
 
 gfx::SurfaceFormat
 BufferTextureData::GetFormat() const
@@ -346,27 +368,27 @@ BufferTextureData::BorrowMappedYCbCrData
   auto ySize = desc.ySize();
   auto cbCrSize = desc.cbCrSize();
 
   aMap.stereoMode = desc.stereoMode();
   aMap.metadata = nullptr;
 
   aMap.y.data = data + desc.yOffset();
   aMap.y.size = ySize;
-  aMap.y.stride = ySize.width;
+  aMap.y.stride = desc.yStride();
   aMap.y.skip = 0;
 
   aMap.cb.data = data + desc.cbOffset();
   aMap.cb.size = cbCrSize;
-  aMap.cb.stride = cbCrSize.width;
+  aMap.cb.stride = desc.cbCrStride();
   aMap.cb.skip = 0;
 
   aMap.cr.data = data + desc.crOffset();
   aMap.cr.size = cbCrSize;
-  aMap.cr.stride = cbCrSize.width;
+  aMap.cr.stride = desc.cbCrStride();
   aMap.cr.skip = 0;
 
   return true;
 }
 
 bool
 BufferTextureData::UpdateFromSurface(gfx::SourceSurface* aSurface)
 {
--- a/gfx/layers/BufferTexture.h
+++ b/gfx/layers/BufferTexture.h
@@ -25,27 +25,31 @@ public:
                                    gfx::BackendType aMoz2DBackend,
                                    LayersBackend aLayersBackend,
                                    TextureFlags aFlags,
                                    TextureAllocationFlags aAllocFlags,
                                    LayersIPCChannel* aAllocator);
 
   static BufferTextureData* CreateForYCbCr(KnowsCompositor* aAllocator,
                                            gfx::IntSize aYSize,
+                                           uint32_t aYStride,
                                            gfx::IntSize aCbCrSize,
+                                           uint32_t aCbCrStride,
                                            StereoMode aStereoMode,
                                            YUVColorSpace aYUVColorSpace,
+                                           uint32_t aBitDepth,
                                            TextureFlags aTextureFlags);
 
   // It is generally better to use CreateForYCbCr instead.
   // This creates a half-initialized texture since we don't know the sizes and
   // offsets in the buffer.
   static BufferTextureData* CreateForYCbCrWithBufferSize(KnowsCompositor* aAllocator,
                                                          int32_t aSize,
                                                          YUVColorSpace aYUVColorSpace,
+                                                         uint32_t aBitDepth,
                                                          TextureFlags aTextureFlags);
 
   virtual bool Lock(OpenMode aMode) override { return true; }
 
   virtual void Unlock() override {}
 
   virtual void FillInfo(TextureData::Info& aInfo) const override;
 
@@ -62,16 +66,18 @@ public:
 
   // Don't use this.
   void SetDesciptor(const BufferDescriptor& aDesc);
 
   Maybe<gfx::IntSize> GetCbCrSize() const;
 
   Maybe<YUVColorSpace> GetYUVColorSpace() const;
 
+  Maybe<uint32_t> GetBitDepth() const;
+
   Maybe<StereoMode> GetStereoMode() const;
 
 protected:
   gfx::IntSize GetSize() const;
 
   gfx::SurfaceFormat GetFormat() const;
 
   static BufferTextureData* CreateInternal(LayersIPCChannel* aAllocator,
--- a/gfx/layers/Effects.h
+++ b/gfx/layers/Effects.h
@@ -156,24 +156,26 @@ struct EffectRGB : public TexturedEffect
     : TexturedEffect(EffectTypes::RGB, aTexture, aPremultiplied, aSamplingFilter)
   {}
 
   virtual const char* Name() { return "EffectRGB"; }
 };
 
 struct EffectYCbCr : public TexturedEffect
 {
-  EffectYCbCr(TextureSource *aSource, YUVColorSpace aYUVColorSpace, gfx::SamplingFilter aSamplingFilter)
+  EffectYCbCr(TextureSource *aSource, YUVColorSpace aYUVColorSpace, uint32_t aBitDepth, gfx::SamplingFilter aSamplingFilter)
     : TexturedEffect(EffectTypes::YCBCR, aSource, false, aSamplingFilter)
     , mYUVColorSpace(aYUVColorSpace)
+    , mBitDepth(aBitDepth)
   {}
 
   virtual const char* Name() { return "EffectYCbCr"; }
 
   YUVColorSpace mYUVColorSpace;
+  uint32_t mBitDepth;
 };
 
 struct EffectNV12 : public TexturedEffect
 {
   EffectNV12(TextureSource *aSource, gfx::SamplingFilter aSamplingFilter)
     : TexturedEffect(EffectTypes::NV12, aSource, false, aSamplingFilter)
   {}
 
@@ -265,17 +267,18 @@ CreateTexturedEffect(TextureHost* aHost,
                      bool isAlphaPremultiplied)
 {
   MOZ_ASSERT(aHost);
   MOZ_ASSERT(aSource);
 
   RefPtr<TexturedEffect> result;
   if (aHost->GetReadFormat() == gfx::SurfaceFormat::YUV) {
     MOZ_ASSERT(aHost->GetYUVColorSpace() != YUVColorSpace::UNKNOWN);
-    result = new EffectYCbCr(aSource, aHost->GetYUVColorSpace(), aSamplingFilter);
+    result = new EffectYCbCr(
+      aSource, aHost->GetYUVColorSpace(), aHost->GetBitDepth(), aSamplingFilter);
   } else {
     result = CreateTexturedEffect(aHost->GetReadFormat(),
                                   aSource,
                                   aSamplingFilter,
                                   isAlphaPremultiplied);
   }
   return result.forget();
 }
--- a/gfx/layers/ImageContainer.cpp
+++ b/gfx/layers/ImageContainer.cpp
@@ -602,36 +602,24 @@ gfxImageFormat
 PlanarYCbCrImage::GetOffscreenFormat()
 {
   return mOffscreenFormat == SurfaceFormat::UNKNOWN ?
     gfxVars::OffscreenFormat() :
     mOffscreenFormat;
 }
 
 bool
-PlanarYCbCrImage::AdoptData(const Data &aData)
+PlanarYCbCrImage::AdoptData(const Data& aData)
 {
   mData = aData;
   mSize = aData.mPicSize;
   mOrigin = gfx::IntPoint(aData.mPicX, aData.mPicY);
   return true;
 }
 
-uint8_t*
-RecyclingPlanarYCbCrImage::AllocateAndGetNewBuffer(uint32_t aSize)
-{
-  // get new buffer
-  mBuffer = AllocateBuffer(aSize);
-  if (mBuffer) {
-    // update buffer size
-    mBufferSize = aSize;
-  }
-  return mBuffer.get();
-}
-
 already_AddRefed<gfx::SourceSurface>
 PlanarYCbCrImage::GetAsSourceSurface()
 {
   if (mSourceSurface) {
     RefPtr<gfx::SourceSurface> surface(mSourceSurface);
     return surface.forget();
   }
 
--- a/gfx/layers/ImageContainer.h
+++ b/gfx/layers/ImageContainer.h
@@ -739,29 +739,31 @@ struct PlanarYCbCrData {
   int32_t mCbSkip;
   int32_t mCrSkip;
   // Picture region
   uint32_t mPicX;
   uint32_t mPicY;
   gfx::IntSize mPicSize;
   StereoMode mStereoMode;
   YUVColorSpace mYUVColorSpace;
+  uint32_t mBitDepth;
 
   gfx::IntRect GetPictureRect() const {
     return gfx::IntRect(mPicX, mPicY,
                      mPicSize.width,
                      mPicSize.height);
   }
 
   PlanarYCbCrData()
     : mYChannel(nullptr), mYStride(0), mYSize(0, 0), mYSkip(0)
     , mCbChannel(nullptr), mCrChannel(nullptr)
     , mCbCrStride(0), mCbCrSize(0, 0) , mCbSkip(0), mCrSkip(0)
     , mPicX(0), mPicY(0), mPicSize(0, 0), mStereoMode(StereoMode::MONO)
     , mYUVColorSpace(YUVColorSpace::BT601)
+    , mBitDepth(8)
   {}
 };
 
 /****** Image subtypes for the different formats ******/
 
 /**
  * We assume that the image data is in the REC 470M color space (see
  * Theora specification, section 4.3.1).
@@ -808,28 +810,19 @@ public:
 
   /**
    * This makes a copy of the data buffers, in order to support functioning
    * in all different layer managers.
    */
   virtual bool CopyData(const Data& aData) = 0;
 
   /**
-   * This doesn't make a copy of the data buffers. Can be used when mBuffer is
-   * pre allocated with AllocateAndGetNewBuffer(size) and then AdoptData is
-   * called to only update the picture size, planes etc. fields in mData.
-   * The GStreamer media backend uses this to decode into PlanarYCbCrImage(s)
-   * directly.
+   * This doesn't make a copy of the data buffers.
    */
-  virtual bool AdoptData(const Data &aData);
-
-  /**
-   * This allocates and returns a new buffer
-   */
-  virtual uint8_t* AllocateAndGetNewBuffer(uint32_t aSize) = 0;
+  virtual bool AdoptData(const Data& aData);
 
   /**
    * Ask this Image to not convert YUV to RGB during SetData, and make
    * the original data available through GetData. This is optional,
    * and not all PlanarYCbCrImages will support it.
    */
   virtual void SetDelayedConversion(bool aDelayed) { }
 
@@ -875,17 +868,16 @@ protected:
   uint32_t mBufferSize;
 };
 
 class RecyclingPlanarYCbCrImage: public PlanarYCbCrImage {
 public:
   explicit RecyclingPlanarYCbCrImage(BufferRecycleBin *aRecycleBin) : mRecycleBin(aRecycleBin) {}
   virtual ~RecyclingPlanarYCbCrImage() override;
   virtual bool CopyData(const Data& aData) override;
-  virtual uint8_t* AllocateAndGetNewBuffer(uint32_t aSize) override;
   virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
 protected:
 
   /**
    * Return a buffer to store image data in.
    */
   mozilla::UniquePtr<uint8_t[]> AllocateBuffer(uint32_t aSize);
 
--- a/gfx/layers/ImageDataSerializer.cpp
+++ b/gfx/layers/ImageDataSerializer.cpp
@@ -66,28 +66,22 @@ ComputeYCbCrBufferSize(const gfx::IntSiz
 {
   MOZ_ASSERT(aYSize.height >= 0 && aYSize.width >= 0);
 
   if (aYSize.height < 0 || aYSize.width < 0 || aCbCrSize.height < 0 || aCbCrSize.width < 0 ||
       !gfx::Factory::AllowedSurfaceSize(IntSize(aYStride, aYSize.height)) ||
       !gfx::Factory::AllowedSurfaceSize(IntSize(aCbCrStride, aCbCrSize.height))) {
     return 0;
   }
+
   // Overflow checks are performed in AllowedSurfaceSize
   return GetAlignedStride<4>(aYSize.height, aYStride) +
          2 * GetAlignedStride<4>(aCbCrSize.height, aCbCrStride);
 }
 
-// Minimum required shmem size in bytes
-uint32_t
-ComputeYCbCrBufferSize(const gfx::IntSize& aYSize, const gfx::IntSize& aCbCrSize)
-{
-  return ComputeYCbCrBufferSize(aYSize, aYSize.width, aCbCrSize, aCbCrSize.width);
-}
-
 uint32_t
 ComputeYCbCrBufferSize(uint32_t aBufferSize)
 {
   return GetAlignedStride<4>(aBufferSize, 1);
 }
 
 void ComputeYCbCrOffsets(int32_t yStride, int32_t yHeight,
                          int32_t cbCrStride, int32_t cbCrHeight,
@@ -132,37 +126,47 @@ Maybe<gfx::IntSize> CbCrSizeFromBufferDe
       return Some(aDescriptor.get_YCbCrDescriptor().cbCrSize());
     default:
       MOZ_CRASH("GFX:  CbCrSizeFromBufferDescriptor");
   }
 }
 
 Maybe<YUVColorSpace> YUVColorSpaceFromBufferDescriptor(const BufferDescriptor& aDescriptor)
 {
-{
   switch (aDescriptor.type()) {
     case BufferDescriptor::TRGBDescriptor:
       return Nothing();
     case BufferDescriptor::TYCbCrDescriptor:
       return Some(aDescriptor.get_YCbCrDescriptor().yUVColorSpace());
     default:
-      MOZ_CRASH("GFX:  CbCrSizeFromBufferDescriptor");
+      MOZ_CRASH("GFX:  YUVColorSpaceFromBufferDescriptor");
   }
 }
+
+Maybe<uint32_t> BitDepthFromBufferDescriptor(const BufferDescriptor& aDescriptor)
+{
+  switch (aDescriptor.type()) {
+    case BufferDescriptor::TRGBDescriptor:
+      return Nothing();
+    case BufferDescriptor::TYCbCrDescriptor:
+      return Some(aDescriptor.get_YCbCrDescriptor().bitDepth());
+    default:
+      MOZ_CRASH("GFX:  BitDepthFromBufferDescriptor");
+  }
 }
 
 Maybe<StereoMode> StereoModeFromBufferDescriptor(const BufferDescriptor& aDescriptor)
 {
   switch (aDescriptor.type()) {
     case BufferDescriptor::TRGBDescriptor:
       return Nothing();
     case BufferDescriptor::TYCbCrDescriptor:
       return Some(aDescriptor.get_YCbCrDescriptor().stereoMode());
     default:
-      MOZ_CRASH("GFX:  CbCrSizeFromBufferDescriptor");
+      MOZ_CRASH("GFX:  StereoModeFromBufferDescriptor");
   }
 }
 
 uint8_t* GetYChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor)
 {
   return aBuffer + aDescriptor.yOffset();
 }
 
@@ -175,19 +179,16 @@ uint8_t* GetCrChannel(uint8_t* aBuffer, 
 {
   return aBuffer + aDescriptor.crOffset();
 }
 
 already_AddRefed<DataSourceSurface>
 DataSourceSurfaceFromYCbCrDescriptor(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor, gfx::DataSourceSurface* aSurface)
 {
   gfx::IntSize ySize = aDescriptor.ySize();
-  gfx::IntSize cbCrSize = aDescriptor.cbCrSize();
-  int32_t yStride = ySize.width;
-  int32_t cbCrStride = cbCrSize.width;
 
   RefPtr<DataSourceSurface> result;
   if (aSurface) {
     MOZ_ASSERT(aSurface->GetSize() == ySize);
     MOZ_ASSERT(aSurface->GetFormat() == gfx::SurfaceFormat::B8G8R8X8);
     if (aSurface->GetSize() == ySize &&
         aSurface->GetFormat() == gfx::SurfaceFormat::B8G8R8X8) {
       result = aSurface;
@@ -204,24 +205,25 @@ DataSourceSurfaceFromYCbCrDescriptor(uin
 
   DataSourceSurface::MappedSurface map;
   if (NS_WARN_IF(!result->Map(DataSourceSurface::MapType::WRITE, &map))) {
     return nullptr;
   }
 
   layers::PlanarYCbCrData ycbcrData;
   ycbcrData.mYChannel     = GetYChannel(aBuffer, aDescriptor);
-  ycbcrData.mYStride      = yStride;
+  ycbcrData.mYStride      = aDescriptor.yStride();
   ycbcrData.mYSize        = ySize;
   ycbcrData.mCbChannel    = GetCbChannel(aBuffer, aDescriptor);
   ycbcrData.mCrChannel    = GetCrChannel(aBuffer, aDescriptor);
-  ycbcrData.mCbCrStride   = cbCrStride;
-  ycbcrData.mCbCrSize     = cbCrSize;
+  ycbcrData.mCbCrStride   = aDescriptor.cbCrStride();
+  ycbcrData.mCbCrSize     = aDescriptor.cbCrSize();
   ycbcrData.mPicSize      = ySize;
   ycbcrData.mYUVColorSpace = aDescriptor.yUVColorSpace();
+  ycbcrData.mBitDepth     = aDescriptor.bitDepth();
 
   gfx::ConvertYCbCrToRGB(ycbcrData,
                          gfx::SurfaceFormat::B8G8R8X8,
                          ySize,
                          map.mData,
                          map.mStride);
 
   result->Unmap();
@@ -232,30 +234,27 @@ void
 ConvertAndScaleFromYCbCrDescriptor(uint8_t* aBuffer,
                                    const YCbCrDescriptor& aDescriptor,
                                    const gfx::SurfaceFormat& aDestFormat,
                                    const gfx::IntSize& aDestSize,
                                    unsigned char* aDestBuffer,
                                    int32_t aStride)
 {
   MOZ_ASSERT(aBuffer);
-  gfx::IntSize ySize = aDescriptor.ySize();
-  gfx::IntSize cbCrSize = aDescriptor.cbCrSize();
-  int32_t yStride = ySize.width;
-  int32_t cbCrStride = cbCrSize.width;
 
   layers::PlanarYCbCrData ycbcrData;
   ycbcrData.mYChannel     = GetYChannel(aBuffer, aDescriptor);
-  ycbcrData.mYStride      = yStride;
-  ycbcrData.mYSize        = ySize;
+  ycbcrData.mYStride      = aDescriptor.yStride();;
+  ycbcrData.mYSize        = aDescriptor.ySize();
   ycbcrData.mCbChannel    = GetCbChannel(aBuffer, aDescriptor);
   ycbcrData.mCrChannel    = GetCrChannel(aBuffer, aDescriptor);
-  ycbcrData.mCbCrStride   = cbCrStride;
-  ycbcrData.mCbCrSize     = cbCrSize;
-  ycbcrData.mPicSize      = ySize;
+  ycbcrData.mCbCrStride   = aDescriptor.cbCrStride();
+  ycbcrData.mCbCrSize     = aDescriptor.cbCrSize();
+  ycbcrData.mPicSize      = aDescriptor.ySize();
   ycbcrData.mYUVColorSpace = aDescriptor.yUVColorSpace();
+  ycbcrData.mBitDepth     = aDescriptor.bitDepth();
 
   gfx::ConvertYCbCrToRGB(ycbcrData, aDestFormat, aDestSize, aDestBuffer, aStride);
 }
 
 } // namespace ImageDataSerializer
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/ImageDataSerializer.h
+++ b/gfx/layers/ImageDataSerializer.h
@@ -40,33 +40,32 @@ uint32_t ComputeRGBBufferSize(gfx::IntSi
 
 ///This function is meant as a helper to know how much shared memory we need
 ///to allocate in a shmem in order to place a shared YCbCr image blob of
 ///given dimensions.
 uint32_t ComputeYCbCrBufferSize(const gfx::IntSize& aYSize,
                                 int32_t aYStride,
                                 const gfx::IntSize& aCbCrSize,
                                 int32_t aCbCrStride);
-uint32_t ComputeYCbCrBufferSize(const gfx::IntSize& aYSize,
-                                const gfx::IntSize& aCbCrSize);
-
 uint32_t ComputeYCbCrBufferSize(uint32_t aBufferSize);
 
 void ComputeYCbCrOffsets(int32_t yStride, int32_t yHeight,
                          int32_t cbCrStride, int32_t cbCrHeight,
                          uint32_t& outYOffset, uint32_t& outCbOffset, uint32_t& outCrOffset);
 
 gfx::SurfaceFormat FormatFromBufferDescriptor(const BufferDescriptor& aDescriptor);
 
 gfx::IntSize SizeFromBufferDescriptor(const BufferDescriptor& aDescriptor);
 
 Maybe<gfx::IntSize> CbCrSizeFromBufferDescriptor(const BufferDescriptor& aDescriptor);
 
 Maybe<YUVColorSpace> YUVColorSpaceFromBufferDescriptor(const BufferDescriptor& aDescriptor);
 
+Maybe<uint32_t> BitDepthFromBufferDescriptor(const BufferDescriptor& aDescriptor);
+
 Maybe<StereoMode> StereoModeFromBufferDescriptor(const BufferDescriptor& aDescriptor);
 
 uint8_t* GetYChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor);
 
 uint8_t* GetCbChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor);
 
 uint8_t* GetCrChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor);
 
--- a/gfx/layers/client/ImageClient.cpp
+++ b/gfx/layers/client/ImageClient.cpp
@@ -96,18 +96,21 @@ ImageClient::CreateTextureClientForImage
   RefPtr<TextureClient> texture;
   if (aImage->GetFormat() == ImageFormat::PLANAR_YCBCR) {
     PlanarYCbCrImage* ycbcr = static_cast<PlanarYCbCrImage*>(aImage);
     const PlanarYCbCrData* data = ycbcr->GetData();
     if (!data) {
       return nullptr;
     }
     texture = TextureClient::CreateForYCbCr(aForwarder,
-                                            data->mYSize, data->mCbCrSize, data->mStereoMode,
+                                            data->mYSize, data->mYStride,
+                                            data->mCbCrSize, data->mCbCrStride,
+                                            data->mStereoMode,
                                             data->mYUVColorSpace,
+                                            data->mBitDepth,
                                             TextureFlags::DEFAULT);
     if (!texture) {
       return nullptr;
     }
 
     TextureClientAutoLock autoLock(texture, OpenMode::OPEN_WRITE_ONLY);
     if (!autoLock.Succeeded()) {
       return nullptr;
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -1251,82 +1251,90 @@ TextureClient::CreateForRawBufferAccess(
 
   return MakeAndAddRef<TextureClient>(texData, aTextureFlags, aAllocator);
 }
 
 // static
 already_AddRefed<TextureClient>
 TextureClient::CreateForYCbCr(KnowsCompositor* aAllocator,
                               gfx::IntSize aYSize,
+                              uint32_t aYStride,
                               gfx::IntSize aCbCrSize,
+                              uint32_t aCbCrStride,
                               StereoMode aStereoMode,
                               YUVColorSpace aYUVColorSpace,
+                              uint32_t aBitDepth,
                               TextureFlags aTextureFlags)
 {
   if (!aAllocator || !aAllocator->GetLayersIPCActor()->IPCOpen()) {
     return nullptr;
   }
 
   if (!gfx::Factory::AllowedSurfaceSize(aYSize)) {
     return nullptr;
   }
 
-  TextureData* data = BufferTextureData::CreateForYCbCr(aAllocator, aYSize, aCbCrSize,
-                                                        aStereoMode, aYUVColorSpace,
-                                                        aTextureFlags);
+  TextureData* data =
+    BufferTextureData::CreateForYCbCr(aAllocator,
+                                      aYSize, aYStride,
+                                      aCbCrSize, aCbCrStride,
+                                      aStereoMode, aYUVColorSpace,
+                                      aBitDepth, aTextureFlags);
   if (!data) {
     return nullptr;
   }
 
   return MakeAndAddRef<TextureClient>(data, aTextureFlags,
                                       aAllocator->GetTextureForwarder());
 }
 
 // static
 already_AddRefed<TextureClient>
 TextureClient::CreateForYCbCrWithBufferSize(KnowsCompositor* aAllocator,
                                             size_t aSize,
                                             YUVColorSpace aYUVColorSpace,
+                                            uint32_t aBitDepth,
                                             TextureFlags aTextureFlags)
 {
   if (!aAllocator || !aAllocator->GetLayersIPCActor()->IPCOpen()) {
     return nullptr;
   }
 
-  TextureData* data =
-    BufferTextureData::CreateForYCbCrWithBufferSize(aAllocator, aSize, aYUVColorSpace,
-                                                    aTextureFlags);
+  TextureData* data = BufferTextureData::CreateForYCbCrWithBufferSize(
+    aAllocator, aSize, aYUVColorSpace, aBitDepth, aTextureFlags);
   if (!data) {
     return nullptr;
   }
 
   return MakeAndAddRef<TextureClient>(data, aTextureFlags,
                                       aAllocator->GetTextureForwarder());
 }
 
-TextureClient::TextureClient(TextureData* aData, TextureFlags aFlags, LayersIPCChannel* aAllocator)
-: AtomicRefCountedWithFinalize("TextureClient")
-, mAllocator(aAllocator)
-, mActor(nullptr)
-, mData(aData)
-, mFlags(aFlags)
-, mOpenMode(OpenMode::OPEN_NONE)
+TextureClient::TextureClient(TextureData* aData,
+                             TextureFlags aFlags,
+                             LayersIPCChannel* aAllocator)
+  : AtomicRefCountedWithFinalize("TextureClient")
+  , mAllocator(aAllocator)
+  , mActor(nullptr)
+  , mData(aData)
+  , mFlags(aFlags)
+  , mOpenMode(OpenMode::OPEN_NONE)
 #ifdef DEBUG
-, mExpectedDtRefs(0)
+  , mExpectedDtRefs(0)
 #endif
-, mIsLocked(false)
-, mIsReadLocked(false)
-, mUpdated(false)
-, mAddedToCompositableClient(false)
-, mWorkaroundAnnoyingSharedSurfaceLifetimeIssues(false)
-, mWorkaroundAnnoyingSharedSurfaceOwnershipIssues(false)
-, mFwdTransactionId(0)
-, mSerial(++sSerialCounter)
+  , mIsLocked(false)
+  , mIsReadLocked(false)
+  , mUpdated(false)
+  , mAddedToCompositableClient(false)
+  , mWorkaroundAnnoyingSharedSurfaceLifetimeIssues(false)
+  , mWorkaroundAnnoyingSharedSurfaceOwnershipIssues(false)
+  , mFwdTransactionId(0)
+  , mSerial(++sSerialCounter)
 #ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
-, mPoolTracker(nullptr)
+  , mPoolTracker(nullptr)
 #endif
 {
   mData->FillInfo(mInfo);
   mFlags |= mData->GetTextureFlags();
 }
 
 bool TextureClient::CopyToTextureClient(TextureClient* aTarget,
                                         const gfx::IntRect* aRect,
@@ -1752,24 +1760,28 @@ UpdateYCbCrTextureClient(TextureClient* 
     return false;
   }
 
   MappedYCbCrTextureData srcData;
   srcData.y.data = aData.mYChannel;
   srcData.y.size = aData.mYSize;
   srcData.y.stride = aData.mYStride;
   srcData.y.skip = aData.mYSkip;
+  MOZ_ASSERT(aData.mBitDepth == 8 || (aData.mBitDepth > 8 && aData.mBitDepth <= 16));
+  srcData.y.bytesPerPixel = (aData.mBitDepth > 8) ? 2 : 1;
   srcData.cb.data = aData.mCbChannel;
   srcData.cb.size = aData.mCbCrSize;
   srcData.cb.stride = aData.mCbCrStride;
   srcData.cb.skip = aData.mCbSkip;
+  srcData.cb.bytesPerPixel = (aData.mBitDepth > 8) ? 2 : 1;
   srcData.cr.data = aData.mCrChannel;
   srcData.cr.size = aData.mCbCrSize;
   srcData.cr.stride = aData.mCbCrStride;
   srcData.cr.skip = aData.mCrSkip;
+  srcData.cr.bytesPerPixel = (aData.mBitDepth > 8) ? 2 : 1;
   srcData.metadata = nullptr;
 
   if (!srcData.CopyInto(mapped)) {
     NS_WARNING("Failed to copy image data!");
     return false;
   }
 
   if (TextureRequiresLocking(aTexture->GetFlags())) {
@@ -1804,25 +1816,29 @@ MappedYCbCrChannelData::CopyInto(MappedY
     return true;
   }
 
   for (int32_t i = 0; i < size.height; ++i) {
     if (aDst.skip == 0 && skip == 0) {
       // fast-ish path
       memcpy(aDst.data + i * aDst.stride,
              data + i * stride,
-             size.width);
+             size.width * bytesPerPixel);
     } else {
       // slow path
       uint8_t* src = data + i * stride;
       uint8_t* dst = aDst.data + i * aDst.stride;
       for (int32_t j = 0; j < size.width; ++j) {
-        *dst = *src;
-        src += 1 + skip;
-        dst += 1 + aDst.skip;
+        for (uint32_t k = 0; k < bytesPerPixel; ++k) {
+          *dst = *src;
+          src += 1;
+          dst += 1;
+        }
+        src += skip;
+        dst += aDst.skip;
       }
     }
   }
   return true;
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/client/TextureClient.h
+++ b/gfx/layers/client/TextureClient.h
@@ -134,16 +134,17 @@ struct MappedTextureData
 };
 
 struct MappedYCbCrChannelData
 {
   uint8_t* data;
   gfx::IntSize size;
   int32_t stride;
   int32_t skip;
+  uint32_t bytesPerPixel;
 
   bool CopyInto(MappedYCbCrChannelData& aDst);
 };
 
 struct MappedYCbCrTextureData {
   MappedYCbCrChannelData y;
   MappedYCbCrChannelData cb;
   MappedYCbCrChannelData cr;
@@ -342,19 +343,22 @@ public:
                     BackendSelector aSelector,
                     TextureFlags aTextureFlags,
                     TextureAllocationFlags aAllocFlags);
 
   // Creates and allocates a TextureClient supporting the YCbCr format.
   static already_AddRefed<TextureClient>
   CreateForYCbCr(KnowsCompositor* aAllocator,
                  gfx::IntSize aYSize,
+                 uint32_t aYStride,
                  gfx::IntSize aCbCrSize,
+                 uint32_t aCbCrStride,
                  StereoMode aStereoMode,
                  YUVColorSpace aYUVColorSpace,
+                 uint32_t aBitDepth,
                  TextureFlags aTextureFlags);
 
   // Creates and allocates a TextureClient (can be accessed through raw
   // pointers).
   static already_AddRefed<TextureClient>
   CreateForRawBufferAccess(KnowsCompositor* aAllocator,
                            gfx::SurfaceFormat aFormat,
                            gfx::IntSize aSize,
@@ -364,16 +368,17 @@ public:
 
   // Creates and allocates a TextureClient (can beaccessed through raw
   // pointers) with a certain buffer size. It's unfortunate that we need this.
   // providing format and sizes could let us do more optimization.
   static already_AddRefed<TextureClient>
   CreateForYCbCrWithBufferSize(KnowsCompositor* aAllocator,
                                size_t aSize,
                                YUVColorSpace aYUVColorSpace,
+                               uint32_t aBitDepth,
                                TextureFlags aTextureFlags);
 
   // Creates and allocates a TextureClient of the same type.
   already_AddRefed<TextureClient>
   CreateSimilar(LayersBackend aLayersBackend = LayersBackend::LAYERS_NONE,
                 TextureFlags aFlags = TextureFlags::DEFAULT,
                 TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const;
 
--- a/gfx/layers/client/TextureClientRecycleAllocator.cpp
+++ b/gfx/layers/client/TextureClientRecycleAllocator.cpp
@@ -103,30 +103,34 @@ YCbCrTextureClientAllocationHelper::IsCo
 
   BufferTextureData* bufferData = aTextureClient->GetInternalData()->AsBufferTextureData();
   if (!bufferData ||
       aTextureClient->GetSize() != mData.mYSize ||
       bufferData->GetCbCrSize().isNothing() ||
       bufferData->GetCbCrSize().ref() != mData.mCbCrSize ||
       bufferData->GetYUVColorSpace().isNothing() ||
       bufferData->GetYUVColorSpace().ref() != mData.mYUVColorSpace ||
+      bufferData->GetBitDepth().isNothing() ||
+      bufferData->GetBitDepth().ref() != mData.mBitDepth ||
       bufferData->GetStereoMode().isNothing() ||
       bufferData->GetStereoMode().ref() != mData.mStereoMode) {
     return false;
   }
   return true;
 }
 
 already_AddRefed<TextureClient>
 YCbCrTextureClientAllocationHelper::Allocate(KnowsCompositor* aAllocator)
 {
   return TextureClient::CreateForYCbCr(aAllocator,
-                                       mData.mYSize, mData.mCbCrSize,
+                                       mData.mYSize, mData.mYStride,
+                                       mData.mCbCrSize, mData.mCbCrStride,
                                        mData.mStereoMode,
                                        mData.mYUVColorSpace,
+                                       mData.mBitDepth,
                                        mTextureFlags);
 }
 
 TextureClientRecycleAllocator::TextureClientRecycleAllocator(KnowsCompositor* aAllocator)
   : mSurfaceAllocator(aAllocator)
   , mMaxPooledSize(kMaxPooledSized)
   , mLock("TextureClientRecycleAllocatorImp.mLock")
   , mIsDestroyed(false)
--- a/gfx/layers/composite/TextureHost.cpp
+++ b/gfx/layers/composite/TextureHost.cpp
@@ -441,17 +441,17 @@ TextureSource::~TextureSource()
 }
 
 const char*
 TextureSource::Name() const
 {
   MOZ_CRASH("GFX: TextureSource without class name");
   return "TextureSource";
 }
-  
+
 BufferTextureHost::BufferTextureHost(const BufferDescriptor& aDesc,
                                      TextureFlags aFlags)
 : TextureHost(aFlags)
 , mUpdateSerial(1)
 , mLocked(false)
 , mNeedsFullUpdate(false)
 {
   mDescriptor = aDesc;
@@ -592,18 +592,18 @@ BufferTextureHost::PushResourceUpdates(w
     wr::ImageDescriptor descriptor(GetSize(),
                                    ImageDataSerializer::ComputeRGBStride(GetFormat(), GetSize().width),
                                    GetFormat());
     (aResources.*method)(aImageKeys[0], descriptor, aExtID, bufferType, 0);
   } else {
     MOZ_ASSERT(aImageKeys.length() == 3);
 
     const layers::YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
-    wr::ImageDescriptor yDescriptor(desc.ySize(), desc.ySize().width, gfx::SurfaceFormat::A8);
-    wr::ImageDescriptor cbcrDescriptor(desc.cbCrSize(), desc.cbCrSize().width, gfx::SurfaceFormat::A8);
+    wr::ImageDescriptor yDescriptor(desc.ySize(), desc.yStride(), gfx::SurfaceFormat::A8);
+    wr::ImageDescriptor cbcrDescriptor(desc.cbCrSize(), desc.cbCrStride(), gfx::SurfaceFormat::A8);
     (aResources.*method)(aImageKeys[0], yDescriptor, aExtID, bufferType, 0);
     (aResources.*method)(aImageKeys[1], cbcrDescriptor, aExtID, bufferType, 1);
     (aResources.*method)(aImageKeys[2], cbcrDescriptor, aExtID, bufferType, 2);
   }
 }
 
 void
 BufferTextureHost::PushDisplayItems(wr::DisplayListBuilder& aBuilder,
@@ -875,16 +875,26 @@ BufferTextureHost::GetYUVColorSpace() co
 {
   if (mFormat == gfx::SurfaceFormat::YUV) {
     const YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
     return desc.yUVColorSpace();
   }
   return YUVColorSpace::UNKNOWN;
 }
 
+uint32_t
+BufferTextureHost::GetBitDepth() const
+{
+  if (mFormat == gfx::SurfaceFormat::YUV) {
+    const YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
+    return desc.bitDepth();
+  }
+  return 8;
+}
+
 bool
 BufferTextureHost::UploadIfNeeded()
 {
   return MaybeUpload(!mNeedsFullUpdate ? &mMaybeUpdatedRegion : nullptr);
 }
 
 bool
 BufferTextureHost::MaybeUpload(nsIntRegion *aRegion)
@@ -981,29 +991,29 @@ BufferTextureHost::Upload(nsIntRegion *a
       MOZ_ASSERT(mFirstSource->GetNextSibling()->GetNextSibling());
       srcY = mFirstSource;
       srcU = mFirstSource->GetNextSibling()->AsDataTextureSource();
       srcV = mFirstSource->GetNextSibling()->GetNextSibling()->AsDataTextureSource();
     }
 
     RefPtr<gfx::DataSourceSurface> tempY =
       gfx::Factory::CreateWrappingDataSourceSurface(ImageDataSerializer::GetYChannel(buf, desc),
-                                                    desc.ySize().width,
+                                                    desc.yStride(),
                                                     desc.ySize(),
-                                                    gfx::SurfaceFormat::A8);
+                                                    SurfaceFormatForAlphaBitDepth(desc.bitDepth()));
     RefPtr<gfx::DataSourceSurface> tempCb =
       gfx::Factory::CreateWrappingDataSourceSurface(ImageDataSerializer::GetCbChannel(buf, desc),
-                                                    desc.cbCrSize().width,
+                                                    desc.cbCrStride(),
                                                     desc.cbCrSize(),
-                                                    gfx::SurfaceFormat::A8);
+                                                    SurfaceFormatForAlphaBitDepth(desc.bitDepth()));
     RefPtr<gfx::DataSourceSurface> tempCr =
       gfx::Factory::CreateWrappingDataSourceSurface(ImageDataSerializer::GetCrChannel(buf, desc),
-                                                    desc.cbCrSize().width,
+                                                    desc.cbCrStride(),
                                                     desc.cbCrSize(),
-                                                    gfx::SurfaceFormat::A8);
+                                                    SurfaceFormatForAlphaBitDepth(desc.bitDepth()));
     // We don't support partial updates for Y U V textures
     NS_ASSERTION(!aRegion, "Unsupported partial updates for YCbCr textures");
     if (!tempY ||
         !tempCb ||
         !tempCr ||
         !srcY->Update(tempY) ||
         !srcU->Update(tempCb) ||
         !srcV->Update(tempCr)) {
--- a/gfx/layers/composite/TextureHost.h
+++ b/gfx/layers/composite/TextureHost.h
@@ -434,16 +434,21 @@ public:
    * Return the format used for reading the texture.
    * Apple's YCBCR_422 is R8G8B8X8.
    */
   virtual gfx::SurfaceFormat GetReadFormat() const { return GetFormat(); }
 
   virtual YUVColorSpace GetYUVColorSpace() const { return YUVColorSpace::UNKNOWN; }
 
   /**
+   * Return the bit depth of the image. Used with YUV textures.
+   */
+  virtual uint32_t GetBitDepth() const { return 8; }
+
+  /**
    * Called during the transaction. The TextureSource may or may not be composited.
    *
    * Note that this is called outside of lock/unlock.
    */
   virtual void PrepareTextureSource(CompositableTextureSourceRef& aTexture) {}
 
   /**
    * Called at composition time, just before compositing the TextureSource composited.
@@ -727,16 +732,18 @@ public:
    *
    * If the shared format is YCbCr and the compositor does not support it,
    * GetFormat will be RGB32 (even though mFormat is SurfaceFormat::YUV).
    */
   virtual gfx::SurfaceFormat GetFormat() const override;
 
   virtual YUVColorSpace GetYUVColorSpace() const override;
 
+  virtual uint32_t GetBitDepth() const override;
+
   virtual gfx::IntSize GetSize() const override { return mSize; }
 
   virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override;
 
   virtual bool HasIntermediateBuffer() const override { return mHasIntermediateBuffer; }
 
   virtual BufferTextureHost* AsBufferTextureHost() override { return this; }
 
--- a/gfx/layers/ipc/LayersSurfaces.ipdlh
+++ b/gfx/layers/ipc/LayersSurfaces.ipdlh
@@ -95,22 +95,25 @@ struct SurfaceDescriptorGPUVideo {
 struct RGBDescriptor {
   IntSize size;
   SurfaceFormat format;
   bool hasIntermediateBuffer;
 };
 
 struct YCbCrDescriptor {
   IntSize ySize;
+  uint32_t yStride;
   IntSize cbCrSize;
+  uint32_t cbCrStride;
   uint32_t yOffset;
   uint32_t cbOffset;
   uint32_t crOffset;
   StereoMode stereoMode;
   YUVColorSpace yUVColorSpace;
+  uint32_t bitDepth;
   bool hasIntermediateBuffer;
 };
 
 union BufferDescriptor {
   RGBDescriptor;
   YCbCrDescriptor;
 };
 
--- a/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp
+++ b/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp
@@ -91,78 +91,48 @@ SharedPlanarYCbCrImage::CopyData(const P
   if (!UpdateYCbCrTextureClient(mTextureClient, aData)) {
     MOZ_ASSERT(false, "Failed to copy YCbCr data into the TextureClient");
     return false;
   }
   mTextureClient->MarkImmutable();
   return true;
 }
 
-// needs to be overriden because the parent class sets mBuffer which we
-// do not want to happen.
-uint8_t*
-SharedPlanarYCbCrImage::AllocateAndGetNewBuffer(uint32_t aSize)
+bool
+SharedPlanarYCbCrImage::AdoptData(const Data& aData)
 {
-  MOZ_ASSERT(!mTextureClient, "This image already has allocated data");
-  size_t size = ImageDataSerializer::ComputeYCbCrBufferSize(aSize);
-  if (!size) {
-    return nullptr;
-  }
-
-  // XXX Add YUVColorSpace handling. Use YUVColorSpace::BT601 for now.
-  mTextureClient = TextureClient::CreateForYCbCrWithBufferSize(mCompositable->GetForwarder(),
-                                                               size,
-                                                               YUVColorSpace::BT601,
-                                                               mCompositable->GetTextureFlags());
-
-  // get new buffer _without_ setting mBuffer.
-  if (!mTextureClient) {
-    return nullptr;
-  }
-
-  // update buffer size
-  mBufferSize = size;
-
-  MappedYCbCrTextureData mapped;
-  if (mTextureClient->BorrowMappedYCbCrData(mapped)) {
-    // The caller expects a pointer to the beginning of the writable part of the
-    // buffer which is where the y channel starts by default.
-    return mapped.y.data;
-  } else {
-    MOZ_CRASH("GFX: Cannot borrow mapped YCbCr data");
-  }
-}
-
-bool
-SharedPlanarYCbCrImage::AdoptData(const Data &aData)
-{
-  // AdoptData is used to update YUV plane offsets without (re)allocating
-  // memory previously allocated with AllocateAndGetNewBuffer().
-
   MOZ_ASSERT(mTextureClient, "This Image should have already allocated data");
   if (!mTextureClient) {
     return false;
   }
   mData = aData;
   mSize = aData.mPicSize;
   mOrigin = gfx::IntPoint(aData.mPicX, aData.mPicY);
 
   uint8_t *base = GetBuffer();
   uint32_t yOffset = aData.mYChannel - base;
   uint32_t cbOffset = aData.mCbChannel - base;
   uint32_t crOffset = aData.mCrChannel - base;
 
   auto fwd = mCompositable->GetForwarder();
-  bool hasIntermediateBuffer = ComputeHasIntermediateBuffer(gfx::SurfaceFormat::YUV,
-                                                            fwd->GetCompositorBackendType());
+  bool hasIntermediateBuffer = ComputeHasIntermediateBuffer(
+    gfx::SurfaceFormat::YUV, fwd->GetCompositorBackendType());
 
-  static_cast<BufferTextureData*>(mTextureClient->GetInternalData())->SetDesciptor(
-    YCbCrDescriptor(aData.mYSize, aData.mCbCrSize, yOffset, cbOffset, crOffset,
-                    aData.mStereoMode, aData.mYUVColorSpace, hasIntermediateBuffer)
-  );
+  static_cast<BufferTextureData*>(mTextureClient->GetInternalData())
+    ->SetDesciptor(YCbCrDescriptor(aData.mYSize,
+                                   aData.mYStride,
+                                   aData.mCbCrSize,
+                                   aData.mCbCrStride,
+                                   yOffset,
+                                   cbOffset,
+                                   crOffset,
+                                   aData.mStereoMode,
+                                   aData.mYUVColorSpace,
+                                   aData.mBitDepth,
+                                   hasIntermediateBuffer));
 
   return true;
 }
 
 bool
 SharedPlanarYCbCrImage::IsValid() {
   return mTextureClient && mTextureClient->IsValid();
 }
@@ -208,28 +178,30 @@ SharedPlanarYCbCrImage::Allocate(PlanarY
   mData.mCrChannel = aData.mCrChannel;
   mData.mYSize = aData.mYSize;
   mData.mCbCrSize = aData.mCbCrSize;
   mData.mPicX = aData.mPicX;
   mData.mPicY = aData.mPicY;
   mData.mPicSize = aData.mPicSize;
   mData.mStereoMode = aData.mStereoMode;
   mData.mYUVColorSpace = aData.mYUVColorSpace;
+  mData.mBitDepth = aData.mBitDepth;
   // those members are not always equal to aData's, due to potentially different
   // packing.
   mData.mYSkip = 0;
   mData.mCbSkip = 0;
   mData.mCrSkip = 0;
-  mData.mYStride = mData.mYSize.width;
-  mData.mCbCrStride = mData.mCbCrSize.width;
+  mData.mYStride = aData.mYStride;
+  mData.mCbCrStride = aData.mCbCrStride;
 
   // do not set mBuffer like in PlanarYCbCrImage because the later
   // will try to manage this memory without knowing it belongs to a
   // shmem.
-  mBufferSize = ImageDataSerializer::ComputeYCbCrBufferSize(mData.mYSize, mData.mCbCrSize);
+  mBufferSize = ImageDataSerializer::ComputeYCbCrBufferSize(
+    mData.mYSize, mData.mYStride, mData.mCbCrSize, mData.mCbCrStride);
   mSize = mData.mPicSize;
   mOrigin = gfx::IntPoint(aData.mPicX, aData.mPicY);
 
   mTextureClient->Unlock();
 
   return mBufferSize > 0;
 }
 
--- a/gfx/layers/ipc/SharedPlanarYCbCrImage.h
+++ b/gfx/layers/ipc/SharedPlanarYCbCrImage.h
@@ -30,20 +30,19 @@ protected:
   virtual ~SharedPlanarYCbCrImage();
 
 public:
   virtual TextureClient* GetTextureClient(KnowsCompositor* aForwarder) override;
   virtual uint8_t* GetBuffer() override;
 
   virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override;
   virtual bool CopyData(const PlanarYCbCrData& aData) override;
-  virtual bool AdoptData(const Data &aData) override;
+  virtual bool AdoptData(const Data& aData) override;
 
   virtual bool Allocate(PlanarYCbCrData& aData);
-  virtual uint8_t* AllocateAndGetNewBuffer(uint32_t aSize) override;
 
   virtual bool IsValid() override;
 
   virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
   {
     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   }
 
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -827,18 +827,28 @@ CompositorOGL::GetShaderConfigFor(Effect
 {
   ShaderConfigOGL config;
 
   switch(aEffect->mType) {
   case EffectTypes::SOLID_COLOR:
     config.SetRenderColor(true);
     break;
   case EffectTypes::YCBCR:
+  {
     config.SetYCbCr(true);
+    EffectYCbCr* effectYCbCr =
+      static_cast<EffectYCbCr*>(aEffect);
+    uint32_t pixelBits = (8 * BytesPerPixel(SurfaceFormatForAlphaBitDepth(effectYCbCr->mBitDepth)));
+    uint32_t paddingBits = pixelBits - effectYCbCr->mBitDepth;
+    // OpenGL expects values between [0,255], this range needs to be adjusted
+    // according to the bit depth.
+    // So we will scale the YUV values by this amount.
+    config.SetColorMultiplier(pow(2, paddingBits));
     break;
+  }
   case EffectTypes::NV12:
     config.SetNV12(true);
     config.SetTextureTarget(LOCAL_GL_TEXTURE_RECTANGLE_ARB);
     break;
   case EffectTypes::COMPONENT_ALPHA:
   {
     config.SetComponentAlpha(true);
     EffectComponentAlpha* effectComponentAlpha =
--- a/gfx/layers/opengl/OGLShaderProgram.cpp
+++ b/gfx/layers/opengl/OGLShaderProgram.cpp
@@ -110,16 +110,23 @@ ShaderConfigOGL::SetOpacity(bool aEnable
 void
 ShaderConfigOGL::SetYCbCr(bool aEnabled)
 {
   SetFeature(ENABLE_TEXTURE_YCBCR, aEnabled);
   MOZ_ASSERT(!(mFeatures & ENABLE_TEXTURE_NV12));
 }
 
 void
+ShaderConfigOGL::SetColorMultiplier(uint32_t aMultiplier)
+{
+  MOZ_ASSERT(mFeatures & ENABLE_TEXTURE_YCBCR, "Multiplier only supported with YCbCr!");
+  mMultiplier = aMultiplier;
+}
+
+void
 ShaderConfigOGL::SetNV12(bool aEnabled)
 {
   SetFeature(ENABLE_TEXTURE_NV12, aEnabled);
   MOZ_ASSERT(!(mFeatures & ENABLE_TEXTURE_YCBCR));
 }
 
 void
 ShaderConfigOGL::SetComponentAlpha(bool aEnabled)
@@ -436,21 +443,22 @@ ProgramProfileOGL::GetProfileFor(ShaderC
           fs << "  COLOR_PRECISION float cb = " << texture2D << "(uCbTexture, coord * uCbCrTexCoordMultiplier).r;" << endl;
           fs << "  COLOR_PRECISION float cr = " << texture2D << "(uCbTexture, coord * uCbCrTexCoordMultiplier).a;" << endl;
         } else {
           fs << "  COLOR_PRECISION float y = " << texture2D << "(uYTexture, coord).r;" << endl;
           fs << "  COLOR_PRECISION float cb = " << texture2D << "(uCbTexture, coord).r;" << endl;
           fs << "  COLOR_PRECISION float cr = " << texture2D << "(uCbTexture, coord).a;" << endl;
         }
       }
-
-      fs << "  y = y - 0.06275;" << endl;
-      fs << "  cb = cb - 0.50196;" << endl;
-      fs << "  cr = cr - 0.50196;" << endl;
       fs << "  vec3 yuv = vec3(y, cb, cr);" << endl;
+      if (aConfig.mMultiplier != 1) {
+        fs << "  yuv *= " << aConfig.mMultiplier << ".0;" << endl;
+      }
+      fs << "  vec3 coeff = vec3(0.06275, 0.50196, 0.50196 );" << endl;
+      fs << "  yuv -= coeff;" << endl;
       fs << "  color.rgb = uYuvColorMatrix * yuv;" << endl;
       fs << "  color.a = 1.0;" << endl;
     } else if (aConfig.mFeatures & ENABLE_TEXTURE_COMPONENT_ALPHA) {
       if (aConfig.mFeatures & ENABLE_TEXTURE_RECT) {
         fs << "  COLOR_PRECISION vec3 onBlack = " << texture2D << "(uBlackTexture, coord * uTexCoordMultiplier).rgb;" << endl;
         fs << "  COLOR_PRECISION vec3 onWhite = " << texture2D << "(uWhiteTexture, coord * uTexCoordMultiplier).rgb;" << endl;
       } else {
         fs << "  COLOR_PRECISION vec3 onBlack = " << texture2D << "(uBlackTexture, coord).rgb;" << endl;
--- a/gfx/layers/opengl/OGLShaderProgram.h
+++ b/gfx/layers/opengl/OGLShaderProgram.h
@@ -207,52 +207,61 @@ public:
     float f1;
     float f16v[16];
   } mValue;
 };
 
 class ShaderConfigOGL
 {
 public:
-  ShaderConfigOGL() :
-    mFeatures(0),
-    mCompositionOp(gfx::CompositionOp::OP_OVER)
-  {}
+  ShaderConfigOGL()
+    : mFeatures(0)
+    , mMultiplier(1)
+    , mCompositionOp(gfx::CompositionOp::OP_OVER)
+  {
+  }
 
   void SetRenderColor(bool aEnabled);
   void SetTextureTarget(GLenum aTarget);
   void SetRBSwap(bool aEnabled);
   void SetNoAlpha(bool aEnabled);
   void SetOpacity(bool aEnabled);
   void SetYCbCr(bool aEnabled);
   void SetNV12(bool aEnabled);
   void SetComponentAlpha(bool aEnabled);
   void SetColorMatrix(bool aEnabled);
   void SetBlur(bool aEnabled);
   void SetMask(bool aEnabled);
   void SetDEAA(bool aEnabled);
   void SetCompositionOp(gfx::CompositionOp aOp);
   void SetNoPremultipliedAlpha();
   void SetDynamicGeometry(bool aEnabled);
+  void SetColorMultiplier(uint32_t aMultiplier);
 
-  bool operator< (const ShaderConfigOGL& other) const {
+  bool operator< (const ShaderConfigOGL& other) const
+  {
     return mFeatures < other.mFeatures ||
            (mFeatures == other.mFeatures &&
-            (int)mCompositionOp < (int)other.mCompositionOp);
+            (int)mCompositionOp < (int)other.mCompositionOp) ||
+           (mFeatures == other.mFeatures &&
+            (int)mCompositionOp == (int)other.mCompositionOp &&
+            mMultiplier < other.mMultiplier);
   }
 
 public:
-  void SetFeature(int aBitmask, bool aState) {
+  void SetFeature(int aBitmask, bool aState)
+  {
     if (aState)
       mFeatures |= aBitmask;
     else
       mFeatures &= (~aBitmask);
   }
 
   int mFeatures;
+  uint32_t mMultiplier;
   gfx::CompositionOp mCompositionOp;
 };
 
 static inline ShaderConfigOGL
 ShaderConfigFromTargetAndFormat(GLenum aTarget,
                                 gfx::SurfaceFormat aFormat)
 {
   ShaderConfigOGL config;
@@ -394,17 +403,17 @@ public:
   void SetTextureTransform(const gfx::Matrix4x4& aMatrix) {
     SetMatrixUniform(KnownUniform::TextureTransform, aMatrix);
   }
 
   void SetTextureRects(const gfx::Rect* aRects) {
     float vals[16] = { aRects[0].x, aRects[0].y, aRects[0].Width(), aRects[0].Height(),
                        aRects[1].x, aRects[1].y, aRects[1].Width(), aRects[1].Height(),
                        aRects[2].x, aRects[2].y, aRects[2].Width(), aRects[2].Height(),
-                       aRects[3].x, aRects[3].y, aRects[3].Width(), aRects[3].Height() }; 
+                       aRects[3].x, aRects[3].y, aRects[3].Width(), aRects[3].Height() };
     SetUniform(KnownUniform::TextureRects, 16, vals);
   }
 
   void SetRenderOffset(const nsIntPoint& aOffset) {
     float vals[4] = { float(aOffset.x), float(aOffset.y) };
     SetUniform(KnownUniform::RenderTargetOffset, 2, vals);
   }
 
--- a/gfx/tests/gtest/TestTextures.cpp
+++ b/gfx/tests/gtest/TestTextures.cpp
@@ -257,16 +257,17 @@ TEST(Layers, TextureYCbCrSerialization) 
   clientData.mCrChannel = crSurface->Data();
   clientData.mYSize = ySurface->GetSize();
   clientData.mPicSize = ySurface->GetSize();
   clientData.mCbCrSize = cbSurface->GetSize();
   clientData.mYStride = ySurface->Stride();
   clientData.mCbCrStride = cbSurface->Stride();
   clientData.mStereoMode = StereoMode::MONO;
   clientData.mYUVColorSpace = YUVColorSpace::BT601;
+  clientData.mBitDepth = 8;
   clientData.mYSkip = 0;
   clientData.mCbSkip = 0;
   clientData.mCrSkip = 0;
   clientData.mCrSkip = 0;
   clientData.mPicX = 0;
   clientData.mPicX = 0;
 
   uint32_t namespaceId = 1;
@@ -285,16 +286,16 @@ TEST(Layers, TextureYCbCrSerialization) 
     retry--;
   }
 
   // Skip this testing if IPDL connection is not ready
   if (!retry && !imageBridge->IPCOpen()) {
     return;
   }
 
-  RefPtr<TextureClient> client = TextureClient::CreateForYCbCr(imageBridge, clientData.mYSize, clientData.mCbCrSize,
+  RefPtr<TextureClient> client = TextureClient::CreateForYCbCr(imageBridge, clientData.mYSize, clientData.mYStride, clientData.mCbCrSize, clientData.mCbCrStride,
                                                                StereoMode::MONO, YUVColorSpace::BT601,
-                                                               TextureFlags::DEALLOCATE_CLIENT);
+                                                               8, TextureFlags::DEALLOCATE_CLIENT);
 
   TestTextureClientYCbCr(client, clientData);
 
   // XXX - Test more texture client types.
 }
--- a/gfx/tests/gtest/TextureHelper.h
+++ b/gfx/tests/gtest/TextureHelper.h
@@ -57,19 +57,24 @@ CreateYCbCrTextureClientWithBackend(Laye
   clientData.mCbSkip = 0;
   clientData.mCrSkip = 0;
   clientData.mCrSkip = 0;
   clientData.mPicX = 0;
   clientData.mPicX = 0;
 
   // Create YCbCrTexture for basice backend.
   if (aLayersBackend == LayersBackend::LAYERS_BASIC) {
-    return TextureClient::CreateForYCbCr(nullptr, clientData.mYSize,
-                                         clientData.mCbCrSize, StereoMode::MONO,
+    return TextureClient::CreateForYCbCr(nullptr,
+                                         clientData.mYSize,
+                                         clientData.mYStride,
+                                         clientData.mCbCrSize,
+                                         clientData.mCbCrStride,
+                                         StereoMode::MONO,
                                          YUVColorSpace::BT601,
+                                         8,
                                          TextureFlags::DEALLOCATE_CLIENT);
   }
 
 #ifdef XP_WIN
   RefPtr<ID3D11Device> device = DeviceManagerDx::Get()->GetContentDevice();
 
   if (device && aLayersBackend == LayersBackend::LAYERS_D3D11) {
     // Create YCbCrD3D11TextureData
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -1918,24 +1918,26 @@ gfxFont::DrawGlyphs(const gfxShapedText 
             if (glyphCount > 0) {
                 const gfxShapedText::DetailedGlyph *details =
                     aShapedText->GetDetailedGlyphs(aOffset + i);
                 NS_ASSERTION(details, "detailedGlyph should not be missing!");
                 for (uint32_t j = 0; j < glyphCount; ++j, ++details) {
                     double advance = details->mAdvance;
 
                     if (glyphData->IsMissing()) {
-                        if (auto* textDrawer = aRunParams.context->GetTextDrawer()) {
-                            textDrawer->FoundUnsupportedFeature();
-                            return false;
-                        }
                         // Default-ignorable chars will have zero advance width;
                         // we don't have to draw the hexbox for them.
                         if (aRunParams.drawMode != DrawMode::GLYPH_PATH &&
                             advance > 0) {
+
+                            if (auto* textDrawer = aRunParams.context->GetTextDrawer()) {
+                                textDrawer->FoundUnsupportedFeature();
+                                return false;
+                            }
+
                             double glyphX = aPt->x;
                             double glyphY = aPt->y;
                             if (aRunParams.isRTL) {
                                 if (aFontParams.isVerticalFont) {
                                     glyphY -= advance;
                                 } else {
                                     glyphX -= advance;
                                 }
--- a/gfx/webrender/res/ps_angle_gradient.glsl
+++ b/gfx/webrender/res/ps_angle_gradient.glsl
@@ -38,27 +38,42 @@ void main(void) {
 
     vTileSize = gradient.tile_size_repeat.xy;
     vTileRepeat = gradient.tile_size_repeat.zw;
 
     vGradientAddress = prim.specific_prim_address + VECS_PER_GRADIENT;
 
     // Whether to repeat the gradient instead of clamping.
     vGradientRepeat = float(int(gradient.extend_mode.x) != EXTEND_MODE_CLAMP);
+
+    write_clip(vi.screen_pos, prim.clip_area);
 }
 #endif
 
 #ifdef WR_FRAGMENT_SHADER
 void main(void) {
     vec2 pos = mod(vPos, vTileRepeat);
 
     if (pos.x >= vTileSize.x ||
         pos.y >= vTileSize.y) {
         discard;
     }
 
     float offset = dot(pos - vStartPoint, vScaledDir);
 
-    oFragColor = sample_gradient(vGradientAddress,
+    vec4 color = sample_gradient(vGradientAddress,
                                  offset,
                                  vGradientRepeat);
+
+    // Un-premultiply the color from sampling the gradient.
+    if (color.a > 0.0) {
+        color.rgb /= color.a;
+
+        // Apply the clip mask
+        color.a = min(color.a, do_clip());
+
+        // Pre-multiply the result.
+        color.rgb *= color.a;
+    }
+
+    oFragColor = color;
 }
 #endif
--- a/gfx/webrender/res/ps_image.glsl
+++ b/gfx/webrender/res/ps_image.glsl
@@ -86,28 +86,35 @@ void main(void) {
 #ifdef WR_FRAGMENT_SHADER
 void main(void) {
 #ifdef WR_FEATURE_TRANSFORM
     float alpha = 0.0;
     vec2 pos = init_transform_fs(vLocalPos, alpha);
 
     // We clamp the texture coordinate calculation here to the local rectangle boundaries,
     // which makes the edge of the texture stretch instead of repeat.
+    vec2 upper_bound_mask = step(vLocalRect.zw, pos);
     vec2 relative_pos_in_rect = clamp(pos, vLocalRect.xy, vLocalRect.zw) - vLocalRect.xy;
 #else
     float alpha = 1.0;
     vec2 relative_pos_in_rect = vLocalPos;
+    vec2 upper_bound_mask = vec2(0.0);
 #endif
 
     alpha = min(alpha, do_clip());
 
     // We calculate the particular tile this fragment belongs to, taking into
     // account the spacing in between tiles. We only paint if our fragment does
     // not fall into that spacing.
-    vec2 position_in_tile = mod(relative_pos_in_rect, vStretchSize + vTileSpacing);
+    // If the pixel is at the local rectangle upper bound, we force the current
+    // tile upper bound in order to avoid wrapping.
+    vec2 position_in_tile = mix(
+        mod(relative_pos_in_rect, vStretchSize + vTileSpacing),
+        vStretchSize,
+        upper_bound_mask);
     vec2 st = vTextureOffset + ((position_in_tile / vStretchSize) * vTextureSize);
     st = clamp(st, vStRect.xy, vStRect.zw);
 
     alpha = alpha * float(all(bvec2(step(position_in_tile, vStretchSize))));
 
     oFragColor = vec4(alpha) * TEX_SAMPLE(sColor0, vec3(st, vLayer));
 }
 #endif
--- a/gfx/webrender/src/frame.rs
+++ b/gfx/webrender/src/frame.rs
@@ -795,25 +795,25 @@ impl Frame {
             SpecificDisplayItem::PopNestedDisplayList => context.pop_nested_display_list_ids(),
 
             // Do nothing; these are dummy items for the display list parser
             SpecificDisplayItem::SetGradientStops => {}
 
             SpecificDisplayItem::PopStackingContext => {
                 unreachable!("Should have returned in parent method.")
             }
-            SpecificDisplayItem::PushTextShadow(shadow) => {
+            SpecificDisplayItem::PushShadow(shadow) => {
                 let mut prim_info = prim_info.clone();
                 prim_info.rect = LayerRect::zero();
                 context
                     .builder
-                    .push_text_shadow(shadow, clip_and_scroll, &prim_info);
+                    .push_shadow(shadow, clip_and_scroll, &prim_info);
             }
-            SpecificDisplayItem::PopTextShadow => {
-                context.builder.pop_text_shadow();
+            SpecificDisplayItem::PopShadow => {
+                context.builder.pop_shadow();
             }
         }
         None
     }
 
     fn flatten_root<'a>(
         &mut self,
         traversal: &mut BuiltDisplayListIter<'a>,
--- a/gfx/webrender/src/frame_builder.rs
+++ b/gfx/webrender/src/frame_builder.rs
@@ -5,33 +5,33 @@
 use api::{BorderDetails, BorderDisplayItem, BorderRadius, BoxShadowClipMode, BuiltDisplayList};
 use api::{ClipAndScrollInfo, ClipId, ColorF};
 use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceUintRect, DeviceUintSize};
 use api::{ExtendMode, FIND_ALL, FilterOp, FontInstance, FontRenderMode};
 use api::{GlyphInstance, GlyphOptions, GradientStop, HitTestFlags, HitTestItem, HitTestResult};
 use api::{ImageKey, ImageRendering, ItemRange, ItemTag, LayerPoint, LayerPrimitiveInfo, LayerRect};
 use api::{LayerSize, LayerToScrollTransform, LayerVector2D, LayoutVector2D, LineOrientation};
 use api::{LineStyle, LocalClip, POINT_RELATIVE_TO_PIPELINE_VIEWPORT, PipelineId, RepeatMode};
-use api::{ScrollSensitivity, SubpixelDirection, TextShadow, TileOffset, TransformStyle};
+use api::{ScrollSensitivity, SubpixelDirection, Shadow, TileOffset, TransformStyle};
 use api::{WorldPixel, WorldPoint, YuvColorSpace, YuvData, device_length};
 use app_units::Au;
 use border::ImageBorderSegment;
 use clip::{ClipMode, ClipRegion, ClipSource, ClipSources, ClipStore, Contains};
 use clip_scroll_node::{ClipInfo, ClipScrollNode, NodeType};
 use clip_scroll_tree::ClipScrollTree;
 use euclid::{SideOffsets2D, vec2, vec3};
 use frame::FrameId;
 use gpu_cache::GpuCache;
 use internal_types::{FastHashMap, FastHashSet, HardwareCompositeOp};
 use plane_split::{BspSplitter, Polygon, Splitter};
 use prim_store::{BoxShadowPrimitiveCpu, TexelRect, YuvImagePrimitiveCpu};
 use prim_store::{GradientPrimitiveCpu, ImagePrimitiveCpu, LinePrimitive, PrimitiveKind};
 use prim_store::{PrimitiveContainer, PrimitiveIndex};
 use prim_store::{PrimitiveStore, RadialGradientPrimitiveCpu};
-use prim_store::{RectanglePrimitive, TextRunPrimitiveCpu, TextShadowPrimitiveCpu};
+use prim_store::{RectanglePrimitive, TextRunPrimitiveCpu, ShadowPrimitiveCpu};
 use profiler::{FrameProfileCounters, GpuCacheProfileCounters, TextureCacheProfileCounters};
 use render_task::{AlphaRenderItem, ClipWorkItem, RenderTask};
 use render_task::{RenderTaskId, RenderTaskLocation, RenderTaskTree};
 use resource_cache::ResourceCache;
 use scene::ScenePipeline;
 use std::{mem, usize, f32, i32};
 use tiling::{ClipScrollGroup, ClipScrollGroupIndex, CompositeOps, Frame};
 use tiling::{ContextIsolation, StackingContextIndex};
@@ -95,17 +95,17 @@ pub struct FrameBuilder {
 
     stacking_context_store: Vec<StackingContext>,
     clip_scroll_group_store: Vec<ClipScrollGroup>,
     // Note: value here is meant to be `ClipScrollGroupIndex`,
     // but we already have `ClipAndScrollInfo` in the key
     clip_scroll_group_indices: FastHashMap<ClipAndScrollInfo, usize>,
     packed_layers: Vec<PackedLayer>,
 
-    // A stack of the current text-shadow primitives.
+    // A stack of the current shadow primitives.
     shadow_prim_stack: Vec<PrimitiveIndex>,
 
     scrollbar_prims: Vec<ScrollbarPrimitive>,
 
     /// A stack of scroll nodes used during display list processing to properly
     /// parent new scroll nodes.
     reference_frame_stack: Vec<ClipId>,
 
@@ -573,53 +573,53 @@ impl FrameBuilder {
 
         clip_scroll_tree.add_node(node, new_node_id);
     }
 
     pub fn pop_reference_frame(&mut self) {
         self.reference_frame_stack.pop();
     }
 
-    pub fn push_text_shadow(
+    pub fn push_shadow(
         &mut self,
-        shadow: TextShadow,
+        shadow: Shadow,
         clip_and_scroll: ClipAndScrollInfo,
         info: &LayerPrimitiveInfo,
     ) {
-        let prim = TextShadowPrimitiveCpu {
+        let prim = ShadowPrimitiveCpu {
             shadow,
             primitives: Vec::new(),
             render_task_id: None,
         };
 
-        // Create an empty text-shadow primitive. Insert it into
+        // Create an empty shadow primitive. Insert it into
         // the draw lists immediately so that it will be drawn
         // before any visual text elements that are added as
-        // part of this text-shadow context.
+        // part of this shadow context.
         let prim_index = self.add_primitive(
             clip_and_scroll,
             info,
             Vec::new(),
-            PrimitiveContainer::TextShadow(prim),
+            PrimitiveContainer::Shadow(prim),
         );
 
         self.shadow_prim_stack.push(prim_index);
     }
 
-    pub fn pop_text_shadow(&mut self) {
+    pub fn pop_shadow(&mut self) {
         let prim_index = self.shadow_prim_stack
             .pop()
             .expect("invalid shadow push/pop count");
 
         // By now, the local rect of the text shadow has been calculated. It
         // is calculated as the items in the shadow are added. It's now
         // safe to offset the local rect by the offset of the shadow, which
         // is then used when blitting the shadow to the final location.
         let metadata = &mut self.prim_store.cpu_metadata[prim_index.0];
-        let prim = &self.prim_store.cpu_text_shadows[metadata.cpu_prim_index.0];
+        let prim = &self.prim_store.cpu_shadows[metadata.cpu_prim_index.0];
 
         metadata.local_rect = metadata.local_rect.translate(&prim.shadow.offset);
     }
 
     pub fn add_solid_rectangle(
         &mut self,
         clip_and_scroll: ClipAndScrollInfo,
         info: &LayerPrimitiveInfo,
@@ -678,25 +678,25 @@ impl FrameBuilder {
         };
 
         let line = LinePrimitive {
             color: *color,
             style: style,
             orientation: orientation,
         };
 
-        let mut fast_text_shadow_prims = Vec::new();
+        let mut fast_shadow_prims = Vec::new();
         for shadow_prim_index in &self.shadow_prim_stack {
             let shadow_metadata = &self.prim_store.cpu_metadata[shadow_prim_index.0];
-            let shadow_prim = &self.prim_store.cpu_text_shadows[shadow_metadata.cpu_prim_index.0];
+            let shadow_prim = &self.prim_store.cpu_shadows[shadow_metadata.cpu_prim_index.0];
             if shadow_prim.shadow.blur_radius == 0.0 {
-                fast_text_shadow_prims.push(shadow_prim.shadow);
+                fast_shadow_prims.push(shadow_prim.shadow);
             }
         }
-        for shadow in fast_text_shadow_prims {
+        for shadow in fast_shadow_prims {
             let mut line = line.clone();
             line.color = shadow.color;
             let mut info = info.clone();
             info.rect = new_rect.translate(&shadow.offset);
             self.add_primitive(
                 clip_and_scroll,
                 &info,
                 Vec::new(),
@@ -715,19 +715,19 @@ impl FrameBuilder {
 
         if color.a > 0.0 {
             self.add_primitive_to_hit_testing_list(&info, clip_and_scroll);
             self.add_primitive_to_draw_list(prim_index, clip_and_scroll);
         }
 
         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::TextShadow);
+            debug_assert_eq!(shadow_metadata.prim_kind, PrimitiveKind::Shadow);
             let shadow_prim =
-                &mut self.prim_store.cpu_text_shadows[shadow_metadata.cpu_prim_index.0];
+                &mut self.prim_store.cpu_shadows[shadow_metadata.cpu_prim_index.0];
 
             // Only run real blurs here (fast path zero blurs are handled above).
             if shadow_prim.shadow.blur_radius > 0.0 {
                 let shadow_rect = new_rect.inflate(
                     shadow_prim.shadow.blur_radius,
                     shadow_prim.shadow.blur_radius,
                 );
                 shadow_metadata.local_rect = shadow_metadata.local_rect.union(&shadow_rect);
@@ -1203,37 +1203,37 @@ impl FrameBuilder {
         // Text shadows that have a blur radius of 0 need to be rendered as normal
         // text elements to get pixel perfect results for reftests. It's also a big
         // performance win to avoid blurs and render target allocations where
         // possible. For any text shadows that have zero blur, create a normal text
         // primitive with the shadow's color and offset. These need to be added
         // *before* the visual text primitive in order to get the correct paint
         // 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_text_shadow_prims = Vec::new();
+        let mut fast_shadow_prims = Vec::new();
         for shadow_prim_index in &self.shadow_prim_stack {
             let shadow_metadata = &self.prim_store.cpu_metadata[shadow_prim_index.0];
-            let shadow_prim = &self.prim_store.cpu_text_shadows[shadow_metadata.cpu_prim_index.0];
+            let shadow_prim = &self.prim_store.cpu_shadows[shadow_metadata.cpu_prim_index.0];
             if shadow_prim.shadow.blur_radius == 0.0 {
                 let mut text_prim = prim.clone();
                 if font.render_mode != FontRenderMode::Bitmap {
                     text_prim.font.color = shadow_prim.shadow.color.into();
                 }
                 // If we have translucent text, we need to ensure it won't go
                 // through the subpixel blend mode, which doesn't work with
                 // traditional alpha blending.
                 if shadow_prim.shadow.color.a != 1.0 {
                     text_prim.font.render_mode = text_prim.font.render_mode.limit_by(FontRenderMode::Alpha);
                 }
                 text_prim.color = shadow_prim.shadow.color;
                 text_prim.offset += shadow_prim.shadow.offset;
-                fast_text_shadow_prims.push(text_prim);
+                fast_shadow_prims.push(text_prim);
             }
         }
-        for text_prim in fast_text_shadow_prims {
+        for text_prim in fast_shadow_prims {
             let rect = info.rect;
             let mut info = info.clone();
             info.rect = rect.translate(&text_prim.offset);
             self.add_primitive(
                 clip_and_scroll,
                 &info,
                 Vec::new(),
                 PrimitiveContainer::TextRun(text_prim),
@@ -1253,25 +1253,25 @@ impl FrameBuilder {
         if color.a > 0.0 {
             self.add_primitive_to_hit_testing_list(info, clip_and_scroll);
             self.add_primitive_to_draw_list(prim_index, clip_and_scroll);
         }
 
         // Now add this primitive index to all the currently active text shadow
         // primitives. Although we're adding the indices *after* the visual
         // primitive here, they will still draw before the visual text, since
-        // the text-shadow primitive itself has been added to the draw cmd
-        // list *before* the visual element, during push_text_shadow. We need
+        // 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::TextShadow);
+            debug_assert_eq!(shadow_metadata.prim_kind, PrimitiveKind::Shadow);
             let shadow_prim =
-                &mut self.prim_store.cpu_text_shadows[shadow_metadata.cpu_prim_index.0];
+                &mut self.prim_store.cpu_shadows[shadow_metadata.cpu_prim_index.0];
 
             // Only run real blurs here (fast path zero blurs are handled above).
             if shadow_prim.shadow.blur_radius > 0.0 {
                 let shadow_rect = rect.inflate(
                     shadow_prim.shadow.blur_radius,
                     shadow_prim.shadow.blur_radius,
                 );
                 shadow_metadata.local_rect = shadow_metadata.local_rect.union(&shadow_rect);
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{BorderRadius, BuiltDisplayList, ColorF, ComplexClipRegion, DeviceIntRect, DeviceIntSize};
 use api::{DevicePoint, ExtendMode, FontInstance, FontRenderMode, GlyphInstance, GlyphKey};
 use api::{GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag, LayerPoint, LayerRect};
-use api::{LayerSize, LayerVector2D, LineOrientation, LineStyle, TextShadow};
+use api::{LayerSize, LayerVector2D, LineOrientation, LineStyle, Shadow};
 use api::{TileOffset, YuvColorSpace, YuvFormat, device_length};
 use app_units::Au;
 use border::BorderCornerInstance;
 use clip::{ClipMode, ClipSourcesHandle, ClipStore, Geometry};
 use euclid::Size2D;
 use frame_builder::PrimitiveContext;
 use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest,
                 ToGpuBlocks};
@@ -105,17 +105,17 @@ pub enum PrimitiveKind {
     TextRun,
     Image,
     YuvImage,
     Border,
     AlignedGradient,
     AngleGradient,
     RadialGradient,
     BoxShadow,
-    TextShadow,
+    Shadow,
     Line,
 }
 
 impl GpuCacheHandle {
     pub fn as_int(&self, gpu_cache: &GpuCache) -> i32 {
         gpu_cache.get_address(self).as_int()
     }
 }
@@ -510,18 +510,18 @@ impl RadialGradientPrimitiveCpu {
         request.extend_from_slice(&self.gpu_blocks);
 
         let gradient_builder = GradientGpuBlockBuilder::new(self.stops_range, display_list);
         gradient_builder.build(false, &mut request);
     }
 }
 
 #[derive(Debug)]
-pub struct TextShadowPrimitiveCpu {
-    pub shadow: TextShadow,
+pub struct ShadowPrimitiveCpu {
+    pub shadow: Shadow,
     pub primitives: Vec<PrimitiveIndex>,
     pub render_task_id: Option<RenderTaskId>,
 }
 
 #[derive(Debug, Clone)]
 pub struct TextRunPrimitiveCpu {
     pub font: FontInstance,
     pub offset: LayerVector2D,
@@ -802,58 +802,58 @@ pub enum PrimitiveContainer {
     TextRun(TextRunPrimitiveCpu),
     Image(ImagePrimitiveCpu),
     YuvImage(YuvImagePrimitiveCpu),
     Border(BorderPrimitiveCpu),
     AlignedGradient(GradientPrimitiveCpu),
     AngleGradient(GradientPrimitiveCpu),
     RadialGradient(RadialGradientPrimitiveCpu),
     BoxShadow(BoxShadowPrimitiveCpu),
-    TextShadow(TextShadowPrimitiveCpu),
+    Shadow(ShadowPrimitiveCpu),
     Line(LinePrimitive),
 }
 
 pub struct PrimitiveStore {
     /// CPU side information only.
     pub cpu_rectangles: Vec<RectanglePrimitive>,
     pub cpu_text_runs: Vec<TextRunPrimitiveCpu>,
-    pub cpu_text_shadows: Vec<TextShadowPrimitiveCpu>,
+    pub cpu_shadows: Vec<ShadowPrimitiveCpu>,
     pub cpu_images: Vec<ImagePrimitiveCpu>,
     pub cpu_yuv_images: Vec<YuvImagePrimitiveCpu>,
     pub cpu_gradients: Vec<GradientPrimitiveCpu>,
     pub cpu_radial_gradients: Vec<RadialGradientPrimitiveCpu>,
     pub cpu_metadata: Vec<PrimitiveMetadata>,
     pub cpu_borders: Vec<BorderPrimitiveCpu>,
     pub cpu_box_shadows: Vec<BoxShadowPrimitiveCpu>,
     pub cpu_lines: Vec<LinePrimitive>,
 }
 
 impl PrimitiveStore {
     pub fn new() -> PrimitiveStore {
         PrimitiveStore {
             cpu_metadata: Vec::new(),
             cpu_rectangles: Vec::new(),
             cpu_text_runs: Vec::new(),
-            cpu_text_shadows: Vec::new(),
+            cpu_shadows: Vec::new(),
             cpu_images: Vec::new(),
             cpu_yuv_images: Vec::new(),
             cpu_gradients: Vec::new(),
             cpu_radial_gradients: Vec::new(),
             cpu_borders: Vec::new(),
             cpu_box_shadows: Vec::new(),
             cpu_lines: Vec::new(),
         }
     }
 
     pub fn recycle(self) -> Self {
         PrimitiveStore {
             cpu_metadata: recycle_vec(self.cpu_metadata),
             cpu_rectangles: recycle_vec(self.cpu_rectangles),
             cpu_text_runs: recycle_vec(self.cpu_text_runs),
-            cpu_text_shadows: recycle_vec(self.cpu_text_shadows),
+            cpu_shadows: recycle_vec(self.cpu_shadows),
             cpu_images: recycle_vec(self.cpu_images),
             cpu_yuv_images: recycle_vec(self.cpu_yuv_images),
             cpu_gradients: recycle_vec(self.cpu_gradients),
             cpu_radial_gradients: recycle_vec(self.cpu_radial_gradients),
             cpu_borders: recycle_vec(self.cpu_borders),
             cpu_box_shadows: recycle_vec(self.cpu_box_shadows),
             cpu_lines: recycle_vec(self.cpu_lines),
         }
@@ -915,25 +915,25 @@ impl PrimitiveStore {
                     prim_kind: PrimitiveKind::TextRun,
                     cpu_prim_index: SpecificPrimitiveIndex(self.cpu_text_runs.len()),
                     ..base_metadata
                 };
 
                 self.cpu_text_runs.push(text_cpu);
                 metadata
             }
-            PrimitiveContainer::TextShadow(text_shadow) => {
+            PrimitiveContainer::Shadow(shadow) => {
                 let metadata = PrimitiveMetadata {
                     opacity: PrimitiveOpacity::translucent(),
-                    prim_kind: PrimitiveKind::TextShadow,
-                    cpu_prim_index: SpecificPrimitiveIndex(self.cpu_text_shadows.len()),
+                    prim_kind: PrimitiveKind::Shadow,
+                    cpu_prim_index: SpecificPrimitiveIndex(self.cpu_shadows.len()),
                     ..base_metadata
                 };
 
-                self.cpu_text_shadows.push(text_shadow);
+                self.cpu_shadows.push(shadow);
                 metadata
             }
             PrimitiveContainer::Image(image_cpu) => {
                 let metadata = PrimitiveMetadata {
                     opacity: PrimitiveOpacity::translucent(),
                     prim_kind: PrimitiveKind::Image,
                     cpu_prim_index: SpecificPrimitiveIndex(self.cpu_images.len()),
                     ..base_metadata
@@ -1030,19 +1030,19 @@ impl PrimitiveStore {
         // Add any dynamic render tasks needed to render this primitive
         let metadata = &self.cpu_metadata[prim_index.0];
 
         let render_task_id = match metadata.prim_kind {
             PrimitiveKind::BoxShadow => {
                 let box_shadow = &self.cpu_box_shadows[metadata.cpu_prim_index.0];
                 box_shadow.render_task_id
             }
-            PrimitiveKind::TextShadow => {
-                let text_shadow = &self.cpu_text_shadows[metadata.cpu_prim_index.0];
-                text_shadow.render_task_id
+            PrimitiveKind::Shadow => {
+                let shadow = &self.cpu_shadows[metadata.cpu_prim_index.0];
+                shadow.render_task_id
             }
             PrimitiveKind::Rectangle |
             PrimitiveKind::TextRun |
             PrimitiveKind::Image |
             PrimitiveKind::AlignedGradient |
             PrimitiveKind::YuvImage |
             PrimitiveKind::Border |
             PrimitiveKind::AngleGradient |
@@ -1109,20 +1109,20 @@ impl PrimitiveStore {
                     cache_key,
                     cache_size,
                     prim_index
                 );
 
                 // ignore the new task if we are in a dependency context
                 box_shadow.render_task_id = render_tasks.map(|rt| rt.add(render_task));
             }
-            PrimitiveKind::TextShadow => {
-                let shadow = &mut self.cpu_text_shadows[metadata.cpu_prim_index.0];
+            PrimitiveKind::Shadow => {
+                let shadow = &mut self.cpu_shadows[metadata.cpu_prim_index.0];
 
-                // This is a text-shadow element. Create a render task that will
+                // This is a shadow element. Create a render task that will
                 // render the text run to a target, and then apply a gaussian
                 // blur to that text run in order to build the actual primitive
                 // which will be blitted to the framebuffer.
                 let cache_width =
                     (metadata.local_rect.size.width * prim_context.device_pixel_ratio).ceil() as i32;
                 let cache_height =
                     (metadata.local_rect.size.height * prim_context.device_pixel_ratio).ceil() as i32;
                 let cache_size = DeviceIntSize::new(cache_width, cache_height);
@@ -1229,18 +1229,18 @@ impl PrimitiveStore {
                 PrimitiveKind::RadialGradient => {
                     let gradient = &self.cpu_radial_gradients[metadata.cpu_prim_index.0];
                     gradient.build_gpu_blocks_for_angle_radial(prim_context.display_list, request);
                 }
                 PrimitiveKind::TextRun => {
                     let text = &self.cpu_text_runs[metadata.cpu_prim_index.0];
                     text.write_gpu_blocks(&mut request);
                 }
-                PrimitiveKind::TextShadow => {
-                    let prim = &self.cpu_text_shadows[metadata.cpu_prim_index.0];
+                PrimitiveKind::Shadow => {
+                    let prim = &self.cpu_shadows[metadata.cpu_prim_index.0];
                     request.push(prim.shadow.color);
                     request.push([
                         prim.shadow.offset.x,
                         prim.shadow.offset.y,
                         prim.shadow.blur_radius,
                         0.0,
                     ]);
                 }
@@ -1369,18 +1369,18 @@ impl PrimitiveStore {
                 Some(device_rect) => Geometry {
                     local_rect,
                     device_rect,
                 },
                 None => return None,
             };
 
             let dependencies = match metadata.prim_kind {
-                PrimitiveKind::TextShadow =>
-                    self.cpu_text_shadows[metadata.cpu_prim_index.0].primitives.clone(),
+                PrimitiveKind::Shadow =>
+                    self.cpu_shadows[metadata.cpu_prim_index.0].primitives.clone(),
                 _ => Vec::new(),
             };
             (geometry, dependencies)
         };
 
         // Recurse into any sub primitives and prepare them for rendering first.
         // TODO(gw): This code is a bit hacky to work around the borrow checker.
         //           Specifically, the clone() below on the primitive list for
--- a/gfx/webrender/src/render_backend.rs
+++ b/gfx/webrender/src/render_backend.rs
@@ -711,13 +711,13 @@ impl ToDebugString for SpecificDisplayIt
             SpecificDisplayItem::Iframe(..) => String::from("iframe"),
             SpecificDisplayItem::Clip(..) => String::from("clip"),
             SpecificDisplayItem::ScrollFrame(..) => String::from("scroll_frame"),
             SpecificDisplayItem::StickyFrame(..) => String::from("sticky_frame"),
             SpecificDisplayItem::PushNestedDisplayList => String::from("push_nested_display_list"),
             SpecificDisplayItem::PopNestedDisplayList => String::from("pop_nested_display_list"),
             SpecificDisplayItem::SetGradientStops => String::from("set_gradient_stops"),
             SpecificDisplayItem::PopStackingContext => String::from("pop_stacking_context"),
-            SpecificDisplayItem::PushTextShadow(..) => String::from("push_text_shadow"),
-            SpecificDisplayItem::PopTextShadow => String::from("pop_text_shadow"),
+            SpecificDisplayItem::PushShadow(..) => String::from("push_shadow"),
+            SpecificDisplayItem::PopShadow => String::from("pop_shadow"),
         }
     }
 }
--- a/gfx/webrender/src/renderer.rs
+++ b/gfx/webrender/src/renderer.rs
@@ -2654,17 +2654,17 @@ impl Renderer {
                     VertexArrayKind::Blur,
                     &BatchTextures::no_texture(),
                 );
             }
         }
 
         // Draw any textrun caches for this target. For now, this
         // is only used to cache text runs that are to be blurred
-        // for text-shadow support. In the future it may be worth
+        // 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();
 
             let _gm = self.gpu_profile.add_marker(GPU_TAG_CACHE_TEXT_RUN);
--- a/gfx/webrender/src/tiling.rs
+++ b/gfx/webrender/src/tiling.rs
@@ -59,17 +59,17 @@ impl AlphaBatchHelpers for PrimitiveStor
                     FontRenderMode::Mono |
                     FontRenderMode::Bitmap => BlendMode::Alpha,
                 }
             }
             PrimitiveKind::Image |
             PrimitiveKind::AlignedGradient |
             PrimitiveKind::AngleGradient |
             PrimitiveKind::RadialGradient |
-            PrimitiveKind::TextShadow => if needs_blending {
+            PrimitiveKind::Shadow => if needs_blending {
                 BlendMode::PremultipliedAlpha
             } else {
                 BlendMode::None
             },
             _ => if needs_blending {
                 BlendMode::Alpha
             } else {
                 BlendMode::None
@@ -536,20 +536,20 @@ impl AlphaRenderItem {
                                         glyph.index_in_text_run,
                                         glyph.uv_rect_address.as_int(),
                                         0,
                                     ));
                                 }
                             },
                         );
                     }
-                    PrimitiveKind::TextShadow => {
-                        let text_shadow =
-                            &ctx.prim_store.cpu_text_shadows[prim_metadata.cpu_prim_index.0];
-                        let cache_task_id = text_shadow.render_task_id.expect("no render task!");
+                    PrimitiveKind::Shadow => {
+                        let shadow =
+                            &ctx.prim_store.cpu_shadows[prim_metadata.cpu_prim_index.0];
+                        let cache_task_id = shadow.render_task_id.expect("no render task!");
                         let cache_task_address = render_tasks.get_task_address(cache_task_id);
                         let textures = BatchTextures::render_target_cache();
                         let kind = BatchKind::Transformable(
                             transform_kind,
                             TransformBatchKind::CacheImage,
                         );
                         let key = BatchKey::new(kind, blend_mode, textures);
                         let batch = batch_list.get_suitable_batch(key, item_bounding_rect);
@@ -1135,18 +1135,18 @@ impl RenderTarget for ColorRenderTarget 
                     blur_direction: BlurDirection::Horizontal,
                 });
             }
             RenderTaskKind::CachePrimitive(prim_index) => {
                 let prim_metadata = ctx.prim_store.get_metadata(prim_index);
                 let prim_address = prim_metadata.gpu_location.as_int(gpu_cache);
 
                 match prim_metadata.prim_kind {
-                    PrimitiveKind::TextShadow => {
-                        let prim = &ctx.prim_store.cpu_text_shadows[prim_metadata.cpu_prim_index.0];
+                    PrimitiveKind::Shadow => {
+                        let prim = &ctx.prim_store.cpu_shadows[prim_metadata.cpu_prim_index.0];
 
                         let task_index = render_tasks.get_task_address(task_id);
 
                         for sub_prim_index in &prim.primitives {
                             let sub_metadata = ctx.prim_store.get_metadata(*sub_prim_index);
                             let sub_prim_address =
                                 gpu_cache.get_address(&sub_metadata.gpu_location);
                             let instance = SimplePrimitiveInstance::new(
@@ -1155,18 +1155,18 @@ impl RenderTarget for ColorRenderTarget 
                                 RenderTaskAddress(0),
                                 PackedLayerIndex(0).into(),
                                 0,
                             ); // z is disabled for rendering cache primitives
 
                             match sub_metadata.prim_kind {
                                 PrimitiveKind::TextRun => {
                                     // Add instances that reference the text run GPU location. Also supply
-                                    // the parent text-shadow prim address as a user data field, allowing
-                                    // the shader to fetch the text-shadow parameters.
+                                    // the parent shadow prim address as a user data field, allowing
+                                    // the shader to fetch the shadow parameters.
                                     let text = &ctx.prim_store.cpu_text_runs
                                         [sub_metadata.cpu_prim_index.0];
                                     let text_run_cache_prims = &mut self.text_run_cache_prims;
 
                                     let mut font = text.font.clone();
                                     font.size = font.size.scale_by(ctx.device_pixel_ratio);
                                     font.render_mode = text.shadow_render_mode;
 
--- a/gfx/webrender_api/src/display_item.rs
+++ b/gfx/webrender_api/src/display_item.rs
@@ -99,18 +99,18 @@ pub enum SpecificDisplayItem {
     Gradient(GradientDisplayItem),
     RadialGradient(RadialGradientDisplayItem),
     Iframe(IframeDisplayItem),
     PushStackingContext(PushStackingContextDisplayItem),
     PopStackingContext,
     SetGradientStops,
     PushNestedDisplayList,
     PopNestedDisplayList,
-    PushTextShadow(TextShadow),
-    PopTextShadow,
+    PushShadow(Shadow),
+    PopShadow,
 }
 
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct ClipDisplayItem {
     pub id: ClipId,
     pub parent_id: ClipId,
     pub image_mask: Option<ImageMask>,
 }
@@ -303,17 +303,17 @@ pub struct BoxShadowDisplayItem {
     pub blur_radius: f32,
     pub spread_radius: f32,
     pub border_radius: f32,
     pub clip_mode: BoxShadowClipMode,
 }
 
 #[repr(C)]
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub struct TextShadow {
+pub struct Shadow {
     pub offset: LayoutVector2D,
     pub color: ColorF,
     pub blur_radius: f32,
 }
 
 #[repr(u32)]
 #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Ord, PartialOrd)]
 pub enum ExtendMode {
--- a/gfx/webrender_api/src/display_list.rs
+++ b/gfx/webrender_api/src/display_list.rs
@@ -7,17 +7,17 @@ use {ClipAndScrollInfo, ClipDisplayItem,
 use {ExtendMode, FastHashMap, FastHashSet, FilterOp, FontInstanceKey, GlyphIndex, GlyphInstance};
 use {GlyphOptions, Gradient, GradientDisplayItem, GradientStop, IframeDisplayItem};
 use {ImageDisplayItem, ImageKey, ImageMask, ImageRendering, LayerPrimitiveInfo, LayoutPoint};
 use {LayoutPrimitiveInfo, LayoutRect, LayoutSize, LayoutTransform, LayoutVector2D};
 use {LineDisplayItem, LineOrientation, LineStyle, LocalClip, MixBlendMode, PipelineId};
 use {PropertyBinding, PushStackingContextDisplayItem, RadialGradient, RadialGradientDisplayItem};
 use {RectangleDisplayItem, ScrollFrameDisplayItem, ScrollPolicy, ScrollSensitivity};
 use {SpecificDisplayItem, StackingContext, StickyFrameDisplayItem, StickyFrameInfo};
-use {TextDisplayItem, TextShadow, TransformStyle, YuvColorSpace, YuvData};
+use {TextDisplayItem, Shadow, TransformStyle, YuvColorSpace, YuvData};
 use YuvImageDisplayItem;
 use bincode;
 use serde::{Deserialize, Serialize, Serializer};
 use serde::ser::{SerializeMap, SerializeSeq};
 use std::marker::PhantomData;
 use time::precise_time_ns;
 
 // We don't want to push a long text-run. If a text-run is too long, split it into several parts.
@@ -981,20 +981,45 @@ impl DisplayListBuilder {
         complex_clips: I,
         image_mask: Option<ImageMask>,
         scroll_sensitivity: ScrollSensitivity,
     ) -> ClipId
     where
         I: IntoIterator<Item = ComplexClipRegion>,
         I::IntoIter: ExactSizeIterator,
     {
+        let parent_id = self.clip_stack.last().unwrap().scroll_node_id;
+        self.define_scroll_frame_with_parent(
+            id,
+            parent_id,
+            content_rect,
+            clip_rect,
+            complex_clips,
+            image_mask,
+            scroll_sensitivity)
+    }
+
+    pub fn define_scroll_frame_with_parent<I>(
+        &mut self,
+        id: Option<ClipId>,
+        parent_id: ClipId,
+        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,
+    {
         let id = self.generate_clip_id(id);
         let item = SpecificDisplayItem::ScrollFrame(ScrollFrameDisplayItem {
             id: id,
-            parent_id: self.clip_stack.last().unwrap().scroll_node_id,
+            parent_id: parent_id,
             image_mask: image_mask,
             scroll_sensitivity,
         });
 
         let info = LayoutPrimitiveInfo {
             rect: content_rect,
             local_clip: LocalClip::from(clip_rect),
             is_backface_visible: true,
@@ -1012,20 +1037,41 @@ impl DisplayListBuilder {
         clip_rect: LayoutRect,
         complex_clips: I,
         image_mask: Option<ImageMask>,
     ) -> ClipId
     where
         I: IntoIterator<Item = ComplexClipRegion>,
         I::IntoIter: ExactSizeIterator,
     {
+        let parent_id = self.clip_stack.last().unwrap().scroll_node_id;
+        self.define_clip_with_parent(
+            id,
+            parent_id,
+            clip_rect,
+            complex_clips,
+            image_mask)
+    }
+
+    pub fn define_clip_with_parent<I>(
+        &mut self,
+        id: Option<ClipId>,
+        parent_id: ClipId,
+        clip_rect: LayoutRect,
+        complex_clips: I,
+        image_mask: Option<ImageMask>,
+    ) -> ClipId
+    where
+        I: IntoIterator<Item = ComplexClipRegion>,
+        I::IntoIter: ExactSizeIterator,
+    {
         let id = self.generate_clip_id(id);
         let item = SpecificDisplayItem::Clip(ClipDisplayItem {
             id,
-            parent_id: self.clip_stack.last().unwrap().scroll_node_id,
+            parent_id: parent_id,
             image_mask: image_mask,
         });
 
         let info = LayoutPrimitiveInfo::new(clip_rect);
 
         self.push_item(item, &info);
         self.push_iter(complex_clips);
         id
@@ -1082,22 +1128,22 @@ impl DisplayListBuilder {
             self.cache_glyphs(font_key, color, built_display_list.get(glyphs));
         }
 
         // Only append the actual items, not any caches
         self.data.extend_from_slice(built_display_list.item_slice());
         self.push_new_empty_item(SpecificDisplayItem::PopNestedDisplayList);
     }
 
-    pub fn push_text_shadow(&mut self, info: &LayoutPrimitiveInfo, shadow: TextShadow) {
-        self.push_item(SpecificDisplayItem::PushTextShadow(shadow), info);
+    pub fn push_shadow(&mut self, info: &LayoutPrimitiveInfo, shadow: Shadow) {
+        self.push_item(SpecificDisplayItem::PushShadow(shadow), info);
     }
 
-    pub fn pop_text_shadow(&mut self) {
-        self.push_new_empty_item(SpecificDisplayItem::PopTextShadow);
+    pub fn pop_shadow(&mut self) {
+        self.push_new_empty_item(SpecificDisplayItem::PopShadow);
     }
 
     pub fn finalize(mut self) -> (PipelineId, LayoutSize, BuiltDisplayList) {
         let glyph_offset = self.data.len();
 
         // Want to use self.push_iter, so can't borrow self
         let glyphs = ::std::mem::replace(&mut self.glyphs, FastHashMap::default());
 
--- a/gfx/webrender_bindings/RenderBufferTextureHost.cpp
+++ b/gfx/webrender_bindings/RenderBufferTextureHost.cpp
@@ -62,25 +62,25 @@ RenderBufferTextureHost::Lock()
       if (NS_WARN_IF(!mSurface->Map(gfx::DataSourceSurface::MapType::READ_WRITE, &mMap))) {
         mSurface = nullptr;
         return false;
       }
     } else {
       const layers::YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
 
       mYSurface = gfx::Factory::CreateWrappingDataSourceSurface(layers::ImageDataSerializer::GetYChannel(GetBuffer(), desc),
-                                                                desc.ySize().width,
+                                                                desc.yStride(),
                                                                 desc.ySize(),
                                                                 gfx::SurfaceFormat::A8);
       mCbSurface = gfx::Factory::CreateWrappingDataSourceSurface(layers::ImageDataSerializer::GetCbChannel(GetBuffer(), desc),
-                                                                 desc.cbCrSize().width,
+                                                                 desc.cbCrStride(),
                                                                  desc.cbCrSize(),
                                                                  gfx::SurfaceFormat::A8);
       mCrSurface = gfx::Factory::CreateWrappingDataSourceSurface(layers::ImageDataSerializer::GetCrChannel(GetBuffer(), desc),
-                                                                 desc.cbCrSize().width,
+                                                                 desc.cbCrStride(),
                                                                  desc.cbCrSize(),
                                                                  gfx::SurfaceFormat::A8);
       if (NS_WARN_IF(!mYSurface || !mCbSurface || !mCrSurface)) {
         mYSurface = mCbSurface = mCrSurface = nullptr;
         return false;
       }
       if (NS_WARN_IF(!mYSurface->Map(gfx::DataSourceSurface::MapType::READ_WRITE, &mYMap) ||
                      !mCbSurface->Map(gfx::DataSourceSurface::MapType::READ_WRITE, &mCbMap) ||
--- a/gfx/webrender_bindings/WebRenderAPI.cpp
+++ b/gfx/webrender_bindings/WebRenderAPI.cpp
@@ -1075,28 +1075,28 @@ DisplayListBuilder::PushLine(const wr::L
     rect.size.height = aLine.end - aLine.start;
   }
 
   PushRect(rect, aClip, aLine.color);
 */
 }
 
 void
-DisplayListBuilder::PushTextShadow(const wr::LayoutRect& aRect,
-                                   const wr::LayoutRect& aClip,
-                                   bool aIsBackfaceVisible,
-                                   const wr::TextShadow& aShadow)
+DisplayListBuilder::PushShadow(const wr::LayoutRect& aRect,
+                               const wr::LayoutRect& aClip,
+                               bool aIsBackfaceVisible,
+                               const wr::Shadow& aShadow)
 {
-  wr_dp_push_text_shadow(mWrState, aRect, aClip, aIsBackfaceVisible, aShadow);
+  wr_dp_push_shadow(mWrState, aRect, aClip, aIsBackfaceVisible, aShadow);
 }
 
 void
-DisplayListBuilder::PopTextShadow()
+DisplayListBuilder::PopShadow()
 {
-  wr_dp_pop_text_shadow(mWrState);
+  wr_dp_pop_shadow(mWrState);
 }
 
 void
 DisplayListBuilder::PushBoxShadow(const wr::LayoutRect& aRect,
                                   const wr::LayoutRect& aClip,
                                   bool aIsBackfaceVisible,
                                   const wr::LayoutRect& aBoxBounds,
                                   const wr::LayoutVector2D& aOffset,
--- a/gfx/webrender_bindings/WebRenderAPI.h
+++ b/gfx/webrender_bindings/WebRenderAPI.h
@@ -362,22 +362,22 @@ public:
                 wr::FontInstanceKey aFontKey,
                 Range<const wr::GlyphInstance> aGlyphBuffer,
                 const wr::GlyphOptions* aGlyphOptions = nullptr);
 
   void PushLine(const wr::LayoutRect& aClip,
                 bool aIsBackfaceVisible,
                 const wr::Line& aLine);
 
-  void PushTextShadow(const wr::LayoutRect& aBounds,
+  void PushShadow(const wr::LayoutRect& aBounds,
                       const wr::LayoutRect& aClip,
                       bool aIsBackfaceVisible,
-                      const wr::TextShadow& aShadow);
+                      const wr::Shadow& aShadow);
 
-  void PopTextShadow();
+  void PopShadow();
 
 
 
   void PushBoxShadow(const wr::LayoutRect& aRect,
                      const wr::LayoutRect& aClip,
                      bool aIsBackfaceVisible,
                      const wr::LayoutRect& aBoxBounds,
                      const wr::LayoutVector2D& aOffset,
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -1434,33 +1434,33 @@ pub extern "C" fn wr_dp_push_text(state:
          .push_text(&prim_info,
                     &glyph_slice,
                     font_key,
                     color,
                     unsafe { glyph_options.as_ref().cloned() });
 }
 
 #[no_mangle]
-pub extern "C" fn wr_dp_push_text_shadow(state: &mut WrState,
-                                         bounds: LayoutRect,
-                                         clip: LayoutRect,
-                                         is_backface_visible: bool,
-                                         shadow: TextShadow) {
+pub extern "C" fn wr_dp_push_shadow(state: &mut WrState,
+                                    bounds: LayoutRect,
+                                    clip: LayoutRect,
+                                    is_backface_visible: bool,
+                                    shadow: Shadow) {
     debug_assert!(unsafe { is_in_main_thread() });
 
     let mut prim_info = LayoutPrimitiveInfo::with_clip_rect(bounds, clip.into());
     prim_info.is_backface_visible = is_backface_visible;
-    state.frame_builder.dl_builder.push_text_shadow(&prim_info, shadow.into());
+    state.frame_builder.dl_builder.push_shadow(&prim_info, shadow.into());
 }
 
 #[no_mangle]
-pub extern "C" fn wr_dp_pop_text_shadow(state: &mut WrState) {
+pub extern "C" fn wr_dp_pop_shadow(state: &mut WrState) {
     debug_assert!(unsafe { is_in_main_thread() });
 
-    state.frame_builder.dl_builder.pop_text_shadow();
+    state.frame_builder.dl_builder.pop_shadow();
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_line(state: &mut WrState,
                                   clip: LayoutRect,
                                   is_backface_visible: bool,
                                   baseline: f32,
                                   start: f32,
--- a/gfx/webrender_bindings/webrender_ffi_generated.h
+++ b/gfx/webrender_bindings/webrender_ffi_generated.h
@@ -541,16 +541,28 @@ struct TypedVector2D_f32__LayerPixel {
            y == aOther.y;
   }
 };
 
 typedef TypedVector2D_f32__LayerPixel LayerVector2D;
 
 typedef LayerVector2D LayoutVector2D;
 
+struct Shadow {
+  LayoutVector2D offset;
+  ColorF color;
+  float blur_radius;
+
+  bool operator==(const Shadow& aOther) const {
+    return offset == aOther.offset &&
+           color == aOther.color &&
+           blur_radius == aOther.blur_radius;
+  }
+};
+
 struct WrFilterOp {
   WrFilterOpType filter_type;
   float argument;
 
   bool operator==(const WrFilterOp& aOther) const {
     return filter_type == aOther.filter_type &&
            argument == aOther.argument;
   }
@@ -583,28 +595,16 @@ struct GlyphInstance {
 struct GlyphOptions {
   FontRenderMode render_mode;
 
   bool operator==(const GlyphOptions& aOther) const {
     return render_mode == aOther.render_mode;
   }
 };
 
-struct TextShadow {
-  LayoutVector2D offset;
-  ColorF color;
-  float blur_radius;
-
-  bool operator==(const TextShadow& aOther) const {
-    return offset == aOther.offset &&
-           color == aOther.color &&
-           blur_radius == aOther.blur_radius;
-  }
-};
-
 typedef YuvColorSpace WrYuvColorSpace;
 
 struct ByteSlice {
   const uint8_t *buffer;
   size_t len;
 
   bool operator==(const ByteSlice& aOther) const {
     return buffer == aOther.buffer &&
@@ -884,21 +884,21 @@ WR_INLINE
 void wr_dp_pop_clip_and_scroll_info(WrState *aState)
 WR_FUNC;
 
 WR_INLINE
 void wr_dp_pop_scroll_layer(WrState *aState)
 WR_FUNC;
 
 WR_INLINE
-void wr_dp_pop_stacking_context(WrState *aState)
+void wr_dp_pop_shadow(WrState *aState)
 WR_FUNC;
 
 WR_INLINE
-void wr_dp_pop_text_shadow(WrState *aState)
+void wr_dp_pop_stacking_context(WrState *aState)
 WR_FUNC;
 
 WR_INLINE
 void wr_dp_push_border(WrState *aState,
                        LayoutRect aRect,
                        LayoutRect aClip,
                        bool aIsBackfaceVisible,
                        BorderWidths aWidths,
@@ -1049,16 +1049,24 @@ void wr_dp_push_rect(WrState *aState,
 WR_FUNC;
 
 WR_INLINE
 void wr_dp_push_scroll_layer(WrState *aState,
                              uint64_t aScrollId)
 WR_FUNC;
 
 WR_INLINE
+void wr_dp_push_shadow(WrState *aState,
+                       LayoutRect aBounds,
+                       LayoutRect aClip,
+                       bool aIsBackfaceVisible,
+                       Shadow aShadow)
+WR_FUNC;
+
+WR_INLINE
 void wr_dp_push_stacking_context(WrState *aState,
                                  LayoutRect aBounds,
                                  uint64_t aAnimationId,
                                  const float *aOpacity,
                                  const LayoutTransform *aTransform,
                                  TransformStyle aTransformStyle,
                                  const LayoutTransform *aPerspective,
                                  MixBlendMode aMixBlendMode,
@@ -1074,24 +1082,16 @@ void wr_dp_push_text(WrState *aState,
                      bool aIsBackfaceVisible,
                      ColorF aColor,
                      WrFontInstanceKey aFontKey,
                      const GlyphInstance *aGlyphs,
                      uint32_t aGlyphCount,
                      const GlyphOptions *aGlyphOptions)
 WR_FUNC;
 
-WR_INLINE
-void wr_dp_push_text_shadow(WrState *aState,
-                            LayoutRect aBounds,
-                            LayoutRect aClip,
-                            bool aIsBackfaceVisible,
-                            TextShadow aShadow)
-WR_FUNC;
-
 // Push a 2 planar NV12 image.
 WR_INLINE
 void wr_dp_push_yuv_NV12_image(WrState *aState,
                                LayoutRect aBounds,
                                LayoutRect aClip,
                                bool aIsBackfaceVisible,
                                WrImageKey aImageKey0,
                                WrImageKey aImageKey1,
--- a/gfx/ycbcr/YCbCrUtils.cpp
+++ b/gfx/ycbcr/YCbCrUtils.cpp
@@ -61,100 +61,183 @@ GetYCbCrToRGBDestFormatAndSize(const lay
     if (aData.mPicX != 0 || aData.mPicY != 0 || yuvtype == YV24)
       prescale = false;
   }
   if (!prescale) {
     aSuggestedSize = aData.mPicSize;
   }
 }
 
+static inline void
+ConvertYCbCr16to8Line(uint8_t* aDst,
+                      int aStride,
+                      const uint16_t* aSrc,
+                      int aStride16,
+                      int aWidth,
+                      int aHeight,
+                      int aBitDepth)
+{
+  uint16_t mask = (1 << aBitDepth) - 1;
+
+  for (int i = 0; i < aHeight; i++) {
+    for (int j = 0; j < aWidth; j++) {
+      uint16_t val = (aSrc[j] & mask) >> (aBitDepth - 8);
+      aDst[j] = val;
+    }
+    aDst += aStride;
+    aSrc += aStride16;
+  }
+}
+
 void
 ConvertYCbCrToRGB(const layers::PlanarYCbCrData& aData,
                   const SurfaceFormat& aDestFormat,
                   const IntSize& aDestSize,
                   unsigned char* aDestBuffer,
                   int32_t aStride)
 {
   // ConvertYCbCrToRGB et al. assume the chroma planes are rounded up if the
   // luma plane is odd sized.
   MOZ_ASSERT((aData.mCbCrSize.width == aData.mYSize.width ||
               aData.mCbCrSize.width == (aData.mYSize.width + 1) >> 1) &&
              (aData.mCbCrSize.height == aData.mYSize.height ||
               aData.mCbCrSize.height == (aData.mYSize.height + 1) >> 1));
+
+  // Used if converting to 8 bits YUV.
+  UniquePtr<uint8_t[]> yChannel;
+  UniquePtr<uint8_t[]> cbChannel;
+  UniquePtr<uint8_t[]> crChannel;
+  layers::PlanarYCbCrData dstData;
+  const layers::PlanarYCbCrData& srcData = aData.mBitDepth == 8 ? aData : dstData;
+
+  if (aData.mBitDepth != 8) {
+    MOZ_ASSERT(aData.mBitDepth > 8 && aData.mBitDepth <= 16);
+    // Convert to 8 bits data first.
+    dstData.mPicSize = aData.mPicSize;
+    dstData.mPicX = aData.mPicX;
+    dstData.mPicY = aData.mPicY;
+    dstData.mYSize = aData.mYSize;
+    // We align the destination stride to 32 bytes, so that libyuv can use
+    // SSE optimised code.
+    dstData.mYStride = (aData.mYSize.width + 31) & ~31;
+    dstData.mCbCrSize = aData.mCbCrSize;
+    dstData.mCbCrStride = (aData.mCbCrSize.width + 31) & ~31;
+    dstData.mYUVColorSpace = aData.mYUVColorSpace;
+    dstData.mBitDepth = 8;
+
+    size_t ySize = GetAlignedStride<1>(dstData.mYStride, aData.mYSize.height);
+    size_t cbcrSize =
+      GetAlignedStride<1>(dstData.mCbCrStride, aData.mCbCrSize.height);
+    if (ySize == 0 || cbcrSize == 0) {
+      return;
+    }
+    yChannel = MakeUnique<uint8_t[]>(ySize);
+    cbChannel = MakeUnique<uint8_t[]>(cbcrSize);
+    crChannel = MakeUnique<uint8_t[]>(cbcrSize);
+
+    dstData.mYChannel = yChannel.get();
+    dstData.mCbChannel = cbChannel.get();
+    dstData.mCrChannel = crChannel.get();
+
+    ConvertYCbCr16to8Line(dstData.mYChannel,
+                          dstData.mYStride,
+                          reinterpret_cast<uint16_t*>(aData.mYChannel),
+                          aData.mYStride / 2,
+                          aData.mYSize.width,
+                          aData.mYSize.height,
+                          aData.mBitDepth);
+
+    ConvertYCbCr16to8Line(dstData.mCbChannel,
+                          dstData.mCbCrStride,
+                          reinterpret_cast<uint16_t*>(aData.mCbChannel),
+                          aData.mCbCrStride / 2,
+                          aData.mCbCrSize.width,
+                          aData.mCbCrSize.height,
+                          aData.mBitDepth);
+
+    ConvertYCbCr16to8Line(dstData.mCrChannel,
+                          dstData.mCbCrStride,
+                          reinterpret_cast<uint16_t*>(aData.mCrChannel),
+                          aData.mCbCrStride / 2,
+                          aData.mCbCrSize.width,
+                          aData.mCbCrSize.height,
+                          aData.mBitDepth);
+  }
+
   YUVType yuvtype =
-    TypeFromSize(aData.mYSize.width,
-                 aData.mYSize.height,
-                 aData.mCbCrSize.width,
-                 aData.mCbCrSize.height);
+    TypeFromSize(srcData.mYSize.width,
+                 srcData.mYSize.height,
+                 srcData.mCbCrSize.width,
+                 srcData.mCbCrSize.height);
 
   // Convert from YCbCr to RGB now, scaling the image if needed.
-  if (aDestSize != aData.mPicSize) {
+  if (aDestSize != srcData.mPicSize) {
 #if defined(HAVE_YCBCR_TO_RGB565)
     if (aDestFormat == SurfaceFormat::R5G6B5_UINT16) {
-      ScaleYCbCrToRGB565(aData.mYChannel,
-                         aData.mCbChannel,
-                         aData.mCrChannel,
+      ScaleYCbCrToRGB565(srcData.mYChannel,
+                         srcData.mCbChannel,
+                         srcData.mCrChannel,
                          aDestBuffer,
-                         aData.mPicX,
-                         aData.mPicY,
-                         aData.mPicSize.width,
-                         aData.mPicSize.height,
+                         srcData.mPicX,
+                         srcData.mPicY,
+                         srcData.mPicSize.width,
+                         srcData.mPicSize.height,
                          aDestSize.width,
                          aDestSize.height,
-                         aData.mYStride,
-                         aData.mCbCrStride,
+                         srcData.mYStride,
+                         srcData.mCbCrStride,
                          aStride,
                          yuvtype,
                          FILTER_BILINEAR);
     } else
 #endif
-      ScaleYCbCrToRGB32(aData.mYChannel, //
-                        aData.mCbChannel,
-                        aData.mCrChannel,
+      ScaleYCbCrToRGB32(srcData.mYChannel, //
+                        srcData.mCbChannel,
+                        srcData.mCrChannel,
                         aDestBuffer,
-                        aData.mPicSize.width,
-                        aData.mPicSize.height,
+                        srcData.mPicSize.width,
+                        srcData.mPicSize.height,
                         aDestSize.width,
                         aDestSize.height,
-                        aData.mYStride,
-                        aData.mCbCrStride,
+                        srcData.mYStride,
+                        srcData.mCbCrStride,
                         aStride,
                         yuvtype,
-                        aData.mYUVColorSpace,
+                        srcData.mYUVColorSpace,
                         FILTER_BILINEAR);
   } else { // no prescale
 #if defined(HAVE_YCBCR_TO_RGB565)
     if (aDestFormat == SurfaceFormat::R5G6B5_UINT16) {
-      ConvertYCbCrToRGB565(aData.mYChannel,
-                           aData.mCbChannel,
-                           aData.mCrChannel,
+      ConvertYCbCrToRGB565(srcData.mYChannel,
+                           srcData.mCbChannel,
+                           srcData.mCrChannel,
                            aDestBuffer,
-                           aData.mPicX,
-                           aData.mPicY,
-                           aData.mPicSize.width,
-                           aData.mPicSize.height,
-                           aData.mYStride,
-                           aData.mCbCrStride,
+                           srcData.mPicX,
+                           srcData.mPicY,
+                           srcData.mPicSize.width,
+                           srcData.mPicSize.height,
+                           srcData.mYStride,
+                           srcData.mCbCrStride,
                            aStride,
                            yuvtype);
     } else // aDestFormat != SurfaceFormat::R5G6B5_UINT16
 #endif
-      ConvertYCbCrToRGB32(aData.mYChannel, //
-                          aData.mCbChannel,
-                          aData.mCrChannel,
+      ConvertYCbCrToRGB32(srcData.mYChannel, //
+                          srcData.mCbChannel,
+                          srcData.mCrChannel,
                           aDestBuffer,
-                          aData.mPicX,
-                          aData.mPicY,
-                          aData.mPicSize.width,
-                          aData.mPicSize.height,
-                          aData.mYStride,
-                          aData.mCbCrStride,
+                          srcData.mPicX,
+                          srcData.mPicY,
+                          srcData.mPicSize.width,
+                          srcData.mPicSize.height,
+                          srcData.mYStride,
+                          srcData.mCbCrStride,
                           aStride,
                           yuvtype,
-                          aData.mYUVColorSpace);
+                          srcData.mYUVColorSpace);
   }
 }
 
 void
 ConvertYCbCrAToARGB(const uint8_t* aSrcY,
                     const uint8_t* aSrcU,
                     const uint8_t* aSrcV,
                     const uint8_t* aSrcA,
--- a/ipc/glue/GeckoChildProcessHost.cpp
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -1268,28 +1268,28 @@ bool GeckoChildProcessHost::sRunSelfAsCo
 #ifdef MOZ_WIDGET_ANDROID
 void
 GeckoChildProcessHost::LaunchAndroidService(const char* type,
                                             const std::vector<std::string>& argv,
                                             const base::file_handle_mapping_vector& fds_to_remap,
                                             ProcessHandle* process_handle)
 {
   MOZ_ASSERT((fds_to_remap.size() > 0) && (fds_to_remap.size() <= 2));
-  JNIEnv* env = mozilla::jni::GetEnvForThread();
+  JNIEnv* const env = mozilla::jni::GetEnvForThread();
   MOZ_ASSERT(env);
 
-  int argvSize = argv.size();
-  jni::ObjectArray::LocalRef jargs = jni::ObjectArray::LocalRef::Adopt(env->NewObjectArray(argvSize, env->FindClass("java/lang/String"), nullptr));
+  const int argvSize = argv.size();
+  jni::ObjectArray::LocalRef jargs = jni::ObjectArray::New<jni::String>(argvSize);
   for (int ix = 0; ix < argvSize; ix++) {
     jargs->SetElement(ix, jni::StringParam(argv[ix].c_str(), env));
   }
   base::file_handle_mapping_vector::const_iterator it = fds_to_remap.begin();
   int32_t ipcFd = it->first;
   it++;
   // If the Crash Reporter is disabled, there will not be a second file descriptor.
   int32_t crashFd = (it != fds_to_remap.end()) ? it->first : -1;
-  int32_t handle = java::GeckoAppShell::StartGeckoServiceChildProcess(type, jargs, crashFd, ipcFd);
+  int32_t handle = java::GeckoProcessManager::Start(type, jargs, crashFd, ipcFd);
 
   if (process_handle) {
     *process_handle = handle;
   }
 }
 #endif
--- a/ipc/glue/ScopedXREEmbed.cpp
+++ b/ipc/glue/ScopedXREEmbed.cpp
@@ -41,27 +41,18 @@ ScopedXREEmbed::SetAppDir(const nsACStri
     NS_WARNING("Invalid application directory passed to content process.");
     mAppDir = nullptr;
   }
 }
 
 void
 ScopedXREEmbed::Start()
 {
-  std::string path;
-#if defined(OS_WIN)
-  path = WideToUTF8(CommandLine::ForCurrentProcess()->program());
-#elif defined(OS_POSIX)
-  path = CommandLine::ForCurrentProcess()->argv()[0];
-#else
-#  error Sorry
-#endif
-
   nsCOMPtr<nsIFile> localFile;
-  nsresult rv = XRE_GetBinaryPath(path.c_str(), getter_AddRefs(localFile));
+  nsresult rv = XRE_GetBinaryPath(getter_AddRefs(localFile));
   if (NS_FAILED(rv))
     return;
 
   nsCOMPtr<nsIFile> parent;
   rv = localFile->GetParent(getter_AddRefs(parent));
   if (NS_FAILED(rv))
     return;
 
--- a/js/xpconnect/idl/xpccomponents.idl
+++ b/js/xpconnect/idl/xpccomponents.idl
@@ -10,16 +10,17 @@
 %}
 
 interface xpcIJSWeakReference;
 interface nsIAddonInterposition;
 interface nsIClassInfo;
 interface nsIComponentManager;
 interface nsICycleCollectorListener;
 interface nsIFile;
+interface nsIURI;
 interface nsIJSCID;
 interface nsIJSIID;
 interface nsIPrincipal;
 interface nsIStackFrame;
 
 /**
 * interface of Components.interfacesByID
 * (interesting stuff only reflected into JavaScript)
@@ -696,16 +697,22 @@ interface nsIXPCComponents_Utils : nsISu
     double now();
 
     /*
      * Reads the given file and returns its contents. If called during early
      * startup, the file will be pre-read on a background thread during profile
      * startup so its contents will be available the next time they're read.
      */
     ACString readFile(in nsIFile file);
+
+    /*
+     * Reads the given local file URL and returns its contents. This has the
+     * same semantics of readFile.
+     */
+    ACString readURI(in nsIURI url);
 };
 
 /**
 * Interface for the 'Components' object.
 *
 * The first interface contains things that are available to non-chrome XBL code
 * that runs in a scope with an ExpandedPrincipal. The second interface
 * includes members that are only exposed to chrome.
--- a/js/xpconnect/loader/URLPreloader.cpp
+++ b/js/xpconnect/loader/URLPreloader.cpp
@@ -78,32 +78,30 @@ URLPreloader::CollectReports(nsIHandleRe
             NS_LITERAL_CSTRING("Memory used to hold cache data for files which "
                                "have been read or pre-loaded during this session."),
             aData);
     }
 
     return NS_OK;
 }
 
-
 URLPreloader&
 URLPreloader::GetSingleton()
 {
-    static RefPtr<URLPreloader> singleton;
-
-    if (!singleton) {
-        singleton = new URLPreloader();
-        ClearOnShutdown(&singleton);
+    if (!sSingleton) {
+        sSingleton = new URLPreloader();
+        ClearOnShutdown(&sSingleton);
     }
 
-    return *singleton;
+    return *sSingleton;
 }
 
+bool URLPreloader::sInitialized = false;
 
-bool URLPreloader::sInitialized = false;
+StaticRefPtr<URLPreloader> URLPreloader::sSingleton;
 
 URLPreloader::URLPreloader()
 {
     if (InitInternal().isOk()) {
         sInitialized = true;
         RegisterWeakMemoryReporter(this);
     }
 }
@@ -151,16 +149,24 @@ URLPreloader::InitInternal()
     } else {
         mStartupFinished = true;
         mReaderInitialized = true;
     }
 
     return Ok();
 }
 
+URLPreloader&
+URLPreloader::ReInitialize()
+{
+    sSingleton = new URLPreloader();
+
+    return *sSingleton;
+}
+
 Result<nsCOMPtr<nsIFile>, nsresult>
 URLPreloader::GetCacheFile(const nsAString& suffix)
 {
     if (!mProfD) {
         return Err(NS_ERROR_NOT_INITIALIZED);
     }
 
     nsCOMPtr<nsIFile> cacheFile;
--- a/js/xpconnect/loader/URLPreloader.h
+++ b/js/xpconnect/loader/URLPreloader.h
@@ -91,23 +91,28 @@ private:
     Result<const nsCString, nsresult> ReadURIInternal(nsIURI* uri, ReadType readType);
 
     Result<const nsCString, nsresult> ReadFileInternal(nsIFile* file, ReadType readType);
 
     static Result<const nsCString, nsresult> Read(const CacheKey& key, ReadType readType);
 
     static bool sInitialized;
 
+    static mozilla::StaticRefPtr<URLPreloader> sSingleton;
+
 protected:
+    friend class AddonManagerStartup;
     friend class ScriptPreloader;
 
     virtual ~URLPreloader();
 
     Result<Ok, nsresult> WriteCache();
 
+    static URLPreloader& ReInitialize();
+
     // Clear leftover entries after the cache has been written.
     void Cleanup();
 
     // Begins reading files off-thread, and ensures that initialization has
     // completed before leaving the current scope. The caller *must* ensure that
     // no code on the main thread access Omnijar, either directly or indirectly,
     // for the lifetime of this guard object.
     struct MOZ_RAII AutoBeginReading final
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -3393,16 +3393,25 @@ nsXPCComponents_Utils::ReadFile(nsIFile*
 {
     NS_ENSURE_TRUE(aFile, NS_ERROR_INVALID_ARG);
 
     MOZ_TRY_VAR(aResult, URLPreloader::ReadFile(aFile));
     return NS_OK;
 }
 
 NS_IMETHODIMP
+nsXPCComponents_Utils::ReadURI(nsIURI* aURI, nsACString& aResult)
+{
+    NS_ENSURE_TRUE(aURI, NS_ERROR_INVALID_ARG);
+
+    MOZ_TRY_VAR(aResult, URLPreloader::ReadURI(aURI));
+    return NS_OK;
+}
+
+NS_IMETHODIMP
 nsXPCComponents_Utils::Now(double* aRetval)
 {
     TimeStamp start = TimeStamp::ProcessCreation();
     *aRetval = (TimeStamp::Now() - start).ToMilliseconds();
     return NS_OK;
 }
 
 /***************************************************************************/
--- a/js/xpconnect/src/XPCShellImpl.cpp
+++ b/js/xpconnect/src/XPCShellImpl.cpp
@@ -1110,17 +1110,17 @@ XRE_XPCShellMain(int argc, char** argv, 
         printf_stderr("*** You are running in chaos test mode. See ChaosMode.h. ***\n");
     }
 
     // The provider needs to outlive the call to shutting down XPCOM.
     XPCShellDirProvider dirprovider;
 
     { // Start scoping nsCOMPtrs
         nsCOMPtr<nsIFile> appFile;
-        rv = XRE_GetBinaryPath(argv[0], getter_AddRefs(appFile));
+        rv = XRE_GetBinaryPath(getter_AddRefs(appFile));
         if (NS_FAILED(rv)) {
             printf("Couldn't find application file.\n");
             return 1;
         }
         nsCOMPtr<nsIFile> appDir;
         rv = appFile->GetParent(getter_AddRefs(appDir));
         if (NS_FAILED(rv)) {
             printf("Couldn't get application directory.\n");
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -3222,16 +3222,35 @@ nsDocumentViewer::GetFullZoom(float* aFu
   // Check the prescontext first because it might have a temporary
   // setting for print-preview
   nsPresContext* pc = GetPresContext();
   *aFullZoom = pc ? pc->GetFullZoom() : mPageZoom;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsDocumentViewer::GetDeviceFullZoom(float* aDeviceFullZoom)
+{
+  NS_ENSURE_ARG_POINTER(aDeviceFullZoom);
+#ifdef NS_PRINT_PREVIEW
+  if (GetIsPrintPreview()) {
+    // Print Preview overrides all zoom; if specified, we use the print preview
+    // zoom, no matter what.
+    *aDeviceFullZoom = mPrintPreviewZoom;
+    return NS_OK;
+  }
+#endif
+  // If not in print preview, ask the prescontext for the device zoom, if a
+  // prescontext is available.
+  nsPresContext* pc = GetPresContext();
+  *aDeviceFullZoom = pc ? pc->GetDeviceFullZoom() : mPageZoom;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDocumentViewer::SetOverrideDPPX(float aDPPX)
 {
   // If we don't have a document, then we need to bail.
   if (!mDocument) {
     return NS_ERROR_FAILURE;
   }
 
   mOverrideDPPX = aDPPX;
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -6139,17 +6139,17 @@ nsLayoutUtils::PaintTextShadow(const nsI
     nscolor shadowColor;
     if (shadowDetails->mHasColor)
       shadowColor = shadowDetails->mColor;
     else
       shadowColor = aForegroundColor;
 
     // Webrender just needs the shadow details
     if (auto* textDrawer = aContext->GetTextDrawer()) {
-      wr::TextShadow wrShadow;
+      wr::Shadow wrShadow;
 
       wrShadow.offset = {
         presCtx->AppUnitsToFloatDevPixels(shadowDetails->mXOffset),
         presCtx->AppUnitsToFloatDevPixels(shadowDetails->mYOffset)
       };
 
       wrShadow.blur_radius = presCtx->AppUnitsToFloatDevPixels(shadowDetails->mRadius);
       wrShadow.color = wr::ToColorF(ToDeviceColor(shadowColor));
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -1359,16 +1359,22 @@ nsPresContext::UpdateEffectiveTextZoom()
   if (HasCachedStyleData()) {
     // Media queries could have changed, since we changed the meaning
     // of 'em' units in them.
     MediaFeatureValuesChanged(eRestyle_ForceDescendants,
                               NS_STYLE_HINT_REFLOW);
   }
 }
 
+float
+nsPresContext::GetDeviceFullZoom()
+{
+  return mDeviceContext->GetFullZoom();
+}
+
 void
 nsPresContext::SetFullZoom(float aZoom)
 {
   if (!mShell || mFullZoom == aZoom) {
     return;
   }
 
   // Re-fetch the view manager's window dimensions in case there's a deferred
@@ -2106,17 +2112,26 @@ nsPresContext::MediaFeatureValuesChanged
   // flushes.  (We're already running off an event.)
   //
   // Note that we do this after the new style from media queries in
   // style sheets has been computed.
 
   if (!mDocument->MediaQueryLists().isEmpty()) {
     // We build a list of all the notifications we're going to send
     // before we send any of them.
-    for (auto mql : mDocument->MediaQueryLists()) {
+
+    // Copy pointers to all the lists into a new array, in case one of our
+    // notifications modifies the list.
+    nsTArray<RefPtr<mozilla::dom::MediaQueryList>> localMediaQueryLists;
+    for (auto* mql : mDocument->MediaQueryLists()) {
+      localMediaQueryLists.AppendElement(mql);
+    }
+
+    // Now iterate our local array of the lists.
+    for (auto mql : localMediaQueryLists) {
       nsAutoMicroTask mt;
       mql->MaybeNotify();
     }
   }
 }
 
 void
 nsPresContext::PostMediaFeatureValuesChangedEvent()
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -599,16 +599,22 @@ public:
       // Media queries could have changed, since we changed the meaning
       // of 'em' units in them.
       MediaFeatureValuesChanged(eRestyle_ForceDescendants,
                                 NS_STYLE_HINT_REFLOW);
     }
   }
 
   float GetFullZoom() { return mFullZoom; }
+  /**
+   * Device full zoom differs from full zoom because it gets the zoom from
+   * the device context, which may be using a different zoom due to rounding
+   * of app units to device pixels.
+   */
+  float GetDeviceFullZoom();
   void SetFullZoom(float aZoom);
 
   float GetOverrideDPPX() { return mOverrideDPPX; }
   void SetOverrideDPPX(float aDPPX);
 
   nscoord GetAutoQualityMinFontSize() {
     return DevPixelsToAppUnits(mAutoQualityMinFontSizePixelsPref);
   }
--- a/layout/generic/TextDrawTarget.h
+++ b/layout/generic/TextDrawTarget.h
@@ -44,17 +44,17 @@ struct SelectionFragment {
 //
 // Would be broken up into 5 SelectedTextRunFragments
 //
 // ["Hello ", "there", " my name ", "is Mega", "man"]
 //
 // For almost all nsTextFrames, there will be only one SelectedTextRunFragment.
 struct SelectedTextRunFragment {
   Maybe<SelectionFragment> selection;
-  nsTArray<wr::TextShadow> shadows;
+  nsTArray<wr::Shadow> shadows;
   nsTArray<TextRunFragment> text;
   nsTArray<wr::Line> beforeDecorations;
   nsTArray<wr::Line> afterDecorations;
 };
 
 // This class is fake DrawTarget, used to intercept text draw calls, while
 // also collecting up the other aspects of text natively.
 //
@@ -158,17 +158,17 @@ public:
     nsTArray<Glyph>& glyphs = fragment->glyphs;
 
     size_t oldLength = glyphs.Length();
     glyphs.SetLength(oldLength + aBuffer.mNumGlyphs);
     PodCopy(glyphs.Elements() + oldLength, aBuffer.mGlyphs, aBuffer.mNumGlyphs);
   }
 
   void
-  AppendShadow(const wr::TextShadow& aShadow) {
+  AppendShadow(const wr::Shadow& aShadow) {
     mCurrentPart->shadows.AppendElement(aShadow);
   }
 
   void
   SetSelectionRect(const LayoutDeviceRect& aRect, const Color& aColor)
   {
     SelectionFragment frag;
     frag.rect = wr::ToLayoutRect(aRect);
@@ -333,18 +333,18 @@ public:
       auto selection = part.selection.value();
       aBuilder.PushRect(selection.rect, wrClipRect, backfaceVisible, selection.color);
     }
   }
 
   for (auto& part : GetParts()) {
     // WR takes the shadows in CSS-order (reverse of rendering order),
     // because the drawing of a shadow actually occurs when it's popped.
-    for (const wr::TextShadow& shadow : part.shadows) {
-      aBuilder.PushTextShadow(wrBoundsRect, wrClipRect, backfaceVisible, shadow);
+    for (const wr::Shadow& shadow : part.shadows) {
+      aBuilder.PushShadow(wrBoundsRect, wrClipRect, backfaceVisible, shadow);
     }
 
     for (const wr::Line& decoration : part.beforeDecorations) {
       aBuilder.PushLine(wrClipRect, backfaceVisible, decoration);
     }
 
     for (const mozilla::layout::TextRunFragment& text : part.text) {
       aManager->WrBridge()->PushGlyphs(aBuilder, text.glyphs, text.font,
@@ -352,17 +352,17 @@ public:
                                        backfaceVisible);
     }
 
     for (const wr::Line& decoration : part.afterDecorations) {
       aBuilder.PushLine(wrClipRect, backfaceVisible, decoration);
     }
 
     for (size_t i = 0; i < part.shadows.Length(); ++i) {
-      aBuilder.PopTextShadow();
+      aBuilder.PopShadow();
     }
   }
 }
 
 
 private:
   // The part of the text we're currently drawing (glyphs, underlines, etc.)
   Phase mCurrentlyDrawing;
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -2798,24 +2798,21 @@ nsIFrame::BuildDisplayListForStackingCon
     const nsIFrame* outerReferenceFrame = this;
     if (this != aBuilder->RootReferenceFrame()) {
       outerReferenceFrame =
         aBuilder->FindReferenceFrameFor(GetParent(), &toOuterReferenceFrame);
     }
     buildingDisplayList.SetReferenceFrameAndCurrentOffset(outerReferenceFrame,
       GetOffsetToCrossDoc(outerReferenceFrame));
 
-    if (!aBuilder->IsForGenerateGlyphMask() &&
-        !aBuilder->IsForPaintingSelectionBG()) {
-      nsDisplayTransform *transformItem =
-        new (aBuilder) nsDisplayTransform(aBuilder, this,
-                                          &resultList, dirtyRect, 0,
-                                          allowAsyncAnimation);
-      resultList.AppendNewToTop(transformItem);
-    }
+    nsDisplayTransform *transformItem =
+      new (aBuilder) nsDisplayTransform(aBuilder, this,
+                                        &resultList, dirtyRect, 0,
+                                        allowAsyncAnimation);
+    resultList.AppendNewToTop(transformItem);
 
     if (hasPerspective) {
       if (clipCapturedBy == ContainerItemType::ePerspective) {
         clipState.Restore();
       }
       resultList.AppendNewToTop(
         new (aBuilder) nsDisplayPerspective(
           aBuilder, this,
--- a/layout/generic/nsSubDocumentFrame.cpp
+++ b/layout/generic/nsSubDocumentFrame.cpp
@@ -23,30 +23,30 @@
 #include "nsPresContext.h"
 #include "nsIPresShell.h"
 #include "nsIDocument.h"
 #include "nsView.h"
 #include "nsViewManager.h"
 #include "nsGkAtoms.h"
 #include "nsStyleConsts.h"
 #include "nsFrameSetFrame.h"
-#include "nsIDOMHTMLFrameElement.h"
 #include "nsIScrollable.h"
 #include "nsNameSpaceManager.h"
 #include "nsDisplayList.h"
 #include "nsIScrollableFrame.h"
 #include "nsIObjectLoadingContent.h"
 #include "nsLayoutUtils.h"
 #include "FrameLayerBuilder.h"
 #include "nsPluginFrame.h"
 #include "nsContentUtils.h"
 #include "nsIPermissionManager.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIDOMMutationEvent.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/dom/HTMLFrameElement.h"
 
 using namespace mozilla;
 using mozilla::layout::RenderFrameParent;
 
 static bool sShowPreviousPage = true;
 
 static nsIDocument*
 GetDocumentFromView(nsView* aView)
@@ -107,19 +107,19 @@ InsertViewsInReverseOrder(nsView* aSibli
 static void
 EndSwapDocShellsForViews(nsView* aView);
 
 void
 nsSubDocumentFrame::Init(nsIContent*       aContent,
                          nsContainerFrame* aParent,
                          nsIFrame*         aPrevInFlow)
 {
+  MOZ_ASSERT(aContent);
   // determine if we are a <frame> or <iframe>
-  nsCOMPtr<nsIDOMHTMLFrameElement> frameElem = do_QueryInterface(aContent);
-  mIsInline = frameElem ? false : true;
+  mIsInline = !aContent->IsHTMLElement(nsGkAtoms::frame);
 
   static bool addedShowPreviousPage = false;
   if (!addedShowPreviousPage) {
     // If layout.show_previous_page is true then during loading of a new page we
     // will draw the previous page if the new page has painting suppressed.
     Preferences::AddBoolVarCache(&sShowPreviousPage, "layout.show_previous_page", true);
     addedShowPreviousPage = true;
   }
@@ -279,19 +279,17 @@ nsSubDocumentFrame::GetSubdocumentSize()
       }
     }
     // Pick some default size for now.  Using 10x10 because that's what the
     // code used to do.
     return ScreenIntSize(10, 10);
   } else {
     nsSize docSizeAppUnits;
     nsPresContext* presContext = PresContext();
-    nsCOMPtr<nsIDOMHTMLFrameElement> frameElem =
-      do_QueryInterface(GetContent());
-    if (frameElem) {
+    if (GetContent()->IsHTMLElement(nsGkAtoms::frame)) {
       docSizeAppUnits = GetSize();
     } else {
       docSizeAppUnits = GetContentRect().Size();
     }
     // Adjust subdocument size, according to 'object-fit' and the
     // subdocument's intrinsic size and ratio.
     nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame();
     if (subDocRoot) {
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -6430,17 +6430,17 @@ nsTextFrame::PaintOneShadow(const PaintS
   if (!shadowContext)
     return;
 
   nscolor shadowColor = aShadowDetails->mHasColor ? aShadowDetails->mColor
                                                   : aParams.foregroundColor;
 
   auto* textDrawer = aParams.context->GetTextDrawer();
   if (textDrawer) {
-    wr::TextShadow wrShadow;
+    wr::Shadow wrShadow;
 
     wrShadow.offset = {
       PresContext()->AppUnitsToFloatDevPixels(aShadowDetails->mXOffset),
       PresContext()->AppUnitsToFloatDevPixels(aShadowDetails->mYOffset)
     };
 
     wrShadow.blur_radius = PresContext()->AppUnitsToFloatDevPixels(aShadowDetails->mRadius);
     wrShadow.color = wr::ToColorF(ToDeviceColor(shadowColor));
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -8082,21 +8082,28 @@ nsDisplayTransform::UpdateScrollData(moz
   }
   return true;
 }
 
 already_AddRefed<Layer> nsDisplayTransform::BuildLayer(nsDisplayListBuilder *aBuilder,
                                                        LayerManager *aManager,
                                                        const ContainerLayerParameters& aContainerParameters)
 {
+  // While generating a glyph mask, the transform vector of the root frame had
+  // been applied into the target context, so stop applying it again here.
+  const bool shouldSkipTransform =
+    (aBuilder->RootReferenceFrame() == mFrame) &&
+    (aBuilder->IsForGenerateGlyphMask() || aBuilder->IsForPaintingSelectionBG());
+
   /* For frames without transform, it would not be removed for
    * backface hidden here.  But, it would be removed by the init
    * function of nsDisplayTransform.
    */
-  const Matrix4x4& newTransformMatrix = GetTransformForRendering();
+  const Matrix4x4 newTransformMatrix =
+    shouldSkipTransform ? Matrix4x4(): GetTransformForRendering();
 
   uint32_t flags = FrameLayerBuilder::CONTAINER_ALLOW_PULL_BACKGROUND_COLOR;
   RefPtr<ContainerLayer> container = aManager->GetLayerBuilder()->
     BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mStoredList.GetChildren(),
                            aContainerParameters, &newTransformMatrix, flags);
 
   if (!container) {
     return nullptr;
--- a/layout/printing/nsPrintEngine.cpp
+++ b/layout/printing/nsPrintEngine.cpp
@@ -35,17 +35,16 @@
 static const char sPrintSettingsServiceContractID[] = "@mozilla.org/gfx/printsettings-service;1";
 
 // Printing Events
 #include "nsPrintPreviewListener.h"
 #include "nsThreadUtils.h"
 
 // Printing
 #include "nsIWebBrowserPrint.h"
-#include "nsIDOMHTMLFrameElement.h"
 #include "nsIDOMHTMLIFrameElement.h"
 
 // Print Preview
 #include "imgIContainer.h" // image animation mode constants
 #include "nsIWebBrowserPrint.h" // needed for PrintPreview Navigation constants
 
 // Print Progress
 #include "nsIPrintProgress.h"
@@ -110,16 +109,17 @@ static const char kPrintingPromptService
 #include "nsIContentViewer.h"
 #include "nsIDocumentViewerPrint.h"
 
 #include "nsFocusManager.h"
 #include "nsRange.h"
 #include "nsCDefaultURIFixup.h"
 #include "nsIURIFixup.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/HTMLFrameElement.h"
 #include "nsContentList.h"
 #include "nsIChannel.h"
 #include "xpcpublic.h"
 #include "nsVariant.h"
 #include "mozilla/StyleSetHandle.h"
 #include "mozilla/StyleSetHandleInlines.h"
 
 using namespace mozilla;
@@ -1372,21 +1372,19 @@ nsPrintEngine::MapContentForPO(const Uni
           po = kid.get();
           break;
         }
       }
 
       // XXX If a subdocument has no onscreen presentation, there will be no PO
       //     This is even if there should be a print presentation
       if (po) {
-
-        nsCOMPtr<nsIDOMHTMLFrameElement> frame(do_QueryInterface(aContent));
         // "frame" elements not in a frameset context should be treated
         // as iframes
-        if (frame && po->mParent->mFrameType == eFrameSet) {
+        if (aContent->IsHTMLElement(nsGkAtoms::frame) && po->mParent->mFrameType == eFrameSet) {
           po->mFrameType = eFrame;
         } else {
           // Assume something iframe-like, i.e. iframe, object, or embed
           po->mFrameType = eIFrame;
           SetPrintAsIs(po, true);
           NS_ASSERTION(po->mParent, "The root must be a parent");
           po->mParent->mPrintAsIs = true;
         }
--- a/layout/style/ServoStyleSet.cpp
+++ b/layout/style/ServoStyleSet.cpp
@@ -1274,17 +1274,25 @@ ServoStyleSet::EnsureUniqueInnerOnCSSShe
   // Bug 1290276 will replicate the nsStyleSet work of checking
   // a nsBindingManager
 
   while (!queue.IsEmpty()) {
     uint32_t idx = queue.Length() - 1;
     StyleSheet* sheet = queue[idx];
     queue.RemoveElementAt(idx);
 
-    sheet->EnsureUniqueInner();
+    // Only call EnsureUniqueInner for complete sheets. If we do call it on
+    // incomplete sheets, we'll cause problems when the sheet is actually
+    // loaded. We don't care about incomplete sheets here anyway, because this
+    // method is only invoked by nsPresContext::EnsureSafeToHandOutCSSRules.
+    // The CSSRule objects we are handing out won't contain any rules derived
+    // from incomplete sheets (because they aren't yet applied in styling).
+    if (sheet->IsComplete()) {
+      sheet->EnsureUniqueInner();
+    }
 
     // Enqueue all the sheet's children.
     sheet->AppendAllChildSheets(queue);
   }
 
   if (mNeedsRestyleAfterEnsureUniqueInner) {
     // TODO(emilio): We could make this faster if needed tracking the specific
     // origins and all that, but the only caller of this doesn't seem to really
--- a/layout/style/nsMediaFeatures.cpp
+++ b/layout/style/nsMediaFeatures.cpp
@@ -274,26 +274,34 @@ GetMonochrome(nsPresContext* aPresContex
   // 0!
   aResult.SetIntValue(0, eCSSUnit_Integer);
 }
 
 static void
 GetResolution(nsPresContext* aPresContext, const nsMediaFeature*,
               nsCSSValue& aResult)
 {
-  float dpi = 96; // Use 96 when resisting fingerprinting.
+  // We're returning resolution in terms of device pixels per css pixel, since
+  // that is the preferred unit for media queries of resolution. This avoids
+  // introducing precision error from conversion to and from less-used
+  // physical units like inches.
+
+  float dppx;
 
   if (!ShouldResistFingerprinting(aPresContext)) {
-    // Resolution measures device pixels per CSS (inch/cm/pixel).  We
-    // return it in device pixels per CSS inches.
-    dpi = float(nsPresContext::AppUnitsPerCSSInch()) /
-          float(aPresContext->AppUnitsPerDevPixel());
+    // Get the actual device pixel ratio, which also takes zoom into account.
+    dppx = float(nsPresContext::AppUnitsPerCSSPixel()) /
+             aPresContext->AppUnitsPerDevPixel();
+  } else {
+    // We are resisting fingerprinting, so pretend we have a device pixel ratio
+    // of 1. In that case, we simply report the zoom level.
+    dppx = aPresContext->GetDeviceFullZoom();
   }
 
-  aResult.SetFloatValue(dpi, eCSSUnit_Inch);
+  aResult.SetFloatValue(dppx, eCSSUnit_Pixel);
 }
 
 static void
 GetScan(nsPresContext* aPresContext, const nsMediaFeature*,
         nsCSSValue& aResult)
 {
   // Since Gecko doesn't support the 'tv' media type, the 'scan'
   // feature is never present.
--- a/layout/style/nsMediaList.cpp
+++ b/layout/style/nsMediaList.cpp
@@ -124,33 +124,33 @@ nsMediaExpression::Matches(nsPresContext
         NS_ASSERTION(actual.GetUnit() == eCSSUnit_Inch ||
                      actual.GetUnit() == eCSSUnit_Pixel ||
                      actual.GetUnit() == eCSSUnit_Centimeter,
                      "bad actual value");
         NS_ASSERTION(required.GetUnit() == eCSSUnit_Inch ||
                      required.GetUnit() == eCSSUnit_Pixel ||
                      required.GetUnit() == eCSSUnit_Centimeter,
                      "bad required value");
-        float actualDPI = actual.GetFloatValue();
+        float actualDPPX = actual.GetFloatValue();
         float overrideDPPX = aPresContext->GetOverrideDPPX();
 
         if (overrideDPPX > 0) {
-          actualDPI = overrideDPPX * 96.0f;
+          actualDPPX = overrideDPPX;
         } else if (actual.GetUnit() == eCSSUnit_Centimeter) {
-          actualDPI = actualDPI * 2.54f;
-        } else if (actual.GetUnit() == eCSSUnit_Pixel) {
-          actualDPI = actualDPI * 96.0f;
+          actualDPPX = actualDPPX * 2.54f / 96.0f;
+        } else if (actual.GetUnit() == eCSSUnit_Inch) {
+          actualDPPX = actualDPPX / 96.0f;
         }
-        float requiredDPI = required.GetFloatValue();
+        float requiredDPPX = required.GetFloatValue();
         if (required.GetUnit() == eCSSUnit_Centimeter) {
-          requiredDPI = requiredDPI * 2.54f;
-        } else if (required.GetUnit() == eCSSUnit_Pixel) {
-          requiredDPI = requiredDPI * 96.0f;
+          requiredDPPX = requiredDPPX * 2.54f / 96.0f;
+        } else if (required.GetUnit() == eCSSUnit_Inch) {
+          requiredDPPX = requiredDPPX / 96.0f;
         }
-        cmp = DoCompare(actualDPI, requiredDPI);
+        cmp = DoCompare(actualDPPX, requiredDPPX);
       }
       break;
     case nsMediaFeature::eEnumerated:
       {
         NS_ASSERTION(actual.GetUnit() == eCSSUnit_Enumerated,
                      "bad actual value");
         NS_ASSERTION(required.GetUnit() == eCSSUnit_Enumerated,
                      "bad required value");
--- a/layout/style/nsStyleSet.cpp
+++ b/layout/style/nsStyleSet.cpp
@@ -2724,17 +2724,25 @@ nsStyleSet::EnsureUniqueInnerOnCSSSheets
     }
   }
 
   while (!queue.IsEmpty()) {
     uint32_t idx = queue.Length() - 1;
     StyleSheet* sheet = queue[idx];
     queue.RemoveElementAt(idx);
 
-    sheet->EnsureUniqueInner();
+    // Only call EnsureUniqueInner for complete sheets. If we do call it on
+    // incomplete sheets, we'll cause problems when the sheet is actually
+    // loaded. We don't care about incomplete sheets here anyway, because this
+    // method is only invoked by nsPresContext::EnsureSafeToHandOutCSSRules.
+    // The CSSRule objects we are handing out won't contain any rules derived
+    // from incomplete sheets (because they aren't yet applied in styling).
+    if (sheet->IsComplete()) {
+      sheet->EnsureUniqueInner();
+    }
 
     // Enqueue all the sheet's children.
     sheet->AppendAllChildSheets(queue);
   }
 
   bool res = mNeedsRestyleAfterEnsureUniqueInner;
   mNeedsRestyleAfterEnsureUniqueInner = false;
   return res;
--- a/mobile/android/app/src/main/res/drawable/firefox_logo_svg.xml
+++ b/mobile/android/app/src/main/res/drawable/firefox_logo_svg.xml
@@ -1,33 +1,34 @@
 <?xml version="1.0" encoding="utf-8"?>
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="200dp"
-    android:height="200dp"
-    android:viewportWidth="200"
-    android:viewportHeight="200">
+    android:width="182.5dp"
+    android:height="182.5dp"
+    android:viewportWidth="182.5"
+    android:viewportHeight="182.5">
 
     <path
         android:fillColor="#D7D7DB"
-        android:pathData="M172.2,124.6c4.2-8.4,10.6-27.2,5.1-42.6c0,0.1,0.1,0.3,0.1,0.4c-0.1-0.2-0.1-0.3-0.1-0.3s-1,3.1-2.5,7.8
-c-0.1-0.6-0.2-1.3-0.3-1.9c0.4-5.8,0.2-12-0.9-17.9c-1.8-9.3-9.1-17.5-12.9-20.1c0.2,0.1,0.4,0.3,0.6,0.5c-0.4-0.3-0.7-0.5-0.7-0.5
-s0.2,0.9,0.4,2.4c-4.1-6.5-10.4-9.6-10.4-9.6s0.2,1.1,0.5,3.4c-12.9-11.5-29.9-18.5-48.6-18.5c-19,0-36.3,7.2-49.3,19.1
-c0.7,0.9,1.5,1.9,2.2,3c0,0,4.9-0.9,11-0.3c10.5-7.4,23-11.4,36.1-11.4c16.8,0,32.5,6.5,44.4,18.4c0.6,0.6,1.2,1.3,1.8,1.9
-c-3.6-2.7-7.9-4.9-11.1-5.2c6.5,4.9,16.2,17,15.2,38.3c-1.7-3.5-3.7-6.3-5.9-8.2c2.3,21.4,0.3,26-1.1,31.7c-0.3-2.6-1.3-4.6-1.8-5.8
-c0,0-0.3,6.7-4.6,16.2c-3.3,7.2-6.7,9.5-8.2,9.2c-1.3-0.2-0.4-1.5-0.7-2.8c0,0-2.3,1.1-4.5,3.4c-2.1,2.2-4.1,4.7-5.1,4
-c0.8-0.6,1.3-1.7,1.9-2.6c-0.8,0.6-3,2.2-7.7,2.9c-1.9,0.3-10.2,1.8-21.3-3.7c1.6-0.2,4-0.8,5.9,0.3c-1.8-2-6.3-1.6-9.5-2.6
-c-2.8-0.9-6.4-4.8-8.5-6.7c8.5,2.1,17.5,0.6,22.7-3c5.3-3.6,8.4-6.2,11.2-5.6c2.8,0.6,4.7-2.2,2.5-4.7c-2.2-2.5-7.5-5.9-14.6-4.1
-c-5,1.3-11.3,6.9-20.8,1.3c-8.1-4.8-8.1-8.7-8.1-11.2c0-1.7,1-4,3-5.1c1.1,0.4,1.7,0.7,1.7,0.7s-0.5-0.7-0.8-1.1
-c0.1,0,0.1,0,0.2-0.1c0.9,0.3,2.8,1,3.8,1.5c1.3,0.7,1.8,1.4,1.8,1.4s0.3-0.2,0-0.9c-0.1-0.3-0.6-1.1-1.9-1.9c0,0,0.1,0,0.1,0
-c0.7,0.3,1.4,0.6,2.2,1.1c0.3-1.2,0.8-2.4,0.4-4.5c-0.2-1.5-0.2-1.9-0.6-2.4c-0.3-0.5,0.1-0.7,0.6-0.3c-0.1-0.4-0.3-0.7-0.5-1.1
-c0,0,0,0,0,0c0-0.1,0.1-0.2,0.2-0.4c1.6-2.2,11.3-7.3,12-7.8c1-0.7,1.9-1.7,2.6-2.9c1-0.6,1.7-3.2,1.9-5.1c0.1-0.8-1.6-1.7-2.5-1.8
-c-0.5-0.1-2.3-0.4-3.6-0.2c0.1,0,0.1,0.1,0.2,0.1c-1.8-0.1-4.2-0.2-7.5-0.1c-3.2,0-5.3-1.8-6.6-3.6c-0.3-0.4-0.5-0.7-0.7-1.1
-c-0.3-0.5-0.5-0.9-0.6-1.3c1.4-5.1,4.2-9.5,8.6-13.1c0.3-0.2-1,0.2-0.8-0.1c0.3-0.3,2.2-1.2,2.5-1.4c0.4-0.3-2-0.9-4.1-0.5
-c-2.1,0.4-2.5,0.7-3.6,1.3c0.4-0.5,1.9-1.2,1.5-1.1c-2.3,0.5-5,2-7.3,3.7c0-0.2,0-0.4,0-0.7c-1.1,0.6-3.7,2.6-4.4,4.1
-c0-0.3,0-0.5,0-0.9c-0.7,0.7-1.5,1.5-2.1,2.4c0,0,0,0,0,0c-6.9-1.7-12.7-1.3-17.5,0.3c-1.6-1.3-4.1-3.7-6-6.7
-c-0.2-0.3-0.2,0.6-0.4,0.3c-1.1-1.9-2-5-2.2-7.4c0-0.2,0-0.5-0.1-0.7c0,0-0.9,0.5-1.9,2.1c-0.4,0.7-0.9,1.7-1.3,2.9
-c-0.2,0.7-0.4,1.1-0.5,1.5c0,0.1,0.1-1.3,0-1.1c-0.2,0.5-0.9,1.3-1.1,2.1c-0.2,0.7-0.4,1.1-0.5,1.9c0,0,0,0,0,0c0-0.2,0-1-0.1-0.8
-c-0.6,1.5-1.1,3.2-1.5,5.1c-0.4,1.4-0.6,3-0.8,4.8c-0.2,2.1-0.2,4.5,0,7.2c0,0.4,0,0.8,0,1.2c-1.8,2.5-3,4.7-3.4,5.8
-c-2.3,4.4-4.6,11.1-6.4,21.6c0,0,1.2-3.9,3.7-8.3c0,0.1,0,0.1,0,0.2c-1.8,5.6-3.2,14.3-2.3,27.3c0-0.4,0.4-3.8,1.4-8.2
-c0.5,8.7,3,19.4,9.8,31c5,8.6,10.9,15,17.5,19.8c1.2,1,2.5,1.9,3.7,2.8c8.1,5.9,20.8,13.2,31.6,14.8c-3.8-1.1-6.1-3.3-6.1-3.3
-s12.8,4.1,22.2,3.8c-2.9-0.5-3.5-1.9-3.5-1.9s26.6,1.5,40.5-9.4c3.1-2.3,5.1-5,5.7-7.5c4.1-2.2,8.5-5.1,13-9.6
-c7.5-7.4,8.3-13,9.2-18.3c0,0.2-0.1,0.3-0.1,0.5C173.3,127.8,173,125.9,172.2,124.6z" />
+        android:pathData="M166.9,84.7L166.9,84.7c-0.2-1.4-0.4-2.2-0.4-2.2s-0.5,0.6-1.4,1.8c-0.3-3.2-0.9-6.5-1.8-9.8
+c-0.6-2.2-1.4-4.3-2.2-6.3C154.2,48.7,139,33.4,138.8,28c-1.5,1.8-2.6,4.7-3,8c-3.5-3.5-6.6-6.1-8.5-7.8c-9.7-9-8.6-13.7-8.6-13.7
+l0,0c0,0-18.1,20.2-10.3,41.3c3.2,8.6,8.2,13.6,13.5,18c7.6,6.3,15.8,11.2,20.1,23.8c-4.1-8.6-11.9-14-15.1-16
+c1.9,4.4,2.9,9.3,2.9,14.4c0,18.9-15.3,34.3-34.3,34.3c-2.6,0-5.2-0.3-7.7-0.9c-3.3-0.6-6.1-1.7-8.5-2.9c-5.2-3.5-8.4-6.6-10.2-9.4
+c0,0,0-0.1-0.1-0.1c0.2,0.1,0.4,0.2,0.6,0.2c1.4,0.5,2.9,0.9,4.3,1.2c6.5,1.2,13,0.3,17.2-2c5.4-3,8.7-5.2,11.4-4.4c0,0,0,0,0,0
+c2.6,0.8,4.7-1.7,2.8-4.3c-1.8-2.6-6.6-6.4-13.7-5.4c-5.4,0.8-10.4,4.6-17.5,0.9c-0.4-0.2-0.9-0.5-1.3-0.8c-0.5-0.3,1.5,0.4,1.1,0.1
+c-1.4-0.7-3.8-2.1-4.5-2.7c-0.1-0.1,1.1,0.3,1,0.3c-2.4-1.8-3.8-3.3-4.7-4.7c-1.8-3.2-0.9-6.1-0.3-7.5c0.6-1.2,2.1-2.4,2.9-2.7
+C69.4,85.7,70,86,70,86s-0.4-0.8-0.6-1.2c0.1,0,0.2,0,0.2-0.1c0.8,0.3,2.6,1.3,3.5,1.8c1.2,0.8,1.6,1.5,1.6,1.5s0.3-0.2,0.1-0.8
+c-0.1-0.3-0.5-1.2-1.7-2l0.1,0c0.7,0.3,1.3,0.7,2,1.3c0.3-1.1,1-2.3,0.8-4.3c-0.1-1.5,0-1.8-0.3-2.4c-0.3-0.5,0.1-0.7,0.6-0.2
+c-0.1-0.4-0.2-0.7-0.4-1.1l0,0c0,0,0.3-0.3,0.4-0.5c0.3-0.3,0.6-0.6,1-0.8c1.7-1.2,4.6-2.5,7.1-3.6c2-0.9,3.6-1.5,4-1.7
+c0.3-0.2,0.8-0.5,1.4-1.1c1.2-1.1,2.8-3,3.2-5.5c0-0.3,0.1-0.6,0.1-0.9c0,0,0,0,0-0.1c0,0,0,0,0,0c0,0,0,0,0-0.1c0,0,0,0,0,0
+c0,0,0,0,0-0.1c0,0,0,0,0,0c0,0,0-0.1,0-0.1c0,0,0,0,0,0c0,0,0,0,0-0.1c0,0,0,0,0,0c0,0,0,0,0-0.1l0,0c-0.3-1.1-2.2-1.9-11.9-2.8
+c-4.7-0.4-6.4-4.7-7-6.5c0,0,0,0,0,0c0,0,0-0.1,0-0.1c1.9-4.8,5-8.8,9.6-11.9c0.2-0.2-1,0.1-0.7-0.2c0.3-0.2,2.2-0.9,2.6-1.1
+c0.4-0.2-1.9-1.1-3.9-0.9c-2.1,0.2-2.5,0.4-3.6,0.9c0.5-0.4,1.9-1,1.6-0.9c-2.2,0.3-5,1.5-7.4,2.8c0-0.2,0-0.4,0.1-0.7
+c-1.1,0.4-3.8,2.1-4.6,3.6c0-0.3,0-0.5,0-0.8c-0.8,0.6-1.6,1.3-2.3,2.1l0,0c-6.4-2.3-12.1-2.5-16.9-1.4c0,0,0,0,0,0c0,0,0.1,0,0.1,0
+c-0.6-0.5-1.7-1.5-3-3.4c0,0,0,0,0-0.1c0,0,0-0.1-0.1-0.1c-0.4-0.6-0.8-1.2-1.1-1.9c-0.3-0.5-0.6-1-0.8-1.6c0,0,0-0.1-0.1-0.1
+c-0.1,0-0.2,0.5-0.3,0.4c0,0,0,0,0,0c-0.9-2.2-1.5-5.6-1.4-8.1c0,0,0,0-0.1,0c-0.4,0.2-2,1.3-3.4,4.5c-0.3,0.7-0.5,1-0.7,1.4
+c0,0,0,0,0-0.2c0-0.3,0.2-1,0.2-1c0,0-0.1,0.1-0.1,0.1c-0.3,0.5-0.9,1.1-1.2,1.8c-0.3,0.7-0.5,1-0.7,1.8c0,0.1,0-0.1,0-0.3
+c0-0.3,0-0.6,0-0.5c0,0.1-0.1,0.1-0.1,0.2c-2.4,4.8-3.5,11.1-3.9,14.6c-0.1,1.3-0.2,2.3-0.1,2.6c0,0,0,0.1,0,0.1
+c-2,2.3-3.4,4.3-3.9,5.3c-2.6,4.1-5.5,10.4-8.3,20.4c0,0,1.6-3.7,4.5-7.8c-2.3,5.3-4.6,13.6-5,26.5c0.1-0.4,0.7-3.6,2.2-7.8
+c-0.4,8.6,0.6,19.2,6,31.1c3.2,7,10.6,21.3,28.6,32.5l0,0c0,0,6.1,4.6,16.7,8c0.8,0.3,1.6,0.6,2.4,0.8c-0.3-0.1-0.5-0.2-0.7-0.3
+c6,1.8,13.3,3.2,21.6,3.2c26,0,34.5-9.9,35.3-10.9v0c1.4-1.3,2.5-2.7,3.1-4.1h0c0.5-0.2,1-0.4,1.5-0.7c0.1,0,0.2-0.1,0.3-0.1
+c0.2-0.1,0.4-0.2,0.6-0.3c3.4-1.6,7.1-3.8,11-6.9c7-5.6,9-10.4,10.2-14.9v0c0.9-2.6,1-5.1,0.1-6.9c0.3-0.4,0.5-0.9,0.8-1.3
+c4.3-6.9,9-18.2,9.2-29.7c0,0,0,0,0,0c0-0.3,0-0.6,0-0.9C167.5,89.1,167.3,86.9,166.9,84.7z" />
 </vector>
\ No newline at end of file
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
@@ -1126,20 +1126,20 @@ public abstract class GeckoApp extends G
             // also happen if we're not the first activity to run within a session.
             mIsRestoringActivity = true;
             Telemetry.addToHistogram("FENNEC_RESTORING_ACTIVITY", 1);
 
         } else {
             final String action = intent.getAction();
             final String args = GeckoApplication.addDefaultGeckoArgs(
                     intent.getStringExtra("args"));
+            final int flags = ACTION_DEBUG.equals(action) ? GeckoThread.FLAG_DEBUGGING : 0;
 
             sAlreadyLoaded = true;
-            GeckoThread.initMainProcess(/* profile */ null, args,
-                                        /* debugging */ ACTION_DEBUG.equals(action));
+            GeckoThread.initMainProcess(/* profile */ null, args, flags);
 
             // Speculatively pre-fetch the profile in the background.
             ThreadUtils.postToBackgroundThread(new Runnable() {
                 @Override
                 public void run() {
                     getProfile();
                 }
             });
--- a/mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/topstories/PocketStoriesLoader.java
+++ b/mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/topstories/PocketStoriesLoader.java
@@ -3,27 +3,29 @@
  * 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.activitystream.homepanel.topstories;
 
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.net.Uri;
+import android.support.annotation.VisibleForTesting;
 import android.support.v4.content.AsyncTaskLoader;
 import android.text.TextUtils;
 import android.util.Log;
 
 import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
 import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.Locales;
 import org.mozilla.gecko.activitystream.homepanel.StreamRecyclerAdapter;
 import org.mozilla.gecko.activitystream.homepanel.model.TopStory;
+import org.mozilla.gecko.annotation.RobocopTarget;
 import org.mozilla.gecko.util.FileUtils;
 import org.mozilla.gecko.util.ProxySelector;
 
 import java.io.BufferedInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.HttpURLConnection;
 import java.net.URI;
@@ -48,16 +50,23 @@ import java.util.concurrent.TimeUnit;
  * and include the Pocket API token in the token file.
  */
 
 public class PocketStoriesLoader extends AsyncTaskLoader<List<TopStory>> {
     public static String LOGTAG = "PocketStoriesLoader";
 
     public static final String POCKET_REFERRER_URI = "https://getpocket.com/recommendations";
 
+    @RobocopTarget
+    @VisibleForTesting public static final String PLACEHOLDER_TITLE = "Placeholder ";
+    private static final String DEFAULT_PLACEHOLDER_URL = "https://www.mozilla.org/#";
+    static {
+        setPlaceholderUrl(DEFAULT_PLACEHOLDER_URL);
+    }
+
     // Pocket SharedPreferences keys
     private static final String POCKET_PREFS_FILE = "PocketStories";
     private static final String CACHE_TIMESTAMP_MILLIS_PREFIX = "timestampMillis-";
     private static final String STORIES_CACHE_PREFIX = "storiesCache-";
 
     // Pocket API params and defaults
     private static final String GLOBAL_ENDPOINT = "https://getpocket.cdn.mozilla.net/v3/firefox/global-recs";
     private static final String PARAM_APIKEY = "consumer_key";
@@ -67,29 +76,34 @@ public class PocketStoriesLoader extends
     private static final String PARAM_LOCALE = "locale_lang";
 
     private static final long REFRESH_INTERVAL_MILLIS = TimeUnit.HOURS.toMillis(1);
 
     private static final int BUFFER_SIZE = 2048;
     private static final int CONNECT_TIMEOUT = (int) TimeUnit.SECONDS.toMillis(15);
     private static final int READ_TIMEOUT = (int) TimeUnit.SECONDS.toMillis(15);
 
+    private static boolean isTesting = false;
+
     private String localeLang;
     private final SharedPreferences sharedPreferences;
 
+    private static String placeholderUrl;
+
     public PocketStoriesLoader(Context context) {
         super(context);
 
         sharedPreferences = context.getSharedPreferences(POCKET_PREFS_FILE, Context.MODE_PRIVATE);
         localeLang = Locales.getLanguageTag(Locale.getDefault());
     }
 
     @Override
     protected void onStartLoading() {
-        if (APIKEY == null) {
+        // We don't want to hit the network if we're testing.
+        if (APIKEY == null || isTesting) {
             deliverResult(makePlaceholderStories());
             return;
         }
         // Check timestamp to determine if we have cached stories. This won't properly handle a client manually
         // changing clock times, but this is not a time-sensitive task.
         final long previousTime = sharedPreferences.getLong(CACHE_TIMESTAMP_MILLIS_PREFIX + localeLang, 0);
         if (System.currentTimeMillis() - previousTime > REFRESH_INTERVAL_MILLIS) {
             forceLoad();
@@ -182,16 +196,30 @@ public class PocketStoriesLoader extends
         } catch (JSONException e) {
             Log.e(LOGTAG, "Couldn't load Pocket response", e);
         }
         return topStories;
     }
 
     private static List<TopStory> makePlaceholderStories() {
         final List<TopStory> stories = new LinkedList<>();
-        final String TITLE_PREFIX = "Placeholder ";
         for (int i = 0; i < DEFAULT_COUNT; i++) {
             // Urls must be different for bookmark/pinning UI to work properly. Assume this is true for Pocket stories.
-            stories.add(new TopStory(TITLE_PREFIX + i, "https://www.mozilla.org/#" + i, null));
+            stories.add(new TopStory(PLACEHOLDER_TITLE + i, placeholderUrl + i, null));
         }
         return stories;
     }
+
+    private static void setPlaceholderUrl(String placeholderUrl) {
+        // See use of placeholderUrl for why suffix is necessary.
+        final String requiredSuffix = "#";
+        if (!placeholderUrl.endsWith(requiredSuffix)) {
+            placeholderUrl = placeholderUrl + requiredSuffix;
+        }
+        PocketStoriesLoader.placeholderUrl = placeholderUrl;
+    }
+
+    @RobocopTarget
+    @VisibleForTesting public static void configureForTesting(final String placeholderUrl) {
+        isTesting = true;
+        setPlaceholderUrl(placeholderUrl);
+    }
 }
--- a/mobile/android/geckoview/src/main/aidl/org/mozilla/gecko/process/IChildProcess.aidl
+++ b/mobile/android/geckoview/src/main/aidl/org/mozilla/gecko/process/IChildProcess.aidl
@@ -4,12 +4,11 @@
 
 package org.mozilla.gecko.process;
 
 import org.mozilla.gecko.process.IProcessManager;
 
 import android.os.ParcelFileDescriptor;
 
 interface IChildProcess {
-    void stop();
     int getPid();
-    void start(in IProcessManager procMan, in String[] args, in ParcelFileDescriptor crashReporterPfd, in ParcelFileDescriptor ipcPfd);
+    boolean start(in IProcessManager procMan, in String[] args, in ParcelFileDescriptor crashReporterPfd, in ParcelFileDescriptor ipcPfd);
 }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
@@ -1805,21 +1805,16 @@ public class GeckoAppShell
             final WindowManager wm = (WindowManager)
                     getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
             final Display disp = wm.getDefaultDisplay();
             sScreenSize = new Rect(0, 0, disp.getWidth(), disp.getHeight());
         }
         return sScreenSize;
     }
 
-    @WrapForJNI
-    private static int startGeckoServiceChildProcess(String type, String[] args, int crashFd, int ipcFd) {
-        return GeckoProcessManager.getInstance().start(type, args, crashFd, ipcFd);
-    }
-
     @WrapForJNI(calledFrom = "gecko")
     public static int getAudioOutputFramesPerBuffer() {
         if (SysInfo.getVersion() < 17) {
             return 0;
         }
         final AudioManager am = (AudioManager)getApplicationContext()
                                 .getSystemService(Context.AUDIO_SERVICE);
         if (am == null) {
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java
@@ -3,16 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko;
 
 import org.mozilla.gecko.annotation.RobocopTarget;
 import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.mozglue.GeckoLoader;
+import org.mozilla.gecko.process.GeckoProcessManager;
 import org.mozilla.gecko.util.GeckoBundle;
 import org.mozilla.gecko.util.FileUtils;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.os.Handler;
@@ -111,65 +112,68 @@ public class GeckoThread extends Thread 
     private static MessageQueue msgQueue;
     @WrapForJNI
     private static int uiThreadId;
 
     private boolean mInitialized;
     private String[] mArgs;
 
     // Main process parameters
+    public static final int FLAG_DEBUGGING = 1; // Debugging mode.
+    public static final int FLAG_PRELOAD_CHILD = 2; // Preload child during main thread start.
+
     private GeckoProfile mProfile;
     private String mExtraArgs;
-    private boolean mDebugging;
+    private int mFlags;
 
     // Child process parameters
     private int mCrashFileDescriptor = -1;
     private int mIPCFileDescriptor = -1;
 
     GeckoThread() {
         setName("Gecko");
     }
 
     @WrapForJNI
     private static boolean isChildProcess() {
         return INSTANCE.mIPCFileDescriptor != -1;
     }
 
     private synchronized boolean init(final GeckoProfile profile, final String[] args,
-                                      final String extraArgs, final boolean debugging,
+                                      final String extraArgs, final int flags,
                                       final int crashFd, final int ipcFd) {
         ThreadUtils.assertOnUiThread();
         uiThreadId = android.os.Process.myTid();
 
         if (mInitialized) {
             return false;
         }
 
         mProfile = profile;
         mArgs = args;
         mExtraArgs = extraArgs;
-        mDebugging = debugging;
+        mFlags = flags;
         mCrashFileDescriptor = crashFd;
         mIPCFileDescriptor = ipcFd;
 
         mInitialized = true;
         notifyAll();
         return true;
     }
 
     public static boolean initMainProcess(final GeckoProfile profile, final String extraArgs,
-                                          final boolean debugging) {
-        return INSTANCE.init(profile, /* args */ null, extraArgs, debugging,
+                                          final int flags) {
+        return INSTANCE.init(profile, /* args */ null, extraArgs, flags,
                                  /* crashFd */ -1, /* ipcFd */ -1);
     }
 
     public static boolean initChildProcess(final String[] args, final int crashFd,
                                            final int ipcFd) {
         return INSTANCE.init(/* profile */ null, args, /* extraArgs */ null,
-                                 /* debugging */ false, crashFd, ipcFd);
+                                 /* flags */ 0, crashFd, ipcFd);
     }
 
     private static boolean canUseProfile(final Context context, final GeckoProfile profile,
                                          final String profileName, final File profileDir) {
         if (profileDir != null && !profileDir.isDirectory()) {
             return false;
         }
 
@@ -214,17 +218,17 @@ public class GeckoThread extends Thread 
 
         if (profile != null) {
             // We already have a compatible profile.
             return true;
         }
 
         // We haven't initialized yet; okay to initialize now.
         return initMainProcess(GeckoProfile.get(context, profileName, profileDir),
-                               args, /* debugging */ false);
+                               args, /* flags */ 0);
     }
 
     public static boolean launch() {
         ThreadUtils.assertOnUiThread();
 
         if (checkAndSetState(State.INITIAL, State.LAUNCHED)) {
             INSTANCE.start();
             return true;
@@ -359,38 +363,48 @@ public class GeckoThread extends Thread 
                 // Keep this IdleHandler
                 return true;
             }
         };
         Looper.myQueue().addIdleHandler(idleHandler);
 
         initGeckoEnvironment();
 
+        if ((mFlags & FLAG_PRELOAD_CHILD) != 0) {
+            ThreadUtils.postToBackgroundThread(new Runnable() {
+                @Override
+                public void run() {
+                    // Preload the content ("tab") child process.
+                    GeckoProcessManager.getInstance().preload("tab");
+                }
+            });
+        }
+
         // Wait until initialization before calling Gecko entry point.
         synchronized (this) {
             while (!mInitialized) {
                 try {
                     wait();
                 } catch (final InterruptedException e) {
                 }
             }
         }
 
         final String[] args = isChildProcess() ? mArgs : getMainProcessArgs();
 
-        if (mDebugging) {
+        if ((mFlags & FLAG_DEBUGGING) != 0) {
             try {
                 Thread.sleep(5 * 1000 /* 5 seconds */);
             } catch (final InterruptedException e) {
             }
         }
 
         Log.w(LOGTAG, "zerdatime " + SystemClock.elapsedRealtime() + " - runGecko");
 
-        if (mDebugging) {
+        if ((mFlags & FLAG_DEBUGGING) != 0) {
             Log.i(LOGTAG, "RunGecko - args = " + TextUtils.join(" ", args));
         }
 
         // And go.
         GeckoLoader.nativeRun(args, mCrashFileDescriptor, mIPCFileDescriptor);
 
         // And... we're done.
         final boolean restarting = isState(State.RESTARTING);
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
@@ -15,16 +15,17 @@ import java.util.Set;
 import org.mozilla.gecko.annotation.ReflectionTarget;
 import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.gfx.LayerView;
 import org.mozilla.gecko.mozglue.JNIObject;
 import org.mozilla.gecko.util.ActivityUtils;
 import org.mozilla.gecko.util.BundleEventListener;
 import org.mozilla.gecko.util.EventCallback;
 import org.mozilla.gecko.util.GeckoBundle;
+import org.mozilla.gecko.util.ThreadUtils;
 
 import android.app.Activity;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.res.Resources;
 import android.database.Cursor;
 import android.net.Uri;
@@ -457,41 +458,44 @@ public class GeckoView extends LayerView
     }
 
     /**
      * Preload GeckoView by starting Gecko in the background, if Gecko is not already running.
      *
      * @param context Activity or Application Context for starting GeckoView.
      */
     public static void preload(final Context context) {
-        preload(context, /* geckoArgs */ null);
+        preload(context, /* geckoArgs */ null, /* multiprocess */ false);
     }
 
     /**
      * Preload GeckoView by starting Gecko with the specified arguments in the background,
      * if Gecko is not already running.
      *
      * @param context Activity or Application Context for starting GeckoView.
-     * @param geckoArgs Arguments to be passed to Gecko, if Gecko is not already running
+     * @param geckoArgs Arguments to be passed to Gecko, if Gecko is not already running.
+     * @param multiprocess True if child process in multiprocess mode should be preloaded.
      */
-    public static void preload(final Context context, final String geckoArgs) {
+    public static void preload(final Context context, final String geckoArgs,
+                               final boolean multiprocess) {
         final Context appContext = context.getApplicationContext();
         if (GeckoAppShell.getApplicationContext() == null) {
             GeckoAppShell.setApplicationContext(appContext);
         }
 
-        if (GeckoThread.initMainProcess(/* profile */ null,
-                                        geckoArgs,
-                                        /* debugging */ false)) {
+        final int flags = multiprocess ? GeckoThread.FLAG_PRELOAD_CHILD : 0;
+        if (GeckoThread.initMainProcess(/* profile */ null, geckoArgs, flags)) {
             GeckoThread.launch();
         }
     }
 
     private void init(final Context context, final GeckoViewSettings settings) {
-        preload(context);
+        final boolean multiprocess = settings != null &&
+                                     settings.getBoolean(GeckoViewSettings.USE_MULTIPROCESS);
+        preload(context, /* geckoArgs */ null, multiprocess);
 
         initializeView();
         mListener.registerListeners();
 
         if (settings == null) {
             mSettings = new GeckoViewSettings(getEventDispatcher());
         } else {
             mSettings = settings;
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/GeckoProcessManager.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/GeckoProcessManager.java
@@ -9,28 +9,25 @@ import org.mozilla.gecko.IGeckoEditableP
 import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.mozglue.GeckoLoader;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
-import android.os.DeadObjectException;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
+import android.os.Process;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.support.v4.util.SimpleArrayMap;
-import android.view.Surface;
 import android.util.Log;
 
 import java.io.IOException;
-import java.util.Collections;
-import java.util.concurrent.TimeUnit;
-import java.util.Map;
 
 public final class GeckoProcessManager extends IProcessManager.Stub {
     private static final String LOGTAG = "GeckoProcessManager";
     private static final GeckoProcessManager INSTANCE = new GeckoProcessManager();
 
     public static GeckoProcessManager getInstance() {
         return INSTANCE;
     }
@@ -39,144 +36,185 @@ public final class GeckoProcessManager e
     private static native IGeckoEditableParent nativeGetEditableParent(long contentId,
                                                                        long tabId);
 
     @Override // IProcessManager
     public IGeckoEditableParent getEditableParent(final long contentId, final long tabId) {
         return nativeGetEditableParent(contentId, tabId);
     }
 
-    private static final class ChildConnection implements ServiceConnection, IBinder.DeathRecipient {
-        public final String mType;
-        private boolean mWait = false;
-        public IChildProcess mChild = null;
-        public int mPid = 0;
+    private static final class ChildConnection implements ServiceConnection,
+                                                          IBinder.DeathRecipient {
+        private static final int WAIT_TIMEOUT = 5000; // 5 seconds
+
+        private final String mType;
+        private boolean mWaiting;
+        private IChildProcess mChild;
+        private int mPid;
+
         public ChildConnection(String type) {
             mType = type;
         }
 
-        void prepareToWait() {
-            mWait = true;
+        public synchronized int getPid() {
+            if (mPid == 0) {
+                try {
+                    mPid = mChild.getPid();
+                } catch (final RemoteException e) {
+                    Log.e(LOGTAG, "Cannot get pid for " + mType, e);
+                }
+            }
+            return mPid;
         }
 
-        void waitForChild() {
-            ThreadUtils.assertNotOnUiThread();
-            synchronized (this) {
-                if (mWait) {
-                    try {
-                        this.wait(5000); // 5 seconds
-                    } catch (final InterruptedException e) {
-                        Log.e(LOGTAG, "Interrupted while waiting for child service", e);
-                    }
+        public synchronized IChildProcess bind() {
+            if (mChild != null) {
+                return mChild;
+            }
+
+            final Context context = GeckoAppShell.getApplicationContext();
+            final Intent intent = new Intent();
+            intent.setClassName(context,
+                                GeckoServiceChildProcess.class.getName() + '$' + mType);
+            GeckoLoader.addEnvironmentToIntent(intent);
+
+            if (context.bindService(intent, this, Context.BIND_AUTO_CREATE)) {
+                waitForChildLocked();
+                if (mChild != null) {
+                    return mChild;
                 }
             }
+
+            Log.e(LOGTAG, "Cannot connect to process " + mType);
+            context.unbindService(this);
+            return null;
+        }
+
+        public synchronized void unbind() {
+            final int pid = getPid();
+            if (pid != 0) {
+                Process.killProcess(pid);
+                waitForChildLocked();
+            }
         }
 
-        void clearWait() {
-            mWait = false;
+        private void waitForChildLocked() {
+            ThreadUtils.assertNotOnUiThread();
+            mWaiting = true;
+
+            final long startTime = SystemClock.uptimeMillis();
+            while (mWaiting && SystemClock.uptimeMillis() - startTime < WAIT_TIMEOUT) {
+                try {
+                    wait(WAIT_TIMEOUT);
+                } catch (final InterruptedException e) {
+                }
+            }
+            mWaiting = false;
         }
 
         @Override
-        public void onServiceConnected(ComponentName name, IBinder service) {
+        public synchronized void onServiceConnected(ComponentName name, IBinder service) {
             try {
                 service.linkToDeath(this, 0);
             } catch (final RemoteException e) {
-                Log.e(LOGTAG, "Failed to link ChildConnection to death of service.", e);
+                Log.e(LOGTAG, "Cannot link to death for " + mType, e);
             }
+
             mChild = IChildProcess.Stub.asInterface(service);
-            try {
-                mPid = mChild.getPid();
-            } catch (final RemoteException e) {
-                Log.e(LOGTAG, "Failed to get child " + mType + " process PID. Process may have died.", e);
-            }
-            synchronized (this) {
-                if (mWait) {
-                    mWait = false;
-                    this.notifyAll();
-                }
-            }
+            mWaiting = false;
+            notifyAll();
         }
 
         @Override
-        public void onServiceDisconnected(ComponentName name) {
-            synchronized (INSTANCE.mConnections) {
-                INSTANCE.mConnections.remove(mType);
-            }
-            synchronized (this) {
-                if (mWait) {
-                    mWait = false;
-                    this.notifyAll();
-                }
-            }
+        public synchronized void onServiceDisconnected(ComponentName name) {
+            mChild = null;
+            mPid = 0;
+            mWaiting = false;
+            notifyAll();
         }
 
         @Override
-        public void binderDied() {
-            Log.e(LOGTAG, "Binder died. Attempt to unbind service: " + mType + " " + mPid);
-            try {
+        public synchronized void binderDied() {
+            Log.i(LOGTAG, "Binder died for " + mType);
+            if (mChild != null) {
+                mChild = null;
                 GeckoAppShell.getApplicationContext().unbindService(this);
-            } catch (final java.lang.IllegalArgumentException e) {
-                Log.e(LOGTAG, "Looks like connection was already unbound", e);
             }
         }
     }
 
-    SimpleArrayMap<String, ChildConnection> mConnections;
+    private final SimpleArrayMap<String, ChildConnection> mConnections;
 
     private GeckoProcessManager() {
         mConnections = new SimpleArrayMap<String, ChildConnection>();
     }
 
-    public int start(String type, String[] args, int crashFd, int ipcFd) {
-        ChildConnection connection = null;
+    private ChildConnection getConnection(final String type) {
         synchronized (mConnections) {
-            connection = mConnections.get(type);
+            ChildConnection connection = mConnections.get(type);
+            if (connection == null) {
+                connection = new ChildConnection(type);
+                mConnections.put(type, connection);
+            }
+            return connection;
+        }
+    }
+
+    public void preload(final String... types) {
+        for (final String type : types) {
+            final ChildConnection connection = getConnection(type);
+            connection.bind();
+            connection.getPid();
         }
-        if (connection != null) {
-            Log.w(LOGTAG, "Attempting to start a child process service that is already running. Attempting to kill existing process first");
-            connection.prepareToWait();
-            try {
-                connection.mChild.stop();
-                connection.waitForChild();
-            } catch (final RemoteException e) {
-                connection.clearWait();
+    }
+
+    @WrapForJNI
+    private static int start(final String type, final String[] args,
+                             final int crashFd, final int ipcFd) {
+        return INSTANCE.start(type, args, crashFd, ipcFd, /* retry */ false);
+    }
+
+    private int start(final String type, final String[] args, final int crashFd,
+                      final int ipcFd, final boolean retry) {
+        final ChildConnection connection = getConnection(type);
+        final IChildProcess child = connection.bind();
+        if (child == null) {
+            return 0;
+        }
+
+        final ParcelFileDescriptor crashPfd;
+        final ParcelFileDescriptor ipcPfd;
+        try {
+            crashPfd = (crashFd >= 0) ? ParcelFileDescriptor.fromFd(crashFd) : null;
+            ipcPfd = ParcelFileDescriptor.fromFd(ipcFd);
+        } catch (final IOException e) {
+            Log.e(LOGTAG, "Cannot create fd for " + type, e);
+            return 0;
+        }
+
+        boolean started = false;
+        try {
+            started = child.start(this, args, crashPfd, ipcPfd);
+        } catch (final RemoteException e) {
+        }
+
+        if (!started) {
+            if (retry) {
+                Log.e(LOGTAG, "Cannot restart child " + type);
+                return 0;
             }
+            Log.w(LOGTAG, "Attempting to kill running child " + type);
+            connection.unbind();
+            return start(type, args, crashFd, ipcFd, /* retry */ true);
         }
 
         try {
-            connection = new ChildConnection(type);
-            Intent intent = new Intent();
-            intent.setClassName(GeckoAppShell.getApplicationContext(),
-                                "org.mozilla.gecko.process.GeckoServiceChildProcess$" + type);
-            GeckoLoader.addEnvironmentToIntent(intent);
-            connection.prepareToWait();
-            GeckoAppShell.getApplicationContext().bindService(intent, connection, Context.BIND_AUTO_CREATE);
-            connection.waitForChild();
-            if (connection.mChild == null) {
-                // FAILED TO CONNECT.
-                Log.e(LOGTAG, "Failed to connect to child process of '" + type + "'");
-                GeckoAppShell.getApplicationContext().unbindService(connection);
-                return 0;
-            }
-            ParcelFileDescriptor crashPfd = null;
-            if (crashFd >= 0) {
-                crashPfd = ParcelFileDescriptor.fromFd(crashFd);
-            }
-            ParcelFileDescriptor ipcPfd = ParcelFileDescriptor.fromFd(ipcFd);
-            connection.mChild.start(this, args, crashPfd, ipcPfd);
             if (crashPfd != null) {
                 crashPfd.close();
             }
             ipcPfd.close();
-            synchronized (mConnections) {
-                mConnections.put(type, connection);
-            }
-            return connection.mPid;
-        } catch (final RemoteException e) {
-            Log.e(LOGTAG, "Unable to create child process for: '" + type + "'. Remote Exception:", e);
         } catch (final IOException e) {
-            Log.e(LOGTAG, "Unable to create child process for: '" + type + "'. Error creating ParcelFileDescriptor needed to create intent:", e);
         }
 
-        return 0;
+        return connection.getPid();
     }
 
 } // GeckoProcessManager
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/GeckoServiceChildProcess.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/GeckoServiceChildProcess.java
@@ -1,16 +1,15 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; 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.process;
 
-import org.mozilla.gecko.annotation.JNITarget;
 import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.IGeckoEditableParent;
 import org.mozilla.gecko.mozglue.GeckoLoader;
 import org.mozilla.gecko.GeckoThread;
 import org.mozilla.gecko.mozglue.SafeIntent;
 import org.mozilla.gecko.util.ThreadUtils;
 
@@ -24,94 +23,85 @@ import android.os.RemoteException;
 import android.util.Log;
 
 public class GeckoServiceChildProcess extends Service {
 
     static private String LOGTAG = "GeckoServiceChildProcess";
 
     private static IProcessManager sProcessManager;
 
-    static private void stop() {
-        ThreadUtils.postToUiThread(new Runnable() {
-            @Override
-            public void run() {
-                Process.killProcess(Process.myPid());
-            }
-        });
-    }
-
     @WrapForJNI(calledFrom = "gecko")
     private static IGeckoEditableParent getEditableParent(final long contentId,
                                                           final long tabId) {
         try {
             return sProcessManager.getEditableParent(contentId, tabId);
         } catch (final RemoteException e) {
             Log.e(LOGTAG, "Cannot get editable", e);
             return null;
         }
     }
 
+    @Override
     public void onCreate() {
         super.onCreate();
-    }
 
-    public void onDestroy() {
-        super.onDestroy();
+        GeckoAppShell.ensureCrashHandling();
+        GeckoAppShell.setApplicationContext(getApplicationContext());
     }
 
+    @Override
     public int onStartCommand(final Intent intent, final int flags, final int startId) {
-        return Service.START_STICKY;
+        return Service.START_NOT_STICKY;
     }
 
-    private Binder mBinder = new IChildProcess.Stub() {
-        @Override
-        public void stop() {
-            GeckoServiceChildProcess.stop();
-        }
-
+    private final Binder mBinder = new IChildProcess.Stub() {
         @Override
         public int getPid() {
             return Process.myPid();
         }
 
         @Override
-        public void start(final IProcessManager procMan,
-                          final String[] args,
-                          final ParcelFileDescriptor crashReporterPfd,
-                          final ParcelFileDescriptor ipcPfd) {
-            if (sProcessManager != null) {
-                Log.e(LOGTAG, "Attempting to start a service that has already been started.");
-                return;
+        public boolean start(final IProcessManager procMan,
+                             final String[] args,
+                             final ParcelFileDescriptor crashReporterPfd,
+                             final ParcelFileDescriptor ipcPfd) {
+            synchronized (GeckoServiceChildProcess.class) {
+                if (sProcessManager != null) {
+                    Log.e(LOGTAG, "Child process already started");
+                    return false;
+                }
+                sProcessManager = procMan;
             }
-            sProcessManager = procMan;
 
-            final int crashReporterFd = crashReporterPfd != null ? crashReporterPfd.detachFd() : -1;
+            final int crashReporterFd = crashReporterPfd != null ?
+                                        crashReporterPfd.detachFd() : -1;
             final int ipcFd = ipcPfd != null ? ipcPfd.detachFd() : -1;
+
             ThreadUtils.postToUiThread(new Runnable() {
                 @Override
                 public void run() {
-                    GeckoAppShell.ensureCrashHandling();
-                    GeckoAppShell.setApplicationContext(getApplicationContext());
                     if (GeckoThread.initChildProcess(args, crashReporterFd, ipcFd)) {
                         GeckoThread.launch();
                     }
                 }
             });
+            return true;
         }
     };
 
+    @Override
     public IBinder onBind(final Intent intent) {
         GeckoLoader.setLastIntent(new SafeIntent(intent));
+        GeckoThread.launch(); // Preload Gecko.
         return mBinder;
     }
 
+    @Override
     public boolean onUnbind(Intent intent) {
         Log.i(LOGTAG, "Service has been unbound. Stopping.");
-        stop();
+        Process.killProcess(Process.myPid());
         return false;
     }
 
-    @JNITarget
-    static public final class geckomediaplugin extends GeckoServiceChildProcess {}
+    public static final class geckomediaplugin extends GeckoServiceChildProcess {}
 
-    @JNITarget
-    static public final class tab extends GeckoServiceChildProcess {}
+    public static final class tab extends GeckoServiceChildProcess {}
 }
--- a/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java
+++ b/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java
@@ -50,33 +50,37 @@ public class GeckoViewActivity extends A
         if (!TextUtils.isEmpty(intentArgs)) {
             if (geckoArgs == null) {
                 geckoArgs = intentArgs;
             } else {
                 geckoArgs += " " + intentArgs;
             }
         }
 
-        GeckoView.preload(this, geckoArgs);
+        final boolean useMultiprocess = getIntent().getBooleanExtra(USE_MULTIPROCESS_EXTRA,
+                                                                    true);
+        GeckoView.preload(this, geckoArgs, useMultiprocess);
 
         setContentView(R.layout.geckoview_activity);
 
         mGeckoView = (GeckoView) findViewById(R.id.gecko_view);
         mGeckoView.setContentListener(new MyGeckoViewContent());
         mGeckoView.setProgressListener(new MyGeckoViewProgress());
         mGeckoView.setNavigationListener(new Navigation());
 
         final BasicGeckoViewPrompt prompt = new BasicGeckoViewPrompt();
         prompt.filePickerRequestCode = REQUEST_FILE_PICKER;
         mGeckoView.setPromptDelegate(prompt);
 
         final MyGeckoViewPermission permission = new MyGeckoViewPermission();
         permission.androidPermissionRequestCode = REQUEST_PERMISSIONS;
         mGeckoView.setPermissionDelegate(permission);
 
+        mGeckoView.getSettings().setBoolean(GeckoViewSettings.USE_MULTIPROCESS,
+                                            useMultiprocess);
         loadSettings(getIntent());
         loadFromIntent(getIntent());
     }
 
     @Override
     protected void onNewIntent(final Intent intent) {
         super.onNewIntent(intent);
         setIntent(intent);
@@ -89,19 +93,16 @@ public class GeckoViewActivity extends A
 
     private void loadFromIntent(final Intent intent) {
         final Uri uri = intent.getData();
         mGeckoView.loadUri(uri != null ? uri.toString() : DEFAULT_URL);
     }
 
     private void loadSettings(final Intent intent) {
         mGeckoView.getSettings().setBoolean(
-            GeckoViewSettings.USE_MULTIPROCESS,
-            intent.getBooleanExtra(USE_MULTIPROCESS_EXTRA, true));
-        mGeckoView.getSettings().setBoolean(
             GeckoViewSettings.USE_REMOTE_DEBUGGER,
             intent.getBooleanExtra(USE_REMOTE_DEBUGGER_EXTRA, false));
     }
 
     @Override
     protected void onActivityResult(final int requestCode, final int resultCode,
                                     final Intent data) {
         if (requestCode == REQUEST_FILE_PICKER) {
--- a/mobile/android/tests/browser/robocop/robocop.ini
+++ b/mobile/android/tests/browser/robocop/robocop.ini
@@ -103,16 +103,17 @@ skip-if = android_version == "18"
 # disabled on 4.3, bug 1098532
 skip-if = android_version == "18"
 [src/org/mozilla/gecko/tests/testAndroidCastDeviceProvider.java]
 
 # Using UITest
 [src/org/mozilla/gecko/tests/testAboutHomePageNavigation.java]
 disabled=see bug 947550, bug 979038 and bug 977952
 [src/org/mozilla/gecko/tests/testAboutHomeVisibility.java]
+[src/org/mozilla/gecko/tests/testActivityStreamPocketReferrer.java]
 [src/org/mozilla/gecko/tests/testAppMenuPathways.java]
 [src/org/mozilla/gecko/tests/testBackButtonInEditMode.java]
 [src/org/mozilla/gecko/tests/testBrowserDatabaseHelperUpgrades.java]
 [src/org/mozilla/gecko/tests/testEventDispatcher.java]
 [src/org/mozilla/gecko/tests/testInputConnection.java]
 [src/org/mozilla/gecko/tests/testJavascriptBridge.java]
 [src/org/mozilla/gecko/tests/testReaderCacheMigration.java]
 [src/org/mozilla/gecko/tests/testReadingListToBookmarksMigration.java]
new file mode 100644
--- /dev/null
+++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testActivityStreamPocketReferrer.java
@@ -0,0 +1,133 @@
+/* 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.tests;
+
+import android.util.Log;
+import com.robotium.solo.Condition;
+import org.mozilla.gecko.activitystream.homepanel.topstories.PocketStoriesLoader;
+import org.mozilla.gecko.tests.helpers.NavigationHelper;
+import org.mozilla.gecko.tests.helpers.WaitHelper;
+import org.mozilla.gecko.util.StringUtils;
+
+import static org.mozilla.gecko.tests.helpers.AssertionHelper.*;
+
+/**
+ * It's very important that suggestions from Pocket are opened with a Pocket URI in the referrer: this is
+ * what this test is for.
+ *
+ * We want to verify:
+ * - Clicking or long clicking a Pocket top story has a Pocket referrer.
+ * - Clicking or long clicking other items on top sites does not have a Pocket referrer.
+ *
+ * The ideal test is to set up a server that will assert that clicks that should have a Pocket referrer
+ * have one in the request headers and clicks that should not have a referrer do not have one. However,
+ * it's non-trivial to set up such a server in our harness so we instead intercept Tab:Load JS events
+ * and verify they have the referrer. This isn't ideal because we might drop the ball passing the referrer
+ * to Gecko, or Gecko might drop the ball, but this test should help regressions if we manually test the
+ * referrers are working.
+ */
+public class testActivityStreamPocketReferrer extends JavascriptBridgeTest {
+
+    private static final String LOGTAG =
+            StringUtils.safeSubstring(testActivityStreamPocketReferrer.class.getSimpleName(), 0, 23);
+
+    private static final String JS_FILE = "testActivityStreamPocketReferrer.js";
+
+    private boolean wasTabLoadReceived = false;
+    private boolean tabLoadContainsPocketReferrer = false;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        // Override the default placeholder URL so we don't access the network during testing.
+        // Note: this actually only seems to take effect after we load a page and go back to about:home.
+        PocketStoriesLoader.configureForTesting(getAbsoluteHostnameUrl(StringHelper.get().ROBOCOP_BLANK_PAGE_01_URL));
+    }
+
+    public void testActivityStreamPocketReferrer() throws Exception {
+        blockForReadyAndLoadJS(JS_FILE);
+        NavigationHelper.goBack(); // to top sites.
+
+        checkReferrerInTopStories();
+        checkReferrerInTopStoriesContextMenu();
+
+        checkNoReferrerInTopSites(); // relies on changes to Top Sites from previous tests.
+
+        // Ideally, we'd also test that there is no referrer for highlights but it's more
+        // challenging to get an item to show up in highlights (bookmark the page) and to scroll
+        // to open it: to save time, I chose not to implement it.
+    }
+
+    private void checkReferrerInTopStories() {
+        Log.d(LOGTAG, "testReferrerInTopStories");
+
+        WaitHelper.waitForPageLoad(new Runnable() {
+            @Override
+            public void run() {
+                mSolo.clickOnText(PocketStoriesLoader.PLACEHOLDER_TITLE); // Click Top Story placeholder item.
+            }
+        });
+
+        assertTabLoadEventContainsPocketReferrer(true);
+        NavigationHelper.goBack(); // to top sites.
+    }
+
+    private void checkReferrerInTopStoriesContextMenu() throws Exception {
+        Log.d(LOGTAG, "testReferrerInTopStoriesContextMenu");
+
+        mSolo.clickLongOnText(PocketStoriesLoader.PLACEHOLDER_TITLE); // Open Top Story context menu.
+        mSolo.clickOnText(StringHelper.get().CONTEXT_MENU_OPEN_IN_NEW_TAB);
+        WaitHelper.waitFor("context menu to close after item selection.", new Condition() {
+            @Override
+            public boolean isSatisfied() {
+                return !mSolo.searchText(StringHelper.get().CONTEXT_MENU_OPEN_IN_NEW_TAB);
+            }
+        }, 5000);
+
+        // There's no simple way to block until a background page loads so instead, we sleep for 500ms.
+        // Our JS listener is attached the whole time so if the message is sent, we'll receive it and cache it
+        // while we're sleeping.
+        Thread.sleep(500);
+        assertTabLoadEventContainsPocketReferrer(true);
+    }
+
+    private void checkNoReferrerInTopSites() {
+        Log.d(LOGTAG, "testNoReferrerInTopSites");
+
+        WaitHelper.waitForPageLoad(new Runnable() {
+            @Override
+            public void run() {
+                // Through the previous tests, we've added a top site called "Browser Blank Page...".
+                // Only part of that label will be visible, however.
+                mSolo.clickOnText("Browser Bl"); // Click on a Top Site.
+            }
+        });
+
+        assertTabLoadEventContainsPocketReferrer(false);
+        NavigationHelper.goBack(); // to top sites.
+    }
+
+    private void assertTabLoadEventContainsPocketReferrer(final boolean expectedContainsReferrer) {
+        // We intercept the Tab:Load event in JS and, due to limitations in JavascriptBridge,
+        // store the data there until Java asks for it.
+        getJS().syncCall("copyTabLoadEventMetadataToJava"); // expected to call copyTabLoad...Receiver
+
+        fAssertTrue("Expected Tab:Load to be called", wasTabLoadReceived);
+        fAssertEquals("Checking for expected existence of pocket referrer from Tab:Load event in JS",
+                expectedContainsReferrer, tabLoadContainsPocketReferrer);
+    }
+
+    // JS methods.
+    public void copyTabLoadEventMetadataToJavaReceiver(final boolean wasTabLoadReceived, final boolean tabLoadContainsPocketReferrer) {
+        Log.d(LOGTAG, "setTabLoadContainsPocketReferrer called via JS: " + wasTabLoadReceived + ", " + tabLoadContainsPocketReferrer);
+        this.wasTabLoadReceived = wasTabLoadReceived;
+        this.tabLoadContainsPocketReferrer = tabLoadContainsPocketReferrer;
+    }
+
+    public void log(final String s) {
+        Log.d(LOGTAG, "jsLog: " + s);
+    }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/tests/browser/robocop/testActivityStreamPocketReferrer.js
@@ -0,0 +1,44 @@
+/* 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/. */
+
+Components.utils.import("resource://gre/modules/Messaging.jsm");
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+let java = new JavaBridge(this);
+do_register_cleanup(() => {
+    EventDispatcher.instance.unregisterListener(listener);
+
+    java.disconnect();
+});
+do_test_pending();
+
+var wasTabLoadReceived = false;
+var tabLoadContainsPocketReferrer = false;
+
+let listener = {
+    onEvent: function(event, data, callback) {
+        java.asyncCall("log", "Tab:Load url: " + data.url);
+        java.asyncCall("log", "Tab:Load referrerURI: " + data.referrerURI);
+        if (event !== "Tab:Load" ||
+                data.url === "about:home") {
+            return;
+        }
+
+        wasTabLoadReceived = true;
+        if (data.referrerURI && data.referrerURI.search("pocket") > 0) {
+            tabLoadContainsPocketReferrer = true;
+        } else {
+            tabLoadContainsPocketReferrer = false;
+        }
+    }
+};
+
+let win = Services.wm.getMostRecentWindow("navigator:browser");
+EventDispatcher.for(win).registerListener(listener, ["Tab:Load"]);
+
+// Java functions.
+function copyTabLoadEventMetadataToJava() {
+    java.syncCall("copyTabLoadEventMetadataToJavaReceiver", wasTabLoadReceived, tabLoadContainsPocketReferrer);
+    wasTabLoadReceived = false;
+}
--- a/netwerk/protocol/res/SubstitutingProtocolHandler.cpp
+++ b/netwerk/protocol/res/SubstitutingProtocolHandler.cpp
@@ -420,17 +420,37 @@ SubstitutingProtocolHandler::ResolveURI(
 
   // Some code relies on an empty path resolving to a file rather than a
   // directory.
   NS_ASSERTION(path.CharAt(0) == '/', "Path must begin with '/'");
   if (path.Length() == 1) {
     rv = baseURI->GetSpec(result);
   } else {
     // Make sure we always resolve the path as file-relative to our target URI.
-    path.Insert('.', 0);
+    // When the baseURI is a nsIFileURL, and the directory it points to doesn't
+    // exist, it doesn't end with a /. In that case, a file-relative resolution
+    // is going to pick something in the parent directory, so we resolve using
+    // an absolute path derived from the full path in that case.
+    nsCOMPtr<nsIFileURL> baseDir = do_QueryInterface(baseURI);
+    if (baseDir) {
+      nsAutoCString basePath;
+      rv = baseURI->GetFilePath(basePath);
+      if (NS_SUCCEEDED(rv) && !StringEndsWith(basePath, NS_LITERAL_CSTRING("/"))) {
+        // Cf. the assertion above, path already starts with a /, so prefixing
+        // with a string that doesn't end with one will leave us wit the right
+        // amount of /.
+        path.Insert(basePath, 0);
+      } else {
+        // Allow to fall through below.
+        baseDir = nullptr;
+      }
+    }
+    if (!baseDir) {
+      path.Insert('.', 0);
+    }
     rv = baseURI->Resolve(path, result);
   }
 
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (MOZ_LOG_TEST(gResLog, LogLevel::Debug)) {
--- a/netwerk/test/httpserver/test/test_basic_functionality.js
+++ b/netwerk/test/httpserver/test/test_basic_functionality.js
@@ -30,17 +30,17 @@ var srv;
 function run_test()
 {
   srv = createServer();
 
   // base path
   // XXX should actually test this works with a file by comparing streams!
   var dirServ = Cc["@mozilla.org/file/directory_service;1"]
                   .getService(Ci.nsIProperties);
-  var path = dirServ.get("CurProcD", Ci.nsIFile);
+  var path = dirServ.get("CurWorkD", Ci.nsIFile);
   srv.registerDirectory("/", path);
 
   // register a few test paths
   srv.registerPathHandler("/objHandler", objHandler);
   srv.registerPathHandler("/functionHandler", functionHandler);
   srv.registerPathHandler("/lotsOfHeaders", lotsOfHeadersHandler);
 
   srv.start(-1);
--- a/netwerk/test/unit/test_bug337744.js
+++ b/netwerk/test/unit/test_bug337744.js
@@ -89,26 +89,32 @@ function run_test() {
   // resource:/// and resource://gre/ are resolved specially, so we need
   // to create a temporary resource package to test the standard logic
   // with.
 
   let resProto = Cc['@mozilla.org/network/protocol;1?name=resource'].getService(Ci.nsIResProtocolHandler);
   let rootFile = Services.dirsvc.get("GreD", Ci.nsIFile);
   let rootURI = Services.io.newFileURI(rootFile);
 
+  rootFile.append("directory-that-does-not-exist");
+  let inexistentURI = Services.io.newFileURI(rootFile);
+
   resProto.setSubstitution("res-test", rootURI);
+  resProto.setSubstitution("res-inexistent", inexistentURI);
   do_register_cleanup(() => {
     resProto.setSubstitution("res-test", null);
+    resProto.setSubstitution("res-inexistent", null);
   });
 
   let baseRoot = resProto.resolveURI(Services.io.newURI("resource:///"));
   let greRoot = resProto.resolveURI(Services.io.newURI("resource://gre/"));
 
   for (var spec of specs) {
     check_safe_resolution(spec, rootURI.spec);
+    check_safe_resolution(spec.replace("res-test", "res-inexistent"), inexistentURI.spec);
     check_safe_resolution(spec.replace("res-test", ""), baseRoot);
     check_safe_resolution(spec.replace("res-test", "gre"), greRoot);
   }
 
   for (var spec of error_specs) {
     check_resolution_error(spec);
   }
 }
--- a/old-configure.in
+++ b/old-configure.in
@@ -4053,25 +4053,16 @@ AC_DEFINE_UNQUOTED(JS_DEFAULT_JITREPORT_
 dnl ========================================================
 dnl =
 dnl = Misc. Options
 dnl =
 dnl ========================================================
 MOZ_ARG_HEADER(Misc. Options)
 
 dnl ========================================================
-dnl = Define default location for MOZILLA_FIVE_HOME
-dnl ========================================================
-MOZ_ARG_WITH_STRING(default-mozilla-five-home,
-[  --with-default-mozilla-five-home
-                          Set the default value for MOZILLA_FIVE_HOME],
-[ val=`echo $withval`
-  AC_DEFINE_UNQUOTED(MOZ_DEFAULT_MOZILLA_FIVE_HOME,"$val") ])
-
-dnl ========================================================
 dnl = Location of the mozilla user directory (default is ~/.mozilla).],
 dnl ========================================================
 MOZ_ARG_WITH_STRING(user-appdir,
 [  --with-user-appdir=DIR  Set user-specific appdir (default=.mozilla)],
 [ val=`echo $withval`
 if echo "$val" | grep "\/" >/dev/null; then
     AC_MSG_ERROR("Homedir must be single relative path.")
 else
--- a/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp
+++ b/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp
@@ -3,19 +3,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/. */
 
 #include "SandboxBrokerPolicyFactory.h"
 #include "SandboxInfo.h"
 #include "SandboxLogging.h"
 
+#include "mozilla/Array.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/SandboxSettings.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/UniquePtrExtensions.h"
 #include "mozilla/dom/ContentChild.h"
 #include "nsPrintfCString.h"
 #include "nsString.h"
 #include "nsThreadUtils.h"
 #include "nsXULAppAPI.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "SpecialSystemDirectory.h"
@@ -23,27 +26,68 @@
 #ifdef ANDROID
 #include "cutils/properties.h"
 #endif
 
 #ifdef MOZ_WIDGET_GTK
 #include <glib.h>
 #endif
 
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
 namespace mozilla {
 
 #if defined(MOZ_CONTENT_SANDBOX)
 namespace {
 static const int rdonly = SandboxBroker::MAY_READ;
 static const int wronly = SandboxBroker::MAY_WRITE;
 static const int rdwr = rdonly | wronly;
 static const int rdwrcr = rdwr | SandboxBroker::MAY_CREATE;
 }
 #endif
 
+static void
+AddMesaSysfsPaths(SandboxBroker::Policy* aPolicy)
+{
+  // Bug 1384178: Mesa driver loader
+  aPolicy->AddPrefix(rdonly, "/sys/dev/char/226:");
+
+  // Bug 1401666: Mesa driver loader part 2: Mesa <= 12 using libudev
+  if (auto dir = opendir("/dev/dri")) {
+    while (auto entry = readdir(dir)) {
+      if (entry->d_name[0] != '.') {
+        nsPrintfCString devPath("/dev/dri/%s", entry->d_name);
+        struct stat sb;
+        if (stat(devPath.get(), &sb) == 0 && S_ISCHR(sb.st_mode)) {
+          // For both the DRI node and its parent (the physical
+          // device), allow reading the "uevent" file.
+          static const Array<const char*, 2> kSuffixes = { "", "/device" };
+          for (const auto suffix : kSuffixes) {
+            nsPrintfCString sysPath("/sys/dev/char/%u:%u%s",
+                                    major(sb.st_rdev),
+                                    minor(sb.st_rdev),
+                                    suffix);
+            // libudev will expand the symlink but not do full
+            // canonicalization, so it will leave in ".." path
+            // components that will be realpath()ed in the
+            // broker.  To match this, allow the canonical paths.
+            UniqueFreePtr<char[]> realSysPath(realpath(sysPath.get(), nullptr));
+            if (realSysPath) {
+              nsPrintfCString ueventPath("%s/uevent", realSysPath.get());
+              aPolicy->AddPath(rdonly, ueventPath.get());
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
 SandboxBrokerPolicyFactory::SandboxBrokerPolicyFactory()
 {
   // Policy entries that are the same in every process go here, and
   // are cached over the lifetime of the factory.
 #if defined(MOZ_CONTENT_SANDBOX)
   SandboxBroker::Policy* policy = new SandboxBroker::Policy;
   policy->AddDir(rdwrcr, "/dev/shm");
   // Write permssions
@@ -115,18 +159,17 @@ SandboxBrokerPolicyFactory::SandboxBroke
   policy->AddDir(rdonly, "/usr/tmp");
   policy->AddDir(rdonly, "/var/tmp");
   // Various places where fonts reside
   policy->AddDir(rdonly, "/usr/X11R6/lib/X11/fonts");
   policy->AddDir(rdonly, "/nix/store");
   policy->AddDir(rdonly, "/run/host/fonts");
   policy->AddDir(rdonly, "/run/host/user-fonts");
 
-  // Bug 1384178: Mesa driver loader
-  policy->AddPrefix(rdonly, "/sys/dev/char/226:");
+  AddMesaSysfsPaths(policy);
 
   // Bug 1385715: NVIDIA PRIME support
   policy->AddPath(rdonly, "/proc/modules");
 
 #ifdef MOZ_PULSEAUDIO
   // See bug 1384986 comment #1.
   if (const auto xauth = PR_GetEnv("XAUTHORITY")) {
     policy->AddPath(rdonly, xauth);
--- a/services/sync/modules/engines/history.js
+++ b/services/sync/modules/engines/history.js
@@ -270,26 +270,33 @@ HistoryStore.prototype = {
 
       if (!visit.type ||
           !Object.values(PlacesUtils.history.TRANSITIONS).includes(visit.type)) {
         this._log.warn("Encountered record with invalid visit type: " +
                        visit.type + "; ignoring.");
         continue;
       }
 
-      // Dates need to be integers.
-      visit.date = Math.round(visit.date);
+      // Dates need to be integers. Future and far past dates are clamped to the
+      // current date and earliest sensible date, respectively.
+      let originalVisitDate = PlacesUtils.toDate(Math.round(visit.date));
+      visit.date = PlacesSyncUtils.history.clampVisitDate(originalVisitDate);
 
-      if (curVisits.indexOf(visit.date + "," + visit.type) != -1) {
+      let visitDateAsPRTime = PlacesUtils.toPRTime(visit.date);
+      let visitKey = visitDateAsPRTime + "," + visit.type;
+      if (curVisits.indexOf(visitKey) != -1) {
         // Visit is a dupe, don't increment 'k' so the element will be
         // overwritten.
         continue;
       }
 
-      visit.date = PlacesUtils.toDate(visit.date);
+      // Note the visit key, so that we don't add duplicate visits with
+      // clamped timestamps.
+      curVisits.push(visitKey);
+
       visit.transition = visit.type;
       k += 1;
     }
     record.visits.length = k; // truncate array
 
     // No update if there aren't any visits to apply.
     // mozIAsyncHistory::updatePlaces() wants at least one visit.
     // In any case, the only thing we could change would be the title
--- a/services/sync/tests/unit/test_addons_store.js
+++ b/services/sync/tests/unit/test_addons_store.js
@@ -32,16 +32,17 @@ function loadSystemAddon() {
   systemAddonFile.lastModifiedTime = Date.now();
   // As we're not running in application, we need to setup the features directory
   // used by system add-ons.
   registerDirectory("XREAppFeat", distroDir);
 }
 
 loadAddonTestFunctions();
 loadSystemAddon();
+awaitPromise(overrideBuiltIns({ "system": [SYSTEM_ADDON_ID] }));
 startupManager();
 
 let engine;
 let tracker;
 let store;
 let reconciler;
 
 /**
--- a/services/sync/tests/unit/test_history_store.js
+++ b/services/sync/tests/unit/test_history_store.js
@@ -1,12 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://testing-common/PlacesTestUtils.jsm");
+Cu.import("resource://gre/modules/PlacesSyncUtils.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://services-common/async.js");
 Cu.import("resource://services-common/utils.js");
 Cu.import("resource://services-sync/engines/history.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
 
 const TIMESTAMP1 = (Date.now() - 103406528) * 1000;
@@ -54,16 +55,22 @@ function promiseOnVisitObserved() {
         Ci.nsINavHistoryObserver,
         Ci.nsINavHistoryObserver_MOZILLA_1_9_1_ADDITIONS,
         Ci.nsISupportsWeakReference
       ])
     }, true);
   });
 }
 
+function isDateApproximately(actual, expected, skewMillis = 1000) {
+  let lowerBound = expected - skewMillis;
+  let upperBound = expected + skewMillis;
+  return actual >= lowerBound && actual <= upperBound;
+}
+
 var engine = new HistoryEngine(Service);
 Async.promiseSpinningly(engine.initialize());
 var store = engine._store;
 async function applyEnsureNoFailures(records) {
   do_check_eq((await store.applyIncomingBatch(records)).length, 0);
 }
 
 var fxuri, fxguid, tburi, tbguid;
@@ -251,16 +258,90 @@ add_task(async function test_invalid_rec
   await applyEnsureNoFailures([
     {id: Utils.makeGUID(),
      histUri: "http://getfirebug.com",
      title: "Get Firebug!",
      visits: []}
   ]);
 });
 
+add_task(async function test_clamp_visit_dates() {
+  let futureVisitTime = Date.now() + 5 * 60 * 1000;
+  let recentVisitTime = Date.now() - 5 * 60 * 1000;
+
+  await applyEnsureNoFailures([{
+    id: "visitAAAAAAA",
+    histUri: "http://example.com/a",
+    title: "A",
+    visits: [{
+      date: "invalidDate",
+      type: Ci.nsINavHistoryService.TRANSITION_LINK,
+    }],
+  }, {
+    id: "visitBBBBBBB",
+    histUri: "http://example.com/b",
+    title: "B",
+    visits: [{
+      date: 100,
+      type: Ci.nsINavHistoryService.TRANSITION_TYPED,
+    }, {
+      date: 250,
+      type: Ci.nsINavHistoryService.TRANSITION_TYPED,
+    }, {
+      date: recentVisitTime * 1000,
+      type: Ci.nsINavHistoryService.TRANSITION_TYPED,
+    }],
+  }, {
+    id: "visitCCCCCCC",
+    histUri: "http://example.com/c",
+    title: "D",
+    visits: [{
+      date: futureVisitTime * 1000,
+      type: Ci.nsINavHistoryService.TRANSITION_BOOKMARK,
+    }],
+  }, {
+    id: "visitDDDDDDD",
+    histUri: "http://example.com/d",
+    title: "D",
+    visits: [{
+      date: recentVisitTime * 1000,
+      type: Ci.nsINavHistoryService.TRANSITION_DOWNLOAD,
+    }],
+  }]);
+
+  let visitsForA = await PlacesSyncUtils.history.fetchVisitsForURL(
+    "http://example.com/a");
+  deepEqual(visitsForA, [], "Should ignore visits with invalid dates");
+
+  let visitsForB = await PlacesSyncUtils.history.fetchVisitsForURL(
+    "http://example.com/b");
+  deepEqual(visitsForB, [{
+    date: recentVisitTime * 1000,
+    type: Ci.nsINavHistoryService.TRANSITION_TYPED,
+  }, {
+    // We should clamp visit dates older than original Mosaic release.
+    date: PlacesSyncUtils.bookmarks.EARLIEST_BOOKMARK_TIMESTAMP * 1000,
+    type: Ci.nsINavHistoryService.TRANSITION_TYPED,
+  }], "Should record clamped visit and valid visit for B");
+
+  let visitsForC = await PlacesSyncUtils.history.fetchVisitsForURL(
+    "http://example.com/c");
+  equal(visitsForC.length, 1, "Should record clamped future visit for C");
+  let visitDateForC = PlacesUtils.toDate(visitsForC[0].date);
+  ok(isDateApproximately(visitDateForC, Date.now()),
+    "Should clamp future visit date for C to now");
+
+  let visitsForD = await PlacesSyncUtils.history.fetchVisitsForURL(
+    "http://example.com/d");
+  deepEqual(visitsForD, [{
+    date: recentVisitTime * 1000,
+    type: Ci.nsINavHistoryService.TRANSITION_DOWNLOAD,
+  }], "Should not clamp valid visit dates");
+});
+
 add_task(async function test_remove() {
   _("Remove an existent record and a non-existent from the store.");
   await applyEnsureNoFailures([{id: fxguid, deleted: true},
                          {id: Utils.makeGUID(), deleted: true}]);
   do_check_false((await store.itemExists(fxguid)));
   let queryres = queryHistoryVisits(fxuri);
   do_check_eq(queryres.length, 0);
 
--- a/servo/Cargo.lock
+++ b/servo/Cargo.lock
@@ -3608,17 +3608,17 @@ dependencies = [
  "url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "uuid 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "webdriver 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "webrender"
 version = "0.52.0"
-source = "git+https://github.com/servo/webrender#8ce0212a0d5c9b32b1ba11a6b20014e88321c605"
+source = "git+https://github.com/servo/webrender#29d325f54bbead84e97dcf7dc536463c45f8eece"
 dependencies = [
  "app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-foundation 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-graphics 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-text 7.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -3636,17 +3636,17 @@ dependencies = [
  "thread_profiler 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
  "webrender_api 0.52.0 (git+https://github.com/servo/webrender)",
 ]
 
 [[package]]
 name = "webrender_api"
 version = "0.52.0"
-source = "git+https://github.com/servo/webrender#8ce0212a0d5c9b32b1ba11a6b20014e88321c605"
+source = "git+https://github.com/servo/webrender#29d325f54bbead84e97dcf7dc536463c45f8eece"
 dependencies = [
  "app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-foundation 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-graphics 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "dwrote 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
--- a/servo/components/layout/webrender_helpers.rs
+++ b/servo/components/layout/webrender_helpers.rs
@@ -453,25 +453,25 @@ impl WebRenderDisplayItemConverter for D
                                         item.color,
                                         item.blur_radius.to_f32_px(),
                                         item.spread_radius.to_f32_px(),
                                         item.border_radius.to_f32_px(),
                                         item.clip_mode.to_clip_mode());
             }
             DisplayItem::PushTextShadow(ref item) => {
                 let rect = item.base.bounds;
-                builder.push_text_shadow(&prim_info(rect, Some(item.base.local_clip)),
-                                         webrender_api::TextShadow {
-                                             blur_radius: item.blur_radius.to_f32_px(),
-                                             offset: item.offset.to_vectorf(),
-                                             color: item.color,
-                                         });
+                builder.push_shadow(&prim_info(rect, Some(item.base.local_clip)),
+                                    webrender_api::Shadow {
+                                        blur_radius: item.blur_radius.to_f32_px(),
+                                        offset: item.offset.to_vectorf(),
+                                        color: item.color,
+                                    });
             }
             DisplayItem::PopTextShadow(_) => {
-                builder.pop_text_shadow();
+                builder.pop_shadow();
             }
             DisplayItem::Iframe(ref item) => {
                 let rect = item.base.bounds;
                 let pipeline_id = item.iframe.to_webrender();
                 builder.push_iframe(&prim_info(rect, Some(item.base.local_clip)), pipeline_id);
             }
             DisplayItem::PushStackingContext(ref item) => {
                 let stacking_context = &item.stacking_context;
--- a/servo/components/style/gecko/media_queries.rs
+++ b/servo/components/style/gecko/media_queries.rs
@@ -368,18 +368,28 @@ impl MediaExpressionValue {
             }
             nsMediaFeature_ValueType::eBoolInteger => {
                 debug_assert!(css_value.mUnit == nsCSSUnit::eCSSUnit_Integer);
                 let i = css_value.integer_unchecked();
                 debug_assert!(i == 0 || i == 1);
                 Some(MediaExpressionValue::BoolInteger(i == 1))
             }
             nsMediaFeature_ValueType::eResolution => {
-                debug_assert!(css_value.mUnit == nsCSSUnit::eCSSUnit_Inch);
-                Some(MediaExpressionValue::Resolution(Resolution::Dpi(css_value.float_unchecked())))
+                // This is temporarily more complicated to allow Gecko Bug
+                // 1376931 to land. Parts of that bug will supply pixel values
+                // and expect them to be passed through without conversion.
+                // After all parts of that bug have landed, Bug 1404097 will
+                // return this function to once again only allow one type of
+                // value to be accepted: this time, only pixel values.
+                let res = match css_value.mUnit {
+                    nsCSSUnit::eCSSUnit_Pixel => Resolution::Dppx(css_value.float_unchecked()),
+                    nsCSSUnit::eCSSUnit_Inch  => Resolution::Dpi(css_value.float_unchecked()),
+                    _                         => unreachable!(),
+                };
+                Some(MediaExpressionValue::Resolution(res))
             }
             nsMediaFeature_ValueType::eEnumerated => {
                 let value = css_value.integer_unchecked() as i16;
                 Some(MediaExpressionValue::Enumerated(value))
             }
             nsMediaFeature_ValueType::eIdent => {
                 debug_assert!(css_value.mUnit == nsCSSUnit::eCSSUnit_Ident);
                 let string = unsafe {
--- a/servo/components/style/properties/declaration_block.rs
+++ b/servo/components/style/properties/declaration_block.rs
@@ -143,54 +143,50 @@ impl<'a> Iterator for NormalDeclarationI
 
     fn size_hint(&self) -> (usize, Option<usize>) {
         self.0.iter.size_hint()
     }
 }
 
 /// Iterator for AnimationValue to be generated from PropertyDeclarationBlock.
 pub struct AnimationValueIterator<'a, 'cx, 'cx_a:'cx> {
-    iter: DeclarationImportanceIterator<'a>,
+    iter: NormalDeclarationIterator<'a>,
     context: &'cx mut Context<'cx_a>,
     default_values: &'a ComputedValues,
     /// Custom properties in a keyframe if exists.
     extra_custom_properties: Option<&'a Arc<::custom_properties::CustomPropertiesMap>>,
 }
 
 impl<'a, 'cx, 'cx_a:'cx> AnimationValueIterator<'a, 'cx, 'cx_a> {
     fn new(
         declarations: &'a PropertyDeclarationBlock,
         context: &'cx mut Context<'cx_a>,
         default_values: &'a ComputedValues,
        extra_custom_properties: Option<&'a Arc<::custom_properties::CustomPropertiesMap>>,
     ) -> AnimationValueIterator<'a, 'cx, 'cx_a> {
         AnimationValueIterator {
-            iter: declarations.declaration_importance_iter(),
+            iter: declarations.normal_declaration_iter(),
             context,
             default_values,
             extra_custom_properties,
         }
     }
 }
 
 impl<'a, 'cx, 'cx_a:'cx> Iterator for AnimationValueIterator<'a, 'cx, 'cx_a> {
     type Item = AnimationValue;
     #[inline]
     fn next(&mut self) -> Option<Self::Item> {
         loop {
             let next = self.iter.next();
-            let (decl, importance) = match next {
-                Some(decl_and_importance) => decl_and_importance,
+            let decl = match next {
+                Some(decl) => decl,
                 None => return None,
             };
 
-            if importance.important() {
-                continue;
-            }
-
             let animation = AnimationValue::from_declaration(
                 decl,
                 &mut self.context,
                 self.extra_custom_properties,
                 self.default_values,
             );
 
             if let Some(anim) = animation {
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -170,17 +170,19 @@ pub mod shorthands {
     <%include file="/shorthand/padding.mako.rs" />
     <%include file="/shorthand/position.mako.rs" />
     <%include file="/shorthand/inherited_svg.mako.rs" />
     <%include file="/shorthand/text.mako.rs" />
 
     // We don't defined the 'all' shorthand using the regular helpers:shorthand
     // mechanism, since it causes some very large types to be generated.
     <% data.declare_shorthand("all",
-                              [p.name for p in data.longhands if p.name not in ['direction', 'unicode-bidi']],
+                              [p.name for p in data.longhands
+                                if p.name not in ['direction', 'unicode-bidi']
+                                      and not p.internal],
                               spec="https://drafts.csswg.org/css-cascade-3/#all-shorthand") %>
 }
 
 /// A module with all the code related to animated properties.
 ///
 /// This needs to be "included" by mako at least after all longhand modules,
 /// given they populate the global data.
 pub mod animated_properties {
--- a/testing/gtest/rungtests.py
+++ b/testing/gtest/rungtests.py
@@ -76,17 +76,16 @@ class GTests(object):
         if not result:
             log.testFail("gtest | test failed with return code %d", proc.proc.returncode)
         return result
 
     def build_core_environment(self, env = {}):
         """
         Add environment variables likely to be used across all platforms, including remote systems.
         """
-        env["MOZILLA_FIVE_HOME"] = self.xre_path
         env["MOZ_XRE_DIR"] = self.xre_path
         env["MOZ_GMP_PATH"] = os.pathsep.join(
             os.path.join(self.xre_path, p, "1.0")
             for p in ('gmp-fake', 'gmp-fakeopenh264')
         )
         env["XPCOM_DEBUG_BREAK"] = "stack-and-abort"
         env["MOZ_CRASHREPORTER_NO_REPORT"] = "1"
         env["MOZ_CRASHREPORTER"] = "1"
--- a/testing/marionette/.eslintrc.js
+++ b/testing/marionette/.eslintrc.js
@@ -12,13 +12,14 @@ module.exports = {
     }],
     "max-len": ["error", 78, {
       "ignoreStrings": true,
       "ignoreUrls": true,
     }],
     "no-fallthrough": "error",
     "no-new-object": "error",
     "no-undef-init": "error",
+    "no-unused-vars": ["error", {}],
     "no-var": "error",
     "object-curly-spacing": ["error", "never"],
     "semi": "error",
   }
 };
--- a/testing/marionette/accessibility.js
+++ b/testing/marionette/accessibility.js
@@ -151,17 +151,17 @@ accessibility.Checks = class {
           reject();
         } else {
           resolve(acc);
         }
         return;
       }
       // Accessibility for the doc is busy, so wait for the state to change.
       let eventObserver = {
-        observe(subject, topic, data) {
+        observe(subject, topic) {
           if (topic !== "accessible-event") {
             return;
           }
 
           // If event type does not match expected type, skip the event.
           let event = subject.QueryInterface(Ci.nsIAccessibleEvent);
           if (event.eventType !== Ci.nsIAccessibleEvent.EVENT_STATE_CHANGE) {
             return;
--- a/testing/marionette/addon.js
+++ b/testing/marionette/addon.js
@@ -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 strict";
 
-const {interfaces: Ci, utils: Cu} = Components;
+const {utils: Cu} = Components;
 
 Cu.import("resource://gre/modules/AddonManager.jsm");
 Cu.import("resource://gre/modules/FileUtils.jsm");
 
 const {UnknownError} = Cu.import("chrome://marionette/content/error.js", {});
 
 this.EXPORTED_SYMBOLS = ["addon"];
 
--- a/testing/marionette/assert.js
+++ b/testing/marionette/assert.js
@@ -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 strict";
 
-const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+const {utils: Cu} = Components;
 
 Cu.import("resource://gre/modules/AppConstants.jsm");
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 const {
   InvalidArgumentError,
   InvalidSessionIDError,
--- a/testing/marionette/browser.js
+++ b/testing/marionette/browser.js
@@ -229,19 +229,17 @@ browser.Context = class {
   /**
    * Close the current window.
    *
    * @return {Promise}
    *     A promise which is resolved when the current window has been closed.
    */
   closeWindow() {
     return new Promise(resolve => {
-      this.window.addEventListener("unload", ev => {
-        resolve();
-      }, {once: true});
+      this.window.addEventListener("unload", resolve, {once: true});
       this.window.close();
     });
   }
 
   /**
    * Close the current tab.
    *
    * @return {Promise}
@@ -258,26 +256,22 @@ browser.Context = class {
         this.tabBrowser.tabs.length === 1 ||
         !this.tab) {
       return this.closeWindow();
     }
 
     return new Promise((resolve, reject) => {
       if (this.tabBrowser.closeTab) {
         // Fennec
-        this.tabBrowser.deck.addEventListener("TabClose", ev => {
-          resolve();
-        }, {once: true});
+        this.tabBrowser.deck.addEventListener("TabClose", resolve, {once: true});
         this.tabBrowser.closeTab(this.tab);
 
       } else if (this.tabBrowser.removeTab) {
         // Firefox
-        this.tab.addEventListener("TabClose", ev => {
-          resolve();
-        }, {once: true});
+        this.tab.addEventListener("TabClose", resolve, {once: true});
         this.tabBrowser.removeTab(this.tab);
 
       } else {
         reject(new UnsupportedOperationError(
             `closeTab() not supported in ${this.driver.appName}`));
       }
     });
   }
--- a/testing/marionette/cert.js
+++ b/testing/marionette/cert.js
@@ -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 strict";
 
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 this.EXPORTED_SYMBOLS = ["cert"];
 
 const registrar =
     Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
--- a/testing/marionette/components/marionette.js
+++ b/testing/marionette/components/marionette.js
@@ -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 strict";
 
-const {Constructor: CC, interfaces: Ci, utils: Cu, classes: Cc} = Components;
+const {interfaces: Ci, utils: Cu, classes: Cc} = Components;
 
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(
     this, "env", "@mozilla.org/process/environment;1", "nsIEnvironment");
 
@@ -60,20 +60,16 @@ const ENV_ENABLED = "MOZ_MARIONETTE";
 // a different profile in order to test things like Firefox refresh.
 // The environment variable itself, if present, is interpreted as a
 // JSON structure, with the keys mapping to preference names in the
 // "marionette." branch, and the values to the values of those prefs. So
 // something like {"port": 4444} would result in the marionette.port
 // pref being set to 4444.
 const ENV_PRESERVE_PREFS = "MOZ_MARIONETTE_PREF_STATE_ACROSS_RESTARTS";
 
-const ServerSocket = CC("@mozilla.org/network/server-socket;1",
-    "nsIServerSocket",
-    "initSpecialConnection");
-
 const {PREF_STRING, PREF_BOOL, PREF_INT, PREF_INVALID} = Ci.nsIPrefBranch;
 
 function getPrefVal(pref) {
   let prefType = Services.prefs.getPrefType(pref);
   let prefValue;
   switch (prefType) {
     case PREF_STRING:
       prefValue = Services.prefs.getStringPref(pref);
@@ -187,17 +183,17 @@ MarionetteComponent.prototype = {
 // Handle -marionette flag
 MarionetteComponent.prototype.handle = function(cmdLine) {
   if (!this.enabled && cmdLine.handleFlag("marionette", false)) {
     this.enabled = true;
     this.logger.info("Enabled via --marionette");
   }
 };
 
-MarionetteComponent.prototype.observe = function(subject, topic, data) {
+MarionetteComponent.prototype.observe = function(subject, topic) {
   this.logger.debug(`Received observer notification "${topic}"`);
 
   switch (topic) {
     case "profile-after-change":
       Services.obs.addObserver(this, "command-line-startup");
       Services.obs.addObserver(this, "sessionstore-windows-restored");
 
       prefs.readFromEnvironment(ENV_PRESERVE_PREFS);
--- a/testing/marionette/dom.js
+++ b/testing/marionette/dom.js
@@ -1,16 +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 strict";
 
-const {utils: Cu} = Components;
-
 this.EXPORTED_SYMBOLS = [
   "ContentEventObserverService",
   "WebElementEventTarget",
 ];
 
 /**
  * The {@link EventTarget} for web elements can be used to observe DOM
  * events in the content document.
@@ -108,17 +106,17 @@ class WebElementEventTarget {
       listener.call(this, event);
 
       if (listener.once) {
         this.removeEventListener(event.type, listener);
       }
     });
   }
 
-  receiveMessage({target, name, data, objects}) {
+  receiveMessage({name, data, objects}) {
     if (name != "Marionette:DOM:OnEvent") {
       return;
     }
 
     let ev = {
       type: data.type,
       target: objects.target,
     };
--- a/testing/marionette/driver.js
+++ b/testing/marionette/driver.js
@@ -2,19 +2,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/. */
 
 "use strict";
 /* global XPCNativeWrapper */
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
-const loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
-    .getService(Ci.mozIJSSubScriptLoader);
-
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 Cu.import("chrome://marionette/content/accessibility.js");
 Cu.import("chrome://marionette/content/addon.js");
 Cu.import("chrome://marionette/content/assert.js");
@@ -24,17 +21,16 @@ const {
   WindowState,
 } = Cu.import("chrome://marionette/content/browser.js", {});
 Cu.import("chrome://marionette/content/capture.js");
 Cu.import("chrome://marionette/content/cert.js");
 Cu.import("chrome://marionette/content/cookie.js");
 Cu.import("chrome://marionette/content/element.js");
 const {
   ElementNotInteractableError,
-  error,
   InsecureCertificateError,
   InvalidArgumentError,
   InvalidCookieDomainError,
   InvalidSelectorError,
   NoAlertOpenError,
   NoSuchFrameError,
   NoSuchWindowError,
   SessionNotCreatedError,
@@ -750,17 +746,17 @@ GeckoDriver.prototype.listeningPromise =
  *     above.
  *
  * @return {Object}
  *     Session ID and capabilities offered by the WebDriver service.
  *
  * @throws {SessionNotCreatedError}
  *     If, for whatever reason, a session could not be created.
  */
-GeckoDriver.prototype.newSession = async function(cmd, resp) {
+GeckoDriver.prototype.newSession = async function(cmd) {
   if (this.sessionID) {
     throw new SessionNotCreatedError("Maximum number of active sessions");
   }
   this.sessionID = element.generateUUID();
   this.newSessionCommandId = cmd.id;
 
   try {
     this.capabilities = session.Capabilities.fromJSON(cmd.parameters);
@@ -865,17 +861,17 @@ GeckoDriver.prototype.getSessionCapabili
 /**
  * Sets the context of the subsequent commands to be either "chrome" or
  * "content".
  *
  * @param {string} value
  *     Name of the context to be switched to.  Must be one of "chrome" or
  *     "content".
  */
-GeckoDriver.prototype.setContext = function(cmd, resp) {
+GeckoDriver.prototype.setContext = function(cmd) {
   let val = cmd.parameters.value;
   let ctx = Context.fromString(val);
   if (ctx === null) {
     throw new WebDriverError(`Invalid context: ${val}`);
   }
   this.context = ctx;
 };
 
@@ -1087,17 +1083,17 @@ GeckoDriver.prototype.execute_ = async f
  *
  * @throws {UnsupportedOperationError}
  *     Not available in current context.
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
-GeckoDriver.prototype.get = async function(cmd, resp) {
+GeckoDriver.prototype.get = async function(cmd) {
   assert.content(this.context);
   assert.window(this.getCurrentWindow());
   assert.noUserPrompt(this.dialog);
 
   let url = cmd.parameters.url;
 
   let get = this.listener.get({url, pageTimeout: this.timeouts.pageLoad});
 
@@ -1131,17 +1127,17 @@ GeckoDriver.prototype.get = async functi
  * When in the context of the chrome, this returns the canonical URL
  * of the current resource.
  *
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
-GeckoDriver.prototype.getCurrentUrl = function(cmd) {
+GeckoDriver.prototype.getCurrentUrl = function() {
   assert.window(this.getCurrentWindow());
   assert.noUserPrompt(this.dialog);
 
   return this.currentURL.toString();
 };
 
 /**
  * Gets the current title of the window.
@@ -1149,17 +1145,17 @@ GeckoDriver.prototype.getCurrentUrl = fu
  * @return {string}
  *     Document title of the top-level browsing context.
  *
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
-GeckoDriver.prototype.getTitle = function(cmd, resp) {
+GeckoDriver.prototype.getTitle = function() {
   assert.window(this.getCurrentWindow());
   assert.noUserPrompt(this.dialog);
 
   return this.title;
 };
 
 /** Gets the current type of the window. */
 GeckoDriver.prototype.getWindowType = function(cmd, resp) {
@@ -1202,17 +1198,17 @@ GeckoDriver.prototype.getPageSource = as
  *
  * @throws {UnsupportedOperationError}
  *     Not available in current context.
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
-GeckoDriver.prototype.goBack = async function(cmd, resp) {
+GeckoDriver.prototype.goBack = async function() {
   assert.content(this.context);
   assert.contentBrowser(this.curBrowser);
   assert.noUserPrompt(this.dialog);
 
   // If there is no history, just return
   if (!this.curBrowser.contentBrowser.webNavigation.canGoBack) {
     return;
   }
@@ -1245,17 +1241,17 @@ GeckoDriver.prototype.goBack = async fun
  *
  * @throws {UnsupportedOperationError}
  *     Not available in current context.
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
-GeckoDriver.prototype.goForward = async function(cmd, resp) {
+GeckoDriver.prototype.goForward = async function() {
   assert.content(this.context);
   assert.contentBrowser(this.curBrowser);
   assert.noUserPrompt(this.dialog);
 
   // If there is no history, just return
   if (!this.curBrowser.contentBrowser.webNavigation.canGoForward) {
     return;
   }
@@ -1289,17 +1285,17 @@ GeckoDriver.prototype.goForward = async 
  *
  * @throws {UnsupportedOperationError}
  *     Not available in current context.
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
-GeckoDriver.prototype.refresh = async function(cmd, resp) {
+GeckoDriver.prototype.refresh = async function() {
   assert.content(this.context);
   assert.window(this.getCurrentWindow());
   assert.noUserPrompt(this.dialog);
 
   let refresh = this.listener.refresh(
       {pageTimeout: this.timeouts.pageLoad});
 
   // If a reload of the frame script interrupts our page load, this will
@@ -1358,34 +1354,34 @@ GeckoDriver.prototype.getIdForBrowser = 
  * be used to switch to this window at a later point.
  *
  * @return {string}
  *     Unique window handle.
  *
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  */
-GeckoDriver.prototype.getWindowHandle = function(cmd, resp) {
+GeckoDriver.prototype.getWindowHandle = function() {
   assert.contentBrowser(this.curBrowser);
 
   return this.curBrowser.curFrameId.toString();
 };
 
 /**
  * Get a list of top-level browsing contexts. On desktop this typically
  * corresponds to the set of open tabs for browser windows, or the window
  * itself for non-browser chrome windows.
  *
  * Each window handle is assigned by the server and is guaranteed unique,
  * however the return array does not have a specified ordering.
  *
  * @return {Array.<string>}
  *     Unique window handles.
  */
-GeckoDriver.prototype.getWindowHandles = function(cmd, resp) {
+GeckoDriver.prototype.getWindowHandles = function() {
   return this.windowHandles.map(String);
 };
 
 /**
  * Get the current window's handle.  This corresponds to a window that
  * may itself contain tabs.
  *
  * Return an opaque server-assigned identifier to this window that
@@ -1411,17 +1407,17 @@ GeckoDriver.prototype.getChromeWindowHan
 
 /**
  * Returns identifiers for each open chrome window for tests interested in
  * managing a set of chrome windows and tabs separately.
  *
  * @return {Array.<string>}
  *     Unique window handles.
  */
-GeckoDriver.prototype.getChromeWindowHandles = function(cmd, resp) {
+GeckoDriver.prototype.getChromeWindowHandles = function() {
   return this.chromeWindowHandles.map(String);
 };
 
 /**
  * Get the current position and size of the browser window currently in focus.
  *
  * Will return the current browser window size in pixels. Refers to
  * window outerWidth and outerHeight values, which include scroll bars,
@@ -1431,17 +1427,17 @@ GeckoDriver.prototype.getChromeWindowHan
  *     Object with |x| and |y| coordinates, and |width| and |height|
  *     of browser window.
  *
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
-GeckoDriver.prototype.getWindowRect = function(cmd, resp) {
+GeckoDriver.prototype.getWindowRect = function() {
   assert.window(this.getCurrentWindow());
   assert.noUserPrompt(this.dialog);
   return this.curBrowser.rect;
 };
 
 /**
  * Set the window position and size of the browser on the operating
  * system window manager.
@@ -1467,17 +1463,17 @@ GeckoDriver.prototype.getWindowRect = fu
  *
  * @throws {UnsupportedOperationError}
  *     Not applicable to application.
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
-GeckoDriver.prototype.setWindowRect = async function(cmd, resp) {
+GeckoDriver.prototype.setWindowRect = async function(cmd) {
   assert.firefox();
   const win = assert.window(this.getCurrentWindow());
   assert.noUserPrompt(this.dialog);
 
   let {x, y, width, height} = cmd.parameters;
   let origRect = this.curBrowser.rect;
 
   // Synchronous resize to |width| and |height| dimensions.
@@ -1552,17 +1548,17 @@ GeckoDriver.prototype.setWindowRect = as
  * precedence.
  *
  * @param {string} name
  *     Target name or ID of the window to switch to.
  * @param {boolean=} focus
  *      A boolean value which determines whether to focus
  *      the window. Defaults to true.
  */
-GeckoDriver.prototype.switchToWindow = async function(cmd, resp) {
+GeckoDriver.prototype.switchToWindow = async function(cmd) {
   let focus = true;
   if (typeof cmd.parameters.focus != "undefined") {
     focus = cmd.parameters.focus;
   }
 
   // Window IDs are internally handled as numbers, but here it could
   // also be the name of the window.
   let switchTo = parseInt(cmd.parameters.name);
@@ -1701,17 +1697,17 @@ GeckoDriver.prototype.getActiveFrame = f
  * Set the current browsing context for future commands to the parent
  * of the current browsing context.
  *
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
-GeckoDriver.prototype.switchToParentFrame = async function(cmd, resp) {
+GeckoDriver.prototype.switchToParentFrame = async function() {
   assert.window(this.getCurrentWindow());
   assert.noUserPrompt(this.dialog);
 
   await this.listener.switchToParentFrame();
 };
 
 /**
  * Switch to a given frame within the current window.
@@ -1722,17 +1718,17 @@ GeckoDriver.prototype.switchToParentFram
  *     If element is not defined, then this holds either the id, name,
  *     or index of the frame to switch to.
  *
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
-GeckoDriver.prototype.switchToFrame = async function(cmd, resp) {
+GeckoDriver.prototype.switchToFrame = async function(cmd) {
   assert.window(this.getCurrentWindow());
   assert.noUserPrompt(this.dialog);
 
   let {id, element, focus} = cmd.parameters;
 
   const otherErrorsExpr = /about:.+(error)|(blocked)\?/;
   const checkTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
 
@@ -1896,39 +1892,39 @@ GeckoDriver.prototype.switchToFrame = as
           this.curBrowser.frameManager.switchToFrame(winId, frameId);
 
       await registerBrowsers;
       await browserListening;
     }
   }
 };
 
-GeckoDriver.prototype.getTimeouts = function(cmd, resp) {
+GeckoDriver.prototype.getTimeouts = function() {
   return this.timeouts;
 };
 
 /**
  * Set timeout for page loading, searching, and scripts.
  *
  * @param {Object.<string, number>}
  *     Dictionary of timeout types and their new value, where all timeout
  *     types are optional.
  *
  * @throws {InvalidArgumentError}
  *     If timeout type key is unknown, or the value provided with it is
  *     not an integer.
  */
-GeckoDriver.prototype.setTimeouts = function(cmd, resp) {
+GeckoDriver.prototype.setTimeouts = function(cmd) {
   // merge with existing timeouts
   let merged = Object.assign(this.timeouts.toJSON(), cmd.parameters);
   this.timeouts = session.Timeouts.fromJSON(merged);
 };
 
 /** Single tap. */
-GeckoDriver.prototype.singleTap = async function(cmd, resp) {
+GeckoDriver.prototype.singleTap = async function(cmd) {
   assert.window(this.getCurrentWindow());
 
   let {id, x, y} = cmd.parameters;
 
   switch (this.context) {
     case Context.CHROME:
       throw new UnsupportedOperationError(
           "Command 'singleTap' is not yet available in chrome context");
@@ -1948,17 +1944,17 @@ GeckoDriver.prototype.singleTap = async 
  *
  * @throws {UnsupportedOperationError}
  *     Not yet available in current context.
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
-GeckoDriver.prototype.performActions = async function(cmd, resp) {
+GeckoDriver.prototype.performActions = async function(cmd) {
   assert.content(this.context,
       "Command 'performActions' is not yet available in chrome context");
   assert.window(this.getCurrentWindow());
   assert.noUserPrompt(this.dialog);
 
   let actions = cmd.parameters.actions;
   await this.listener.performActions({"actions": actions});
 };
@@ -1968,17 +1964,17 @@ GeckoDriver.prototype.performActions = a
  *
  * @throws {UnsupportedOperationError}
  *     Not available in current context.
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
-GeckoDriver.prototype.releaseActions = async function(cmd, resp) {
+GeckoDriver.prototype.releaseActions = async function() {
   assert.content(this.context);
   assert.window(this.getCurrentWindow());
   assert.noUserPrompt(this.dialog);
 
   await this.listener.releaseActions();
 };
 
 /**
@@ -2031,17 +2027,17 @@ GeckoDriver.prototype.actionChain = asyn
  *
  * @throws {UnsupportedOperationError}
  *     Not available in current context.
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
-GeckoDriver.prototype.multiAction = async function(cmd, resp) {
+GeckoDriver.prototype.multiAction = async function(cmd) {
   assert.content(this.context);
   assert.window(this.getCurrentWindow());
   assert.noUserPrompt(this.dialog);
 
   let {value, max_length} = cmd.parameters;  // eslint-disable-line camelcase
 
   this.addFrameCloseListener("multi action chain");
   await this.listener.multiAction(value, max_length);
@@ -2170,17 +2166,17 @@ GeckoDriver.prototype.getActiveElement =
  * @param {string} id
  *     Reference ID to the element that will be clicked.
  *
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
-GeckoDriver.prototype.clickElement = async function(cmd, resp) {
+GeckoDriver.prototype.clickElement = async function(cmd) {
   assert.window(this.getCurrentWindow());
   assert.noUserPrompt(this.dialog);
 
   let id = cmd.parameters.id;
 
   switch (this.context) {
     case Context.CHROME:
       let el = this.curBrowser.seenEls.get(id);
@@ -2530,17 +2526,17 @@ GeckoDriver.prototype.getElementRect = a
  * @param {string} value
  *     Value to send to the element.
  *
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
-GeckoDriver.prototype.sendKeysToElement = async function(cmd, resp) {
+GeckoDriver.prototype.sendKeysToElement = async function(cmd) {
   assert.window(this.getCurrentWindow());
   assert.noUserPrompt(this.dialog);
 
   let {id, text} = cmd.parameters;
   assert.string(text);
 
   switch (this.context) {
     case Context.CHROME:
@@ -2561,17 +2557,17 @@ GeckoDriver.prototype.sendKeysToElement 
  * @param {string} id
  *     Reference ID to the element that will be cleared.
  *
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
-GeckoDriver.prototype.clearElement = async function(cmd, resp) {
+GeckoDriver.prototype.clearElement = async function(cmd) {
   assert.window(this.getCurrentWindow());
   assert.noUserPrompt(this.dialog);
 
   let id = cmd.parameters.id;
 
   switch (this.context) {
     case Context.CHROME:
       // the selenium atom doesn't work here
@@ -2589,17 +2585,17 @@ GeckoDriver.prototype.clearElement = asy
   }
 };
 
 /**
  * Switch to shadow root of the given host element.
  *
  * @param {string} id element id.
  */
-GeckoDriver.prototype.switchToShadowRoot = async function(cmd, resp) {
+GeckoDriver.prototype.switchToShadowRoot = async function(cmd) {
   assert.content(this.context);
   assert.window(this.getCurrentWindow());
 
   let id = cmd.parameters.id;
   await this.listener.switchToShadowRoot(id);
 };
 
 /**
@@ -2614,17 +2610,17 @@ GeckoDriver.prototype.switchToShadowRoot
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  * @throws {InvalidCookieDomainError}
  *     If <var>cookie</var> is for a different domain than the active
  *     document's host.
  */
-GeckoDriver.prototype.addCookie = function(cmd, resp) {
+GeckoDriver.prototype.addCookie = function(cmd) {
   assert.content(this.context);
   assert.window(this.getCurrentWindow());
   assert.noUserPrompt(this.dialog);
 
   let {protocol, hostname} = this.currentURL;
 
   const networkSchemes = ["ftp:", "http:", "https:"];
   if (!networkSchemes.includes(protocol)) {
@@ -2666,17 +2662,17 @@ GeckoDriver.prototype.getCookies = funct
  *
  * @throws {UnsupportedOperationError}
  *     Not available in current context.
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
-GeckoDriver.prototype.deleteAllCookies = function(cmd, resp) {
+GeckoDriver.prototype.deleteAllCookies = function() {
   assert.content(this.context);
   assert.window(this.getCurrentWindow());
   assert.noUserPrompt(this.dialog);
 
   let {hostname, pathname} = this.currentURL;
   for (let toDelete of cookie.iter(hostname, pathname)) {
     cookie.remove(toDelete);
   }
@@ -2687,17 +2683,17 @@ GeckoDriver.prototype.deleteAllCookies =
  *
  * @throws {UnsupportedOperationError}
  *     Not available in current context.
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
-GeckoDriver.prototype.deleteCookie = function(cmd, resp) {
+GeckoDriver.prototype.deleteCookie = function(cmd) {
   assert.content(this.context);
   assert.window(this.getCurrentWindow());
   assert.noUserPrompt(this.dialog);
 
   let {hostname, pathname} = this.currentURL;
   let candidateName = assert.string(cmd.parameters.name);
   for (let toDelete of cookie.iter(hostname, pathname)) {
     if (toDelete.name === candidateName) {
@@ -2720,17 +2716,17 @@ GeckoDriver.prototype.deleteCookie = fun
  * @return {Array.<string>}
  *     Unique window handles of remaining windows.
  *
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
-GeckoDriver.prototype.close = async function(cmd, resp) {
+GeckoDriver.prototype.close = async function() {
   assert.contentBrowser(this.curBrowser);
   assert.noUserPrompt(this.dialog);
 
   let nwins = 0;
 
   for (let win of this.windows) {
     // For browser windows count the tabs. Otherwise take the window itself.
     let tabbrowser = browser.getTabBrowser(win);
@@ -2761,17 +2757,17 @@ GeckoDriver.prototype.close = async func
  *
  * If it is the last window currently open, the chrome window will not be
  * closed to prevent a shutdown of the application. Instead the returned
  * list of chrome window handles is empty.
  *
  * @return {Array.<string>}
  *     Unique chrome window handles of remaining chrome windows.
  */
-GeckoDriver.prototype.closeChromeWindow = async function(cmd, resp) {
+GeckoDriver.prototype.closeChromeWindow = async function() {
   assert.firefox();
   assert.window(this.getCurrentWindow(Context.CHROME));
 
   let nwins = 0;
 
   // eslint-disable-next-line
   for (let _ of this.windows) {
     nwins++;
@@ -2791,17 +2787,17 @@ GeckoDriver.prototype.closeChromeWindow 
     this.mm.removeDelayedFrameScript(FRAME_SCRIPT);
   }
 
   await this.curBrowser.closeWindow();
   return this.chromeWindowHandles.map(String);
 };
 
 /** Delete Marionette session. */
-GeckoDriver.prototype.deleteSession = function(cmd, resp) {
+GeckoDriver.prototype.deleteSession = function() {
   if (this.curBrowser !== null) {
     // frame scripts can be safely reused
     Preferences.set(CONTENT_LISTENER_PREF, false);
 
     // delete session in each frame in each browser
     for (let win in this.browsers) {
       let browser = this.browsers[win];
       for (let i in browser.knownFrames) {
@@ -2879,17 +2875,17 @@ GeckoDriver.prototype.deleteSession = fu
  *     Scroll to element if |id| is provided.  If undefined, it will
  *     scroll to the element.
  *
  * @return {string}
  *     If <var>hash</var> is false, PNG image encoded as Base64 encoded
  *     string.  If <var>hash</var> is true, hex digest of the SHA-256
  *     hash of the Base64 encoded string.
  */
-GeckoDriver.prototype.takeScreenshot = function(cmd, resp) {
+GeckoDriver.prototype.takeScreenshot = function(cmd) {
   let win = assert.window(this.getCurrentWindow());
 
   let {id, highlights, full, hash} = cmd.parameters;
   highlights = highlights || [];
   let format = hash ? capture.Format.Hash : capture.Format.Base64;
 
   switch (this.context) {
     case Context.CHROME:
@@ -2949,17 +2945,17 @@ GeckoDriver.prototype.getScreenOrientati
  * The supplied orientation should be given as one of the valid
  * orientation values.  If the orientation is unknown, an error will
  * be raised.
  *
  * Valid orientations are "portrait" and "landscape", which fall
  * back to "portrait-primary" and "landscape-primary" respectively,
  * and "portrait-secondary" as well as "landscape-secondary".
  */
-GeckoDriver.prototype.setScreenOrientation = function(cmd, resp) {
+GeckoDriver.prototype.setScreenOrientation = function(cmd) {
   assert.fennec();
   let win = assert.window(this.getCurrentWindow());
 
   const ors = [
     "portrait", "landscape",
     "portrait-primary", "landscape-primary",
     "portrait-secondary", "landscape-secondary",
   ];
@@ -2989,17 +2985,17 @@ GeckoDriver.prototype.setScreenOrientati
  *
  * @throws {UnsupportedOperationError}
  *     Not available for current application.
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
-GeckoDriver.prototype.minimizeWindow = async function(cmd, resp) {
+GeckoDriver.prototype.minimizeWindow = async function() {
   assert.firefox();
   const win = assert.window(this.getCurrentWindow());
   assert.noUserPrompt(this.dialog);
 
   if (WindowState.from(win.windowState) == WindowState.Fullscreen) {
     await exitFullscreen(win);
   }
 
@@ -3026,17 +3022,17 @@ GeckoDriver.prototype.minimizeWindow = a
  *
  * @throws {UnsupportedOperationError}
  *     Not available for current application.
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
-GeckoDriver.prototype.maximizeWindow = async function(cmd, resp) {
+GeckoDriver.prototype.maximizeWindow = async function() {
   assert.firefox();
   const win = assert.window(this.getCurrentWindow());
   assert.noUserPrompt(this.dialog);
 
   switch (WindowState.from(win.windowState)) {
     case WindowState.Fullscreen:
       await exitFullscreen(win);
       break;
@@ -3047,17 +3043,17 @@ GeckoDriver.prototype.maximizeWindow = a
   }
 
   const origSize = {
     outerWidth: win.outerWidth,
     outerHeight: win.outerHeight,
   };
 
   // Wait for the window size to change.
-  async function windowSizeChange(from) {
+  async function windowSizeChange() {
     return wait.until((resolve, reject) => {
       let curSize = {
         outerWidth: win.outerWidth,
         outerHeight: win.outerHeight,
       };
       if (curSize.outerWidth != origSize.outerWidth ||
           curSize.outerHeight != origSize.outerHeight) {
         resolve();
@@ -3112,17 +3108,17 @@ GeckoDriver.prototype.maximizeWindow = a
  *
  * @throws {UnsupportedOperationError}
  *     Not available for current application.
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
-GeckoDriver.prototype.fullscreenWindow = async function(cmd, resp) {
+GeckoDriver.prototype.fullscreenWindow = async function() {
   assert.firefox();
   const win = assert.window(this.getCurrentWindow());
   assert.noUserPrompt(this.dialog);
 
   if (WindowState.from(win.windowState) == WindowState.Minimized) {
     await restoreWindow(win, this.curBrowser.eventObserver);
   }
 
@@ -3135,30 +3131,30 @@ GeckoDriver.prototype.fullscreenWindow =
 
   return this.curBrowser.rect;
 };
 
 /**
  * Dismisses a currently displayed tab modal, or returns no such alert if
  * no modal is displayed.
  */
-GeckoDriver.prototype.dismissDialog = function(cmd, resp) {
+GeckoDriver.prototype.dismissDialog = function() {
   assert.window(this.getCurrentWindow());
   this._checkIfAlertIsPresent();
 
   let {button0, button1} = this.dialog.ui;
   (button1 ? button1 : button0).click();
   this.dialog = null;
 };
 
 /**
  * Accepts a currently displayed tab modal, or returns no such alert if
  * no modal is displayed.
  */
-GeckoDriver.prototype.acceptDialog = function(cmd, resp) {
+GeckoDriver.prototype.acceptDialog = function() {
   assert.window(this.getCurrentWindow());
   this._checkIfAlertIsPresent();
 
   let {button0} = this.dialog.ui;
   button0.click();
   this.dialog = null;
 };
 
@@ -3188,17 +3184,17 @@ GeckoDriver.prototype.getTextFromDialog 
  * @throws {ElementNotInteractableError}
  *     If the current user prompt is an alert or confirm.
  * @throws {NoSuchAlertError}
  *     If there is no current user prompt.
  * @throws {UnsupportedOperationError}
  *     If the current user prompt is something other than an alert,
  *     confirm, or a prompt.
  */
-GeckoDriver.prototype.sendKeysToDialog = function(cmd, resp) {
+GeckoDriver.prototype.sendKeysToDialog = function(cmd) {
   let win = assert.window(this.getCurrentWindow());
   this._checkIfAlertIsPresent();
 
   // see toolkit/components/prompts/content/commonDialog.js
   let {loginContainer, loginTextbox} = this.dialog.ui;
   if (loginContainer.hidden) {
     throw new ElementNotInteractableError(
         "This prompt does not accept text input");
@@ -3228,17 +3224,17 @@ GeckoDriver.prototype._checkIfAlertIsPre
  * end-up in a non-recoverable state if it hasn't been enabled before.
  *
  * This method is used for custom in application shutdowns via
  * marionette.quit() or marionette.restart(), like File -> Quit.
  *
  * @param {boolean} state
  *     True if the server should accept new socket connections.
  */
-GeckoDriver.prototype.acceptConnections = function(cmd, resp) {
+GeckoDriver.prototype.acceptConnections = function(cmd) {
   assert.boolean(cmd.parameters.value);
   this._server.acceptConnections = cmd.parameters.value;
 };
 
 /**
  * Quits the application with the provided flags.
  *
  * Marionette will stop accepting new connections before ending the
@@ -3308,30 +3304,30 @@ GeckoDriver.prototype.quit = async funct
   });
 
   Services.startup.quit(mode);
 
   resp.body.cause = await quitApplication;
   resp.send();
 };
 
-GeckoDriver.prototype.installAddon = function(cmd, resp) {
+GeckoDriver.prototype.installAddon = function(cmd) {
   assert.firefox();
 
   let path = cmd.parameters.path;
   let temp = cmd.parameters.temporary || false;
   if (typeof path == "undefined" || typeof path != "string" ||
       typeof temp != "boolean") {
     throw InvalidArgumentError();
   }
 
   return addon.install(path, temp);
 };
 
-GeckoDriver.prototype.uninstallAddon = function(cmd, resp) {
+GeckoDriver.prototype.uninstallAddon = function(cmd) {
   assert.firefox();
 
   let id = cmd.parameters.id;
   if (typeof id == "undefined" || typeof id != "string") {
     throw new InvalidArgumentError();
   }
 
   return addon.uninstall(id);
@@ -3461,17 +3457,17 @@ GeckoDriver.prototype.localizeProperty =
   }
 
   resp.body.value = l10n.localizeProperty(urls, id);
 };
 
 /**
  * Initialize the reftest mode
  */
-GeckoDriver.prototype.setupReftest = async function(cmd, resp) {
+GeckoDriver.prototype.setupReftest = async function(cmd) {
   if (this._reftest) {
     throw new UnsupportedOperationError(
         "Called reftest:setup with a reftest session already active");
   }
 
   if (this.context !== Context.CHROME) {
     throw new UnsupportedOperationError(
         "Must set chrome context before running reftests");
@@ -3506,17 +3502,17 @@ GeckoDriver.prototype.runReftest = async
 };
 
 /**
  * End a reftest run.
  *
  * Closes the reftest window (without changing the current window handle),
  * and removes cached canvases.
  */
-GeckoDriver.prototype.teardownReftest = function(cmd, resp) {
+GeckoDriver.prototype.teardownReftest = function() {
   if (!this._reftest) {
     throw new UnsupportedOperationError(
         "Called reftest:teardown before reftest:start");
   }
 
   this._reftest.abort();
   this._reftest = null;
 };
--- a/testing/marionette/element.js
+++ b/testing/marionette/element.js
@@ -919,19 +919,17 @@ element.getPointerInteractablePaintTree 
   let centre = element.getInViewCentrePoint(rects[0], win);
 
   // step 5
   return doc.elementsFromPoint(centre.x, centre.y);
 };
 
 // TODO(ato): Not implemented.
 // In fact, it's not defined in the spec.
-element.isKeyboardInteractable = function(el) {
-  return true;
-};
+element.isKeyboardInteractable = () => true;
 
 /**
  * Attempts to scroll into view |el|.
  *
  * @param {DOMElement} el
  *     Element to scroll into view.
  */
 element.scrollIntoView = function(el) {
--- a/testing/marionette/evaluate.js
+++ b/testing/marionette/evaluate.js
@@ -8,34 +8,31 @@ const {classes: Cc, interfaces: Ci, util
 
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://gre/modules/NetUtil.jsm");
 Cu.import("resource://gre/modules/Timer.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 Cu.import("chrome://marionette/content/element.js");
 const {
-  error,
   JavaScriptError,
   ScriptTimeoutError,
   WebDriverError,
 } = Cu.import("chrome://marionette/content/error.js", {});
 
-const logger = Log.repository.getLogger("Marionette");
+const log = Log.repository.getLogger("Marionette");
 
 this.EXPORTED_SYMBOLS = ["evaluate", "sandbox", "Sandboxes"];
 
 const ARGUMENTS = "__webDriverArguments";
 const CALLBACK = "__webDriverCallback";
 const COMPLETE = "__webDriverComplete";
 const DEFAULT_TIMEOUT = 10000; // ms
 const FINISH = "finish";
 const MARIONETTE_SCRIPT_FINISHED = "marionetteScriptFinished";
-const ELEMENT_KEY = "element";
-const W3C_ELEMENT_KEY = "element-6066-11e4-a52e-4f735466cecf";
 
 /** @namespace */
 this.evaluate = {};
 
 /**
  * Evaluate a script in given sandbox.
  *
  * If the option var>directInject</var> is not specified, the script
@@ -287,17 +284,17 @@ evaluate.toJSON = function(obj, seenEls)
 
   // arbitrary objects + files
   let rv = {};
   for (let prop in obj) {
     try {
       rv[prop] = evaluate.toJSON(obj[prop], seenEls);
     } catch (e) {
       if (e.result == Cr.NS_ERROR_NOT_IMPLEMENTED) {
-        logger.debug(`Skipping ${prop}: ${e.message}`);
+        log.debug(`Skipping ${prop}: ${e.message}`);
       } else {
         throw e;
       }
     }
   }
   return rv;
 };
 
--- a/testing/marionette/event.js
+++ b/testing/marionette/event.js
@@ -5,31 +5,22 @@
 /** Provides functionality for creating and sending DOM events. */
 this.event = {};
 
 "use strict";
 /* global content, is */
 
 const {interfaces: Ci, utils: Cu, classes: Cc} = Components;
 
-Cu.import("resource://gre/modules/Log.jsm");
-const logger = Log.repository.getLogger("Marionette");
-
 Cu.import("chrome://marionette/content/element.js");
 const {ElementNotInteractableError} =
     Cu.import("chrome://marionette/content/error.js", {});
 
 this.EXPORTED_SYMBOLS = ["event"];
 
-// must be synchronised with nsIDOMWindowUtils
-const COMPOSITION_ATTR_RAWINPUT = 0x02;
-const COMPOSITION_ATTR_SELECTEDRAWTEXT = 0x03;
-const COMPOSITION_ATTR_CONVERTEDTEXT = 0x04;
-const COMPOSITION_ATTR_SELECTEDCONVERTEDTEXT = 0x05;
-
 // TODO(ato): Document!
 let seenEvent = false;
 
 function getDOMWindowUtils(win) {
   if (!win) {
     win = window;
   }
 
--- a/testing/marionette/interaction.js
+++ b/testing/marionette/interaction.js
@@ -6,17 +6,16 @@
 
 const {utils: Cu} = Components;
 
 Cu.import("chrome://marionette/content/accessibility.js");
 Cu.import("chrome://marionette/content/atom.js");
 const {
   ElementClickInterceptedError,
   ElementNotInteractableError,
-  InvalidArgument,
   InvalidArgumentError,
   InvalidElementStateError,
   pprint,
 } = Cu.import("chrome://marionette/content/error.js", {});
 Cu.import("chrome://marionette/content/element.js");
 Cu.import("chrome://marionette/content/event.js");
 
 Cu.importGlobalProperties(["File"]);
@@ -320,17 +319,17 @@ interaction.selectOption = function(el) 
  *
  * @return {Promise}
  *     Promise is accepted once event queue is flushed, or rejected if
  *     <var>win</var> has closed or been unloaded before the queue can
  *     be flushed.
  */
 interaction.flushEventLoop = async function(win) {
   return new Promise(resolve => {
-    let handleEvent = event => {
+    let handleEvent = () => {
       win.removeEventListener("beforeunload", this);
       resolve();
     };
 
     if (win.closed) {
       resolve();
       return;
     }
--- a/testing/marionette/l10n.js
+++ b/testing/marionette/l10n.js
@@ -10,17 +10,17 @@
  * The localization (https://mzl.la/2eUMjyF) of UI elements in Gecko
  * based applications is done via entities and properties. For static
  * values entities are used, which are located in .dtd files. Whereby for
  * dynamically updated content the values come from .property files. Both
  * types of elements can be identifed via a unique id, and the translated
  * content retrieved.
  */
 
-const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+const {utils: Cu} = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(
     this, "domParser", "@mozilla.org/xmlextras/domparser;1", "nsIDOMParser");
 
 const {NoSuchElementError} =
--- a/testing/marionette/listener.js
+++ b/testing/marionette/listener.js
@@ -2,22 +2,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* eslint-env mozilla/frame-script */
 /* global XPCNativeWrapper */
 
 "use strict";
 
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
-const uuidGen = Cc["@mozilla.org/uuid-generator;1"]
-    .getService(Ci.nsIUUIDGenerator);
-const loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
-    .getService(Ci.mozIJSSubScriptLoader);
 const winUtil = content.QueryInterface(Ci.nsIInterfaceRequestor)
     .getInterface(Ci.nsIDOMWindowUtils);
 
 Cu.import("resource://gre/modules/FileUtils.jsm");
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
@@ -44,18 +40,16 @@ const {ContentEventObserverService} = Cu
 Cu.import("chrome://marionette/content/interaction.js");
 Cu.import("chrome://marionette/content/legacyaction.js");
 Cu.import("chrome://marionette/content/navigate.js");
 Cu.import("chrome://marionette/content/proxy.js");
 Cu.import("chrome://marionette/content/session.js");
 
 Cu.importGlobalProperties(["URL"]);
 
-let marionetteTestName;
-
 let listenerId = null;  // unique ID of this listener
 let curContainer = {frame: content, shadowRoot: null};
 let previousContainer = null;
 
 const seenEls = new element.Store();
 const SUPPORTED_STRATEGIES = new Set([
   element.Strategy.ClassName,
   element.Strategy.Selector,
@@ -66,60 +60,27 @@ const SUPPORTED_STRATEGIES = new Set([
   element.Strategy.TagName,
   element.Strategy.XPath,
 ]);
 
 let capabilities;
 
 let legacyactions = new legacyaction.Chain(checkForInterrupted);
 
-// the unload handler
-let onunload;
-
-// Flag to indicate whether an async script is currently running or not.
-let asyncTestRunning = false;
-let asyncTestCommandId;
-let asyncTestTimeoutId;
-
-let inactivityTimeoutId = null;
-
-let originalOnError;
-// Send move events about this often
-let EVENT_INTERVAL = 30; // milliseconds
 // last touch for each fingerId
 let multiLast = {};
 
-const asyncChrome = proxy.toChromeAsync({
-  addMessageListener: addMessageListenerId.bind(this),
-  removeMessageListener: removeMessageListenerId.bind(this),
-  sendAsyncMessage: sendAsyncMessage.bind(this),
-});
-const syncChrome = proxy.toChrome(sendSyncMessage.bind(this));
-
 const logger = Log.repository.getLogger("Marionette");
 // Append only once to avoid duplicated output after listener.js gets reloaded
 if (logger.ownAppenders.length == 0) {
   logger.addAppender(new Log.DumpAppender());
 }
 
-const modalHandler = function() {
-  // This gets called on the system app only since it receives the
-  // mozbrowserprompt event
-  sendSyncMessage("Marionette:switchedToFrame",
-      {frameValue: null, storePrevious: true});
-  let isLocal = sendSyncMessage("MarionetteFrame:handleModal", {})[0].value;
-  if (isLocal) {
-    previousContainer = curContainer;
-  }
-  curContainer = {frame: content, shadowRoot: null};
-};
-
 // sandbox storage and name of the current sandbox
 const sandboxes = new Sandboxes(() => curContainer.frame);
-let sandboxName = "default";
 
 const eventObservers = new ContentEventObserverService(
     content, sendAsyncMessage.bind(this));
 
 /**
  * The load listener singleton helps to keep track of active page load
  * activities, and can be used by any command which might cause a navigation
  * to happen. In the specific case of a reload of the frame script it allows
@@ -379,17 +340,17 @@ const loadListener = {
         this.stop();
         sendError(
             new TimeoutError(`Timeout loading page after ${this.timeout}ms`),
             this.commandID);
         break;
     }
   },
 
-  observe(subject, topic, data) {
+  observe(subject, topic) {
     const win = curContainer.frame;
     const winID = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
     const curWinID = win.QueryInterface(Ci.nsIInterfaceRequestor)
         .getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
 
     logger.debug(`Received observer notification "${topic}" for "${winID}"`);
 
     switch (topic) {
@@ -449,17 +410,17 @@ const loadListener = {
     if (loadEventExpected) {
       let startTime = new Date().getTime();
       this.start(commandID, timeout, startTime, true);
     }
 
     return (async () => {
       await trigger();
 
-    })().then(val => {
+    })().then(() => {
       if (!loadEventExpected) {
         sendOk(commandID);
         return;
       }
 
       // If requested setup a timer to detect a possible page load
       if (useUnloadTimer) {
         this.timerPageUnload = Cc["@mozilla.org/timer;1"]
@@ -631,33 +592,33 @@ function newSession(msg) {
   capabilities = session.Capabilities.fromJSON(msg.json);
   resetValues();
 }
 
 /**
  * Puts the current session to sleep, so all listeners are removed except
  * for the 'restart' listener.
  */
-function sleepSession(msg) {
+function sleepSession() {
   deleteSession();
   addMessageListener("Marionette:restart", restart);
 }
 
 /**
  * Restarts all our listeners after this listener was put to sleep
  */
-function restart(msg) {
+function restart() {
   removeMessageListener("Marionette:restart", restart);
   registerSelf();
 }
 
 /**
  * Removes all listeners
  */
-function deleteSession(msg) {
+function deleteSession() {
   removeMessageListenerId("Marionette:newSession", newSession);
   removeMessageListenerId("Marionette:execute", executeFn);
   removeMessageListenerId("Marionette:executeInSandbox", executeInSandboxFn);
   removeMessageListenerId("Marionette:singleTap", singleTapFn);
   removeMessageListenerId("Marionette:performActions", performActionsFn);
   removeMessageListenerId("Marionette:releaseActions", releaseActionsFn);
   removeMessageListenerId("Marionette:actionChain", actionChainFn);
   removeMessageListenerId("Marionette:multiAction", multiActionFn);
--- a/testing/marionette/message.js
+++ b/testing/marionette/message.js
@@ -1,30 +1,26 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {utils: Cu} = Components;
 
-Cu.import("resource://gre/modules/Log.jsm");
-
 Cu.import("chrome://marionette/content/assert.js");
 Cu.import("chrome://marionette/content/error.js");
 
 this.EXPORTED_SYMBOLS = [
   "Command",
   "Message",
   "MessageOrigin",
   "Response",
 ];
 
-const logger = Log.repository.getLogger("Marionette");
-
 /**
  * Messages may originate from either the server or the client.
  * Because the remote protocol is full duplex, both endpoints may be the
  * origin of both commands and responses.
  *
  * @enum
  * @see {@link Message}
  */
--- a/testing/marionette/navigate.js
+++ b/testing/marionette/navigate.js
@@ -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 strict";
 
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+const {utils: Cu} = Components;
 
 Cu.importGlobalProperties(["URL"]);
 
 this.EXPORTED_SYMBOLS = ["navigate"];
 
 /** @namespace */
 this.navigate = {};
 
--- a/testing/marionette/packets.js
+++ b/testing/marionette/packets.js
@@ -385,19 +385,19 @@ function RawPacket(transport, data) {
   Packet.call(this, transport);
   this._data = data;
   this.length = data.length;
   this._done = false;
 }
 
 RawPacket.prototype = Object.create(Packet.prototype);
 
-RawPacket.prototype.read = function(stream) {
-  // This hasn't yet been needed for testing.
-  throw Error("Not implmented.");
+RawPacket.prototype.read = function() {
+  // this has not yet been needed for testing
+  throw new Error("Not implemented");
 };
 
 RawPacket.prototype.write = function(stream) {
   let written = stream.write(this._data, this._data.length);
   this._data = this._data.slice(written);
   this._done = !this._data.length;
 };
 
--- a/testing/marionette/reftest.js
+++ b/testing/marionette/reftest.js
@@ -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 strict";
 
-const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+const {interfaces: Ci, utils: Cu} = Components;
 
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://gre/modules/Preferences.jsm");
 
 Cu.import("chrome://marionette/content/assert.js");
 Cu.import("chrome://marionette/content/capture.js");
 const {InvalidArgumentError} =
     Cu.import("chrome://marionette/content/error.js", {});
--- a/testing/marionette/server.js
+++ b/testing/marionette/server.js
@@ -1,18 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const {Constructor: CC, classes: Cc, interfaces: Ci, utils: Cu} = Components;
+const {Constructor: CC, interfaces: Ci, utils: Cu} = Components;
 
-const loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
-    .getService(Ci.mozIJSSubScriptLoader);
 const ServerSocket = CC(
     "@mozilla.org/network/server-socket;1",
     "nsIServerSocket",
     "initSpecialConnection");
 
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
@@ -455,17 +453,17 @@ server.TCPConnection = class {
     // lookup of commands sent by server to client by message ID
     this.commands_ = new Map();
   }
 
   /**
    * Debugger transport callback that cleans up
    * after a connection is closed.
    */
-  onClosed(reason) {
+  onClosed() {
     this.driver.deleteSession();
     if (this.onclose) {
       this.onclose(this);
     }
   }
 
   /**
    * Callback that receives data packets from the client.
--- a/testing/marionette/session.js
+++ b/testing/marionette/session.js
@@ -3,31 +3,24 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 Cu.importGlobalProperties(["URL"]);
 
-Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 Cu.import("chrome://marionette/content/assert.js");
-const {
-  error,
-  InvalidArgumentError,
-} = Cu.import("chrome://marionette/content/error.js", {});
+const {InvalidArgumentError} = Cu.import("chrome://marionette/content/error.js", {});
 
 this.EXPORTED_SYMBOLS = ["session"];
 
-const logger = Log.repository.getLogger("Marionette");
-const {pprint} = error;
-
 // Enable testing this module, as Services.appinfo.* is not available
 // in xpcshell tests.
 const appinfo = {name: "<missing>", version: "<missing>"};
 try { appinfo.name = Services.appinfo.name.toLowerCase(); } catch (e) {}
 try { appinfo.version = Services.appinfo.version; } catch (e) {}
 
 /**
  * State associated with a WebDriver session.
--- a/testing/marionette/stream-utils.js
+++ b/testing/marionette/stream-utils.js
@@ -190,17 +190,17 @@ StreamCopier.prototype = {
     this._streamReadyCallback();
   },
 
   // nsIOutputStreamCallback
   onOutputStreamReady() {
     this._streamReadyCallback();
   },
 
-  _debug(msg) {
+  _debug() {
   },
 
 };
 
 /**
  * Read from a stream, one byte at a time, up to the next
  * <var>delimiter</var> character, but stopping if we've read |count|
  * without finding it.  Reading also terminates early if there are less
--- a/testing/marionette/transport.js
+++ b/testing/marionette/transport.js
@@ -1,18 +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 strict";
 
 /* global Pipe, ScriptableInputStream, uneval */
 
-const {Constructor: CC, classes: Cc, interfaces: Ci, utils: Cu, results: Cr} =
-    Components;
+const {Constructor: CC, classes: Cc, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/EventEmitter.jsm");
 const {StreamUtils} =
     Cu.import("chrome://marionette/content/stream-utils.js", {});
 const {Packet, JSONPacket, BulkPacket} =
     Cu.import("chrome://marionette/content/packets.js", {});
 
--- a/testing/marionette/wait.js
+++ b/testing/marionette/wait.js
@@ -6,16 +6,17 @@
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 const {
   error,
   TimeoutError,
 } = Cu.import("chrome://marionette/content/error.js", {});
 
+/* exported TimedPromise */
 this.EXPORTED_SYMBOLS = ["wait", "TimedPromise"];
 
 /**
  * Poll-waiting utilities.
  *
  * @namespace
  */
 this.wait = {};
@@ -145,17 +146,17 @@ wait.until = function(func, timeout = 20
  *     Timed promise.
  */
 function TimedPromise(fn, {timeout = 1500, throws = TimeoutError} = {}) {
   const timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
 
   return new Promise((resolve, reject) => {
     // Reject only if |throws| is given.  Otherwise it is assumed that
     // the user is OK with the promise timing out.
-    let bail = res => {
+    let bail = () => {
       if (throws !== null) {
         let err = new throws();
         reject(err);
       } else {
         resolve();
       }
     };
 
--- a/testing/mozbase/mozrunner/mozrunner/utils.py
+++ b/testing/mozbase/mozrunner/mozrunner/utils.py
@@ -99,17 +99,16 @@ def test_environment(xrePath, env=None, 
     else:
         ldLibraryPath = xrePath
 
     envVar = None
     dmdLibrary = None
     preloadEnvVar = None
     if mozinfo.isUnix:
         envVar = "LD_LIBRARY_PATH"
-        env['MOZILLA_FIVE_HOME'] = xrePath
         dmdLibrary = "libdmd.so"
         preloadEnvVar = "LD_PRELOAD"
     elif mozinfo.isMac:
         envVar = "DYLD_LIBRARY_PATH"
         dmdLibrary = "libdmd.dylib"
         preloadEnvVar = "DYLD_INSERT_LIBRARIES"
     elif mozinfo.isWin:
         envVar = "PATH"
--- a/testing/mozharness/mozharness/mozilla/building/buildbase.py
+++ b/testing/mozharness/mozharness/mozilla/building/buildbase.py
@@ -1117,35 +1117,36 @@ or run without that action (ie: --no-{ac
         c = self.config
         dirs = self.query_abs_dirs()
         toolchains = os.environ.get('MOZ_TOOLCHAINS')
         manifest_src = os.environ.get('TOOLTOOL_MANIFEST')
         if not manifest_src:
             manifest_src = c.get('tooltool_manifest_src')
         if not manifest_src and not toolchains:
             return self.warning(ERROR_MSGS['tooltool_manifest_undetermined'])
-        tooltool_manifest_path = os.path.join(dirs['abs_src_dir'],
-                                              manifest_src)
         cmd = [
             sys.executable, '-u',
             os.path.join(dirs['abs_src_dir'], 'mach'),
             'artifact',
             'toolchain',
             '-v',
             '--retry', '4',
-            '--tooltool-manifest',
-            tooltool_manifest_path,
             '--artifact-manifest',
             os.path.join(dirs['abs_src_dir'], 'toolchains.json'),
-            '--tooltool-url',
-            c['tooltool_url'],
         ]
-        auth_file = self._get_tooltool_auth_file()
-        if auth_file:
-            cmd.extend(['--authentication-file', auth_file])
+        if manifest_src:
+            cmd.extend([
+                '--tooltool-manifest',
+                os.path.join(dirs['abs_src_dir'], manifest_src),
+                '--tooltool-url',
+                c['tooltool_url'],
+            ])
+            auth_file = self._get_tooltool_auth_file()
+            if auth_file:
+                cmd.extend(['--authentication-file', auth_file])
         cache = c['env'].get('TOOLTOOL_CACHE')
         if cache:
             cmd.extend(['--cache-dir', cache])
         if toolchains:
             cmd.extend(toolchains.split())
         self.info(str(cmd))
         self.run_command_m(cmd, cwd=dirs['abs_src_dir'], halt_on_failure=True,
                            env=env)
--- a/testing/mozharness/scripts/desktop_l10n.py
+++ b/testing/mozharness/scripts/desktop_l10n.py
@@ -1048,41 +1048,42 @@ class DesktopSingleLocale(LocalesMixin, 
         config = self.config
         dirs = self.query_abs_dirs()
         toolchains = os.environ.get('MOZ_TOOLCHAINS')
         manifest_src = os.environ.get('TOOLTOOL_MANIFEST')
         if not manifest_src:
             manifest_src = config.get('tooltool_manifest_src')
         if not manifest_src and not toolchains:
             return
-        tooltool_manifest_path = os.path.join(dirs['abs_mozilla_dir'],
-                                              manifest_src)
         python = sys.executable
         # A mock environment is a special case, the system python isn't
         # available there
         if 'mock_target' in self.config:
             python = 'python2.7'
 
         cmd = [
             python, '-u',
             os.path.join(dirs['abs_mozilla_dir'], 'mach'),
             'artifact',
             'toolchain',
             '-v',
             '--retry', '4',
-            '--tooltool-manifest',
-            tooltool_manifest_path,
             '--artifact-manifest',
             os.path.join(dirs['abs_mozilla_dir'], 'toolchains.json'),
-            '--tooltool-url',
-            config['tooltool_url'],
         ]
-        auth_file = self._get_tooltool_auth_file()
-        if auth_file and os.path.exists(auth_file):
-            cmd.extend(['--authentication-file', auth_file])
+        if manifest_src:
+            cmd.extend([
+                '--tooltool-manifest',
+                os.path.join(dirs['abs_mozilla_dir'], manifest_src),
+                '--tooltool-url',
+                config['tooltool_url'],
+            ])
+            auth_file = self._get_tooltool_auth_file()
+            if auth_file and os.path.exists(auth_file):
+                cmd.extend(['--authentication-file', auth_file])
         cache = config['bootstrap_env'].get('TOOLTOOL_CACHE')
         if cache:
             cmd.extend(['--cache-dir', cache])
         if toolchains:
             cmd.extend(toolchains.split())
         self.info(str(cmd))
         self.run_command(cmd, cwd=dirs['abs_mozilla_dir'], halt_on_failure=True,
                          env=env)
--- a/testing/remotecppunittests.py
+++ b/testing/remotecppunittests.py
@@ -102,17 +102,16 @@ class RemoteCPPUnitTests(cppunittests.CP
                 self.remote_bin_dir, os.path.basename(local_file))
             self.device.pushFile(local_file, remote_file)
 
     def build_environment(self):
         env = self.build_core_environment()
         env['LD_LIBRARY_PATH'] = self.remote_bin_dir
         env["TMPDIR"] = self.remote_tmp_dir
         env["HOME"] = self.remote_home_dir
-        env["MOZILLA_FIVE_HOME"] = self.remote_home_dir
         env["MOZ_XRE_DIR"] = self.remote_bin_dir
         if self.options.add_env:
             for envdef in self.options.add_env:
                 envdef_parts = envdef.split("=", 1)
                 if len(envdef_parts) == 2:
                     env[envdef_parts[0]] = envdef_parts[1]
                 elif len(envdef_parts) == 1:
                     env[envdef_parts[0]] = ""
--- a/testing/runcppunittests.py
+++ b/testing/runcppunittests.py
@@ -79,17 +79,16 @@ class CPPUnitTests(object):
             else:
                 self.log.test_end(basename, status='PASS', expected='PASS')
             return result
 
     def build_core_environment(self, env={}):
         """
         Add environment variables likely to be used across all platforms, including remote systems.
         """
-        env["MOZILLA_FIVE_HOME"] = self.xre_path
         env["MOZ_XRE_DIR"] = self.xre_path
         # TODO: switch this to just abort once all C++ unit tests have
         # been fixed to enable crash reporting
         env["XPCOM_DEBUG_BREAK"] = "stack-and-abort"
         env["MOZ_CRASHREPORTER_NO_REPORT"] = "1"
         env["MOZ_CRASHREPORTER"] = "1"
         return env
 
--- a/testing/specialpowers/content/specialpowersAPI.js
+++ b/testing/specialpowers/content/specialpowersAPI.js
@@ -1417,16 +1417,19 @@ SpecialPowersAPI.prototype = {
   },
   resetConsole() {
     Services.console.reset();
   },
 
   getFullZoom(window) {
     return this._getMUDV(window).fullZoom;
   },
+  getDeviceFullZoom(window) {
+    return this._getMUDV(window).deviceFullZoom;
+  },
   setFullZoom(window, zoom) {
     this._getMUDV(window).fullZoom = zoom;
   },
   getTextZoom(window) {
     return this._getMUDV(window).textZoom;
   },
   setTextZoom(window, zoom) {
     this._getMUDV(window).textZoom = zoom;
--- a/toolkit/components/places/PlacesSyncUtils.jsm
+++ b/toolkit/components/places/PlacesSyncUtils.jsm
@@ -71,16 +71,35 @@ function* chunkArray(array, chunkLength)
   let startIndex = 0;
   while (startIndex < array.length) {
     yield array.slice(startIndex, startIndex += chunkLength);
   }
 }
 
 const HistorySyncUtils = PlacesSyncUtils.history = Object.freeze({
   /**
+   * Clamps a history visit date between the current date and the earliest
+   * sensible date.
+   *
+   * @param {Date} visitDate
+   *        The visit date.
+   * @return {Date} The clamped visit date.
+   */
+  clampVisitDate(visitDate) {
+    let currentDate = new Date();
+    if (visitDate > currentDate) {
+      return currentDate;
+    }
+    if (visitDate < BookmarkSyncUtils.EARLIEST_BOOKMARK_TIMESTAMP) {
+      return new Date(BookmarkSyncUtils.EARLIEST_BOOKMARK_TIMESTAMP);
+    }
+    return visitDate;
+  },
+
+  /**
    * Fet