Merge inbound to mozilla-central. a=merge
authorNoemi Erli <nerli@mozilla.com>
Thu, 02 Aug 2018 11:54:46 +0300
changeset 429748 bd79b07f57a34e0d8fe01bdc3f34815d77c01444
parent 429686 42493203a61f848b2ab931ac3bb7dc6029fe821e (current diff)
parent 429747 424f4541472c8942c7c85053c7d7eb224548e24a (diff)
child 429777 5f968aa5516d347a3e255076af5784bd3f4847c7
child 429857 66834f1745ee4557a5f63299e81420bb9d89b728
push id34372
push usernerli@mozilla.com
push dateThu, 02 Aug 2018 08:55:28 +0000
treeherdermozilla-central@bd79b07f57a3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone63.0a1
first release with
nightly linux32
bd79b07f57a3 / 63.0a1 / 20180802100128 / files
nightly linux64
bd79b07f57a3 / 63.0a1 / 20180802100128 / files
nightly mac
bd79b07f57a3 / 63.0a1 / 20180802100128 / files
nightly win32
bd79b07f57a3 / 63.0a1 / 20180802100128 / files
nightly win64
bd79b07f57a3 / 63.0a1 / 20180802100128 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
browser/base/content/tabbrowser.js
browser/components/uitour/content-UITour.js
browser/components/uitour/jar.mn
build/build-clang/bug38105.patch
build/build-clang/bug38139.patch
dom/base/nsGlobalWindowInner.cpp
dom/base/test/gtest/TestParserDialogOptions.cpp
dom/ipc/manifestMessages.js
modules/libpref/init/all.js
testing/web-platform/meta/css/CSS2/backgrounds/background-position-001.xht.ini
testing/web-platform/meta/css/CSS2/backgrounds/background-position-002.xht.ini
testing/web-platform/tests/html/semantics/tabular-data/processing-model-1/span-limits-2.html
toolkit/components/normandy/content/shield-content-frame.js
xpcom/ds/make_dafsa.py
xpcom/reflect/xptinfo/perfecthash.py
--- a/accessible/tests/mochitest/common.js
+++ b/accessible/tests/mochitest/common.js
@@ -707,19 +707,17 @@ function eventTypeToString(aEventType) {
 /**
  * Convert relation type to human readable string.
  */
 function relationTypeToString(aRelationType) {
   return gAccService.getStringRelationType(aRelationType);
 }
 
 function getLoadContext() {
-  return window.QueryInterface(Ci.nsIInterfaceRequestor)
-               .getInterface(Ci.nsIWebNavigation)
-               .QueryInterface(Ci.nsILoadContext);
+  return window.docShell.QueryInterface(Ci.nsILoadContext);
 }
 
 /**
  * Return text from clipboard.
  */
 function getTextFromClipboard() {
   var trans = Cc["@mozilla.org/widget/transferable;1"].
     createInstance(Ci.nsITransferable);
--- a/accessible/tests/mochitest/layout.js
+++ b/accessible/tests/mochitest/layout.js
@@ -54,20 +54,17 @@ function testOffsetAtPoint(aHyperTextID,
      "Wrong offset at given point (" + aX + ", " + aY + ") for " +
      prettyName(aHyperTextID));
 }
 
 /**
  * Zoom the given document.
  */
 function zoomDocument(aDocument, aZoom) {
-  var docShell = aDocument.defaultView.
-    QueryInterface(Ci.nsIInterfaceRequestor).
-    getInterface(Ci.nsIWebNavigation).
-    QueryInterface(Ci.nsIDocShell);
+  var docShell = aDocument.defaultView.docShell;
   var docViewer = docShell.contentViewer;
 
   docViewer.fullZoom = aZoom;
 }
 
 /**
  * Set the relative resolution of this document. This is what apz does.
  * On non-mobile platforms you won't see a visible change.
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -232,18 +232,17 @@ XPCOMUtils.defineLazyGetter(this, "Win7F
   return null;
 });
 
 var gBrowser;
 var gLastValidURLStr = "";
 var gInPrintPreviewMode = false;
 var gContextMenu = null; // nsContextMenu instance
 var gMultiProcessBrowser =
-  window.QueryInterface(Ci.nsIInterfaceRequestor)
-        .getInterface(Ci.nsIWebNavigation)
+  window.docShell
         .QueryInterface(Ci.nsILoadContext)
         .useRemoteTabs;
 
 if (AppConstants.platform != "macosx") {
   var gEditUIVisible = true;
 }
 
 /* globals gNavToolbox, gURLBar:true */
@@ -1233,19 +1232,17 @@ var gBrowserInit = {
     ToolbarIconColor.init();
   },
 
   onDOMContentLoaded() {
     gBrowser = window._gBrowser;
     delete window._gBrowser;
     gBrowser.init();
 
-    window.QueryInterface(Ci.nsIInterfaceRequestor)
-          .getInterface(Ci.nsIWebNavigation)
-          .QueryInterface(Ci.nsIDocShellTreeItem).treeOwner
+    window.docShell.treeOwner
           .QueryInterface(Ci.nsIInterfaceRequestor)
           .getInterface(Ci.nsIXULWindow)
           .XULBrowserWindow = window.XULBrowserWindow;
     window.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow =
       new nsBrowserAccess();
     BrowserWindowTracker.track(window);
 
     let initBrowser = gBrowser.initialBrowser;
@@ -1318,19 +1315,17 @@ var gBrowserInit = {
     FeedHandler.init();
     TrackingProtection.init();
     CaptivePortalWatcher.init();
     ZoomUI.init(window);
 
     let mm = window.getGroupMessageManager("browsers");
     mm.loadFrameScript("chrome://browser/content/tab-content.js", true);
     mm.loadFrameScript("chrome://browser/content/content.js", true);
-    mm.loadFrameScript("chrome://browser/content/content-UITour.js", true);
     mm.loadFrameScript("chrome://global/content/content-HybridContentTelemetry.js", true);
-    mm.loadFrameScript("chrome://global/content/manifestMessages.js", true);
 
     window.messageManager.addMessageListener("Browser:LoadURI", RedirectLoad);
 
     if (!gMultiProcessBrowser) {
       // There is a Content:Click message manually sent from content.
       Services.els.addSystemEventListener(gBrowser.tabpanels, "click",
         contentAreaClick, true);
     }
@@ -1931,19 +1926,17 @@ var gBrowserInit = {
       WebAuthnPromptHelper.uninit();
       PanelUI.uninit();
       AutoShowBookmarksToolbar.uninit();
     }
 
     // Final window teardown, do this last.
     gBrowser.destroy();
     window.XULBrowserWindow = null;
-    window.QueryInterface(Ci.nsIInterfaceRequestor)
-          .getInterface(Ci.nsIWebNavigation)
-          .QueryInterface(Ci.nsIDocShellTreeItem).treeOwner
+    window.docShell.treeOwner
           .QueryInterface(Ci.nsIInterfaceRequestor)
           .getInterface(Ci.nsIXULWindow)
           .XULBrowserWindow = null;
     window.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow = null;
   },
 };
 
 function HandleAppCommandEvent(evt) {
@@ -2476,19 +2469,17 @@ function getPostDataStream(aPostDataStri
   let mimeStream = Cc["@mozilla.org/network/mime-input-stream;1"]
                      .createInstance(Ci.nsIMIMEInputStream);
   mimeStream.addHeader("Content-Type", aType);
   mimeStream.setData(dataStream);
   return mimeStream.QueryInterface(Ci.nsIInputStream);
 }
 
 function getLoadContext() {
-  return window.QueryInterface(Ci.nsIInterfaceRequestor)
-               .getInterface(Ci.nsIWebNavigation)
-               .QueryInterface(Ci.nsILoadContext);
+  return window.docShell.QueryInterface(Ci.nsILoadContext);
 }
 
 function readFromClipboard() {
   var url;
 
   try {
     // Create transferable that will transfer the text.
     var trans = Cc["@mozilla.org/widget/transferable;1"]
@@ -2548,19 +2539,17 @@ async function BrowserViewSourceOfDocume
     let doc = aArgsOrDocument;
     // Deprecated API - callers should pass args object instead.
     if (Cu.isCrossProcessWrapper(doc)) {
       throw new Error("BrowserViewSourceOfDocument cannot accept a CPOW " +
                       "as a document.");
     }
 
     let win = doc.defaultView;
-    let browser = win.getInterface(Ci.nsIWebNavigation)
-                     .QueryInterface(Ci.nsIDocShell)
-                     .chromeEventHandler;
+    let browser = win.docShell.chromeEventHandler;
     let outerWindowID = win.windowUtils.outerWindowID;
     let URL = browser.currentURI.spec;
     args = { browser, outerWindowID, URL };
   } else {
     args = aArgsOrDocument;
   }
 
   // Check if external view source is enabled.  If so, try it.  If it fails,
--- a/browser/base/content/content-refreshblocker.js
+++ b/browser/base/content/content-refreshblocker.js
@@ -155,19 +155,17 @@ var RefreshBlocker = {
     return false;
   },
 
   receiveMessage(message) {
     let data = message.data;
 
     if (message.name == "RefreshBlocker:Refresh") {
       let win = Services.wm.getOuterWindowWithId(data.outerWindowID);
-      let refreshURI = win.QueryInterface(Ci.nsIInterfaceRequestor)
-                          .getInterface(Ci.nsIDocShell)
-                          .QueryInterface(Ci.nsIRefreshURI);
+      let refreshURI = win.docShell.QueryInterface(Ci.nsIRefreshURI);
 
       let URI = Services.io.newURI(data.URI);
 
       refreshURI.forceRefreshURI(URI, null, data.delay, true);
     }
   },
 
   QueryInterface: ChromeUtils.generateQI([Ci.nsIWebProgressListener2, Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference]),
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -225,21 +225,17 @@ nsContextMenu.prototype = {
     this.targetSelectors = gContextMenuContentData
                            ? gContextMenuContentData.popupNodeSelectors
                            : [];
 
     if (this.isRemote) {
       this.browser = gContextMenuContentData.browser;
       this.selectionInfo = gContextMenuContentData.selectionInfo;
     } else {
-      this.browser = this.ownerDoc.defaultView
-                         .QueryInterface(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsIWebNavigation)
-                         .QueryInterface(Ci.nsIDocShell)
-                         .chromeEventHandler;
+      this.browser = this.ownerDoc.defaultView.docShell.chromeEventHandler;
       this.selectionInfo = BrowserUtils.getSelectionDetails(window);
     }
 
     this.textSelected      = this.selectionInfo.text;
     this.isTextSelected    = this.textSelected.length != 0;
     this.webExtBrowserType = this.browser.getAttribute("webextension-view-type");
     this.inWebExtBrowser   = !!this.webExtBrowserType;
     this.inTabBrowser      = this.browser.ownerGlobal.gBrowser ?
@@ -255,18 +251,17 @@ nsContextMenu.prototype = {
       }
     }
 
     if (context.shouldInitInlineSpellCheckerUIWithChildren) {
       if (this.isRemote) {
         InlineSpellCheckerUI.initFromRemote(gContextMenuContentData.spellInfo);
       } else {
         var targetWin = this.ownerDoc.defaultView;
-        var editingSession = targetWin.QueryInterface(Ci.nsIInterfaceRequestor)
-                                      .getInterface(Ci.nsIWebNavigation)
+        var editingSession = targetWin.docShell
                                       .QueryInterface(Ci.nsIInterfaceRequestor)
                                       .getInterface(Ci.nsIEditingSession);
 
         InlineSpellCheckerUI.init(editingSession.getEditorForWindow(targetWin));
         InlineSpellCheckerUI.initFromEvent(document.popupRangeParent,
                                            document.popupRangeOffset);
       }
 
--- a/browser/base/content/pageinfo/pageInfo.js
+++ b/browser/base/content/pageinfo/pageInfo.js
@@ -233,19 +233,17 @@ const PREFERENCES_CONTRACTID    = "@mozi
 
 // a number of services I'll need later
 // the cache services
 const nsICacheStorageService = Ci.nsICacheStorageService;
 const nsICacheStorage = Ci.nsICacheStorage;
 const cacheService = Cc["@mozilla.org/netwerk/cache-storage-service;1"].getService(nsICacheStorageService);
 
 var loadContextInfo = Services.loadContextInfo.fromLoadContext(
-  window.QueryInterface(Ci.nsIInterfaceRequestor)
-        .getInterface(Ci.nsIWebNavigation)
-        .QueryInterface(Ci.nsILoadContext), false);
+  window.docShell.QueryInterface(Ci.nsILoadContext), false);
 var diskStorage = cacheService.diskCacheStorage(loadContextInfo, false);
 
 const nsICookiePermission  = Ci.nsICookiePermission;
 const nsIPermissionManager = Ci.nsIPermissionManager;
 
 const nsICertificateDialogs = Ci.nsICertificateDialogs;
 const CERTIFICATEDIALOGS_CONTRACTID = "@mozilla.org/nsCertificateDialogs;1";
 
--- a/browser/base/content/tab-content.js
+++ b/browser/base/content/tab-content.js
@@ -85,16 +85,22 @@ addMessageListener("Browser:Reload", fun
 
 addMessageListener("MixedContent:ReenableProtection", function() {
   docShell.mixedContentChannel = null;
 });
 
 XPCOMUtils.defineLazyProxy(this, "LightweightThemeChildHelper",
   "resource:///modules/LightweightThemeChildHelper.jsm");
 
+XPCOMUtils.defineLazyProxy(this, "ManifestMessages", () => {
+  let tmp = {};
+  ChromeUtils.import("resource://gre/modules/ManifestMessages.jsm", tmp);
+  return new tmp.ManifestMessages(global);
+});
+
 let themeablePagesWhitelist = new Set([
   "about:home",
   "about:newtab",
   "about:welcome",
 ]);
 
 addEventListener("pageshow", function({ originalTarget }) {
   if (originalTarget.defaultView == content && themeablePagesWhitelist.has(content.document.documentURI)) {
@@ -512,8 +518,13 @@ addEventListener("MozAfterPaint", functi
 
 // Remove this once bug 1397365 is fixed.
 addEventListener("MozAfterPaint", function onFirstNonBlankPaint() {
   if (content.document.documentURI == "about:blank" && !content.opener)
     return;
   removeEventListener("MozAfterPaint", onFirstNonBlankPaint);
   sendAsyncMessage("Browser:FirstNonBlankPaint");
 });
+
+addMessageListener("DOM:WebManifest:hasManifestLink", ManifestMessages);
+addMessageListener("DOM:ManifestObtainer:Obtain", ManifestMessages);
+addMessageListener("DOM:Manifest:FireAppInstalledEvent", ManifestMessages);
+addMessageListener("DOM:WebManifest:fetchIcon", ManifestMessages);
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -655,20 +655,17 @@ window._gBrowser = {
     return this._outerWindowIDBrowserMap.get(aID);
   },
 
   _getTabForContentWindow(aWindow) {
     // When not using remote browsers, we can take a fast path by getting
     // directly from the content window to the browser without looping
     // over all browsers.
     if (!gMultiProcessBrowser) {
-      let browser = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                           .getInterface(Ci.nsIWebNavigation)
-                           .QueryInterface(Ci.nsIDocShell)
-                           .chromeEventHandler;
+      let browser = aWindow.docShell.chromeEventHandler;
       return this.getTabForBrowser(browser);
     }
 
     for (let i = 0; i < this.browsers.length; i++) {
       // NB: We use contentWindowAsCPOW so that this code works both
       // for remote browsers as well. aWindow may be a CPOW.
       if (this.browsers[i].contentWindowAsCPOW == aWindow)
         return this.tabs[i];
@@ -3065,21 +3062,17 @@ window._gBrowser = {
     // immediately without animation before the docshell swap, to avoid
     // about:blank being painted.
     let [closeWindow] = aOtherTab._endRemoveArgs;
     if (closeWindow) {
       let win = aOtherTab.ownerGlobal;
       win.windowUtils.suppressAnimation(true);
       // Only suppressing window animations isn't enough to avoid
       // an empty content area being painted.
-      let baseWin = win.QueryInterface(Ci.nsIInterfaceRequestor)
-                       .getInterface(Ci.nsIDocShell)
-                       .QueryInterface(Ci.nsIDocShellTreeItem)
-                       .treeOwner
-                       .QueryInterface(Ci.nsIBaseWindow);
+      let baseWin = win.docShell.treeOwner.QueryInterface(Ci.nsIBaseWindow);
       baseWin.visibility = false;
     }
 
     let modifiedAttrs = [];
     if (aOtherTab.hasAttribute("muted")) {
       aOurTab.setAttribute("muted", "true");
       aOurTab.muteReason = aOtherTab.muteReason;
       ourBrowser.mute();
--- a/browser/base/content/test/general/browser_bug1297539.js
+++ b/browser/base/content/test/general/browser_bug1297539.js
@@ -13,19 +13,17 @@
  * "pasteTransferable" runs only in the content process.
  * This doesn't test the remote case.
  *
  */
 
 "use strict";
 
 function getLoadContext() {
-  return window.QueryInterface(Ci.nsIInterfaceRequestor)
-               .getInterface(Ci.nsIWebNavigation)
-               .QueryInterface(Ci.nsILoadContext);
+  return window.docShell.QueryInterface(Ci.nsILoadContext);
 }
 
 function getTransferableFromClipboard(asHTML) {
   let trans = Cc["@mozilla.org/widget/transferable;1"].
                     createInstance(Ci.nsITransferable);
   trans.init(getLoadContext());
   if (asHTML) {
     trans.addDataFlavor("text/html");
--- a/browser/base/content/test/performance/browser_startup_content.js
+++ b/browser/base/content/test/performance/browser_startup_content.js
@@ -18,28 +18,22 @@
 const kDumpAllStacks = false;
 
 const whitelist = {
   components: new Set([
     "ContentProcessSingleton.js",
     "extension-process-script.js",
   ]),
   modules: new Set([
-    // From the test harness
     "chrome://mochikit/content/ShutdownLeaksCollector.jsm",
-    "resource://specialpowers/MockColorPicker.jsm",
-    "resource://specialpowers/MockFilePicker.jsm",
-    "resource://specialpowers/MockPermissionPrompt.jsm",
 
     // General utilities
     "resource://gre/modules/AppConstants.jsm",
     "resource://gre/modules/AsyncShutdown.jsm",
     "resource://gre/modules/DeferredTask.jsm",
-    "resource://gre/modules/FileUtils.jsm",
-    "resource://gre/modules/NetUtil.jsm",
     "resource://gre/modules/PromiseUtils.jsm",
     "resource://gre/modules/Services.jsm", // bug 1464542
     "resource://gre/modules/Timer.jsm",
     "resource://gre/modules/XPCOMUtils.jsm",
 
     // Logging related
     "resource://gre/modules/Log.jsm",
 
@@ -71,19 +65,16 @@ const whitelist = {
     // PDF.js
     "resource://pdf.js/PdfJsRegistration.jsm",
     "resource://pdf.js/PdfjsContentUtils.jsm",
 
     // Extensions
     "resource://gre/modules/ExtensionUtils.jsm",
     "resource://gre/modules/MessageChannel.jsm",
 
-    // Service workers
-    "resource://gre/modules/ServiceWorkerCleanUp.jsm",
-
     // Shield
     "resource://normandy-content/AboutPages.jsm",
   ]),
 };
 
 // Items on this list are allowed to be loaded but not
 // required, as opposed to items in the main whitelist,
 // which are all required.
--- a/browser/base/content/test/performance/head.js
+++ b/browser/base/content/test/performance/head.js
@@ -52,19 +52,17 @@ async function recordReflows(testPromise
       // Interruptible reflows are the reflows caused by the refresh
       // driver ticking. These are fine.
     },
 
     QueryInterface: ChromeUtils.generateQI([Ci.nsIReflowObserver,
                                             Ci.nsISupportsWeakReference])
   };
 
-  let docShell = win.QueryInterface(Ci.nsIInterfaceRequestor)
-                    .getInterface(Ci.nsIWebNavigation)
-                    .QueryInterface(Ci.nsIDocShell);
+  let docShell = win.docShell;
   docShell.addWeakReflowObserver(observer);
 
   let dirtyFrameFn = event => {
     if (event.type != "MozAfterPaint") {
       dirtyFrame(win);
     }
   };
   Services.els.addListenerForAllEvents(win, dirtyFrameFn, true);
--- a/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_font.html
+++ b/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_font.html
@@ -7,21 +7,17 @@
 <head>
   <meta charset="utf-8">
   <title>Test 2 for Bug 909920</title>
   <link rel="stylesheet" type="text/css" href="https://example.com/browser/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_font.css" />
 <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
 
 <script type="text/javascript">
   function checkLoadStates() {
-   var ui = SpecialPowers.wrap(window)
-            .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
-             .getInterface(SpecialPowers.Ci.nsIWebNavigation)
-             .QueryInterface(SpecialPowers.Ci.nsIDocShell)
-             .securityUI;
+   var ui = SpecialPowers.wrap(window).docShell.securityUI;
 
    var loadedMixedActive = ui &&
      !!(ui.state & SpecialPowers.Ci.nsIWebProgressListener.STATE_LOADED_MIXED_ACTIVE_CONTENT);
    is(loadedMixedActive, false, "OK: Should not load mixed active content!");
 
    var blockedMixedActive = ui &&
      !!(ui.state & SpecialPowers.Ci.nsIWebProgressListener.STATE_BLOCKED_MIXED_ACTIVE_CONTENT);
    is(blockedMixedActive, false, "OK: Should not block mixed active content!");
--- a/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_font2.html
+++ b/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_font2.html
@@ -7,21 +7,17 @@
 <head>
   <meta charset="utf-8">
   <title>Test 3 for Bug 909920</title>
   <link rel="stylesheet" type="text/css" href="https://example.com/browser/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_font2.css" />
 <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
 
 <script type="text/javascript">
   function checkLoadStates() {
-   var ui = SpecialPowers.wrap(window)
-        .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
-             .getInterface(SpecialPowers.Ci.nsIWebNavigation)
-             .QueryInterface(SpecialPowers.Ci.nsIDocShell)
-             .securityUI;
+   var ui = SpecialPowers.wrap(window).docShell.securityUI;
 
    var loadedMixedActive = ui &&
      !!(ui.state & SpecialPowers.Ci.nsIWebProgressListener.STATE_LOADED_MIXED_ACTIVE_CONTENT);
    is(loadedMixedActive, false, "OK: Should not load mixed active content!");
 
    var blockedMixedActive = ui &&
      !!(ui.state & SpecialPowers.Ci.nsIWebProgressListener.STATE_BLOCKED_MIXED_ACTIVE_CONTENT);
    is(blockedMixedActive, false, "OK: Should not block mixed active content!");
--- a/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_img.html
+++ b/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_img.html
@@ -7,21 +7,17 @@
 <head>
   <meta charset="utf-8">
   <title>Test 1 for Bug 909920</title>
   <link rel="stylesheet" type="text/css" href="https://example.com/browser/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_img.css" />
 <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
 
 <script type="text/javascript">
   function checkLoadStates() {
-   var ui = SpecialPowers.wrap(window)
-            .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
-             .getInterface(SpecialPowers.Ci.nsIWebNavigation)
-             .QueryInterface(SpecialPowers.Ci.nsIDocShell)
-             .securityUI;
+   var ui = SpecialPowers.wrap(window).docShell.securityUI;
 
    var loadedMixedActive = ui &&
      !!(ui.state & SpecialPowers.Ci.nsIWebProgressListener.STATE_LOADED_MIXED_ACTIVE_CONTENT);
    is(loadedMixedActive, false, "OK: Should not load mixed active content!");
 
    var blockedMixedActive = ui &&
      !!(ui.state & SpecialPowers.Ci.nsIWebProgressListener.STATE_BLOCKED_MIXED_ACTIVE_CONTENT);
    is(blockedMixedActive, false, "OK: Should not block mixed active content!");
--- a/browser/components/customizableui/CustomizableUI.jsm
+++ b/browser/components/customizableui/CustomizableUI.jsm
@@ -1709,21 +1709,18 @@ var CustomizableUIInternal = {
     // a toolbarbutton/item, or the panel:
     while (true && target) {
       // Skip out of iframes etc:
       if (target.nodeType == target.DOCUMENT_NODE) {
         if (!target.defaultView) {
           // Err, we're done.
           break;
         }
-        // Cue some voodoo
-        target = target.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
-                                   .getInterface(Ci.nsIWebNavigation)
-                                   .QueryInterface(Ci.nsIDocShell)
-                                   .chromeEventHandler;
+        // Find containing browser or iframe element in the parent doc.
+        target = target.defaultView.docShell.chromeEventHandler;
         if (!target) {
           break;
         }
       }
       let tagName = target.localName;
       inInput = tagName == "input" || tagName == "textbox";
       inItem = tagName == "toolbaritem" || tagName == "toolbarbutton";
       let isMenuItem = tagName == "menuitem";
--- a/browser/components/downloads/DownloadsTaskbar.jsm
+++ b/browser/components/downloads/DownloadsTaskbar.jsm
@@ -117,19 +117,17 @@ var DownloadsTaskbar = {
     }
   },
 
   /**
    * On Windows, attaches the taskbar indicator to the specified browser window.
    */
   _attachIndicator(aWindow) {
     // Activate the indicator on the specified window.
-    let docShell = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                          .getInterface(Ci.nsIWebNavigation)
-                          .QueryInterface(Ci.nsIDocShellTreeItem).treeOwner
+    let docShell = aWindow.docShell.treeOwner
                           .QueryInterface(Ci.nsIInterfaceRequestor)
                           .getInterface(Ci.nsIXULWindow).docShell;
     this._taskbarProgress = gWinTaskbar.getTaskbarProgress(docShell);
 
     // If the DownloadSummary object has already been created, we should update
     // the state of the new indicator, otherwise it will be updated as soon as
     // the DownloadSummary view is registered.
     if (this._summary) {
--- a/browser/components/extensions/test/browser/browser_ext_find.js
+++ b/browser/components/extensions/test/browser/browser_ext_find.js
@@ -1,17 +1,15 @@
 /* global browser */
 "use strict";
 
 function frameScript() {
   function getSelectedText() {
     let frame = this.content.frames[0].frames[1];
-    let docShell = frame.QueryInterface(Ci.nsIInterfaceRequestor)
-                        .getInterface(Ci.nsIWebNavigation)
-                        .QueryInterface(Ci.nsIDocShell);
+    let docShell = frame.docShell;
     let controller = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                              .getInterface(Ci.nsISelectionDisplay)
                              .QueryInterface(Ci.nsISelectionController);
     let selection = controller.getSelection(controller.SELECTION_FIND);
     let range = selection.getRangeAt(0);
     let scope = {};
     ChromeUtils.import("resource://gre/modules/FindContent.jsm", scope);
     let highlighter = (new scope.FindContent(docShell)).highlighter;
--- a/browser/components/extensions/test/browser/browser_ext_windows_create.js
+++ b/browser/components/extensions/test/browser/browser_ext_windows_create.js
@@ -113,18 +113,17 @@ add_task(async function testWindowCreate
     }
     if (expected.hiddenChrome) {
       let chromeHidden = latestWindow.document.documentElement.getAttribute("chromehidden");
       is(chromeHidden.trim().split(/\s+/).sort().join(" "),
          expected.hiddenChrome.sort().join(" "),
          "Got expected hidden chrome");
     }
     if (expected.chromeFlags) {
-      let {chromeFlags} = latestWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                                      .getInterface(Ci.nsIDocShell)
+      let {chromeFlags} = latestWindow.docShell
                                       .treeOwner.QueryInterface(Ci.nsIInterfaceRequestor)
                                       .getInterface(Ci.nsIXULWindow);
       for (let flag of expected.chromeFlags) {
         ok(chromeFlags & Ci.nsIWebBrowserChrome[flag],
            `Expected window to have the ${flag} flag`);
       }
     }
 
--- a/browser/components/feeds/FeedWriter.js
+++ b/browser/components/feeds/FeedWriter.js
@@ -71,18 +71,17 @@ function FeedWriter() {
   this._selectedApp = undefined;
   this._selectedAppMenuItem = null;
   this._subscribeCallback = null;
   this._defaultHandlerMenuItem = null;
 
   Services.telemetry.scalarAdd("browser.feeds.preview_loaded", 1);
 
   XPCOMUtils.defineLazyGetter(this, "_mm", () =>
-    this._window.QueryInterface(Ci.nsIInterfaceRequestor).
-                 getInterface(Ci.nsIDocShell).
+    this._window.docShell.
                  QueryInterface(Ci.nsIInterfaceRequestor).
                  getInterface(Ci.nsIContentFrameMessageManager));
 }
 
 FeedWriter.prototype = {
   _getPropertyAsBag(container, property) {
     return container.fields.getProperty(property).
                      QueryInterface(Ci.nsIPropertyBag2);
@@ -705,19 +704,17 @@ FeedWriter.prototype = {
 
   /**
    * Returns the original URI object of the feed and ensures that this
    * component is only ever invoked from the preview document.
    * @param aWindow
    *        The window of the document invoking the BrowserFeedWriter
    */
   _getOriginalURI(aWindow) {
-    let docShell = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                          .getInterface(Ci.nsIWebNavigation)
-                          .QueryInterface(Ci.nsIDocShell);
+    let docShell = aWindow.docShell;
     let chan = docShell.currentDocumentChannel;
 
     // We probably need to call Inherit() for this, but right now we can't call
     // it from JS.
     let attrs = docShell.getOriginAttributes();
     let ssm = Services.scriptSecurityManager;
     let nullPrincipal = ssm.createNullPrincipal(attrs);
 
--- a/browser/components/feeds/WebContentConverter.js
+++ b/browser/components/feeds/WebContentConverter.js
@@ -318,19 +318,17 @@ WebContentConverterRegistrar.prototype =
 
 function WebContentConverterRegistrarContent() {
 }
 
 WebContentConverterRegistrarContent.prototype = {
   registerProtocolHandler(aProtocol, aURIString, aTitle, aBrowserOrWindow) {
     aProtocol = (aProtocol || "").toLowerCase();
     // aBrowserOrWindow must be a window.
-    let messageManager = aBrowserOrWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                                         .getInterface(Ci.nsIWebNavigation)
-                                         .QueryInterface(Ci.nsIDocShell)
+    let messageManager = aBrowserOrWindow.docShell
                                          .QueryInterface(Ci.nsIInterfaceRequestor)
                                          .getInterface(Ci.nsITabChild)
                                          .messageManager;
 
     let uri = Utils.checkAndGetURI(aURIString, aBrowserOrWindow);
     Utils.checkProtocolHandlerAllowed(aProtocol, aURIString, aBrowserOrWindow);
 
     messageManager.sendAsyncMessage("WCCR:registerProtocolHandler",
--- a/browser/components/nsBrowserContentHandler.js
+++ b/browser/components/nsBrowserContentHandler.js
@@ -205,18 +205,17 @@ function openBrowserWindow(cmdLine, urlO
   if (cmdLine.state == Ci.nsICommandLine.STATE_INITIAL_LAUNCH) {
     let win = Services.wm.getMostRecentWindow("navigator:blank");
     if (win) {
       // Remove the windowtype of our blank window so that we don't close it
       // later on when seeing cmdLine.preventDefault is true.
       win.document.documentElement.removeAttribute("windowtype");
 
       if (forcePrivate) {
-        win.QueryInterface(Ci.nsIInterfaceRequestor)
-           .getInterface(Ci.nsIWebNavigation)
+        win.docShell
            .QueryInterface(Ci.nsILoadContext)
            .usePrivateBrowsing = true;
       }
 
       win.location = chromeURL;
       win.arguments = args; // <-- needs to be a plain JS array here.
 
       return;
@@ -415,18 +414,17 @@ nsBrowserContentHandler.prototype = {
 
     // The global PB Service consumes this flag, so only eat it in per-window
     // PB builds.
     if (cmdLine.handleFlag("private", false) && PrivateBrowsingUtils.enabled) {
       PrivateBrowsingUtils.enterTemporaryAutoStartMode();
       if (cmdLine.state == Ci.nsICommandLine.STATE_INITIAL_LAUNCH) {
         let win = Services.wm.getMostRecentWindow("navigator:blank");
         if (win) {
-          win.QueryInterface(Ci.nsIInterfaceRequestor)
-             .getInterface(Ci.nsIWebNavigation)
+          win.docShell
              .QueryInterface(Ci.nsILoadContext)
              .usePrivateBrowsing = true;
         }
       }
     }
     if (cmdLine.handleFlag("setDefaultBrowser", false)) {
       ShellService.setDefaultBrowser(true, true);
     }
--- a/browser/components/preferences/in-content/findInPage.js
+++ b/browser/components/preferences/in-content/findInPage.js
@@ -180,19 +180,17 @@ var gSearchResultsPane = {
   /**
    * Get the selection instance from given window
    *
    * @param Object win
    *   The window object points to frame's window
    */
   getFindSelection(win) {
     // Yuck. See bug 138068.
-    let docShell = win.QueryInterface(Ci.nsIInterfaceRequestor)
-                      .getInterface(Ci.nsIWebNavigation)
-                      .QueryInterface(Ci.nsIDocShell);
+    let docShell = win.docShell;
 
     let controller = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                               .getInterface(Ci.nsISelectionDisplay)
                               .QueryInterface(Ci.nsISelectionController);
 
     let selection = controller.getSelection(Ci.nsISelectionController.SELECTION_FIND);
     selection.setColors("currentColor", "#ffe900", "currentColor", "#003eaa");
 
--- a/browser/components/preferences/in-content/subdialogs.js
+++ b/browser/components/preferences/in-content/subdialogs.js
@@ -517,20 +517,17 @@ SubDialog.prototype = {
 
   _untrapFocus() {
     this._frame.contentDocument.removeEventListener("keydown", this, true);
     this._closeButton.removeEventListener("keydown", this);
     window.removeEventListener("focus", this);
   },
 
   _getBrowser() {
-    return window.QueryInterface(Ci.nsIInterfaceRequestor)
-                 .getInterface(Ci.nsIWebNavigation)
-                 .QueryInterface(Ci.nsIDocShell)
-                 .chromeEventHandler;
+    return window.docShell.chromeEventHandler;
   },
 };
 
 var gSubDialog = {
   /**
    * New dialogs are stacked on top of the existing ones, and they are pushed
    * to the end of the _dialogs array.
    * @type {Array}
--- a/browser/components/preferences/in-content/sync.js
+++ b/browser/components/preferences/in-content/sync.js
@@ -351,20 +351,17 @@ var gSyncPane = {
       return;
     }
     win.switchToTabHavingURI(url, true, options);
   },
 
   // Replace the current tab with the specified URL.
   replaceTabWithUrl(url) {
     // Get the <browser> element hosting us.
-    let browser = window.QueryInterface(Ci.nsIInterfaceRequestor)
-      .getInterface(Ci.nsIWebNavigation)
-      .QueryInterface(Ci.nsIDocShell)
-      .chromeEventHandler;
+    let browser = window.docShell.chromeEventHandler;
     // And tell it to load our URL.
     browser.loadURI(url);
   },
 
   async signIn() {
     const url = await FxAccounts.config.promiseSignInURI(this._getEntryPoint());
     this.replaceTabWithUrl(url);
   },
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_context_and_chromeFlags.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_context_and_chromeFlags.js
@@ -7,20 +7,18 @@
  * both the window's nsILoadContext, as well as on the initial
  * browser's content docShell nsILoadContext.
  *
  * @param win (nsIDOMWindow)
  *        An nsIDOMWindow in the parent process.
  * @return Promise
  */
 function assertWindowIsPrivate(win) {
-  let winDocShell = win.QueryInterface(Ci.nsIInterfaceRequestor)
-                       .getInterface(Ci.nsIDocShell);
-  let chromeFlags = winDocShell.QueryInterface(Ci.nsIDocShellTreeItem)
-                               .treeOwner
+  let winDocShell = win.docShell;
+  let chromeFlags = winDocShell.treeOwner
                                .QueryInterface(Ci.nsIInterfaceRequestor)
                                .getInterface(Ci.nsIXULWindow)
                                .chromeFlags;
 
   if (!win.gBrowser.selectedBrowser.hasContentOpener) {
     Assert.ok(chromeFlags & Ci.nsIWebBrowserChrome.CHROME_PRIVATE_WINDOW,
               "Should have the private window chrome flag");
   }
--- a/browser/components/sessionstore/SessionStorage.jsm
+++ b/browser/components/sessionstore/SessionStorage.jsm
@@ -68,19 +68,17 @@ var SessionStorageInternal = {
    *        A tab's global, i.e. the root frame we want to collect for.
    * @return Returns a nested object that will have hosts as keys and per-origin
    *         session storage data as strings. For example:
    *         {"https://example.com^userContextId=1": {"key": "value", "my_number": "123"}}
    */
   collect(content) {
     let data = {};
     let visitedOrigins = new Set();
-    let docShell = content.QueryInterface(Ci.nsIInterfaceRequestor)
-                          .getInterface(Ci.nsIWebNavigation)
-                          .QueryInterface(Ci.nsIDocShell);
+    let docShell = content.docShell;
 
     forEachNonDynamicChildFrame(content, frame => {
       let principal = getPrincipalForFrame(docShell, frame);
       if (!principal) {
         return;
       }
 
       // Get the origin of the current history entry
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -1685,21 +1685,17 @@ var SessionStoreInternal = {
     // the user can't start changing any window state while we're waiting
     // for the flushes to finish.
     for (let window of this._browserWindows) {
       windowPromises.set(window, TabStateFlusher.flushWindow(window));
 
       // We have to wait for these messages to come up from
       // each window and each browser. In the meantime, hide
       // the windows to improve perceived shutdown speed.
-      let baseWin = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                          .getInterface(Ci.nsIDocShell)
-                          .QueryInterface(Ci.nsIDocShellTreeItem)
-                          .treeOwner
-                          .QueryInterface(Ci.nsIBaseWindow);
+      let baseWin = window.docShell.treeOwner.QueryInterface(Ci.nsIBaseWindow);
       baseWin.visibility = false;
     }
 
     progress.total = windowPromises.size;
     progress.current = 0;
 
     // We'll iterate through the Promise array, yielding each one, so as to
     // provide useful progress information to AsyncShutdown.
@@ -4468,17 +4464,17 @@ var SessionStoreInternal = {
       let docElem = aWindow.document.documentElement;
       let attr = parseInt(docElem.getAttribute(aAttribute), 10);
       if (attr) {
         if (aAttribute != "width" && aAttribute != "height") {
           return attr;
         }
         // Width and height attribute report the inner size, but we want
         // to store the outer size, so add the difference.
-        let xulWin = aWindow.getInterface(Ci.nsIDocShell)
+        let xulWin = aWindow.docShell
                             .treeOwner
                             .QueryInterface(Ci.nsIInterfaceRequestor)
                             .getInterface(Ci.nsIXULWindow);
         let diff = aAttribute == "width"
           ? xulWin.outerToInnerWidthDifferenceInCSSPixels
           : xulWin.outerToInnerHeightDifferenceInCSSPixels;
         return attr + diff;
       }
--- a/browser/components/sessionstore/test/browser_docshell_uuid_consistency.js
+++ b/browser/components/sessionstore/test/browser_docshell_uuid_consistency.js
@@ -1,47 +1,44 @@
 // First test - open a tab and duplicate it, using session restore to restore the history into the new tab.
 add_task(async function duplicateTab() {
   const TEST_URL = "data:text/html,foo";
   let tab = BrowserTestUtils.addTab(gBrowser, TEST_URL);
   await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
 
   await ContentTask.spawn(tab.linkedBrowser, null, function() {
-    let docshell = content.window.QueryInterface(Ci.nsIInterfaceRequestor)
-                                 .getInterface(Ci.nsIWebNavigation)
-                                 .QueryInterface(Ci.nsIDocShell);
+    let docshell = content.window.docShell
+                                 .QueryInterface(Ci.nsIWebNavigation);
     let shEntry = docshell.sessionHistory.legacySHistory.getEntryAtIndex(0, false);
     is(shEntry.docshellID.toString(), docshell.historyID.toString());
   });
 
   let tab2 = gBrowser.duplicateTab(tab);
   await BrowserTestUtils.browserLoaded(tab2.linkedBrowser);
 
   await ContentTask.spawn(tab2.linkedBrowser, null, function() {
-    let docshell = content.window.QueryInterface(Ci.nsIInterfaceRequestor)
-                                 .getInterface(Ci.nsIWebNavigation)
-                                 .QueryInterface(Ci.nsIDocShell);
+    let docshell = content.window.docShell
+                                 .QueryInterface(Ci.nsIWebNavigation);
     let shEntry = docshell.sessionHistory.legacySHistory.getEntryAtIndex(0, false);
     is(shEntry.docshellID.toString(), docshell.historyID.toString());
   });
 
   BrowserTestUtils.removeTab(tab);
   BrowserTestUtils.removeTab(tab2);
 });
 
 // Second test - open a tab and navigate across processes, which triggers sessionrestore to persist history.
 add_task(async function contentToChromeNavigate() {
   const TEST_URL = "data:text/html,foo";
   let tab = BrowserTestUtils.addTab(gBrowser, TEST_URL);
   await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
 
   await ContentTask.spawn(tab.linkedBrowser, null, function() {
-    let docshell = content.window.QueryInterface(Ci.nsIInterfaceRequestor)
-                                 .getInterface(Ci.nsIWebNavigation)
-                                 .QueryInterface(Ci.nsIDocShell);
+    let docshell = content.window.docShell
+                                 .QueryInterface(Ci.nsIWebNavigation);
     let sh = docshell.sessionHistory;
     is(sh.count, 1);
     is(sh.legacySHistory.getEntryAtIndex(0, false).docshellID.toString(), docshell.historyID.toString());
   });
 
   // Force the browser to navigate to the chrome process.
   await ContentTask.spawn(tab.linkedBrowser, null, function() {
     const CHROME_URL = "about:config";
rename from browser/components/uitour/content-UITour.js
rename to browser/components/uitour/ContentUITour.jsm
--- a/browser/components/uitour/content-UITour.js
+++ b/browser/components/uitour/ContentUITour.jsm
@@ -1,35 +1,39 @@
 /* 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/. */
 
-/* eslint-env mozilla/frame-script */
+var EXPORTED_SYMBOLS = ["UITourListener"];
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 const PREF_TEST_WHITELIST = "browser.uitour.testingOrigins";
 const UITOUR_PERMISSION   = "uitour";
 
-var UITourListener = {
+class UITourListener {
+  constructor(mm) {
+    this.mm = mm;
+  }
+
   handleEvent(event) {
     if (!Services.prefs.getBoolPref("browser.uitour.enabled")) {
       return;
     }
     if (!this.ensureTrustedOrigin()) {
       return;
     }
-    addMessageListener("UITour:SendPageCallback", this);
-    addMessageListener("UITour:SendPageNotification", this);
-    sendAsyncMessage("UITour:onPageEvent", {
+    this.mm.addMessageListener("UITour:SendPageCallback", this);
+    this.mm.addMessageListener("UITour:SendPageNotification", this);
+    this.mm.sendAsyncMessage("UITour:onPageEvent", {
       detail: event.detail,
       type: event.type,
-      pageVisibilityState: content.document.visibilityState,
+      pageVisibilityState: this.mm.content.document.visibilityState,
     });
-  },
+  }
 
   isTestingOrigin(aURI) {
     if (Services.prefs.getPrefType(PREF_TEST_WHITELIST) != Services.prefs.PREF_STRING) {
       return false;
     }
 
     // Add any testing origins (comma-seperated) to the whitelist for the session.
     for (let origin of Services.prefs.getCharPref(PREF_TEST_WHITELIST).split(",")) {
@@ -38,68 +42,68 @@ var UITourListener = {
         if (aURI.prePath == testingURI.prePath) {
           return true;
         }
       } catch (ex) {
         Cu.reportError(ex);
       }
     }
     return false;
-  },
+  }
 
   // This function is copied from UITour.jsm.
   isSafeScheme(aURI) {
     let allowedSchemes = new Set(["https", "about"]);
     if (!Services.prefs.getBoolPref("browser.uitour.requireSecure"))
       allowedSchemes.add("http");
 
     if (!allowedSchemes.has(aURI.scheme))
       return false;
 
     return true;
-  },
+  }
 
   ensureTrustedOrigin() {
+    let {content} = this.mm;
+
     if (content.top != content)
       return false;
 
     let uri = content.document.documentURIObject;
 
     if (uri.schemeIs("chrome"))
       return true;
 
     if (!this.isSafeScheme(uri))
       return false;
 
     let permission = Services.perms.testPermission(uri, UITOUR_PERMISSION);
     if (permission == Services.perms.ALLOW_ACTION)
       return true;
 
     return this.isTestingOrigin(uri);
-  },
+  }
 
   receiveMessage(aMessage) {
     switch (aMessage.name) {
       case "UITour:SendPageCallback":
         this.sendPageEvent("Response", aMessage.data);
         break;
       case "UITour:SendPageNotification":
         this.sendPageEvent("Notification", aMessage.data);
         break;
       }
-  },
+  }
 
   sendPageEvent(type, detail) {
     if (!this.ensureTrustedOrigin()) {
       return;
     }
 
-    let doc = content.document;
+    let win = this.mm.content;
     let eventName = "mozUITour" + type;
-    let event = new doc.defaultView.CustomEvent(eventName, {
+    let event = new win.CustomEvent(eventName, {
       bubbles: true,
-      detail: Cu.cloneInto(detail, doc.defaultView)
+      detail: Cu.cloneInto(detail, win),
     });
-    doc.dispatchEvent(event);
+    win.document.dispatchEvent(event);
   }
-};
-
-addEventListener("mozUITour", UITourListener, false, true);
+}
deleted file mode 100644
--- a/browser/components/uitour/jar.mn
+++ /dev/null
@@ -1,6 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-browser.jar:
-       content/browser/content-UITour.js
--- a/browser/components/uitour/moz.build
+++ b/browser/components/uitour/moz.build
@@ -1,16 +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/.
 
 EXTRA_JS_MODULES += [
+    'ContentUITour.jsm',
     'UITour.jsm',
 ]
 
-JAR_MANIFESTS += ['jar.mn']
-
 BROWSER_CHROME_MANIFESTS += [
     'test/browser.ini',
 ]
 
 with Files('**'):
     BUG_COMPONENT = ('Firefox', 'Tours')
--- a/browser/extensions/formautofill/FormAutofillContent.jsm
+++ b/browser/extensions/formautofill/FormAutofillContent.jsm
@@ -261,18 +261,17 @@ let ProfileAutocomplete = {
         }
         this._fillFromAutocompleteRow(FormAutofillContent.activeInput);
         break;
       }
     }
   },
 
   _frameMMFromWindow(contentWindow) {
-    return contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                        .getInterface(Ci.nsIDocShell)
+    return contentWindow.docShell
                         .QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsIContentFrameMessageManager);
   },
 
   _getSelectedIndex(contentWindow) {
     let mm = this._frameMMFromWindow(contentWindow);
     let selectedIndexResult = mm.sendSyncMessage("FormAutoComplete:GetSelectedIndex", {});
     if (selectedIndexResult.length != 1 || !Number.isInteger(selectedIndexResult[0])) {
@@ -626,19 +625,17 @@ var FormAutofillContent = {
     if (!field || ChromeUtils.getClassName(field) !== "HTMLInputElement") {
       return;
     }
 
     formFillController.markAsAutofillField(field);
   },
 
   _messageManagerFromWindow(win) {
-    return win.QueryInterface(Ci.nsIInterfaceRequestor)
-              .getInterface(Ci.nsIWebNavigation)
-              .QueryInterface(Ci.nsIDocShell)
+    return win.docShell
               .QueryInterface(Ci.nsIInterfaceRequestor)
               .getInterface(Ci.nsIContentFrameMessageManager);
   },
 
   _onKeyDown(e) {
     delete FormAutofillContent._keyDownEnterForInput;
     let lastAutoCompleteResult = ProfileAutocomplete.lastProfileAutoCompleteResult;
     let focusedInput = FormAutofillContent.activeInput;
--- a/browser/extensions/formautofill/content/FormAutofillFrameScript.js
+++ b/browser/extensions/formautofill/content/FormAutofillFrameScript.js
@@ -83,17 +83,17 @@ var FormAutofillFrameScript = {
   },
 
   receiveMessage(message) {
     if (!FormAutofill.isAutofillEnabled) {
       return;
     }
 
     const doc = content.document;
-    const {chromeEventHandler} = doc.ownerGlobal.getInterface(Ci.nsIDocShell);
+    const {chromeEventHandler} = doc.ownerGlobal.docShell;
 
     switch (message.name) {
       case "FormAutofill:PreviewProfile": {
         FormAutofillContent.previewProfile(doc);
         break;
       }
       case "FormAutofill:ClearForm": {
         FormAutofillContent.clearForm();
--- a/browser/extensions/mortar/host/pdf/ppapi-content-sandbox.js
+++ b/browser/extensions/mortar/host/pdf/ppapi-content-sandbox.js
@@ -256,19 +256,17 @@ mm.addMessageListener("ppapipdf.js:save"
     if ("nsIPrivateBrowsingChannel" in Ci &&
         channel instanceof Ci.nsIPrivateBrowsingChannel) {
       channel.setPrivate(docIsPrivate);
     }
 
     let listener = {
       extListener: null,
       onStartRequest(aRequest, aContext) {
-        var loadContext = containerWindow
-                            .QueryInterface(Ci.nsIInterfaceRequestor)
-                            .getInterface(Ci.nsIWebNavigation)
+        var loadContext = containerWindow.docShell
                             .QueryInterface(Ci.nsILoadContext);
         this.extListener = extHelperAppSvc.doContent(
           "application/pdf", aRequest, loadContext, false);
         this.extListener.onStartRequest(aRequest, aContext);
       },
       onStopRequest(aRequest, aContext, aStatusCode) {
         if (this.extListener) {
           this.extListener.onStopRequest(aRequest, aContext, aStatusCode);
--- a/browser/extensions/pdfjs/README.mozilla
+++ b/browser/extensions/pdfjs/README.mozilla
@@ -1,5 +1,5 @@
 This is the PDF.js project output, https://github.com/mozilla/pdf.js
 
-Current extension version is: 2.0.694
+Current extension version is: 2.0.719
 
-Taken from upstream commit: 1aaeaf33
+Taken from upstream commit: 35214245
--- a/browser/extensions/pdfjs/content/PdfStreamConverter.jsm
+++ b/browser/extensions/pdfjs/content/PdfStreamConverter.jsm
@@ -263,19 +263,17 @@ class ChromeActions {
       if ("nsIPrivateBrowsingChannel" in Ci &&
           channel instanceof Ci.nsIPrivateBrowsingChannel) {
         channel.setPrivate(docIsPrivate);
       }
 
       var listener = {
         extListener: null,
         onStartRequest(aRequest, aContext) {
-          var loadContext = self.domWindow
-                                .QueryInterface(Ci.nsIInterfaceRequestor)
-                                .getInterface(Ci.nsIWebNavigation)
+          var loadContext = self.domWindow.docShell
                                 .QueryInterface(Ci.nsILoadContext);
           this.extListener = extHelperAppSvc.doContent(
             (data.isAttachment ? "application/octet-stream" :
                                  "application/pdf"),
             aRequest, loadContext, false);
           this.extListener.onStartRequest(aRequest, aContext);
         },
         onStopRequest(aRequest, aContext, aStatusCode) {
@@ -409,18 +407,17 @@ class ChromeActions {
     } else {
       message = getLocalizedString(strings, "unsupported_feature");
     }
     PdfJsTelemetry.onFallback();
     PdfjsContentUtils.displayWarning(domWindow, message,
       getLocalizedString(strings, "open_with_different_viewer"),
       getLocalizedString(strings, "open_with_different_viewer", "accessKey"));
 
-    let winmm = domWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsIDocShell)
+    let winmm = domWindow.docShell
                          .QueryInterface(Ci.nsIInterfaceRequestor)
                          .getInterface(Ci.nsIContentFrameMessageManager);
 
     winmm.addMessageListener("PDFJS:Child:fallbackDownload",
       function fallbackDownload(msg) {
         let data = msg.data;
         sendResponse(data.download);
 
@@ -437,18 +434,17 @@ class ChromeActions {
     var result = data.result;
     var findPrevious = data.findPrevious;
     var findPreviousType = typeof findPrevious;
     if ((typeof result !== "number" || result < 0 || result > 3) ||
         (findPreviousType !== "undefined" && findPreviousType !== "boolean")) {
       return;
     }
 
-    var winmm = this.domWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                              .getInterface(Ci.nsIDocShell)
+    var winmm = this.domWindow.docShell
                               .QueryInterface(Ci.nsIInterfaceRequestor)
                               .getInterface(Ci.nsIContentFrameMessageManager);
 
     winmm.sendAsyncMessage("PDFJS:Parent:updateControlState", data);
   }
 
   setPreferences(prefs, sendResponse) {
     var defaultBranch = Services.prefs.getDefaultBranch(PREF_PREFIX + ".");
@@ -754,18 +750,17 @@ class RequestListener {
 
 /**
  * Forwards events from the eventElement to the contentWindow only if the
  * content window matches the currently selected browser window.
  */
 class FindEventManager {
   constructor(contentWindow) {
     this.contentWindow = contentWindow;
-    this.winmm = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                              .getInterface(Ci.nsIDocShell)
+    this.winmm = contentWindow.docShell
                               .QueryInterface(Ci.nsIInterfaceRequestor)
                               .getInterface(Ci.nsIContentFrameMessageManager);
   }
 
   bind() {
     var unload = (evt) => {
       this.unbind();
       this.contentWindow.removeEventListener(evt.type, unload);
--- a/browser/extensions/pdfjs/content/PdfjsContentUtils.jsm
+++ b/browser/extensions/pdfjs/content/PdfjsContentUtils.jsm
@@ -86,18 +86,17 @@ var PdfjsContentUtils = {
   },
 
   /*
    * Request the display of a notification warning in the associated window
    * when the renderer isn't sure a pdf displayed correctly.
    */
   displayWarning(aWindow, aMessage, aLabel, aAccessKey) {
     // the child's dom frame mm associated with the window.
-    let winmm = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                       .getInterface(Ci.nsIDocShell)
+    let winmm = aWindow.docShell
                        .QueryInterface(Ci.nsIInterfaceRequestor)
                        .getInterface(Ci.nsIContentFrameMessageManager);
     winmm.sendAsyncMessage("PDFJS:Parent:displayWarning", {
       message: aMessage,
       label: aLabel,
       accessKey: aAccessKey,
     });
   },
--- a/browser/extensions/pdfjs/content/build/pdf.js
+++ b/browser/extensions/pdfjs/content/build/pdf.js
@@ -118,18 +118,18 @@ return /******/ (function(modules) { // 
 /************************************************************************/
 /******/ ([
 /* 0 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
-var pdfjsVersion = '2.0.694';
-var pdfjsBuild = '1aaeaf33';
+var pdfjsVersion = '2.0.719';
+var pdfjsBuild = '35214245';
 var pdfjsSharedUtil = __w_pdfjs_require__(1);
 var pdfjsDisplayAPI = __w_pdfjs_require__(7);
 var pdfjsDisplayTextLayer = __w_pdfjs_require__(19);
 var pdfjsDisplayAnnotationLayer = __w_pdfjs_require__(20);
 var pdfjsDisplayDOMUtils = __w_pdfjs_require__(8);
 var pdfjsDisplaySVG = __w_pdfjs_require__(21);
 let pdfjsDisplayWorkerOptions = __w_pdfjs_require__(13);
 let pdfjsDisplayAPICompatibility = __w_pdfjs_require__(10);
@@ -4218,17 +4218,17 @@ function _fetchDocument(worker, source, 
     return Promise.reject(new Error('Worker was destroyed'));
   }
   if (pdfDataRangeTransport) {
     source.length = pdfDataRangeTransport.length;
     source.initialData = pdfDataRangeTransport.initialData;
   }
   return worker.messageHandler.sendWithPromise('GetDocRequest', {
     docId,
-    apiVersion: '2.0.694',
+    apiVersion: '2.0.719',
     source: {
       data: source.data,
       url: source.url,
       password: source.password,
       disableAutoFetch: source.disableAutoFetch,
       rangeChunkSize: source.rangeChunkSize,
       length: source.length
     },
@@ -5558,18 +5558,18 @@ var InternalRenderTask = function Intern
         }
       });
     }
   };
   return InternalRenderTask;
 }();
 var version, build;
 {
-  exports.version = version = '2.0.694';
-  exports.build = build = '1aaeaf33';
+  exports.version = version = '2.0.719';
+  exports.build = build = '35214245';
 }
 exports.getDocument = getDocument;
 exports.LoopbackPort = LoopbackPort;
 exports.PDFDataRangeTransport = PDFDataRangeTransport;
 exports.PDFWorker = PDFWorker;
 exports.PDFDocumentProxy = PDFDocumentProxy;
 exports.PDFPageProxy = PDFPageProxy;
 exports.setPDFNetworkStreamFactory = setPDFNetworkStreamFactory;
--- a/browser/extensions/pdfjs/content/build/pdf.worker.js
+++ b/browser/extensions/pdfjs/content/build/pdf.worker.js
@@ -118,18 +118,18 @@ return /******/ (function(modules) { // 
 /************************************************************************/
 /******/ ([
 /* 0 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
-var pdfjsVersion = '2.0.694';
-var pdfjsBuild = '1aaeaf33';
+var pdfjsVersion = '2.0.719';
+var pdfjsBuild = '35214245';
 var pdfjsCoreWorker = __w_pdfjs_require__(1);
 exports.WorkerMessageHandler = pdfjsCoreWorker.WorkerMessageHandler;
 
 /***/ }),
 /* 1 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
@@ -322,17 +322,17 @@ var WorkerMessageHandler = {
     });
   },
   createDocumentHandler(docParams, port) {
     var pdfManager;
     var terminated = false;
     var cancelXHRs = null;
     var WorkerTasks = [];
     let apiVersion = docParams.apiVersion;
-    let workerVersion = '2.0.694';
+    let workerVersion = '2.0.719';
     if (apiVersion !== workerVersion) {
       throw new Error(`The API version "${apiVersion}" does not match ` + `the Worker version "${workerVersion}".`);
     }
     var docId = docParams.docId;
     var docBaseUrl = docParams.docBaseUrl;
     var workerHandlerName = docParams.docId + '_worker';
     var handler = new _message_handler.MessageHandler(workerHandlerName, docId, port);
     handler.postMessageTransfers = docParams.postMessageTransfers;
@@ -5221,22 +5221,22 @@ exports.ChunkedStreamManager = ChunkedSt
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 exports.PDFDocument = exports.Page = undefined;
 
+var _util = __w_pdfjs_require__(2);
+
 var _obj = __w_pdfjs_require__(11);
 
 var _primitives = __w_pdfjs_require__(12);
 
-var _util = __w_pdfjs_require__(2);
-
 var _stream = __w_pdfjs_require__(14);
 
 var _annotation = __w_pdfjs_require__(26);
 
 var _crypto = __w_pdfjs_require__(24);
 
 var _parser = __w_pdfjs_require__(13);
 
@@ -5487,16 +5487,17 @@ var PDFDocument = function PDFDocumentCl
     this.pdfManager = pdfManager;
     this.stream = stream;
     this.xref = new _obj.XRef(stream, pdfManager);
     let evaluatorOptions = pdfManager.evaluatorOptions;
     this.pdfFunctionFactory = new _function.PDFFunctionFactory({
       xref: this.xref,
       isEvalSupported: evaluatorOptions.isEvalSupported
     });
+    this._pagePromises = [];
   }
   function find(stream, needle, limit, backwards) {
     var pos = stream.pos;
     var end = stream.end;
     var strBuf = [];
     if (pos + limit > end) {
       limit = end - pos;
     }
@@ -5507,30 +5508,26 @@ var PDFDocument = function PDFDocumentCl
     stream.pos = pos;
     var index = backwards ? str.lastIndexOf(needle) : str.indexOf(needle);
     if (index === -1) {
       return false;
     }
     stream.pos += index;
     return true;
   }
-  var DocumentInfoValidators = {
-    get entries() {
-      return (0, _util.shadow)(this, 'entries', {
-        Title: _util.isString,
-        Author: _util.isString,
-        Subject: _util.isString,
-        Keywords: _util.isString,
-        Creator: _util.isString,
-        Producer: _util.isString,
-        CreationDate: _util.isString,
-        ModDate: _util.isString,
-        Trapped: _primitives.isName
-      });
-    }
+  const DocumentInfoValidators = {
+    Title: _util.isString,
+    Author: _util.isString,
+    Subject: _util.isString,
+    Keywords: _util.isString,
+    Creator: _util.isString,
+    Producer: _util.isString,
+    CreationDate: _util.isString,
+    ModDate: _util.isString,
+    Trapped: _primitives.isName
   };
   PDFDocument.prototype = {
     parse: function PDFDocument_parse(recoveryMode) {
       this.setup(recoveryMode);
       var version = this.catalog.catDict.get('Version');
       if ((0, _primitives.isName)(version)) {
         this.pdfFormatVersion = version.name;
       }
@@ -5547,26 +5544,24 @@ var PDFDocument = function PDFDocumentCl
         if (ex instanceof _util.MissingDataException) {
           throw ex;
         }
         (0, _util.info)('Something wrong with AcroForm entry');
         this.acroForm = null;
       }
     },
     get linearization() {
-      var linearization = null;
-      if (this.stream.length) {
-        try {
-          linearization = _parser.Linearization.create(this.stream);
-        } catch (err) {
-          if (err instanceof _util.MissingDataException) {
-            throw err;
-          }
-          (0, _util.info)(err);
-        }
+      let linearization = null;
+      try {
+        linearization = _parser.Linearization.create(this.stream);
+      } catch (err) {
+        if (err instanceof _util.MissingDataException) {
+          throw err;
+        }
+        (0, _util.info)(err);
       }
       return (0, _util.shadow)(this, 'linearization', linearization);
     },
     get startXRef() {
       var stream = this.stream;
       var startXRef = 0;
       var linearization = this.linearization;
       if (linearization) {
@@ -5634,58 +5629,44 @@ var PDFDocument = function PDFDocumentCl
       }
     },
     parseStartXRef: function PDFDocument_parseStartXRef() {
       var startXRef = this.startXRef;
       this.xref.setStartXRef(startXRef);
     },
     setup: function PDFDocument_setup(recoveryMode) {
       this.xref.parse(recoveryMode);
-      var pageFactory = {
-        createPage: (pageIndex, dict, ref, fontCache, builtInCMapCache) => {
-          return new Page({
-            pdfManager: this.pdfManager,
-            xref: this.xref,
-            pageIndex,
-            pageDict: dict,
-            ref,
-            fontCache,
-            builtInCMapCache,
-            pdfFunctionFactory: this.pdfFunctionFactory
-          });
-        }
-      };
-      this.catalog = new _obj.Catalog(this.pdfManager, this.xref, pageFactory);
+      this.catalog = new _obj.Catalog(this.pdfManager, this.xref);
     },
     get numPages() {
       var linearization = this.linearization;
       var num = linearization ? linearization.numPages : this.catalog.numPages;
       return (0, _util.shadow)(this, 'numPages', num);
     },
     get documentInfo() {
-      var docInfo = {
+      const docInfo = {
         PDFFormatVersion: this.pdfFormatVersion,
+        IsLinearized: !!this.linearization,
         IsAcroFormPresent: !!this.acroForm,
         IsXFAPresent: !!this.xfa
       };
-      var infoDict;
+      let infoDict;
       try {
         infoDict = this.xref.trailer.get('Info');
       } catch (err) {
         if (err instanceof _util.MissingDataException) {
           throw err;
         }
         (0, _util.info)('The document information dictionary is invalid.');
       }
-      if (infoDict) {
-        var validEntries = DocumentInfoValidators.entries;
-        for (var key in validEntries) {
+      if ((0, _primitives.isDict)(infoDict)) {
+        for (let key in DocumentInfoValidators) {
           if (infoDict.has(key)) {
-            var value = infoDict.get(key);
-            if (validEntries[key](value)) {
+            const value = infoDict.get(key);
+            if (DocumentInfoValidators[key](value)) {
               docInfo[key] = typeof value !== 'string' ? value : (0, _util.stringToPDFString)(value);
             } else {
               (0, _util.info)('Bad value in document info for "' + key + '"');
             }
           }
         }
       }
       return (0, _util.shadow)(this, 'documentInfo', docInfo);
@@ -5704,18 +5685,51 @@ var PDFDocument = function PDFDocumentCl
         hash = (0, _crypto.calculateMD5)(this.stream.bytes.subarray(0, FINGERPRINT_FIRST_BYTES), 0, FINGERPRINT_FIRST_BYTES);
       }
       for (var i = 0, n = hash.length; i < n; i++) {
         var hex = hash[i].toString(16);
         fileID += hex.length === 1 ? '0' + hex : hex;
       }
       return (0, _util.shadow)(this, 'fingerprint', fileID);
     },
-    getPage: function PDFDocument_getPage(pageIndex) {
-      return this.catalog.getPage(pageIndex);
+    _getLinearizationPage(pageIndex) {
+      const { catalog, linearization } = this;
+      (0, _util.assert)(linearization && linearization.pageFirst === pageIndex);
+      const ref = new _primitives.Ref(linearization.objectNumberFirst, 0);
+      return this.xref.fetchAsync(ref).then(obj => {
+        if ((0, _primitives.isDict)(obj, 'Page') || (0, _primitives.isDict)(obj) && !obj.has('Type') && obj.has('Contents')) {
+          if (ref && !catalog.pageKidsCountCache.has(ref)) {
+            catalog.pageKidsCountCache.put(ref, 1);
+          }
+          return [obj, ref];
+        }
+        throw new _util.FormatError('The Linearization dictionary doesn\'t point ' + 'to a valid Page dictionary.');
+      }).catch(reason => {
+        (0, _util.info)(reason);
+        return catalog.getPageDict(pageIndex);
+      });
+    },
+    getPage(pageIndex) {
+      if (this._pagePromises[pageIndex] !== undefined) {
+        return this._pagePromises[pageIndex];
+      }
+      const { catalog, linearization } = this;
+      const promise = linearization && linearization.pageFirst === pageIndex ? this._getLinearizationPage(pageIndex) : catalog.getPageDict(pageIndex);
+      return this._pagePromises[pageIndex] = promise.then(([pageDict, ref]) => {
+        return new Page({
+          pdfManager: this.pdfManager,
+          xref: this.xref,
+          pageIndex,
+          pageDict,
+          ref,
+          fontCache: catalog.fontCache,
+          builtInCMapCache: catalog.builtInCMapCache,
+          pdfFunctionFactory: this.pdfFunctionFactory
+        });
+      });
     },
     cleanup: function PDFDocument_cleanup() {
       return this.catalog.cleanup();
     }
   };
   return PDFDocument;
 }();
 exports.Page = Page;
@@ -5741,28 +5755,26 @@ var _parser = __w_pdfjs_require__(13);
 
 var _chunked_stream = __w_pdfjs_require__(9);
 
 var _crypto = __w_pdfjs_require__(24);
 
 var _colorspace = __w_pdfjs_require__(25);
 
 var Catalog = function CatalogClosure() {
-  function Catalog(pdfManager, xref, pageFactory) {
+  function Catalog(pdfManager, xref) {
     this.pdfManager = pdfManager;
     this.xref = xref;
     this.catDict = xref.getCatalogObj();
     if (!(0, _primitives.isDict)(this.catDict)) {
       throw new _util.FormatError('catalog object is not a dictionary');
     }
     this.fontCache = new _primitives.RefSetCache();
-    this.builtInCMapCache = Object.create(null);
+    this.builtInCMapCache = new Map();
     this.pageKidsCountCache = new _primitives.RefSetCache();
-    this.pageFactory = pageFactory;
-    this.pagePromises = [];
   }
   Catalog.prototype = {
     get metadata() {
       var streamRef = this.catDict.getRaw('Metadata');
       if (!(0, _primitives.isRef)(streamRef)) {
         return (0, _util.shadow)(this, 'metadata', null);
       }
       var encryptMetadata = !this.xref.encrypt ? false : this.xref.encrypt.encryptMetadata;
@@ -6128,26 +6140,18 @@ var Catalog = function CatalogClosure() 
         promises.push(promise);
       });
       return Promise.all(promises).then(translatedFonts => {
         for (var i = 0, ii = translatedFonts.length; i < ii; i++) {
           var font = translatedFonts[i].dict;
           delete font.translated;
         }
         this.fontCache.clear();
-        this.builtInCMapCache = Object.create(null);
-      });
-    },
-    getPage: function Catalog_getPage(pageIndex) {
-      if (!(pageIndex in this.pagePromises)) {
-        this.pagePromises[pageIndex] = this.getPageDict(pageIndex).then(([dict, ref]) => {
-          return this.pageFactory.createPage(pageIndex, dict, ref, this.fontCache, this.builtInCMapCache);
-        });
-      }
-      return this.pagePromises[pageIndex];
+        this.builtInCMapCache.clear();
+      });
     },
     getPageDict: function Catalog_getPageDict(pageIndex) {
       var capability = (0, _util.createPromiseCapability)();
       var nodesToVisit = [this.catDict.getRaw('Pages')];
       var count,
           currentPageIndex = 0;
       var xref = this.xref,
           pageKidsCountCache = this.pageKidsCountCache;
@@ -8634,16 +8638,17 @@ var StringStream = function StringStream
     Stream.call(this, bytes);
   }
   StringStream.prototype = Stream.prototype;
   return StringStream;
 }();
 var DecodeStream = function DecodeStreamClosure() {
   var emptyBuffer = new Uint8Array(0);
   function DecodeStream(maybeMinBufferLength) {
+    this._rawMinBufferLength = maybeMinBufferLength || 0;
     this.pos = 0;
     this.bufferLength = 0;
     this.eof = false;
     this.buffer = emptyBuffer;
     this.minBufferLength = 512;
     if (maybeMinBufferLength) {
       while (this.minBufferLength < maybeMinBufferLength) {
         this.minBufferLength *= 2;
@@ -8751,17 +8756,26 @@ var DecodeStream = function DecodeStream
       return [];
     }
   };
   return DecodeStream;
 }();
 var StreamsSequenceStream = function StreamsSequenceStreamClosure() {
   function StreamsSequenceStream(streams) {
     this.streams = streams;
-    DecodeStream.call(this, null);
+    let maybeLength = 0;
+    for (let i = 0, ii = streams.length; i < ii; i++) {
+      const stream = streams[i];
+      if (stream instanceof DecodeStream) {
+        maybeLength += stream._rawMinBufferLength;
+      } else {
+        maybeLength += stream.length;
+      }
+    }
+    DecodeStream.call(this, maybeLength);
   }
   StreamsSequenceStream.prototype = Object.create(DecodeStream.prototype);
   StreamsSequenceStream.prototype.readBlock = function streamSequenceStreamReadBlock() {
     var streams = this.streams;
     if (streams.length === 0) {
       this.eof = true;
       return;
     }
@@ -19003,23 +19017,22 @@ var PartialEvaluator = function PartialE
     this.handler = handler;
     this.pageIndex = pageIndex;
     this.idFactory = idFactory;
     this.fontCache = fontCache;
     this.builtInCMapCache = builtInCMapCache;
     this.options = options || DefaultPartialEvaluatorOptions;
     this.pdfFunctionFactory = pdfFunctionFactory;
     this.fetchBuiltInCMap = name => {
-      var cachedCMap = this.builtInCMapCache[name];
-      if (cachedCMap) {
-        return Promise.resolve(cachedCMap);
+      if (this.builtInCMapCache.has(name)) {
+        return Promise.resolve(this.builtInCMapCache.get(name));
       }
       return this.handler.sendWithPromise('FetchBuiltInCMap', { name }).then(data => {
         if (data.compressionType !== _util.CMapCompressionType.NONE) {
-          this.builtInCMapCache[name] = data;
+          this.builtInCMapCache.set(name, data);
         }
         return data;
       });
     };
   }
   var TIME_SLOT_DURATION_MS = 20;
   var CHECK_TIME_EVERY = 100;
   function TimeSlotManager() {
--- a/browser/extensions/pdfjs/content/web/viewer.html
+++ b/browser/extensions/pdfjs/content/web/viewer.html
@@ -351,16 +351,20 @@ See https://github.com/adobe-type-tools/
               <span data-l10n-id="document_properties_version">PDF Version:</span> <p id="versionField">-</p>
             </div>
             <div class="row">
               <span data-l10n-id="document_properties_page_count">Page Count:</span> <p id="pageCountField">-</p>
             </div>
             <div class="row">
               <span data-l10n-id="document_properties_page_size">Page Size:</span> <p id="pageSizeField">-</p>
             </div>
+            <div class="separator"></div>
+            <div class="row">
+              <span data-l10n-id="document_properties_linearized">Fast Web View:</span> <p id="linearizedField">-</p>
+            </div>
             <div class="buttonRow">
               <button id="documentPropertiesClose" class="overlayButton"><span data-l10n-id="document_properties_close">Close</span></button>
             </div>
           </div>
         </div>
       </div>  <!-- overlayContainer -->
 
     </div> <!-- outerContainer -->
--- a/browser/extensions/pdfjs/content/web/viewer.js
+++ b/browser/extensions/pdfjs/content/web/viewer.js
@@ -226,17 +226,18 @@ function getViewerConfiguration() {
         'subject': document.getElementById('subjectField'),
         'keywords': document.getElementById('keywordsField'),
         'creationDate': document.getElementById('creationDateField'),
         'modificationDate': document.getElementById('modificationDateField'),
         'creator': document.getElementById('creatorField'),
         'producer': document.getElementById('producerField'),
         'version': document.getElementById('versionField'),
         'pageCount': document.getElementById('pageCountField'),
-        'pageSize': document.getElementById('pageSizeField')
+        'pageSize': document.getElementById('pageSizeField'),
+        'linearized': document.getElementById('linearizedField')
       }
     },
     errorWrapper: {
       container: document.getElementById('errorWrapper'),
       errorMessage: document.getElementById('errorMessage'),
       closeButton: document.getElementById('errorClose'),
       errorMoreInfo: document.getElementById('errorMoreInfo'),
       moreInfoButton: document.getElementById('errorShowMore'),
@@ -381,92 +382,48 @@ let PDFViewerApplication = {
   initialize(appConfig) {
     this.preferences = this.externalServices.createPreferences();
     this.appConfig = appConfig;
     return this._readPreferences().then(() => {
       return this._parseHashParameters();
     }).then(() => {
       return this._initializeL10n();
     }).then(() => {
+      if (this.isViewerEmbedded && _app_options.AppOptions.get('externalLinkTarget') === _pdfjsLib.LinkTarget.NONE) {
+        _app_options.AppOptions.set('externalLinkTarget', _pdfjsLib.LinkTarget.TOP);
+      }
       return this._initializeViewerComponents();
     }).then(() => {
       this.bindEvents();
       this.bindWindowEvents();
       let appContainer = appConfig.appContainer || document.documentElement;
       this.l10n.translate(appContainer).then(() => {
         this.eventBus.dispatch('localized');
       });
-      if (this.isViewerEmbedded && _app_options.AppOptions.get('externalLinkTarget') === _pdfjsLib.LinkTarget.NONE) {
-        _app_options.AppOptions.set('externalLinkTarget', _pdfjsLib.LinkTarget.TOP);
-      }
       this.initialized = true;
     });
   },
   _readPreferences() {
-    let { preferences } = this;
-    return Promise.all([preferences.get('enableWebGL').then(function resolved(value) {
-      _app_options.AppOptions.set('enableWebGL', value);
-    }), preferences.get('sidebarViewOnLoad').then(function resolved(value) {
-      _app_options.AppOptions.set('sidebarViewOnLoad', value);
-    }), preferences.get('cursorToolOnLoad').then(function resolved(value) {
-      _app_options.AppOptions.set('cursorToolOnLoad', value);
-    }), preferences.get('pdfBugEnabled').then(function resolved(value) {
-      _app_options.AppOptions.set('pdfBugEnabled', value);
-    }), preferences.get('showPreviousViewOnLoad').then(function resolved(value) {
-      _app_options.AppOptions.set('showPreviousViewOnLoad', value);
-    }), preferences.get('defaultZoomValue').then(function resolved(value) {
-      _app_options.AppOptions.set('defaultZoomValue', value);
-    }), preferences.get('textLayerMode').then(function resolved(value) {
-      if (_app_options.AppOptions.get('textLayerMode') === _ui_utils.TextLayerMode.DISABLE) {
-        return;
-      }
-      _app_options.AppOptions.set('textLayerMode', value);
-    }), preferences.get('disableRange').then(function resolved(value) {
-      if (_app_options.AppOptions.get('disableRange') === true) {
-        return;
-      }
-      _app_options.AppOptions.set('disableRange', value);
-    }), preferences.get('disableStream').then(function resolved(value) {
-      if (_app_options.AppOptions.get('disableStream') === true) {
-        return;
-      }
-      _app_options.AppOptions.set('disableStream', value);
-    }), preferences.get('disableAutoFetch').then(function resolved(value) {
-      _app_options.AppOptions.set('disableAutoFetch', value);
-    }), preferences.get('disableFontFace').then(function resolved(value) {
-      if (_app_options.AppOptions.get('disableFontFace') === true) {
-        return;
-      }
-      _app_options.AppOptions.set('disableFontFace', value);
-    }), preferences.get('useOnlyCssZoom').then(function resolved(value) {
-      _app_options.AppOptions.set('useOnlyCssZoom', value);
-    }), preferences.get('externalLinkTarget').then(function resolved(value) {
-      if (_app_options.AppOptions.get('externalLinkTarget') !== _pdfjsLib.LinkTarget.NONE) {
-        return;
-      }
-      _app_options.AppOptions.set('externalLinkTarget', value);
-    }), preferences.get('renderer').then(function resolved(value) {
-      _app_options.AppOptions.set('renderer', value);
-    }), preferences.get('renderInteractiveForms').then(function resolved(value) {
-      _app_options.AppOptions.set('renderInteractiveForms', value);
-    }), preferences.get('disablePageMode').then(function resolved(value) {
-      _app_options.AppOptions.set('disablePageMode', value);
-    }), preferences.get('disablePageLabels').then(function resolved(value) {
-      _app_options.AppOptions.set('disablePageLabels', value);
-    }), preferences.get('enablePrintAutoRotate').then(function resolved(value) {
-      _app_options.AppOptions.set('enablePrintAutoRotate', value);
-    }), preferences.get('scrollModeOnLoad').then(function resolved(value) {
-      _app_options.AppOptions.set('scrollModeOnLoad', value);
-    }), preferences.get('spreadModeOnLoad').then(function resolved(value) {
-      _app_options.AppOptions.set('spreadModeOnLoad', value);
-    })]).catch(function (reason) {});
+    const OVERRIDES = {
+      disableFontFace: true,
+      disableRange: true,
+      disableStream: true,
+      textLayerMode: _ui_utils.TextLayerMode.DISABLE
+    };
+    return this.preferences.getAll().then(function (prefs) {
+      for (let name in prefs) {
+        if (name in OVERRIDES && _app_options.AppOptions.get(name) === OVERRIDES[name]) {
+          continue;
+        }
+        _app_options.AppOptions.set(name, prefs[name]);
+      }
+    }, function (reason) {});
   },
   _parseHashParameters() {
-    let { appConfig } = this;
-    let waitOn = [];
+    const waitOn = [];
     if (_app_options.AppOptions.get('pdfBugEnabled')) {
       let hash = document.location.hash.substring(1);
       let hashParams = (0, _ui_utils.parseQueryString)(hash);
       if ('disableworker' in hashParams && hashParams['disableworker'] === 'true') {
         waitOn.push(loadFakeWorker());
       }
       if ('disablerange' in hashParams) {
         _app_options.AppOptions.set('disableRange', hashParams['disablerange'] === 'true');
@@ -495,17 +452,17 @@ let PDFViewerApplication = {
       if ('textlayer' in hashParams) {
         switch (hashParams['textlayer']) {
           case 'off':
             _app_options.AppOptions.set('textLayerMode', _ui_utils.TextLayerMode.DISABLE);
             break;
           case 'visible':
           case 'shadow':
           case 'hover':
-            let viewer = appConfig.viewerContainer;
+            let viewer = this.appConfig.viewerContainer;
             viewer.classList.add('textLayer-' + hashParams['textlayer']);
             break;
         }
       }
       if ('pdfbug' in hashParams) {
         _app_options.AppOptions.set('pdfBug', true);
         let enabled = hashParams['pdfbug'].split(',');
         waitOn.push(loadAndEnablePDFBug(enabled));
@@ -776,17 +733,16 @@ let PDFViewerApplication = {
     if (typeof PDFBug !== 'undefined') {
       PDFBug.cleanup();
     }
     return promise;
   },
   open(file, args) {
     if (this.pdfLoadingTask) {
       return this.close().then(() => {
-        this.preferences.reload();
         return this.open(file, args);
       });
     }
     const workerParameters = _app_options.AppOptions.getAll('worker');
     for (let key in workerParameters) {
       _pdfjsLib.GlobalWorkerOptions[key] = workerParameters[key];
     }
     let parameters = Object.create(null);
@@ -3816,32 +3772,33 @@ class PDFDocumentProperties {
       const pagesRotation = this._pagesRotation;
       if (this.fieldData && currentPageNumber === this.fieldData['_currentPageNumber'] && pagesRotation === this.fieldData['_pagesRotation']) {
         this._updateUI();
         return;
       }
       this.pdfDocument.getMetadata().then(({ info, metadata, contentDispositionFilename }) => {
         return Promise.all([info, metadata, contentDispositionFilename || (0, _ui_utils.getPDFFileNameFromURL)(this.url), this._parseFileSize(this.maybeFileSize), this._parseDate(info.CreationDate), this._parseDate(info.ModDate), this.pdfDocument.getPage(currentPageNumber).then(pdfPage => {
           return this._parsePageSize((0, _ui_utils.getPageSizeInches)(pdfPage), pagesRotation);
-        })]);
-      }).then(([info, metadata, fileName, fileSize, creationDate, modDate, pageSize]) => {
+        }), this._parseLinearization(info.IsLinearized)]);
+      }).then(([info, metadata, fileName, fileSize, creationDate, modDate, pageSize, isLinearized]) => {
         freezeFieldData({
           'fileName': fileName,
           'fileSize': fileSize,
           'title': info.Title,
           'author': info.Author,
           'subject': info.Subject,
           'keywords': info.Keywords,
           'creationDate': creationDate,
           'modificationDate': modDate,
           'creator': info.Creator,
           'producer': info.Producer,
           'version': info.PDFFormatVersion,
           'pageCount': this.pdfDocument.numPages,
           'pageSize': pageSize,
+          'linearized': isLinearized,
           '_currentPageNumber': currentPageNumber,
           '_pagesRotation': pagesRotation
         });
         this._updateUI();
         return this.pdfDocument.getDownloadInfo();
       }).then(({ length }) => {
         this.maybeFileSize = length;
         return this._parseFileSize(length);
@@ -3996,16 +3953,19 @@ class PDFDocumentProperties {
     let date = new Date(Date.UTC(year, month, day, hours, minutes, seconds));
     let dateString = date.toLocaleDateString();
     let timeString = date.toLocaleTimeString();
     return this.l10n.get('document_properties_date_string', {
       date: dateString,
       time: timeString
     }, '{{date}}, {{time}}');
   }
+  _parseLinearization(isLinearized) {
+    return this.l10n.get('document_properties_linearized_' + (isLinearized ? 'yes' : 'no'), null, isLinearized ? 'Yes' : 'No');
+  }
 }
 exports.PDFDocumentProperties = PDFDocumentProperties;
 
 /***/ }),
 /* 15 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
@@ -9108,43 +9068,42 @@ class BasePreferences {
       Object.defineProperty(this, 'defaults', {
         value: Object.freeze(defaults),
         writable: false,
         enumerable: true,
         configurable: false
       });
       this.prefs = Object.assign(Object.create(null), defaults);
       return this._readFromStorage(defaults);
-    }).then(prefObj => {
-      if (prefObj) {
-        this.prefs = prefObj;
+    }).then(prefs => {
+      if (!prefs) {
+        return;
+      }
+      for (let name in prefs) {
+        const defaultValue = this.defaults[name],
+              prefValue = prefs[name];
+        if (defaultValue === undefined || typeof prefValue !== typeof defaultValue) {
+          continue;
+        }
+        this.prefs[name] = prefValue;
       }
     });
   }
   _writeToStorage(prefObj) {
     return Promise.reject(new Error('Not implemented: _writeToStorage'));
   }
   _readFromStorage(prefObj) {
     return Promise.reject(new Error('Not implemented: _readFromStorage'));
   }
   reset() {
     return this._initializedPromise.then(() => {
       this.prefs = Object.assign(Object.create(null), this.defaults);
       return this._writeToStorage(this.defaults);
     });
   }
-  reload() {
-    return this._initializedPromise.then(() => {
-      return this._readFromStorage(this.defaults);
-    }).then(prefObj => {
-      if (prefObj) {
-        this.prefs = prefObj;
-      }
-    });
-  }
   set(name, value) {
     return this._initializedPromise.then(() => {
       if (this.defaults[name] === undefined) {
         throw new Error(`Set preference: "${name}" is undefined.`);
       } else if (value === undefined) {
         throw new Error('Set preference: no value is specified.');
       }
       let valueType = typeof value;
@@ -9173,16 +9132,21 @@ class BasePreferences {
         let prefValue = this.prefs[name];
         if (prefValue !== undefined) {
           return prefValue;
         }
       }
       return defaultValue;
     });
   }
+  getAll() {
+    return this._initializedPromise.then(() => {
+      return Object.assign(Object.create(null), this.defaults, this.prefs);
+    });
+  }
 }
 exports.BasePreferences = BasePreferences;
 
 /***/ }),
 /* 36 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
--- a/browser/extensions/pdfjs/moz.yaml
+++ b/browser/extensions/pdfjs/moz.yaml
@@ -15,15 +15,15 @@ origin:
   description: Portable Document Format (PDF) viewer that is built with HTML5
 
   # Full URL for the package's homepage/etc
   # Usually different from repository url
   url: https://github.com/mozilla/pdf.js
 
   # Human-readable identifier for this version/release
   # Generally "version NNN", "tag SSS", "bookmark SSS"
-  release: version 2.0.688
+  release: version 2.0.719
 
   # The package's license, where possible using the mnemonic from
   # https://spdx.org/licenses/
   # Multiple licenses can be specified (as a YAML list)
   # A "LICENSE" file must exist containing the full license text
   license: Apache-2.0
--- a/browser/locales/en-US/pdfviewer/viewer.properties
+++ b/browser/locales/en-US/pdfviewer/viewer.properties
@@ -115,16 +115,21 @@ document_properties_page_size_name_legal
 # LOCALIZATION NOTE (document_properties_page_size_dimension_string):
 # "{{width}}", "{{height}}", {{unit}}, and {{orientation}} will be replaced by
 # the size, respectively their unit of measurement and orientation, of the (current) page.
 document_properties_page_size_dimension_string={{width}} × {{height}} {{unit}} ({{orientation}})
 # LOCALIZATION NOTE (document_properties_page_size_dimension_name_string):
 # "{{width}}", "{{height}}", {{unit}}, {{name}}, and {{orientation}} will be replaced by
 # the size, respectively their unit of measurement, name, and orientation, of the (current) page.
 document_properties_page_size_dimension_name_string={{width}} × {{height}} {{unit}} ({{name}}, {{orientation}})
+# LOCALIZATION NOTE (document_properties_linearized): The linearization status of
+# the document; usually called "Fast Web View" in English locales of Adobe software.
+document_properties_linearized=Fast Web View:
+document_properties_linearized_yes=Yes
+document_properties_linearized_no=No
 document_properties_close=Close
 
 print_progress_message=Preparing document for printing…
 # LOCALIZATION NOTE (print_progress_percent): "{{progress}}" will be replaced by
 # a numerical per cent value.
 print_progress_percent={{progress}}%
 print_progress_close=Cancel
 
--- a/browser/modules/BlockedSiteContent.jsm
+++ b/browser/modules/BlockedSiteContent.jsm
@@ -127,19 +127,17 @@ var BlockedSiteContent = {
     if (/e=malwareBlocked/.test(ownerDoc.documentURI)) {
       reason = "malware";
     } else if (/e=unwantedBlocked/.test(ownerDoc.documentURI)) {
       reason = "unwanted";
     } else if (/e=harmfulBlocked/.test(ownerDoc.documentURI)) {
       reason = "harmful";
     }
 
-    let docShell = ownerDoc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
-                                       .getInterface(Ci.nsIWebNavigation)
-                                      .QueryInterface(Ci.nsIDocShell);
+    let docShell = ownerDoc.defaultView.docShell;
 
     global.sendAsyncMessage("Browser:SiteBlockedError", {
       location: ownerDoc.location.href,
       reason,
       elementId: targetElement.getAttribute("id"),
       isTopFrame: (ownerDoc.defaultView.parent === ownerDoc.defaultView),
       blockedInfo: getSiteBlockedErrorDetails(docShell),
     });
--- a/browser/modules/ContentObservers.js
+++ b/browser/modules/ContentObservers.js
@@ -32,18 +32,17 @@ var gDecoderDoctorObserver = function(su
   let win = subject.top;
   let mm = getMessageManagerForWindow(win);
   if (mm) {
     mm.sendAsyncMessage("DecoderDoctor:Notification", data);
   }
 };
 
 function getMessageManagerForWindow(aContentWindow) {
-  let ir = aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsIDocShell)
+  let ir = aContentWindow.docShell
                          .sameTypeRootTreeItem
                          .QueryInterface(Ci.nsIInterfaceRequestor);
   try {
     // If e10s is disabled, this throws NS_NOINTERFACE for closed tabs.
     return ir.getInterface(Ci.nsIContentFrameMessageManager);
   } catch (e) {
     if (e.result == Cr.NS_NOINTERFACE) {
       return null;
--- a/browser/modules/ContentWebRTC.jsm
+++ b/browser/modules/ContentWebRTC.jsm
@@ -414,27 +414,20 @@ function getTabStateForContentWindow(aCo
   return tabState;
 }
 
 function getInnerWindowIDForWindow(aContentWindow) {
   return aContentWindow.windowUtils.currentInnerWindowID;
 }
 
 function getMessageManagerForWindow(aContentWindow) {
-  aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor);
-
-  let docShell;
-  try {
-    // This throws NS_NOINTERFACE for closed tabs.
-    docShell = aContentWindow.getInterface(Ci.nsIDocShell);
-  } catch (e) {
-    if (e.result == Cr.NS_NOINTERFACE) {
-      return null;
-    }
-    throw e;
+  let docShell = aContentWindow.docShell;
+  if (!docShell) {
+    // Closed tab.
+    return null;
   }
 
   let ir = docShell.sameTypeRootTreeItem
                    .QueryInterface(Ci.nsIInterfaceRequestor);
   try {
     // This throws NS_NOINTERFACE for closed tabs (only with e10s enabled).
     return ir.getInterface(Ci.nsIContentFrameMessageManager);
   } catch (e) {
--- a/browser/modules/FormSubmitObserver.jsm
+++ b/browser/modules/FormSubmitObserver.jsm
@@ -31,20 +31,18 @@ FormSubmitObserver.prototype =
   /*
    * Public apis
    */
 
   init(aWindow, aTabChildGlobal) {
     this._content = aWindow;
     this._tab = aTabChildGlobal;
     this._mm =
-      this._content.QueryInterface(Ci.nsIInterfaceRequestor)
-                   .getInterface(Ci.nsIDocShell)
+      this._content.docShell
                    .sameTypeRootTreeItem
-                   .QueryInterface(Ci.nsIDocShell)
                    .QueryInterface(Ci.nsIInterfaceRequestor)
                    .getInterface(Ci.nsIContentFrameMessageManager);
 
     this._tab.addEventListener("pageshow", this);
     this._tab.addEventListener("unload", this);
   },
 
   uninit() {
--- a/browser/modules/LightWeightThemeWebInstallListener.jsm
+++ b/browser/modules/LightWeightThemeWebInstallListener.jsm
@@ -46,14 +46,13 @@ var LightWeightThemeWebInstallListener =
 
   _resetPreviewWindow() {
     this._previewWindow.removeEventListener("pagehide", this, true);
     this._previewWindow = null;
   }
 };
 
 function getMessageManagerForContent(content) {
-  return content.QueryInterface(Ci.nsIInterfaceRequestor)
-                .getInterface(Ci.nsIDocShell)
+  return content.docShell
                 .sameTypeRootTreeItem
                 .QueryInterface(Ci.nsIInterfaceRequestor)
                 .getInterface(Ci.nsIContentFrameMessageManager);
 }
--- a/browser/modules/LightweightThemeChildHelper.jsm
+++ b/browser/modules/LightweightThemeChildHelper.jsm
@@ -36,18 +36,17 @@ var LightweightThemeChildHelper = {
   /**
    * Update the theme data for the whole process
    * @param {Array} changedKeys The sharedData keys that were changed.
    */
   _updateProcess(changedKeys) {
     const windowEnumerator = Services.ww.getWindowEnumerator();
     while (windowEnumerator.hasMoreElements()) {
       const window = windowEnumerator.getNext().QueryInterface(Ci.nsIDOMWindow);
-      const tabChildGlobal = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                                   .getInterface(Ci.nsIDocShell)
+      const tabChildGlobal = window.docShell
                                    .sameTypeRootTreeItem
                                    .QueryInterface(Ci.nsIInterfaceRequestor)
                                    .getInterface(Ci.nsIContentFrameMessageManager);
       const {chromeOuterWindowID, content} = tabChildGlobal;
       if (changedKeys.includes(`theme/${chromeOuterWindowID}`) &&
           content && this.whitelist.has(content.document.documentURI)) {
         this.update(chromeOuterWindowID, content);
       }
--- a/browser/modules/PermissionUI.jsm
+++ b/browser/modules/PermissionUI.jsm
@@ -403,22 +403,17 @@ var PermissionPromptForRequestPrototype 
 
   get browser() {
     // In the e10s-case, the <xul:browser> will be at request.element.
     // In the single-process case, we have to use some XPCOM incantations
     // to resolve to the <xul:browser>.
     if (this.request.element) {
       return this.request.element;
     }
-    return this.request
-               .window
-               .QueryInterface(Ci.nsIInterfaceRequestor)
-               .getInterface(Ci.nsIWebNavigation)
-               .QueryInterface(Ci.nsIDocShell)
-               .chromeEventHandler;
+    return this.request.window.docShell.chromeEventHandler;
   },
 
   get principal() {
     return this.request.principal;
   },
 
   cancel() {
     this.request.cancel();
--- a/browser/modules/WindowsPreviewPerTab.jsm
+++ b/browser/modules/WindowsPreviewPerTab.jsm
@@ -424,20 +424,17 @@ TabWindow.prototype = {
     this.previews.set(tab, controller.preview);
     AeroPeek.addPreview(controller.preview);
     // updateTitleAndTooltip relies on having controller.preview which is lazily resolved.
     // Now that we've updated this.previews, it will resolve successfully.
     controller.updateTitleAndTooltip();
   },
 
   createTabPreview(controller) {
-    let docShell = this.win
-                  .QueryInterface(Ci.nsIInterfaceRequestor)
-                  .getInterface(Ci.nsIWebNavigation)
-                  .QueryInterface(Ci.nsIDocShell);
+    let docShell = this.win.docShell;
     let preview = AeroPeek.taskbar.createTaskbarTabPreview(docShell, controller);
     preview.visible = AeroPeek.enabled;
     preview.active = this.tabbrowser.selectedTab == controller.tab;
     this.onLinkIconAvailable(controller.tab.linkedBrowser,
                              controller.tab.getAttribute("image"));
     return preview;
   },
 
--- a/browser/tools/mozscreenshots/browser_boundingbox.js
+++ b/browser/tools/mozscreenshots/browser_boundingbox.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 "use strict";
 
 add_task(async function() {
-  const scale = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                      .getInterface(Ci.nsIDocShell).QueryInterface(Ci.nsIBaseWindow)
+  const scale = window.docShell
+                      .QueryInterface(Ci.nsIBaseWindow)
                       .devicePixelsPerDesktopPixel;
   let {bounds, rects} = TestRunner._findBoundingBox(["#tabbrowser-tabs"]);
   let element = document.querySelector("#tabbrowser-tabs");
   let tabBar = element.ownerDocument.getBoxObjectFor(element);
 
   // Calculate expected values
   let expectedLeft = scale * (tabBar.screenX - TestRunner.croppingPadding);
   let expectedTop = scale * (tabBar.screenY - TestRunner.croppingPadding);
deleted file mode 100644
--- a/build/build-clang/bug38105.patch
+++ /dev/null
@@ -1,49 +0,0 @@
-ThinLTO build fix from https://bugs.llvm.org/show_bug.cgi?id=38105#c4 and https://reviews.llvm.org/D49138
-
---- a/llvm/include/llvm/LTO/LTO.h
-+++ b/llvm/include/llvm/LTO/LTO.h
-@@ -365,7 +365,7 @@
-
-   void addModuleToGlobalRes(ArrayRef<InputFile::Symbol> Syms,
-                             ArrayRef<SymbolResolution> Res, unsigned Partition,
--                            bool InSummary);
-+                            bool InSummary, const Triple &TT);
-
-   // These functions take a range of symbol resolutions [ResI, ResE) and consume
-   // the resolutions used by a single input module by incrementing ResI. After
---- a/llvm/lib/LTO/LTO.cpp
-+++ b/llvm/lib/LTO/LTO.cpp
-@@ -420,7 +420,8 @@
- // their partitions.
- void LTO::addModuleToGlobalRes(ArrayRef<InputFile::Symbol> Syms,
-                                ArrayRef<SymbolResolution> Res,
--                               unsigned Partition, bool InSummary) {
-+                               unsigned Partition, bool InSummary,
-+                               const Triple &TT) {
-   auto *ResI = Res.begin();
-   auto *ResE = Res.end();
-   (void)ResE;
-@@ -428,7 +429,13 @@
-     assert(ResI != ResE);
-     SymbolResolution Res = *ResI++;
-
--    auto &GlobalRes = GlobalResolutions[Sym.getName()];
-+    StringRef Name = Sym.getName();
-+    // Strip the __imp_ prefix from COFF dllimport symbols (similar to the
-+    // way they are handled by lld), otherwise we can end up with two
-+    // global resolutions (one with and one for a copy of the symbol without).
-+    if (TT.isOSBinFormatCOFF() && Name.startswith("__imp_"))
-+      Name = Name.substr(strlen("__imp_"));
-+    auto &GlobalRes = GlobalResolutions[Name];
-     GlobalRes.UnnamedAddr &= Sym.isUnnamedAddr();
-     if (Res.Prevailing) {
-       assert(!GlobalRes.Prevailing &&
-@@ -518,7 +525,7 @@
-   auto ModSyms = Input.module_symbols(ModI);
-   addModuleToGlobalRes(ModSyms, {ResI, ResE},
-                        LTOInfo->IsThinLTO ? ThinLTO.ModuleMap.size() + 1 : 0,
--                       LTOInfo->HasSummary);
-+                       LTOInfo->HasSummary, Triple(Input.getTargetTriple()));
-
-   if (LTOInfo->IsThinLTO)
-     return addThinLTO(BM, ModSyms, ResI, ResE);
deleted file mode 100644
--- a/build/build-clang/bug38139.patch
+++ /dev/null
@@ -1,15 +0,0 @@
-ThinLTO build fix from https://bugs.llvm.org/show_bug.cgi?id=38139#c5
-
-diff --git a/llvm/lib/Transforms/IPO/PassManagerBuilder.cpp b/llvm/lib/Transforms/IPO/PassManagerBuilder.cpp
---- a/llvm/lib/Transforms/IPO/PassManagerBuilder.cpp
-+++ b/llvm/lib/Transforms/IPO/PassManagerBuilder.cpp
-@@ -904,6 +904,9 @@
-     legacy::PassManagerBase &PM) {
-   PerformThinLTO = true;
-
-+  if (LibraryInfo)
-+    PM.add(new TargetLibraryInfoWrapperPass(*LibraryInfo));
-+
-   if (VerifyInput)
-     PM.add(createVerifierPass());
-
--- a/build/build-clang/clang-win64.json
+++ b/build/build-clang/clang-win64.json
@@ -1,21 +1,19 @@
 {
-    "llvm_revision": "336407",
+    "llvm_revision": "338614",
     "stages": "3",
     "build_libcxx": false,
     "build_type": "Release",
     "assertions": false,
     "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/trunk",
     "clang_repo": "https://llvm.org/svn/llvm-project/cfe/trunk",
     "lld_repo": "https://llvm.org/svn/llvm-project/lld/trunk",
     "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/trunk",
     "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/trunk",
     "python_path": "c:/mozilla-build/python/python.exe",
     "cc": "cl.exe",
     "cxx": "cl.exe",
     "ml": "ml64.exe",
     "patches": [
-      "bug38139.patch",
-      "bug38105.patch",
       "loosen-msvc-detection.patch"
     ]
 }
--- a/build/moz.configure/init.configure
+++ b/build/moz.configure/init.configure
@@ -867,16 +867,17 @@ add_old_configure_assignment('OS_ARCH',
 set_config('OS_TEST', target_variables.OS_TEST)
 add_old_configure_assignment('OS_TEST',
                              target_variables.OS_TEST)
 set_config('CPU_ARCH', target.cpu)
 add_old_configure_assignment('CPU_ARCH', target.cpu)
 set_config('INTEL_ARCHITECTURE', target_variables.INTEL_ARCHITECTURE)
 set_config('TARGET_CPU', target.raw_cpu)
 set_config('TARGET_OS', target.raw_os)
+set_config('TARGET_ENDIANNESS', target.endianness)
 
 
 @depends(host)
 def host_variables(host):
     if host.kernel == 'kFreeBSD':
         os_arch = 'GNU_kFreeBSD'
     else:
         os_arch = host.kernel
--- a/build/virtualenv_packages.txt
+++ b/build/virtualenv_packages.txt
@@ -60,16 +60,17 @@ mozilla.pth:tools
 mozilla.pth:testing/web-platform
 mozilla.pth:testing/web-platform/tests/tools/third_party/html5lib
 mozilla.pth:testing/web-platform/tests/tools/third_party/webencodings
 mozilla.pth:testing/web-platform/tests/tools/wptrunner
 mozilla.pth:testing/web-platform/tests/tools/wptserve
 mozilla.pth:testing/web-platform/tests/tools/six
 mozilla.pth:testing/xpcshell
 mozilla.pth:third_party/python/mock-1.0.0
+mozilla.pth:xpcom/ds/tools
 mozilla.pth:xpcom/typelib/xpt/tools
 mozilla.pth:tools/docs
 mozilla.pth:third_party/python/cbor2
 mozilla.pth:third_party/python/pyasn1
 mozilla.pth:third_party/python/pyasn1-modules
 mozilla.pth:third_party/python/rsa
 mozilla.pth:third_party/python/PyECC
 optional:packages.txt:comm/build/virtualenv_packages.txt
--- a/caps/tests/mochitest/test_disableScript.xul
+++ b/caps/tests/mochitest/test_disableScript.xul
@@ -46,21 +46,17 @@ https://bugzilla.mozilla.org/show_bug.cg
     });
   }
 
   function navigateBack(ifr) {
     return new Promise(resolve => {
 
       // pageshow events don't fire on the iframe element, so we need to use the
       // chrome event handler for the docshell.
-      var browser = ifr.contentWindow
-                       .QueryInterface(Ci.nsIInterfaceRequestor)
-                       .getInterface(Ci.nsIWebNavigation)
-                       .QueryInterface(Ci.nsIDocShell)
-                       .chromeEventHandler;
+      var browser = ifr.contentWindow.docShell.chromeEventHandler;
       function onpageshow(evt) {
         info("Navigated back. Persisted: " + evt.persisted);
         browser.removeEventListener('pageshow', onpageshow);
         resolve();
       }
       browser.addEventListener('pageshow', onpageshow, false);
       ifr.contentWindow.history.back();
     });
@@ -84,19 +80,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 
   function checkScriptEnabled(win, expectEnabled) {
     win.wrappedJSObject.gFiredOnclick = false;
     win.document.body.dispatchEvent(new win.Event('click'));
     is(win.wrappedJSObject.gFiredOnclick, expectEnabled, "Checking script-enabled for " + win.name + " (" + win.location + ")");
   }
 
   function setScriptEnabledForDocShell(win, enabled) {
-    win.QueryInterface(Ci.nsIInterfaceRequestor)
-       .getInterface(Ci.nsIDocShell)
-       .allowJavascript = enabled;
+    win.docShell.allowJavascript = enabled;
   }
 
   function testList(expectEnabled, win, list, idx) {
     idx = idx || 0;
     return new Promise(resolve => {
       let target = list[idx] + path;
       info("Testing scriptability for: " + target + ". expecting " + expectEnabled);
       navigateFrame(win.frameElement, target).then(function() {
--- a/config/makefiles/xpidl/Makefile.in
+++ b/config/makefiles/xpidl/Makefile.in
@@ -27,17 +27,17 @@ include $(topsrcdir)/config/rules.mk
 idl_deps_dir := .deps
 
 dist_idl_dir := $(DIST)/idl
 dist_include_dir := $(DIST)/include
 dist_xpcrs_dir := $(DIST)/xpcrs
 process_py := $(topsrcdir)/python/mozbuild/mozbuild/action/xpidl-process.py
 generated_file := $(topobjdir)/xpcom/reflect/xptinfo/xptdata.cpp
 code_gen_py := $(topsrcdir)/xpcom/reflect/xptinfo/xptcodegen.py
-code_gen_deps := $(topsrcdir)/xpcom/reflect/xptinfo/perfecthash.py
+code_gen_deps := $(topsrcdir)/xpcom/ds/tools/perfecthash.py
 
 # TODO we should use py_action, but that would require extra directories to be
 # in the virtualenv.
 %.xpt:
 	$(REPORT_BUILD)
 	$(PYTHON_PATH) $(PLY_INCLUDE) -I$(topsrcdir)/xpcom/idl-parser -I$(DEPTH)/xpcom/idl-parser/xpidl \
 		$(process_py) --cache-dir $(DEPTH)/xpcom/idl-parser/xpidl --depsdir $(idl_deps_dir) \
 		--bindings-conf $(topsrcdir)/dom/bindings/Bindings.conf \
--- a/devtools/client/responsive.html/test/browser/browser_device_width.js
+++ b/devtools/client/responsive.html/test/browser/browser_device_width.js
@@ -18,19 +18,17 @@ addRDMTask(TEST_URL, async function({ ui
   info("Checking for screen props");
   await checkScreenProps(ui);
 
   info("Changing the RDM size using input keys");
   await setViewportSizeWithInputKeys(ui);
 
   info("Setting docShell.deviceSizeIsPageSize to false");
   await ContentTask.spawn(ui.getViewportBrowser(), {}, async function() {
-    const docShell = content.QueryInterface(Ci.nsIInterfaceRequestor)
-                          .getInterface(Ci.nsIWebNavigation)
-                          .QueryInterface(Ci.nsIDocShell);
+    const docShell = content.docShell;
     docShell.deviceSizeIsPageSize = false;
   });
 
   info("Checking for screen props once again.");
   await checkScreenProps2(ui);
 });
 
 async function setViewportSizeWithInputKeys(ui) {
--- a/devtools/client/shared/DOMHelpers.jsm
+++ b/devtools/client/shared/DOMHelpers.jsm
@@ -138,19 +138,17 @@ DOMHelpers.prototype = {
    * interactive (DOMContentLoaded).
    *
    * It is based on the chromeEventHandler. This is useful when
    * chrome iframes are loaded in content docshells (in Firefox
    * tabs for example).
    */
   onceDOMReady: function Helpers_onLocationChange(callback, targetURL) {
     const window = this.window;
-    const docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsIWebNavigation)
-                         .QueryInterface(Ci.nsIDocShell);
+    const docShell = window.docShell;
     const onReady = function(event) {
       if (event.target == window.document) {
         docShell.chromeEventHandler.removeEventListener("DOMContentLoaded", onReady);
         // If in `callback` the URL of the window is changed and a listener to DOMContentLoaded
         // is attached, the event we just received will be also be caught by the new listener.
         // We want to avoid that so we execute the callback in the next queue.
         Services.tm.dispatchToMainThread(callback);
       }
--- a/devtools/client/shared/test/test-actor.js
+++ b/devtools/client/shared/test/test-actor.js
@@ -449,19 +449,17 @@ var TestActor = exports.TestActor = prot
       if (actorID) {
         const actor = this.conn.getActor(actorID);
         const {_highlighter: h} = actor;
         h.once("updated", resolve);
       } else {
         resolve();
       }
 
-      const docShell = this.content.QueryInterface(Ci.nsIInterfaceRequestor)
-                                 .getInterface(Ci.nsIWebNavigation)
-                                 .QueryInterface(Ci.nsIDocShell);
+      const docShell = this.content.docShell;
       docShell.contentViewer.fullZoom = level;
     });
   },
 
   /**
    * Get all box-model regions' adjusted boxquads for the given element
    * @param {String} selector The node selector to target a given element
    * @return {Object} An object with each property being a box-model region, each
--- a/devtools/client/shared/zoom-keys.js
+++ b/devtools/client/shared/zoom-keys.js
@@ -1,15 +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 { Ci } = require("chrome");
 const Services = require("Services");
 const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
 
 const ZOOM_PREF = "devtools.toolbox.zoomValue";
 const MIN_ZOOM = 0.5;
 const MAX_ZOOM = 2;
 
 const {LocalizationHelper} = require("devtools/shared/l10n");
@@ -21,19 +20,17 @@ const L10N = new LocalizationHelper("dev
  *
  * @param {DOMWindow} The window on which we should listent to key strokes and
  *                    modify the zoom factor.
  */
 exports.register = function(window) {
   const shortcuts = new KeyShortcuts({
     window
   });
-  const docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
-    .getInterface(Ci.nsIWebNavigation)
-    .QueryInterface(Ci.nsIDocShell);
+  const docShell = window.docShell;
   const contViewer = docShell.contentViewer;
   let zoomValue = parseFloat(Services.prefs.getCharPref(ZOOM_PREF));
   const zoomIn = function(event) {
     setZoom(zoomValue + 0.1);
     event.preventDefault();
   };
 
   const zoomOut = function(event) {
--- a/devtools/client/webconsole/utils/context-menu.js
+++ b/devtools/client/webconsole/utils/context-menu.js
@@ -187,19 +187,17 @@ exports.createContextMenu = createContex
  * with docshell commands to provide the right enabled state and editor
  * functionality.
  *
  * You'll need to call menu.popup() yourself, this just returns the Menu instance.
  *
  * @returns {Menu}
  */
 function createEditContextMenu() {
-  const docshell = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsIWebNavigation)
-                         .QueryInterface(Ci.nsIDocShell);
+  const docshell = window.docShell;
   const menu = new Menu({
     id: "webconsole-menu"
   });
   menu.append(new MenuItem({
     id: "editmenu-undo",
     l10nID: "editmenu-undo",
     disabled: !docshell.isCommandEnabled("cmd_undo"),
     click: () => {
--- a/devtools/client/webide/content/webide.js
+++ b/devtools/client/webide/content/webide.js
@@ -86,20 +86,17 @@ var UI = {
       const addons = GetAvailableAddons();
       addons.adb.install();
     }
 
     Services.prefs.setBoolPref("devtools.webide.autoinstallADBHelper", false);
 
     this.setupDeck();
 
-    this.contentViewer = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                               .getInterface(Ci.nsIWebNavigation)
-                               .QueryInterface(Ci.nsIDocShell)
-                               .contentViewer;
+    this.contentViewer = window.docShell.contentViewer;
     this.contentViewer.fullZoom = Services.prefs.getCharPref("devtools.webide.zoom");
 
     gDevToolsBrowser.isWebIDEInitialized.resolve();
   },
 
   destroy: function() {
     window.removeEventListener("focus", this.onfocus, true);
     AppManager.off("app-manager-update", this.appManagerUpdate);
--- a/devtools/client/webide/test/test_zoom.html
+++ b/devtools/client/webide/test/test_zoom.html
@@ -15,20 +15,17 @@
   <body>
 
     <script type="application/javascript">
       window.onload = function() {
         SimpleTest.waitForExplicitFinish();
 
         (async function() {
           let win = await openWebIDE();
-          let viewer = win.QueryInterface(Ci.nsIInterfaceRequestor)
-                            .getInterface(Ci.nsIWebNavigation)
-                            .QueryInterface(Ci.nsIDocShell)
-                            .contentViewer;
+          let viewer = win.docShell.contentViewer;
 
           win.Cmds.zoomOut();
           win.Cmds.zoomOut();
           win.Cmds.zoomOut();
           win.Cmds.zoomOut();
           win.Cmds.zoomOut();
           win.Cmds.zoomOut();
           win.Cmds.zoomOut();
@@ -50,20 +47,17 @@
           win.Cmds.zoomIn();
 
           roundZoom = Math.round(10 * viewer.fullZoom) / 10;
           is(roundZoom, 1.4, "Reach max zoom");
 
           await closeWebIDE(win);
 
           win = await openWebIDE();
-          viewer = win.QueryInterface(Ci.nsIInterfaceRequestor)
-                        .getInterface(Ci.nsIWebNavigation)
-                        .QueryInterface(Ci.nsIDocShell)
-                        .contentViewer;
+          viewer = win.docShell.contentViewer;
 
           roundZoom = Math.round(10 * viewer.fullZoom) / 10;
           is(roundZoom, 1.4, "Zoom restored");
 
           win.Cmds.resetZoom();
 
           is(viewer.fullZoom, 1, "Zoom reset");
 
--- a/devtools/server/actors/addon/webextension-inspected-window.js
+++ b/devtools/server/actors/addon/webextension-inspected-window.js
@@ -87,19 +87,17 @@ function logAccessDeniedWarning(window, 
   }
 
   error.initWithWindowID(msg, url, lineNumber, 0, 0, errorFlag, "webExtensions",
                          innerWindowId);
   Services.console.logMessage(error);
 }
 
 function CustomizedReload(params) {
-  this.docShell = params.targetActor.window
-                        .QueryInterface(Ci.nsIInterfaceRequestor)
-                        .getInterface(Ci.nsIDocShell);
+  this.docShell = params.targetActor.window.docShell;
   this.docShell.QueryInterface(Ci.nsIWebProgress);
 
   this.inspectedWindowEval = params.inspectedWindowEval;
   this.callerInfo = params.callerInfo;
 
   this.ignoreCache = params.ignoreCache;
   this.injectedScript = params.injectedScript;
   this.userAgent = params.userAgent;
@@ -168,19 +166,17 @@ CustomizedReload.prototype = {
     const document = subject;
     const window = document && document.defaultView;
 
     // Filter out non interesting documents.
     if (!document || !document.location || !window) {
       return;
     }
 
-    const subjectDocShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                                .getInterface(Ci.nsIWebNavigation)
-                                .QueryInterface(Ci.nsIDocShell);
+    const subjectDocShell = window.docShell;
 
     // Keep track of the set of window objects where we are going to inject
     // the injectedScript: the top level window and all its descendant
     // that are still of type content (filtering out loaded XUL pages, if any).
     if (window == this.window) {
       this.customizedReloadWindows.add(window);
     } else if (subjectDocShell.sameTypeParent) {
       const parentWindow = subjectDocShell.sameTypeParent.domWindow;
--- a/devtools/server/actors/highlighters.js
+++ b/devtools/server/actors/highlighters.js
@@ -635,19 +635,17 @@ HighlighterEnvironment.prototype = {
   },
 
   get document() {
     return this.window && this.window.document;
   },
 
   get docShell() {
     return this.window &&
-           this.window.QueryInterface(Ci.nsIInterfaceRequestor)
-                      .getInterface(Ci.nsIWebNavigation)
-                      .QueryInterface(Ci.nsIDocShell);
+           this.window.docShell;
   },
 
   get webProgress() {
     return this.docShell &&
            this.docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsIWebProgress);
   },
 
--- a/devtools/server/actors/reflow.js
+++ b/devtools/server/actors/reflow.js
@@ -425,30 +425,24 @@ exports.releaseLayoutChangesObserver = r
  */
 class ReflowObserver extends Observable {
   constructor(targetActor, callback) {
     super(targetActor, callback);
   }
 
   _startListeners(windows) {
     for (const window of windows) {
-      const docshell = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                     .getInterface(Ci.nsIWebNavigation)
-                     .QueryInterface(Ci.nsIDocShell);
-      docshell.addWeakReflowObserver(this);
+      window.docShell.addWeakReflowObserver(this);
     }
   }
 
   _stopListeners(windows) {
     for (const window of windows) {
       try {
-        const docshell = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                       .getInterface(Ci.nsIWebNavigation)
-                       .QueryInterface(Ci.nsIDocShell);
-        docshell.removeWeakReflowObserver(this);
+        window.docShell.removeWeakReflowObserver(this);
       } catch (e) {
         // Corner cases where a global has already been freed may happen, in
         // which case, no need to remove the observer.
       }
     }
   }
 
   reflow(start, end) {
--- a/devtools/server/actors/source.js
+++ b/devtools/server/actors/source.js
@@ -397,26 +397,25 @@ const SourceActor = ActorClassWithSpec(s
       // that doesn't match the document shown in the browser.
       const loadFromCache = this.isInlineSource && this.isCacheEnabled;
 
       // Fetch the sources with the same principal as the original document
       const win = this.threadActor._parent.window;
       let principal, cacheKey;
       // On xpcshell, we don't have a window but a Sandbox
       if (!isWorker && win instanceof Ci.nsIDOMWindow) {
-        const webNav = win.QueryInterface(Ci.nsIInterfaceRequestor)
-                        .getInterface(Ci.nsIWebNavigation);
-        const channel = webNav.currentDocumentChannel;
+        const docShell = win.docShell;
+        const channel = docShell.currentDocumentChannel;
         principal = channel.loadInfo.loadingPrincipal;
 
         // Retrieve the cacheKey in order to load POST requests from cache
         // Note that chrome:// URLs don't support this interface.
         if (loadFromCache &&
-          webNav.currentDocumentChannel instanceof Ci.nsICacheInfoChannel) {
-          cacheKey = webNav.currentDocumentChannel.cacheKey;
+          docShell.currentDocumentChannel instanceof Ci.nsICacheInfoChannel) {
+          cacheKey = docShell.currentDocumentChannel.cacheKey;
         }
       }
 
       const sourceFetched = fetch(this.url, {
         principal,
         cacheKey,
         loadFromCache
       });
--- a/devtools/server/actors/targets/browsing-context.js
+++ b/devtools/server/actors/targets/browsing-context.js
@@ -390,19 +390,17 @@ const browsingContextTargetPrototype = {
    * if you want it to show information relative to the iframe that's currently
    * being inspected in the toolbox.
    */
   get originalDocShell() {
     if (!this._originalWindow) {
       return this.docShell;
     }
 
-    return this._originalWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                               .getInterface(Ci.nsIWebNavigation)
-                               .QueryInterface(Ci.nsIDocShell);
+    return this._originalWindow.docShell;
   },
 
   /**
    * Getter for the original window this actor got attached to in the first
    * place.
    * Note that your actor should normally *not* rely on this top level window if
    * you want it to show information relative to the iframe that's currently
    * being inspected in the toolbox.
@@ -419,19 +417,17 @@ const browsingContextTargetPrototype = {
       .QueryInterface(Ci.nsIInterfaceRequestor)
       .getInterface(Ci.nsIWebProgress);
   },
 
   /**
    * Getter for the nsIWebNavigation for the target.
    */
   get webNavigation() {
-    return this.docShell
-      .QueryInterface(Ci.nsIInterfaceRequestor)
-      .getInterface(Ci.nsIWebNavigation);
+    return this.docShell.QueryInterface(Ci.nsIWebNavigation);
   },
 
   /**
    * Getter for the browsing context's document.
    */
   get contentDocument() {
     return this.webNavigation.document;
   },
@@ -1211,19 +1207,17 @@ const browsingContextTargetPrototype = {
       this._windowReady(window, true);
       DevToolsUtils.executeSoon(() => {
         this._navigate(window, true);
       });
     });
   },
 
   _setWindow(window) {
-    const docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsIWebNavigation)
-                         .QueryInterface(Ci.nsIDocShell);
+    const docShell = window.docShell;
     // Here is the very important call where we switch the currently targeted
     // browsing context (it will indirectly update this.window and many other
     // attributes defined from docShell).
     Object.defineProperty(this, "docShell", {
       value: docShell,
       enumerable: true,
       configurable: true
     });
--- a/devtools/server/actors/targets/chrome-window.js
+++ b/devtools/server/actors/targets/chrome-window.js
@@ -49,18 +49,17 @@ const chromeWindowTargetPrototype = exte
  * @param connection DebuggerServerConnection
  *        The connection to the client.
  * @param window DOMWindow
  *        The window.
  */
 chromeWindowTargetPrototype.initialize = function(connection, window) {
   BrowsingContextTargetActor.prototype.initialize.call(this, connection);
 
-  const docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                       .getInterface(Ci.nsIDocShell);
+  const docShell = window.docShell;
   Object.defineProperty(this, "docShell", {
     value: docShell,
     configurable: true
   });
 };
 
 // Bug 1266561: This setting is mysteriously named, we should split up the
 // functionality that is triggered by it.
--- a/devtools/server/actors/targets/content-process.js
+++ b/devtools/server/actors/targets/content-process.js
@@ -37,18 +37,17 @@ function ContentProcessTargetActor(conne
   });
 
   const sandboxPrototype = {
     get tabs() {
       const tabs = [];
       const windowEnumerator = Services.ww.getWindowEnumerator();
       while (windowEnumerator.hasMoreElements()) {
         const window = windowEnumerator.getNext().QueryInterface(Ci.nsIDOMWindow);
-        const tabChildGlobal = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                                     .getInterface(Ci.nsIDocShell)
+        const tabChildGlobal = window.docShell
                                      .sameTypeRootTreeItem
                                      .QueryInterface(Ci.nsIInterfaceRequestor)
                                      .getInterface(Ci.nsIContentFrameMessageManager);
         tabs.push(tabChildGlobal);
       }
       return tabs;
     },
   };
--- a/devtools/server/actors/targets/parent-process.js
+++ b/devtools/server/actors/targets/parent-process.js
@@ -71,19 +71,17 @@ parentProcessTargetPrototype.initialize 
     try {
       window = Services.appShell.hiddenDOMWindow;
     } catch (e) {
       // On XPCShell, the above line will throw.
     }
   }
 
   // On XPCShell, there is no window/docshell
-  const docShell = window ? window.QueryInterface(Ci.nsIInterfaceRequestor)
-                                .getInterface(Ci.nsIDocShell)
-                        : null;
+  const docShell = window ? window.docShell : null;
   Object.defineProperty(this, "docShell", {
     value: docShell,
     configurable: true
   });
 };
 
 parentProcessTargetPrototype.isRootActor = true;
 
@@ -93,19 +91,17 @@ parentProcessTargetPrototype.isRootActor
  */
 Object.defineProperty(parentProcessTargetPrototype, "docShells", {
   get: function() {
     // Iterate over all top-level windows and all their docshells.
     let docShells = [];
     const e = Services.ww.getWindowEnumerator();
     while (e.hasMoreElements()) {
       const window = e.getNext();
-      const docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                           .getInterface(Ci.nsIWebNavigation)
-                           .QueryInterface(Ci.nsIDocShell);
+      const docShell = window.docShell;
       docShells = docShells.concat(getChildDocShells(docShell));
     }
 
     return docShells;
   }
 });
 
 parentProcessTargetPrototype.observe = function(subject, topic, data) {
@@ -133,19 +129,17 @@ parentProcessTargetPrototype._attach = f
   // Listen for any new/destroyed chrome docshell
   Services.obs.addObserver(this, "chrome-webnavigation-create");
   Services.obs.addObserver(this, "chrome-webnavigation-destroy");
 
   // Iterate over all top-level windows.
   const e = Services.ww.getWindowEnumerator();
   while (e.hasMoreElements()) {
     const window = e.getNext();
-    const docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsIWebNavigation)
-                         .QueryInterface(Ci.nsIDocShell);
+    const docShell = window.docShell;
     if (docShell == this.docShell) {
       continue;
     }
     this._progressListener.watch(docShell);
   }
   return undefined;
 };
 
@@ -156,19 +150,17 @@ parentProcessTargetPrototype._detach = f
 
   Services.obs.removeObserver(this, "chrome-webnavigation-create");
   Services.obs.removeObserver(this, "chrome-webnavigation-destroy");
 
   // Iterate over all top-level windows.
   const e = Services.ww.getWindowEnumerator();
   while (e.hasMoreElements()) {
     const window = e.getNext();
-    const docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsIWebNavigation)
-                         .QueryInterface(Ci.nsIDocShell);
+    const docShell = window.docShell;
     if (docShell == this.docShell) {
       continue;
     }
     this._progressListener.unwatch(docShell);
   }
 
   BrowsingContextTargetActor.prototype._detach.call(this);
   return undefined;
--- a/devtools/server/actors/webconsole.js
+++ b/devtools/server/actors/webconsole.js
@@ -1917,19 +1917,17 @@ WebConsoleActor.prototype =
    * Find the XUL window that owns the content window.
    *
    * @return Window
    *         The XUL window that owns the content window.
    */
   chromeWindow: function() {
     let window = null;
     try {
-      window = this.window.QueryInterface(Ci.nsIInterfaceRequestor)
-             .getInterface(Ci.nsIWebNavigation).QueryInterface(Ci.nsIDocShell)
-             .chromeEventHandler.ownerGlobal;
+      window = this.window.docShell.chromeEventHandler.ownerGlobal;
     } catch (ex) {
       // The above can fail because chromeEventHandler is not available for all
       // kinds of |this.window|.
     }
 
     return window;
   },
 
--- a/devtools/server/actors/webconsole/listeners.js
+++ b/devtools/server/actors/webconsole/listeners.js
@@ -374,19 +374,17 @@ ConsoleAPIListener.prototype =
  * @param object window
  *        The window for which we need to track reflow.
  * @param object owner
  *        The listener owner which needs to implement:
  *        - onReflowActivity(reflowInfo)
  */
 
 function ConsoleReflowListener(window, listener) {
-  this.docshell = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsIWebNavigation)
-                         .QueryInterface(Ci.nsIDocShell);
+  this.docshell = window.docShell;
   this.listener = listener;
   this.docshell.addWeakReflowObserver(this);
 }
 
 exports.ConsoleReflowListener = ConsoleReflowListener;
 
 ConsoleReflowListener.prototype =
 {
--- a/devtools/server/performance/timeline.js
+++ b/devtools/server/performance/timeline.js
@@ -310,19 +310,17 @@ Timeline.prototype = {
   },
 
   /**
    * When a new window becomes available in the targetActor, start recording its
    * markers if we were recording.
    */
   _onWindowReady: function({ window }) {
     if (this._isRecording) {
-      const docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                           .getInterface(Ci.nsIWebNavigation)
-                           .QueryInterface(Ci.nsIDocShell);
+      const docShell = window.docShell;
       docShell.recordProfileTimelineMarkers = true;
     }
   },
 
   /**
    * Fired when the Memory component emits a `garbage-collection` event. Used to
    * take the data and make it look like the rest of our markers.
    *
--- a/devtools/shared/DevToolsUtils.js
+++ b/devtools/shared/DevToolsUtils.js
@@ -541,18 +541,17 @@ function mainThreadFetch(urlIn, aOptions
   // SHEntry and offer ways to restore POST requests from cache.
   if (aOptions.loadFromCache &&
       aOptions.cacheKey != 0 && channel instanceof Ci.nsICacheInfoChannel) {
     channel.cacheKey = aOptions.cacheKey;
   }
 
   if (aOptions.window) {
     // Respect private browsing.
-    channel.loadGroup = aOptions.window.QueryInterface(Ci.nsIInterfaceRequestor)
-                          .getInterface(Ci.nsIWebNavigation)
+    channel.loadGroup = aOptions.window.docShell
                           .QueryInterface(Ci.nsIDocumentLoader)
                           .loadGroup;
   }
 
   const deferred = defer();
   const onResponse = (stream, status, request) => {
     if (!components.isSuccessCode(status)) {
       deferred.reject(new Error(`Failed to fetch ${url}. Code ${status}.`));
--- a/devtools/shared/gcli/commands/screenshot.js
+++ b/devtools/shared/gcli/commands/screenshot.js
@@ -368,19 +368,17 @@ function saveToClipboard(context, reply)
     try {
       const channel = NetUtil.newChannel({
         uri: reply.data,
         loadUsingSystemPrincipal: true,
         contentPolicyType: Ci.nsIContentPolicy.TYPE_INTERNAL_IMAGE
       });
       const input = channel.open2();
 
-      const loadContext = context.environment.chromeWindow
-                                 .QueryInterface(Ci.nsIInterfaceRequestor)
-                                 .getInterface(Ci.nsIWebNavigation)
+      const loadContext = context.environment.chromeWindow.docShell
                                  .QueryInterface(Ci.nsILoadContext);
 
       const callback = {
         onImageReady(container, status) {
           if (!container) {
             console.error("imgTools.decodeImageAsync failed");
             reply.destinations.push(l10n.lookup("screenshotErrorCopying"));
             resolve();
--- a/devtools/shared/layout/utils.js
+++ b/devtools/shared/layout/utils.js
@@ -36,19 +36,17 @@ function utilsFor(win) {
 
 /**
  * like win.top, but goes through mozbrowsers and mozapps iframes.
  *
  * @param {DOMWindow} win
  * @return {DOMWindow}
  */
 function getTopWindow(win) {
-  const docShell = win.QueryInterface(Ci.nsIInterfaceRequestor)
-                    .getInterface(Ci.nsIWebNavigation)
-                    .QueryInterface(Ci.nsIDocShell);
+  const docShell = win.docShell;
 
   if (!docShell.isMozBrowser) {
     return win.top;
   }
 
   const topDocShell =
     docShell.getSameTypeRootTreeItemIgnoreBrowserBoundaries();
 
@@ -97,19 +95,17 @@ exports.isWindowIncluded = isWindowInclu
  * @param {DOMWindow} win
  * @return {DOMWindow}
  */
 function getParentWindow(win) {
   if (isTopWindow(win)) {
     return null;
   }
 
-  const docShell = win.QueryInterface(Ci.nsIInterfaceRequestor)
-                 .getInterface(Ci.nsIWebNavigation)
-                 .QueryInterface(Ci.nsIDocShell);
+  const docShell = win.docShell;
 
   if (!docShell.isMozBrowser) {
     return win.parent;
   }
 
   const parentDocShell =
     docShell.getSameTypeParentIgnoreBrowserBoundaries();
 
--- a/devtools/shared/webconsole/network-monitor.js
+++ b/devtools/shared/webconsole/network-monitor.js
@@ -1336,18 +1336,17 @@ NetworkMonitor.prototype = {
         httpActivity.url == this.window.location.href) {
       // If the request URL is the same as the current page URL, then
       // we can try to get the posted text from the page directly.
       // This check is necessary as otherwise the
       //   NetworkHelper.readPostTextFromPageViaWebNav()
       // function is called for image requests as well but these
       // are not web pages and as such don't store the posted text
       // in the cache of the webpage.
-      const webNav = this.window.QueryInterface(Ci.nsIInterfaceRequestor)
-                   .getInterface(Ci.nsIWebNavigation);
+      const webNav = this.window.docShell.QueryInterface(Ci.nsIWebNavigation);
       sentBody = NetworkHelper
                  .readPostTextFromPageViaWebNav(webNav, httpActivity.charset);
     }
 
     if (sentBody !== null) {
       httpActivity.sentBody = sentBody;
     }
   },
@@ -2108,19 +2107,17 @@ ConsoleProgressListener.prototype = {
    * Initialize the ConsoleProgressListener.
    * @private
    */
   _init: function() {
     if (this._initialized) {
       return;
     }
 
-    this._webProgress = this.window.QueryInterface(Ci.nsIInterfaceRequestor)
-                        .getInterface(Ci.nsIWebNavigation)
-                        .QueryInterface(Ci.nsIWebProgress);
+    this._webProgress = this.window.docShell.QueryInterface(Ci.nsIWebProgress);
     this._webProgress.addProgressListener(this,
                                           Ci.nsIWebProgress.NOTIFY_STATE_ALL);
 
     this._initialized = true;
   },
 
   /**
    * Start a monitor/tracker related to the current nsIWebProgressListener
--- a/docshell/test/browser/browser_bug655273.js
+++ b/docshell/test/browser/browser_bug655273.js
@@ -14,17 +14,17 @@ add_task(async function test() {
   await BrowserTestUtils.withNewTab({ gBrowser, url: "http://example.com" },
     async function(browser) {
       await ContentTask.spawn(browser, null, async function() {
         let cw = content;
         let oldTitle = cw.document.title;
         ok(oldTitle, 'Content window should initially have a title.');
         cw.history.pushState('', '', 'new_page');
 
-        let shistory = cw.QueryInterface(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsIWebNavigation)
+        let shistory = cw.docShell
+                         .QueryInterface(Ci.nsIWebNavigation)
                          .sessionHistory;
 
         is(shistory.legacySHistory.getEntryAtIndex(shistory.index, false).title,
            oldTitle, 'SHEntry title after pushstate.');
       });
     });
 });
--- a/docshell/test/browser/browser_bug673467.js
+++ b/docshell/test/browser/browser_bug673467.js
@@ -29,19 +29,18 @@ function test() {
           let iframe = content.document.getElementById('iframe');
           iframe.addEventListener('load', function listener(aEvent) {
 
             // Wait for the iframe to load the new document, not about:blank.
             if (!iframe.src)
               return;
 
             iframe.removeEventListener('load', listener, true);
-            let shistory = content
-                            .QueryInterface(Ci.nsIInterfaceRequestor)
-                            .getInterface(Ci.nsIWebNavigation)
+            let shistory = content.docShell
+                            .QueryInterface(Ci.nsIWebNavigation)
                             .sessionHistory;
 
             Assert.equal(shistory.count, 1, "shistory count should be 1.");
             resolve();
           }, true);
         }, true);
       });
     });
--- a/docshell/test/browser/browser_tab_touch_events.js
+++ b/docshell/test/browser/browser_tab_touch_events.js
@@ -17,33 +17,27 @@ async function test_body() {
   is(docshell.touchEventsOverride, Ci.nsIDocShell.TOUCHEVENTS_OVERRIDE_NONE,
     "touchEventsOverride flag should be initially set to NONE");
 
   docshell.touchEventsOverride = Ci.nsIDocShell.TOUCHEVENTS_OVERRIDE_DISABLED;
   is(docshell.touchEventsOverride, Ci.nsIDocShell.TOUCHEVENTS_OVERRIDE_DISABLED,
     "touchEventsOverride flag should be changed to DISABLED");
 
   let frameWin = content.document.querySelector("#test-iframe").contentWindow;
-  docshell = frameWin.QueryInterface(Ci.nsIInterfaceRequestor)
-                     .getInterface(Ci.nsIWebNavigation)
-                     .QueryInterface(Ci.nsIDocShell);
+  docshell = frameWin.docShell;
   is(docshell.touchEventsOverride, Ci.nsIDocShell.TOUCHEVENTS_OVERRIDE_DISABLED,
     "touchEventsOverride flag should be passed on to frames.");
 
   let newFrame = content.document.createElement("iframe");
   content.document.body.appendChild(newFrame);
 
   let newFrameWin = newFrame.contentWindow;
-  docshell = newFrameWin.QueryInterface(Ci.nsIInterfaceRequestor)
-                        .getInterface(Ci.nsIWebNavigation)
-                        .QueryInterface(Ci.nsIDocShell);
+  docshell = newFrameWin.docShell;
   is(docshell.touchEventsOverride, Ci.nsIDocShell.TOUCHEVENTS_OVERRIDE_DISABLED,
     "Newly created frames should use the new touchEventsOverride flag");
 
   newFrameWin.location.reload();
   await ContentTaskUtils.waitForEvent(newFrameWin, "load");
 
-  docshell = newFrameWin.QueryInterface(Ci.nsIInterfaceRequestor)
-                        .getInterface(Ci.nsIWebNavigation)
-                        .QueryInterface(Ci.nsIDocShell);
+  docshell = newFrameWin.docShell;
   is(docshell.touchEventsOverride, Ci.nsIDocShell.TOUCHEVENTS_OVERRIDE_DISABLED,
     "New touchEventsOverride flag should persist across reloads");
 }
--- a/docshell/test/browser/frame-head.js
+++ b/docshell/test/browser/frame-head.js
@@ -42,19 +42,17 @@ this.finish = function() {
  *             true.
  *        setup is a function that takes the docshell as an argument.
  *             It should start the test.
  *        check is a function that takes an array of markers
  *             as an argument and checks the results of the test.
  */
 this.timelineContentTest = function(tests) {
   (async function() {
-    let docShell = content.QueryInterface(Ci.nsIInterfaceRequestor)
-                          .getInterface(Ci.nsIWebNavigation)
-                          .QueryInterface(Ci.nsIDocShell);
+    let docShell = content.docShell;
 
     info("Start recording");
     docShell.recordProfileTimelineMarkers = true;
 
     for (let {desc, searchFor, setup, check} of tests) {
 
       info("Running test: " + desc);
 
--- a/docshell/test/chrome/bug662200_window.xul
+++ b/docshell/test/chrome/bug662200_window.xul
@@ -82,22 +82,20 @@
                            title: "C"},
                           {type: "pageshow", 
                            title: "B"} ],
         onNavComplete: nextTest
       };
       doPageNavigation(navData);
       yield undefined;
 
-      var docshell = TestWindow.getWindow()
-                               .QueryInterface(Ci.nsIInterfaceRequestor)
-                               .getInterface(Ci.nsIWebNavigation)
-                               .QueryInterface(Ci.nsIDocShell);
-      var shistory = docshell.QueryInterface(Ci.nsIInterfaceRequestor)
-                             .getInterface(Ci.nsISHistory)
+      var docshell = TestWindow.getWindow().docShell;
+      var shistory = docshell.QueryInterface(Ci.nsIWebNavigation)
+                             .sessionHistory
+                             .legacySHistory
                              .QueryInterface(Ci.nsIWebNavigation);
 
       // Reload.
       navData = {
         eventsToListenFor: ["pageshow", "pagehide"],
         expectedEvents: [ {type: "pagehide", 
                            title: "B"},
                           {type: "pageshow", 
--- a/docshell/test/chrome/mozFrameType_window.xul
+++ b/docshell/test/chrome/mozFrameType_window.xul
@@ -11,20 +11,17 @@
 
   <script type="application/javascript" src="docshell_helpers.js" />
   <script type="application/javascript"><![CDATA[
     function runTests() {
       let opener = window.opener;
       let SimpleTest = opener.wrappedJSObject.SimpleTest;
 
       function getDocShellType(frame) {
-        return frame.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                                  .getInterface(Ci.nsIDocShell)
-                                  .QueryInterface(Ci.nsIDocShellTreeItem)
-                                  .itemType;
+        return frame.contentWindow.docShell.itemType;
       }
 
       var normalFrame = document.getElementById("normalFrame");
       var typeContentFrame = document.getElementById("typeContentFrame");
 
       SimpleTest.is(getDocShellType(normalFrame), Ci.nsIDocShellTreeItem.typeChrome,
                     "normal iframe in chrome document is typeChrome");
       SimpleTest.is(getDocShellType(typeContentFrame), Ci.nsIDocShellTreeItem.typeContent,
--- a/docshell/test/chrome/test_allowContentRetargeting.html
+++ b/docshell/test/chrome/test_allowContentRetargeting.html
@@ -21,45 +21,38 @@ function runNextTest() {
 }
 
 var tests = [
 
   // Set allowContentRetargeting = false, load a downloadable URL, verify the
   // downloadable stops loading.
   function basic() {
     var iframe = insertIframe();
-    docshellForWindow(iframe.contentWindow).allowContentRetargeting = false;
+    iframe.contentWindow.docShell.allowContentRetargeting = false;
     loadIframe(iframe);
   },
 
   // Set allowContentRetargeting = false on parent docshell, load a downloadable
   // URL, verify the downloadable stops loading.
   function inherit() {
-    var docshell = docshellForWindow(window);
+    var docshell = window.docShell;
     docshell.allowContentRetargeting = false;
     loadIframe(insertIframe());
   },
 ];
 
-function docshellForWindow(win) {
-  return win.
-         QueryInterface(Ci.nsIInterfaceRequestor).
-         getInterface(Ci.nsIWebNavigation).
-         QueryInterface(Ci.nsIDocShell);
-}
-
 function insertIframe() {
   var iframe = document.createElement("iframe");
   document.body.appendChild(iframe);
   return iframe;
 }
 
 function loadIframe(iframe) {
   iframe.setAttribute("src", TEST_URL);
-  docshellForWindow(iframe.contentWindow).
+  iframe.contentWindow.docShell.
     QueryInterface(Ci.nsIInterfaceRequestor).
     getInterface(Ci.nsIWebProgress).
     addProgressListener(progressListener,
                         Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT);
 }
 
 var progressListener = {
   onStateChange: function (webProgress, req, flags, status) {
--- a/docshell/test/chrome/test_bug909218.html
+++ b/docshell/test/chrome/test_bug909218.html
@@ -25,17 +25,17 @@ var TEST_URL = "http://mochi.test:8888/c
 // The test entry-point.  The basic outline is:
 // * Create an iframe and set defaultLoadFlags on its docShell.
 // * Add a web progress listener to observe each request as the iframe is
 //   loaded, and check that each request has the flags we specified.
 // * Load our test URL into the iframe and wait for the load to complete.
 function test() {
   var iframe = document.createElement("iframe");
   document.body.appendChild(iframe);
-  var docShell = docshellForWindow(iframe.contentWindow);
+  var docShell = iframe.contentWindow.docShell;
   // Add our progress listener - when it notices the top-level document is
   // complete, the test will end.
   RequestWatcher.init(docShell, SimpleTest.finish);
   // Set the flags we care about, then load our test URL.
   docShell.defaultLoadFlags = TEST_FLAGS;
   iframe.setAttribute("src", TEST_URL);
 }
 
@@ -110,18 +110,11 @@ RequestWatcher = {
     }
   },
   QueryInterface: ChromeUtils.generateQI([
     Ci.nsIWebProgressListener,
     Ci.nsISupportsWeakReference,
   ])
 }
 
-function docshellForWindow(win) {
-  return win.
-         QueryInterface(Ci.nsIInterfaceRequestor).
-         getInterface(Ci.nsIWebNavigation).
-         QueryInterface(Ci.nsIDocShell);
-}
-
 </script>
 </head>
 </html>
--- a/docshell/test/chrome/test_viewsource_forbidden_in_iframe.xul
+++ b/docshell/test/chrome/test_viewsource_forbidden_in_iframe.xul
@@ -58,18 +58,17 @@ https://bugzilla.mozilla.org/show_bug.cg
           var errorMsg = matchArray[1];
           resolve(decodeURIComponent(errorMsg));
         },
 
         QueryInterface: ChromeUtils.generateQI([Ci.nsIWebProgressListener,
                                                 Ci.nsISupportsWeakReference])
       };
 
-      frame.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsIWebNavigation)
+      frame.contentWindow.docShell
                          .QueryInterface(Ci.nsIInterfaceRequestor)
                          .getInterface(Ci.nsIWebProgress)
                          .addProgressListener(progressListener,
                                               Ci.nsIWebProgress.NOTIFY_LOCATION |
                                               Ci.nsIWebProgress.NOTIFY_STATE_REQUEST);
     });
   }
 
--- a/docshell/test/navigation/NavigationUtils.js
+++ b/docshell/test/navigation/NavigationUtils.js
@@ -104,19 +104,17 @@ function xpcEnumerateContentWindows(call
                         .getService(Ci.nsIWindowWatcher);
   var enumerator = ww.getWindowEnumerator();
 
   var contentWindows = [];
 
   while (enumerator.hasMoreElements()) {
     var win = enumerator.getNext();
     if (win.isChromeWindow) {
-      var docshellTreeNode = win.QueryInterface(Ci.nsIInterfaceRequestor)
-                                .getInterface(Ci.nsIWebNavigation)
-                                .QueryInterface(Ci.nsIDocShellTreeItem);
+      var docshellTreeNode = win.docShell;
       var childCount = docshellTreeNode.childCount;
       for (var i = 0; i < childCount; ++i) {
         var childTreeNode = docshellTreeNode.getChildAt(i);
 
         // we're only interested in content docshells
         if (SpecialPowers.unwrap(childTreeNode.itemType) != Ci.nsIDocShellTreeItem.typeContent)
           continue;
 
--- a/docshell/test/navigation/browser_bug343515.js
+++ b/docshell/test/navigation/browser_bug343515.js
@@ -90,19 +90,17 @@ function step3() {
   ok(ctx.tab1Browser.docShellIsActive, "Tab 1 should be active");
 
   // Tab 2's window _and_ its iframes should be inactive
   ok(!ctx.tab2Browser.docShellIsActive, "Tab 2 should be inactive");
   ContentTask.spawn(ctx.tab2Browser, null, async function() {
     Assert.equal(content.frames.length, 2, "Tab 2 should have 2 iframes");
     for (var i = 0; i < content.frames.length; i++) {
       info("step 3, frame " + i + " info: " + content.frames[i].location);
-      let docshell = content.frames[i].QueryInterface(Ci.nsIInterfaceRequestor)
-                                      .getInterface(Ci.nsIWebNavigation)
-                                      .QueryInterface(Ci.nsIDocShell);
+      let docshell = content.frames[i].docShell;
 
       Assert.ok(!docShell.isActive, `Tab2 iframe ${i} should be inactive`);
     }
   }).then(() => {
     // Navigate tab 2 to a different page
     ctx.tab2Browser.loadURI(testPath + "bug343515_pg3.html");
 
     // bug343515_pg3.html consists of a page with two iframes, one of which
@@ -110,19 +108,17 @@ function step3() {
     nShotsListener(ctx.tab2Browser, "load", step4, 4);
   });
 }
 
 function step4() {
   function checkTab2Active(expected) {
     return ContentTask.spawn(ctx.tab2Browser, expected, async function(expected) {
       function isActive(aWindow) {
-        var docshell = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                              .getInterface(Ci.nsIWebNavigation)
-                              .QueryInterface(Ci.nsIDocShell);
+        var docshell = aWindow.docShell;
         return docshell.isActive;
       }
 
       let active = expected ? "active" : "inactive";
       Assert.equal(content.frames.length, 2, "Tab 2 should have 2 iframes");
       for (var i = 0; i < content.frames.length; i++)
         info("step 4, frame " + i + " info: " + content.frames[i].location);
       Assert.equal(content.frames[0].frames.length, 1, "Tab 2 iframe 0 should have 1 iframes");
@@ -160,19 +156,17 @@ function step4() {
 
 function step5() {
   // Check everything
   ok(!ctx.tab0Browser.docShellIsActive, "Tab 0 should be inactive");
   ok(!ctx.tab1Browser.docShellIsActive, "Tab 1 should be inactive");
   ok(ctx.tab2Browser.docShellIsActive, "Tab 2 should be active");
   ContentTask.spawn(ctx.tab2Browser, null, async function() {
     for (var i = 0; i < content.frames.length; i++) {
-      let docshell = content.frames[i].QueryInterface(Ci.nsIInterfaceRequestor)
-                                      .getInterface(Ci.nsIWebNavigation)
-                                      .QueryInterface(Ci.nsIDocShell);
+      let docshell = content.frames[i].docShell;
 
       Assert.ok(docShell.isActive, `Tab2 iframe ${i} should be active`);
     }
   }).then(() => {
     // Switch to tab 1
     return BrowserTestUtils.switchTab(gBrowser, ctx.tab1);
   }).then(() => {
     // Navigate to page 3
@@ -186,51 +180,45 @@ function step5() {
 
 function step6() {
 
   // Check everything
   ok(!ctx.tab0Browser.docShellIsActive, "Tab 0 should be inactive");
   ok(ctx.tab1Browser.docShellIsActive, "Tab 1 should be active");
   ContentTask.spawn(ctx.tab1Browser, null, async function() {
     function isActive(aWindow) {
-      var docshell = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                            .getInterface(Ci.nsIWebNavigation)
-                            .QueryInterface(Ci.nsIDocShell);
+      var docshell = aWindow.docShell;
       return docshell.isActive;
     }
 
     Assert.ok(isActive(content.frames[0]), "Tab1 iframe 0 should be active");
     Assert.ok(isActive(content.frames[0].frames[0]), "Tab1 iframe 0 subiframe 0 should be active");
     Assert.ok(isActive(content.frames[1]), "Tab1 iframe 1 should be active");
   }).then(() => {
     ok(!ctx.tab2Browser.docShellIsActive, "Tab 2 should be inactive");
     return ContentTask.spawn(ctx.tab2Browser, null, async function() {
       for (var i = 0; i < content.frames.length; i++) {
-        let docshell = content.frames[i].QueryInterface(Ci.nsIInterfaceRequestor)
-                                        .getInterface(Ci.nsIWebNavigation)
-                                        .QueryInterface(Ci.nsIDocShell);
+        let docshell = content.frames[i].docShell;
 
         Assert.ok(!docShell.isActive, `Tab2 iframe ${i} should be inactive`);
       }
     });
   }).then(() => {
     // Go forward on tab 2
     waitForPageshow(ctx.tab2Browser, step7);
     ctx.tab2Browser.goForward();
   });
 }
 
 function step7() {
   function checkBrowser(browser, tabNum, active) {
     return ContentTask.spawn(browser, { tabNum, active },
                              async function({ tabNum, active }) {
              function isActive(aWindow) {
-               var docshell = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                                     .getInterface(Ci.nsIWebNavigation)
-                                     .QueryInterface(Ci.nsIDocShell);
+               var docshell = aWindow.docShell;
                return docshell.isActive;
              }
 
              let activestr = active ? "active" : "inactive";
              Assert.equal(isActive(content.frames[0]), active,
                 `Tab${tabNum} iframe 0 should be ${activestr}`);
              Assert.equal(isActive(content.frames[0].frames[0]), active,
                 `Tab${tabNum} iframe 0 subiframe 0 should be ${activestr}`);
--- a/docshell/test/navigation/browser_test-content-chromeflags.js
+++ b/docshell/test/navigation/browser_test-content-chromeflags.js
@@ -20,19 +20,17 @@ add_task(async function() {
   await BrowserTestUtils.withNewTab({
     gBrowser,
     url: TEST_PAGE
   }, async function(browser) {
     let openedPromise = BrowserTestUtils.waitForNewWindow();
     BrowserTestUtils.synthesizeMouse("a", 0, 0, {}, browser);
     let win = await openedPromise;
 
-    let chromeFlags = win.QueryInterface(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsIWebNavigation)
-                         .QueryInterface(Ci.nsIDocShellTreeItem)
+    let chromeFlags = win.docShell
                          .treeOwner
                          .QueryInterface(Ci.nsIInterfaceRequestor)
                          .getInterface(Ci.nsIXULWindow)
                          .chromeFlags;
 
     // In the multi-process case, the new window will have the
     // CHROME_REMOTE_WINDOW flag set.
     const EXPECTED = gMultiProcessBrowser ? CHROME_ALL | CHROME_REMOTE_WINDOW
--- a/docshell/test/navigation/file_bug1300461.html
+++ b/docshell/test/navigation/file_bug1300461.html
@@ -20,18 +20,18 @@
        * 4) file_bug1300461_redirect.html redirects UA to
        *    file_bug1300461_back.html through HTTP 301 header.
        *
        * We verify the history index, canGoBack, canGoForward, etc. keep correct
        * in this process.
        */
       let Ci = SpecialPowers.Ci;
       let webNav = SpecialPowers.wrap(window)
-                     .QueryInterface(Ci.nsIInterfaceRequestor)
-                     .getInterface(Ci.nsIWebNavigation);
+                     .docShell
+                     .QueryInterface(Ci.nsIWebNavigation);
       let shistory = webNav.sessionHistory;
       let testSteps = [
         function() {
           opener.is(shistory.count, 1, 'check history length');
           opener.is(shistory.index, 0, 'check history index');
           opener.ok(!webNav.canGoForward, 'check canGoForward');
           setTimeout(() => window.location = 'file_bug1300461_back.html', 0);
         },
--- a/docshell/test/navigation/file_bug1300461_back.html
+++ b/docshell/test/navigation/file_bug1300461_back.html
@@ -4,18 +4,18 @@
     <meta http-equiv="content-type" content="text/html; charset=utf-8">
     <title>Bug 1300461</title>
   </head>
   <!-- The empty unload handler is to prevent bfcache. -->
   <body onload="test();" onunload="">
     <script>
       let Ci = SpecialPowers.Ci;
       let webNav = SpecialPowers.wrap(window)
-                     .QueryInterface(Ci.nsIInterfaceRequestor)
-                     .getInterface(Ci.nsIWebNavigation);
+                     .docShell
+                     .QueryInterface(Ci.nsIWebNavigation);
       let shistory = webNav.sessionHistory;
       function test() {
         if (opener) {
           opener.info("file_bug1300461_back.html");
           opener.is(shistory.count, 2, 'check history length');
           opener.is(shistory.index, 1, 'check history index');
           opener.is(shistory.legacySHistory.requestedIndex, -1, 'check requestedIndex');
           opener.ok(webNav.canGoBack, 'check canGoBack');
--- a/docshell/test/navigation/file_bug1326251.html
+++ b/docshell/test/navigation/file_bug1326251.html
@@ -17,18 +17,18 @@
         await loadUriInFrame(document.getElementById('dynamicFrame'), 'frame1.html');
         await loadUriInFrame(document.getElementById('staticFrame'), 'frame2.html');
         await loadUriInFrame(document.getElementById('dynamicFrame'), 'frame2.html');
         opener.is(history.length, 5, 'history.length');
         window.location = 'goback.html';
       },
       async function() {
         let webNav = SpecialPowers.wrap(window)
-                       .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
-                       .getInterface(SpecialPowers.Ci.nsIWebNavigation);
+                       .docShell
+                       .QueryInterface(SpecialPowers.Ci.nsIWebNavigation);
         let shistory = webNav.sessionHistory;
         opener.is(webNav.canGoForward, true, 'canGoForward');
         opener.is(shistory.index, 4, 'shistory.index');
         opener.is(history.length, 6, 'history.length');
         opener.is(document.getElementById('staticFrame').contentWindow.location.href, BASE_URL + 'frame2.html', 'staticFrame location');
         opener.is(document.getElementById('dynamicFrame').contentWindow.location.href, BASE_URL + 'frame2.html', 'dynamicFrame location');
 
         // Test 2: Load another page in dynamic iframe, canGoForward should be
@@ -39,21 +39,20 @@
         opener.is(history.length, 6, 'history.length');
 
         // Test 3: Navigate to antoher page with bfcache disabled, all dynamic
         // iframe entries should be removed.
         window.onunload = function(){}; // disable bfcache
         window.location = 'goback.html';
       },
       async function() {
-        let windowWrap = SpecialPowers.wrap(window)
-                         .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor);
-        let docShell = windowWrap.getInterface(SpecialPowers.Ci.nsIDocShell);
-        let shistory = windowWrap.getInterface(SpecialPowers.Ci.nsIWebNavigation)
-                                 .sessionHistory;
+        let windowWrap = SpecialPowers.wrap(window);
+        let docShell = windowWrap.docShell;
+        let shistory = docShell.QueryInterface(SpecialPowers.Ci.nsIWebNavigation)
+                               .sessionHistory;
         // Now staticFrame has frame0 -> frame1 -> frame2.
         opener.is(docShell.previousTransIndex, 3, 'docShell.previousTransIndex');
         opener.is(docShell.loadedTransIndex, 2, 'docShell.loadedTransIndex');
         opener.is(shistory.index, 2, 'shistory.index');
         opener.is(history.length, 4, 'history.length');
         opener.is(document.getElementById('staticFrame').contentWindow.location.href, BASE_URL + 'frame2.html', 'staticFrame location');
         opener.ok(!document.getElementById('dynamicFrame'), 'dynamicFrame should not exist');
 
@@ -99,21 +98,20 @@
         // staticFrame:       frame0 -> frame1 -> frame2 -> iframe_static
         // innerStaticFrame:                                frame0        -> frame1
         // innerDynamicFrame:                                                frame2 -> frame3
         opener.is(shistory.index, 5, 'shistory.index');
         opener.is(history.length, 6, 'history.length');
         window.location = 'file_bug1326251_evict_cache.html';
       },
       async function() {
-        let windowWrap = SpecialPowers.wrap(window)
-                         .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor);
-        let docShell = windowWrap.getInterface(SpecialPowers.Ci.nsIDocShell);
-        let shistory = windowWrap.getInterface(SpecialPowers.Ci.nsIWebNavigation)
-                                 .sessionHistory;
+        let windowWrap = SpecialPowers.wrap(window);
+        let docShell = windowWrap.docShell;
+        let shistory = docShell.QueryInterface(SpecialPowers.Ci.nsIWebNavigation)
+                               .sessionHistory;
         // staticFrame:       frame0 -> frame1 -> frame2 -> iframe_static
         // innerStaticFrame:                                frame0        -> frame1
         opener.is(docShell.previousTransIndex, 5, 'docShell.previousTransIndex');
         opener.is(docShell.loadedTransIndex, 4, 'docShell.loadedTransIndex');
         opener.is(shistory.index, 4, 'shistory.index');
         opener.is(history.length, 6, 'history.length');
         let staticFrame = document.getElementById('staticFrame');
         let innerStaticFrame = staticFrame.contentDocument.getElementById('staticFrame');
--- a/docshell/test/navigation/file_bug1326251_evict_cache.html
+++ b/docshell/test/navigation/file_bug1326251_evict_cache.html
@@ -4,18 +4,18 @@
     <meta charset="utf-8">
     <title>Bug 1326251</title>
     <script>
     SpecialPowers.Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
 
     // Evict bfcache and then go back.
     async function evictCache() {
       let shistory = SpecialPowers.wrap(window)
-                       .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
-                       .getInterface(SpecialPowers.Ci.nsIWebNavigation)
+                       .docShell
+                       .QueryInterface(SpecialPowers.Ci.nsIWebNavigation)
                        .sessionHistory;
       let shPrivate = shistory.legacySHistory.QueryInterface(SpecialPowers.Ci.nsISHistoryInternal);
       shPrivate.evictAllContentViewers();
       history.back();
     }
     </script>
   </head>
   <body onload="setTimeout(evictCache, 0);">
--- a/docshell/test/navigation/test_bug1375833.html
+++ b/docshell/test/navigation/test_bug1375833.html
@@ -24,18 +24,18 @@ https://bugzilla.mozilla.org/show_bug.cg
   let webNav, shistory;
   let frameDocShellId;
   window.addEventListener("message", e => {
     switch (count++) {
     case 0:
       ok(e.data.endsWith("file_bug1375833-frame2.html"), "check location");
 
       webNav = SpecialPowers.wrap(testWin)
-               .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
-               .getInterface(SpecialPowers.Ci.nsIWebNavigation);
+               .docShell
+               .QueryInterface(SpecialPowers.Ci.nsIWebNavigation);
       shistory = webNav.sessionHistory;
       is(shistory.count, 2, "check history length");
       is(shistory.index, 1, "check history index");
 
       frameDocShellId = String(getFrameDocShell().historyID);
       ok(frameDocShellId, "sanity check for docshell ID");
 
       testWin.location.reload();
@@ -77,19 +77,17 @@ https://bugzilla.mozilla.org/show_bug.cg
       is(shistory.index, 0, "check history index");
 
       testWin.close();
       SimpleTest.finish();
     }
   });
 
   function getFrameDocShell() {
-    return SpecialPowers.wrap(testWin.window[0])
-           .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
-           .getInterface(SpecialPowers.Ci.nsIDocShell)
+    return SpecialPowers.wrap(testWin.window[0]).docShell;
   }
 
   </script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1375833">Mozilla Bug 1375833</a>
 <p id="display"></p>
 <div id="content" style="display: none">
--- a/docshell/test/test_bug509055.html
+++ b/docshell/test/test_bug509055.html
@@ -66,18 +66,18 @@ function* runTest() {
   popup.document.title = "Changed";
 
   // Wait for listeners to be notified of the title change.
   shortWait();
   dump('Got second hashchange.  Spinning event loop.\n');
   yield undefined;
 
   var sh = SpecialPowers.wrap(popup)
-                        .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
-                        .getInterface(SpecialPowers.Ci.nsIWebNavigation)
+                        .docShell
+                        .QueryInterface(SpecialPowers.Ci.nsIWebNavigation)
                         .sessionHistory;
 
   // Get the title of the inner popup's current SHEntry
   var sheTitle = sh.legacySHistory.getEntryAtIndex(sh.index, false).title;
   is(sheTitle, "Changed", "SHEntry's title should change when we change.");
 
   popup.close();
 
--- a/docshell/test/test_bug590573.html
+++ b/docshell/test/test_bug590573.html
@@ -91,18 +91,18 @@ function page2PageShow()
   }
   else {
     dump('Ignoring page2 pageshow.\n');
   }
 }
 
 function dumpSHistory(theWindow)
 {
-  let sh = SpecialPowers.wrap(theWindow).QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
-                    .getInterface(SpecialPowers.Ci.nsIWebNavigation)
+  let sh = SpecialPowers.wrap(theWindow).docShell
+                    .QueryInterface(SpecialPowers.Ci.nsIWebNavigation)
                     .sessionHistory;
   if (!sh) {
     dump(" window has no shistory.\n");
     return;
   }
 
   dump(" count: " + sh.count + "\n");
   dump(" index: " + sh.index + "\n");
--- a/dom/animation/test/mozilla/file_restyles.html
+++ b/dom/animation/test/mozilla/file_restyles.html
@@ -55,21 +55,17 @@ body {
 }
 </style>
 </head>
 <body>
 <script>
 'use strict';
 
 function getDocShellForObservingRestylesForWindow(aWindow) {
-  const docShell =
-    SpecialPowers.wrap(aWindow)
-                 .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
-                 .getInterface(SpecialPowers.Ci.nsIWebNavigation)
-                 .QueryInterface(SpecialPowers.Ci.nsIDocShell);
+  const docShell = SpecialPowers.wrap(aWindow).docShell;
 
   docShell.recordProfileTimelineMarkers = true;
   docShell.popProfileTimelineMarkers();
 
   return docShell;
 }
 
 // Returns the animation restyle markers observed during |frameCount| refresh
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -4221,141 +4221,16 @@ nsGlobalWindowInner::GetRealFrameElement
  * around GetRealFrameElement.
  */
 Element*
 nsGlobalWindowInner::GetFrameElement()
 {
   return GetRealFrameElement(IgnoreErrors());
 }
 
-/* static */ bool
-nsGlobalWindowInner::TokenizeDialogOptions(nsAString& aToken,
-                                           nsAString::const_iterator& aIter,
-                                           nsAString::const_iterator aEnd)
-{
-  while (aIter != aEnd && nsCRT::IsAsciiSpace(*aIter)) {
-    ++aIter;
-  }
-
-  if (aIter == aEnd) {
-    return false;
-  }
-
-  if (*aIter == ';' || *aIter == ':' || *aIter == '=') {
-    aToken.Assign(*aIter);
-    ++aIter;
-    return true;
-  }
-
-  nsAString::const_iterator start = aIter;
-
-  // Skip characters until we find whitespace, ';', ':', or '='
-  while (aIter != aEnd && !nsCRT::IsAsciiSpace(*aIter) &&
-         *aIter != ';' &&
-         *aIter != ':' &&
-         *aIter != '=') {
-    ++aIter;
-  }
-
-  aToken.Assign(Substring(start, aIter));
-  return true;
-}
-
-// Helper for converting window.showModalDialog() options (list of ';'
-// separated name (:|=) value pairs) to a format that's parsable by
-// our normal window opening code.
-
-/* static */
-void
-nsGlobalWindowInner::ConvertDialogOptions(const nsAString& aOptions,
-                                          nsAString& aResult)
-{
-  nsAString::const_iterator end;
-  aOptions.EndReading(end);
-
-  nsAString::const_iterator iter;
-  aOptions.BeginReading(iter);
-
-  nsAutoString token;
-  nsAutoString name;
-  nsAutoString value;
-
-  while (true) {
-    if (!TokenizeDialogOptions(name, iter, end)) {
-      break;
-    }
-
-    // Invalid name.
-    if (name.EqualsLiteral("=") ||
-        name.EqualsLiteral(":") ||
-        name.EqualsLiteral(";")) {
-      break;
-    }
-
-    if (!TokenizeDialogOptions(token, iter, end)) {
-      break;
-    }
-
-    if (!token.EqualsLiteral(":") && !token.EqualsLiteral("=")) {
-      continue;
-    }
-
-    // We found name followed by ':' or '='. Look for a value.
-    if (!TokenizeDialogOptions(value, iter, end)) {
-      break;
-    }
-
-    if (name.LowerCaseEqualsLiteral("center")) {
-      if (value.LowerCaseEqualsLiteral("on")  ||
-          value.LowerCaseEqualsLiteral("yes") ||
-          value.LowerCaseEqualsLiteral("1")) {
-        aResult.AppendLiteral(",centerscreen=1");
-      }
-    } else if (name.LowerCaseEqualsLiteral("dialogwidth")) {
-      if (!value.IsEmpty()) {
-        aResult.AppendLiteral(",width=");
-        aResult.Append(value);
-      }
-    } else if (name.LowerCaseEqualsLiteral("dialogheight")) {
-      if (!value.IsEmpty()) {
-        aResult.AppendLiteral(",height=");
-        aResult.Append(value);
-      }
-    } else if (name.LowerCaseEqualsLiteral("dialogtop")) {
-      if (!value.IsEmpty()) {
-        aResult.AppendLiteral(",top=");
-        aResult.Append(value);
-      }
-    } else if (name.LowerCaseEqualsLiteral("dialogleft")) {
-      if (!value.IsEmpty()) {
-        aResult.AppendLiteral(",left=");
-        aResult.Append(value);
-      }
-    } else if (name.LowerCaseEqualsLiteral("resizable")) {
-      if (value.LowerCaseEqualsLiteral("on")  ||
-          value.LowerCaseEqualsLiteral("yes") ||
-          value.LowerCaseEqualsLiteral("1")) {
-        aResult.AppendLiteral(",resizable=1");
-      }
-    } else if (name.LowerCaseEqualsLiteral("scroll")) {
-      if (value.LowerCaseEqualsLiteral("off")  ||
-          value.LowerCaseEqualsLiteral("no") ||
-          value.LowerCaseEqualsLiteral("0")) {
-        aResult.AppendLiteral(",scrollbars=0");
-      }
-    }
-
-    if (iter == end ||
-        !TokenizeDialogOptions(token, iter, end) ||
-        !token.EqualsLiteral(";")) {
-      break;
-    }
-  }
-}
-
 void
 nsGlobalWindowInner::UpdateCommands(const nsAString& anAction,
                                     Selection* aSel,
                                     int16_t aReason)
 {
   if (GetOuterWindowInternal()) {
     GetOuterWindowInternal()->UpdateCommands(anAction, aSel, aReason);
   }
--- a/dom/base/nsGlobalWindowInner.h
+++ b/dom/base/nsGlobalWindowInner.h
@@ -694,23 +694,16 @@ public:
 
   // https://w3c.github.io/webappsec-secure-contexts/#dom-window-issecurecontext
   bool IsSecureContext() const;
 
   void GetSidebar(mozilla::dom::OwningExternalOrWindowProxy& aResult,
                   mozilla::ErrorResult& aRv);
   already_AddRefed<mozilla::dom::External> GetExternal(mozilla::ErrorResult& aRv);
 
-  // Exposed only for testing
-  static bool
-  TokenizeDialogOptions(nsAString& aToken, nsAString::const_iterator& aIter,
-                        nsAString::const_iterator aEnd);
-  static void
-  ConvertDialogOptions(const nsAString& aOptions, nsAString& aResult);
-
   mozilla::dom::Worklet*
   GetPaintWorklet(mozilla::ErrorResult& aRv);
 
   void
   GetRegionalPrefsLocales(nsTArray<nsString>& aLocales);
 
   mozilla::dom::IntlUtils*
   GetIntlUtils(mozilla::ErrorResult& aRv);
--- a/dom/base/nsIMessageManager.idl
+++ b/dom/base/nsIMessageManager.idl
@@ -2,24 +2,24 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
 interface nsIContent;
 
-[shim(MessageSender), shimfile(MessageManager),
- uuid(bb5d79e4-e73c-45e7-9651-4d718f4b994c)]
+// NOTE: Only scriptable for Ci.nsIMessageSender
+[scriptable, builtinclass, uuid(bb5d79e4-e73c-45e7-9651-4d718f4b994c)]
 interface nsIMessageSender : nsISupports
 {
 };
 
-[shim(ContentFrameMessageManager), shimfile(MessageManager),
- uuid(694e367c-aa25-4446-8499-2c527c4bd838)]
+// NOTE: Only scriptable for Ci.nsIContentFrameMessageManager
+[scriptable, builtinclass, uuid(694e367c-aa25-4446-8499-2c527c4bd838)]
 interface nsIContentFrameMessageManager : nsIMessageSender
 {
 };
 
 [uuid(b39a3324-b574-4f85-8cdb-274d04f807ef)]
 interface nsIInProcessContentFrameMessageManager : nsIContentFrameMessageManager
 {
   [notxpcom] nsIContent getOwnerContent();
--- a/dom/base/test/browser_promiseDocumentFlushed.js
+++ b/dom/base/test/browser_promiseDocumentFlushed.js
@@ -119,19 +119,17 @@ add_task(async function test_can_get_res
       reflow() {
         Assert.ok(false, "A reflow should not have occurred.");
       },
       reflowInterruptible() {},
       QueryInterface: ChromeUtils.generateQI([Ci.nsIReflowObserver,
                                               Ci.nsISupportsWeakReference])
     };
 
-    let docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsIWebNavigation)
-                         .QueryInterface(Ci.nsIDocShell);
+    let docShell = window.docShell;
     docShell.addWeakReflowObserver(observer);
 
     let toolboxRect = gNavToolbox.getBoundingClientRect();
 
     docShell.removeWeakReflowObserver(observer);
     return toolboxRect;
   });
 
@@ -152,18 +150,17 @@ add_task(async function test_can_get_res
  * tick, the promiseDocumentFlushed Promise is still resolved, and
  * the callback is still called.
  */
 add_task(async function test_resolved_in_window_close() {
   let win = await BrowserTestUtils.openNewBrowserWindow();
 
   await win.promiseDocumentFlushed(() => {});
 
-  let docShell = win.QueryInterface(Ci.nsIInterfaceRequestor)
-                    .getInterface(Ci.nsIDocShell);
+  let docShell = win.docShell;
   docShell.contentViewer.pausePainting();
 
   win.gNavToolbox.style.padding = "5px";
 
   const EXPECTED = 1234;
   let promise = win.promiseDocumentFlushed(() => {
     // Despite the window not painting before closing, this
     // callback should be fired when the window gets torn
--- a/dom/base/test/chrome/file_bug1209621.xul
+++ b/dom/base/test/chrome/file_bug1209621.xul
@@ -15,17 +15,17 @@ https://bugzilla.mozilla.org/show_bug.cg
     opener.wrappedJSObject.ok(cond, msg);
   }
 
   function is(actual, expected, msg) {
     opener.wrappedJSObject.is(actual, expected, msg);
   }
 
   function run() {
-    var docshell = window.getInterface(Ci.nsIDocShell);
+    var docshell = window.docShell;
     ok(docshell, "Active window should have a DocShell");
     var treeOwner = docshell.treeOwner;
     ok(treeOwner, "Active docshell should have a TreeOwner!");
 
     is(treeOwner.primaryContentShell, null,
        "There shouldn't be primaryContentShell because no browser has primary=true.");
     is(treeOwner.primaryTabParent, null,
        "There shouldn't be primaryTabParent because no remote browser has primary=true.");
--- a/dom/base/test/chrome/file_bug549682.xul
+++ b/dom/base/test/chrome/file_bug549682.xul
@@ -135,21 +135,19 @@ https://bugzilla.mozilla.org/show_bug.cg
       sendAsyncMessage('weak', {});\
       sendAsyncMessage('weak', {});\
       sendAsyncMessage('weakdone', {});", false);
   }
 
   function run() {
     var localmm = document.getElementById('ifr').messageManager;
 
-    var wn = document.getElementById('ifr').contentWindow
-      .getInterface(Ci.nsIWebNavigation)
-      .QueryInterface(Ci.nsIInterfaceRequestor);
-    ok(wn, "Should have webnavigation");
-    var cfmm = wn.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIContentFrameMessageManager);
+    var docShell = document.getElementById('ifr').contentWindow.docShell;
+    ok(docShell, "Should have docshell");
+    var cfmm = docShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIContentFrameMessageManager);
     ok(cfmm, "Should have content messageManager");
 
     var didGetSyncMessage = false;
     function syncContinueTestFn() {
       didGetSyncMessage = true;
     }
     localmm.addMessageListener("syncContinueTest", syncContinueTestFn);
     cfmm.sendSyncMessage("syncContinueTest", {});
--- a/dom/base/test/chrome/test_bug1339722.html
+++ b/dom/base/test/chrome/test_bug1339722.html
@@ -29,19 +29,17 @@ https://bugzilla.mozilla.org/show_bug.cg
       Services.obs.removeObserver(this, TOPIC);
 
       // Query window proxy so it triggers DOMWindowCreated.
       let channel = subject.QueryInterface(Ci.nsIHttpChannel);
       let win = channel.notificationCallbacks.getInterface(Ci.mozIDOMWindowProxy);
     }
   }, TOPIC);
 
-  let docShell = SpecialPowers.wrap(window)
-                              .QueryInterface(Ci.nsIInterfaceRequestor)
-                              .getInterface(Ci.nsIDocShell);
+  let docShell = SpecialPowers.wrap(window).docShell;
   docShell.chromeEventHandler.addEventListener('DOMWindowCreated', function handler(e) {
     docShell.chromeEventHandler.removeEventListener('DOMWindowCreated', handler);
     let iframe = document.getElementById('testFrame');
     is(e.target, iframe.contentDocument, 'verify event target');
 
     // Remove the iframe to cause frameloader destroy.
     iframe.remove();
     setTimeout($ => {
--- a/dom/base/test/copypaste.js
+++ b/dom/base/test/copypaste.js
@@ -19,29 +19,25 @@ function modifySelection(s) {
       e.remove();
       g.removeAllRanges();
       g.addRange(l);
   }, 0)
 }
 
 function getLoadContext() {
   var Ci = SpecialPowers.Ci;
-  return SpecialPowers.wrap(window).QueryInterface(Ci.nsIInterfaceRequestor)
-                                   .getInterface(Ci.nsIWebNavigation)
+  return SpecialPowers.wrap(window).docShell
                                    .QueryInterface(Ci.nsILoadContext);
 }
 
 async function testCopyPaste (isXHTML) {
   var suppressUnicodeCheckIfHidden = !!isXHTML;
   var suppressHTMLCheck = !!isXHTML;
 
-  var webnav = SpecialPowers.wrap(window).QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
-                     .getInterface(SpecialPowers.Ci.nsIWebNavigation)
-
-  var docShell = webnav.QueryInterface(SpecialPowers.Ci.nsIDocShell);
+  var docShell = SpecialPowers.wrap(window).docShell;
 
   var documentViewer = docShell.contentViewer
                                .QueryInterface(SpecialPowers.Ci.nsIContentViewerEdit);
 
   var clipboard = SpecialPowers.Services.clipboard;
 
   var textarea = SpecialPowers.wrap(document.getElementById('input'));
 
deleted file mode 100644
--- a/dom/base/test/gtest/TestParserDialogOptions.cpp
+++ /dev/null
@@ -1,140 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "gtest/gtest.h"
-#include "nsGlobalWindow.h"
-
-// XXX(nika): I can't find any non-test callers of this code, can we remove it?
-
-struct dialog_test {
-  const char* input;
-  const char* output;
-};
-
-void runTokenizeTest(dialog_test& test)
-{
-  NS_ConvertUTF8toUTF16 input(test.input);
-
-  nsAString::const_iterator end;
-  input.EndReading(end);
-
-  nsAString::const_iterator iter;
-  input.BeginReading(iter);
-
-  nsAutoString result;
-  nsAutoString token;
-
-  while (nsGlobalWindowInner::TokenizeDialogOptions(token, iter, end)) {
-    if (!result.IsEmpty()) {
-      result.Append(',');
-    }
-
-    result.Append(token);
-  }
-
-  ASSERT_STREQ(test.output, NS_ConvertUTF16toUTF8(result).get()) << "Testing " << test.input;
-}
-
-void runTest(dialog_test& test)
-{
-  NS_ConvertUTF8toUTF16 input(test.input);
-
-  nsAutoString result;
-  nsGlobalWindowInner::ConvertDialogOptions(input, result);
-
-  ASSERT_STREQ(test.output, NS_ConvertUTF16toUTF8(result).get()) << "Testing " << test.input;
-}
-
-TEST(GlobalWindowDialogOptions, TestDialogTokenize)
-{
-  dialog_test tests[] = {
-    /// Empty strings
-    { "", "" },
-    { " ", "" },
-    { "   ", "" },
-
-    // 1 token
-    { "a", "a" },
-    { " a", "a" },
-    { "  a  ", "a" },
-    { "aa", "aa" },
-    { " aa", "aa" },
-    { "  aa  ", "aa" },
-    { ";", ";" },
-    { ":", ":" },
-    { "=", "=" },
-
-    // 2 tokens
-    { "a=", "a,=" },
-    { "  a=  ", "a,=" },
-    { "  a  =  ", "a,=" },
-    { "aa=", "aa,=" },
-    { "  aa=  ", "aa,=" },
-    { "  aa  =  ", "aa,=" },
-    { ";= ", ";,=" },
-    { "==", "=,=" },
-    { "::", ":,:" },
-
-    // 3 tokens
-    { "a=2", "a,=,2" },
-    { "===", "=,=,=" },
-    { ";:=", ";,:,=" },
-
-    // more
-    { "aaa;bbb:ccc", "aaa,;,bbb,:,ccc" },
-
-    // sentinel
-    { nullptr, nullptr }
-  };
-
-  for (uint32_t i = 0; tests[i].input; ++i) {
-    runTokenizeTest(tests[i]);
-  }
-}
-TEST(GlobalWindowDialogOptions, TestDialogOptions)
-{
-  dialog_test tests[] = {
-    /// Empty strings
-    { "", "" },
-    { " ", "" },
-    { "   ", "" },
-
-    // Name without params
-    { "a", "" },
-    { " a", "" },
-    { "  a  ", "" },
-    { "a=", "" },
-    { "  a=  ", "" },
-    { "  a  =  ", "" },
-
-    // 1 unknown value
-    { "a=2", "" },
-    { " a=2 ", "" },
-    { "  a  =  2 ", "" },
-    { "a:2", "" },
-    { " a:2 ", "" },
-    { "  a  :  2 ", "" },
-
-    // 1 known value, wrong value
-    { "center=2", "" },
-    { "center:2", "" },
-
-    // 1 known value, good value
-    { "center=on", ",centerscreen=1" },
-    { "center:on", ",centerscreen=1" },
-    { " center : on ", ",centerscreen=1" },
-
-    // nonsense stuff
-    { " ; ", "" },
-
-    // sentinel
-    { nullptr, nullptr }
-  };
-
-  for (uint32_t i = 0; tests[i].input; ++i) {
-    runTest(tests[i]);
-  }
-}
--- a/dom/base/test/gtest/moz.build
+++ b/dom/base/test/gtest/moz.build
@@ -1,16 +1,15 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, you can obtain one at http://mozilla.org/MPL/2.0/.
 
 UNIFIED_SOURCES += [
-    'TestParserDialogOptions.cpp',
     'TestPlainTextSerializer.cpp',
     'TestXPathGenerator.cpp',
 ]
 
 LOCAL_INCLUDES += [
     '/dom/base'
 ]
 
--- a/dom/base/test/test_bug1101364.html
+++ b/dom/base/test/test_bug1101364.html
@@ -25,39 +25,34 @@ https://bugzilla.mozilla.org/show_bug.cg
 <iframe id="test2" srcdoc="<div contenteditable id='test2'>AAA<span id='test2Inner'>BBB</span></div>"></iframe>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 function test()
 {
   var iframe1 = document.getElementById('test1');
   iframe1.focus();
-  var Ci = SpecialPowers.Ci;
-  var webnav = SpecialPowers.wrap(iframe1.contentWindow).QueryInterface(Ci.nsIInterfaceRequestor)
-                                                        .getInterface(Ci.nsIWebNavigation)
-  var docShell = webnav.QueryInterface(Ci.nsIDocShell);
+  var docShell = SpecialPowers.wrap(iframe1.contentWindow).docShell;
 
   // test1
   docShell.doCommand("cmd_selectAll");
   var withoutContenteditable = snapshotWindow(iframe1.contentWindow);
 
   iframe1.contentDocument.getElementById('testDiv').setAttribute('contentEditable', true);
   docShell.doCommand("cmd_selectAll");
   var withContenteditable = snapshotWindow(iframe1.contentWindow);
   dump(withoutContenteditable.toDataURL());
   dump(withContenteditable.toDataURL());
 
   ok(compareSnapshots(withoutContenteditable, withContenteditable, true)[0], 'Select all should look identical');
 
   // test2
   var iframe2 = document.getElementById('test2');
   iframe2.focus();
-  var webnav = SpecialPowers.wrap(iframe2.contentWindow).QueryInterface(Ci.nsIInterfaceRequestor)
-                                                        .getInterface(Ci.nsIWebNavigation)
-  var docShell = webnav.QueryInterface(Ci.nsIDocShell);
+  var docShell = SpecialPowers.wrap(iframe2.contentWindow).docShell;
   var test2Inner = iframe2.contentDocument.getElementById('test2Inner');
   test2Inner.style.MozUserSelect = 'text';
   docShell.doCommand("cmd_selectAll");
   var withoutUserSelect = snapshotWindow(iframe2.contentWindow);
 
   test2Inner.style.MozUserSelect = 'none';
   docShell.doCommand("cmd_selectAll");
   var withUserSelect = snapshotWindow(iframe2.contentWindow);
--- a/dom/base/test/test_bug166235.html
+++ b/dom/base/test/test_bug166235.html
@@ -24,32 +24,28 @@ https://bugzilla.mozilla.org/show_bug.cg
 <pre id="test">
 <script type="application/javascript">
   "use strict";
 
 /** Test for Bug 166235 **/
   var Cc = SpecialPowers.Cc;
   var Ci = SpecialPowers.Ci;
 
-  var webnav = SpecialPowers.wrap(window).QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
-                                         .getInterface(SpecialPowers.Ci.nsIWebNavigation)
-
-  var docShell = webnav.QueryInterface(SpecialPowers.Ci.nsIDocShell);
+  var docShell = SpecialPowers.wrap(window).docShell;
 
   var documentViewer = docShell.contentViewer
                                .QueryInterface(SpecialPowers.Ci.nsIContentViewerEdit);
 
   var clipboard = Cc["@mozilla.org/widget/clipboard;1"]
                     .getService(SpecialPowers.Ci.nsIClipboard);
 
   var textarea = SpecialPowers.wrap(document.getElementById('input'));
 
   function getLoadContext() {
-    return SpecialPowers.wrap(window).QueryInterface(Ci.nsIInterfaceRequestor)
-                                     .getInterface(Ci.nsIWebNavigation)
+    return SpecialPowers.wrap(window).docShell
                                      .QueryInterface(Ci.nsILoadContext);
   }
 
   function copyChildrenToClipboard(id) {
     textarea.blur();
     clipboard.emptyClipboard(1);
     window.getSelection().selectAllChildren(document.getElementById(id));
     documentViewer.copySelection();
--- a/dom/base/test/test_copyimage.html
+++ b/dom/base/test/test_copyimage.html
@@ -21,18 +21,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 function testCopyImage () {
   var Ci = SpecialPowers.Ci;
   var Cc = SpecialPowers.Cc;
   var clipboard = SpecialPowers.Services.clipboard;
 
   function getClipboardData(mime) {
     var transferable = Cc['@mozilla.org/widget/transferable;1']
                        .createInstance(Ci.nsITransferable);
-    var loadingContext = SpecialPowers.wrap(window).QueryInterface(Ci.nsIInterfaceRequestor)
-                                      .getInterface(Ci.nsIWebNavigation)
+    var loadingContext = SpecialPowers.wrap(window).docShell
                                       .QueryInterface(Ci.nsILoadContext);
     transferable.init(loadingContext);
     transferable.addDataFlavor(mime);
     clipboard.getData(transferable, 1);
     var data = SpecialPowers.createBlankObject();
     transferable.getTransferData(mime, data, {});
     return data;
   }
@@ -45,20 +44,17 @@ function testCopyImage () {
   }
 
   //--------- Prepare data and copy it.
 
   // Select the node.
   var node = document.getElementById('logo');
 
   // Set node and copy image.
-  var webnav = SpecialPowers.wrap(window)
-               .QueryInterface(Ci.nsIInterfaceRequestor)
-               .getInterface(Ci.nsIWebNavigation)
-  var docShell = webnav.QueryInterface(Ci.nsIDocShell);
+  var docShell = SpecialPowers.wrap(window).docShell;
   var documentViewer = docShell.contentViewer
                                .QueryInterface(Ci.nsIContentViewerEdit);
   documentViewer.setCommandNode(node);
   documentViewer.copyImage(documentViewer.COPY_IMAGE_ALL);
 
   //--------- Let's check the content of the clipboard now.
 
   // Does the clipboard contain text/unicode data ?
--- a/dom/base/test/test_copypaste.xul
+++ b/dom/base/test/test_copypaste.xul
@@ -14,19 +14,17 @@ SimpleTest.waitForExplicitFinish();
 addLoadEvent(runTest);
 
 function runTest() {
   let desc = document.querySelector("description");
   window.getSelection().selectAllChildren(desc);
 
   let expected = "\n    hello\n    world\n  ";
   SimpleTest.waitForClipboard(expected, function() {
-    let webnav = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                       .getInterface(Ci.nsIWebNavigation);
-    webnav.QueryInterface(Ci.nsIDocShell)
+    window.docShell
           .contentViewer
           .QueryInterface(Ci.nsIContentViewerEdit)
           .copySelection();
   }, function() {
     ok(true, "Paste is not HTML, so it should not be pretty printed");
     SimpleTest.finish();
   });
 }
--- a/dom/base/test/test_postMessage_originAttributes.html
+++ b/dom/base/test/test_postMessage_originAttributes.html
@@ -15,18 +15,17 @@ add_task(async function() {
   await SpecialPowers.pushPrefEnv(
     { "set": [["network.disable.ipc.security", true]] });
 });
 
 add_task(async function() {
   let iframe = document.querySelector("#target-iframe");
 
   let win = SpecialPowers.wrap(iframe).contentWindow;
-  let docShell = win.QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
-                    .getInterface(SpecialPowers.Ci.nsIDocShell);
+  let docShell = win.docShell;
 
   // Add private browsing ID to docShell origin and load document.
   docShell.setOriginAttributes({privateBrowsingId: 1});
 
   await new Promise(resolve => {
     iframe.addEventListener("load", resolve, true);
 
     iframe.src = SimpleTest.getTestFileURL("file_receiveMessage.html");
--- a/dom/browser-element/BrowserElementChildPreload.js
+++ b/dom/browser-element/BrowserElementChildPreload.js
@@ -737,19 +737,17 @@ BrowserElementChild.prototype = {
     debug("Closing window " + win);
     sendAsyncMsg('close');
 
     // Inform the window implementation that we handled this close ourselves.
     e.preventDefault();
   },
 
   _windowCreatedHandler: function(e) {
-    let targetDocShell = e.target.defaultView
-          .QueryInterface(Ci.nsIInterfaceRequestor)
-          .getInterface(Ci.nsIWebNavigation);
+    let targetDocShell = e.target.defaultView.docShell;
     if (targetDocShell != docShell) {
       return;
     }
 
     let uri = docShell.QueryInterface(Ci.nsIWebNavigation).currentURI;
     debug("Window created: " + uri.spec);
     if (uri.spec != "about:blank") {
       this._addMozAfterPaintHandler(function () {
--- a/dom/browser-element/BrowserElementCopyPaste.js
+++ b/dom/browser-element/BrowserElementCopyPaste.js
@@ -103,19 +103,17 @@ var CopyPasteAssistent = {
     while (currentWindow.realFrameElement) {
       let currentRect = currentWindow.realFrameElement.getBoundingClientRect();
       detail.rect.top += currentRect.top;
       detail.rect.bottom += currentRect.top;
       detail.rect.left += currentRect.left;
       detail.rect.right += currentRect.left;
       currentWindow = currentWindow.realFrameElement.ownerGlobal;
 
-      let targetDocShell = currentWindow
-          .QueryInterface(Ci.nsIInterfaceRequestor)
-          .getInterface(Ci.nsIWebNavigation);
+      let targetDocShell = currentWindow.docShell;
       if(targetDocShell.isMozBrowser) {
         break;
       }
     }
 
     sendAsyncMsg('caretstatechanged', detail);
   },
 };
--- a/dom/events/test/test_bug1412775.xul
+++ b/dom/events/test/test_bug1412775.xul
@@ -25,18 +25,17 @@ https://bugzilla.mozilla.org/show_bug.cg
       var b = win.document.getElementById("browser");
       var d = b.contentWindow.document;
       var e = new d.defaultView.Event("foo");
       var didCallChromeSide = false;
       var didCallContentSide = false;
       b.addEventListener("foo", function(e) {
         didCallChromeSide = true;
         var path = e.composedPath();
-        var mm = d.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
-                              .getInterface(Ci.nsIWebNavigation)
+        var mm = d.defaultView.docShell
                               .QueryInterface(Ci.nsIInterfaceRequestor)
                               .getInterface(Ci.nsIContentFrameMessageManager);
         is(path.length, 5, "Should have 5 items in composedPath in chrome.");
         is(path[0], mm, "TabChildGlobal is the chrome handler.");
         is(path[1], b, "browser element should be in the path.");
         is(path[2], b.parentNode, "window element should be in the path.");
         is(path[3], win.document, "Document object should be in the path.");
         is(path[4], win, "Window object should be in the path.");
--- a/dom/events/test/test_paste_image.html
+++ b/dom/events/test/test_paste_image.html
@@ -66,21 +66,17 @@
       canvas.getContext('2d').drawImage(aImage, 0, 0);
       return canvas;
     }
   }
 
   function copyImage(aImageId) {
     // selection of the node
     var node = document.getElementById(aImageId);
-    var webnav = SpecialPowers.wrap(window)
-                 .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
-                 .getInterface(SpecialPowers.Ci.nsIWebNavigation)
-
-    var docShell = webnav.QueryInterface(SpecialPowers.Ci.nsIDocShell);
+    var docShell = SpecialPowers.wrap(window).docShell;
 
     // let's copy the node
     var documentViewer = docShell.contentViewer
                          .QueryInterface(SpecialPowers.Ci.nsIContentViewerEdit);
     documentViewer.setCommandNode(node);
     documentViewer.copyImage(documentViewer.COPY_IMAGE_ALL);
   }
 
--- a/dom/html/test/browser_bug649778.js
+++ b/dom/html/test/browser_bug649778.js
@@ -34,18 +34,18 @@ function checkCache(url, inMemory, shoul
     };
   };
 
   storage.asyncOpenURI(Services.io.newURI(url), "",
                        Ci.nsICacheStorage.OPEN_READONLY,
                        new CheckCacheListener(inMemory, shouldExist));
 }
 function getPopupURL() {
-  var sh = popup.QueryInterface(Ci.nsIInterfaceRequestor)
-                .getInterface(Ci.nsIWebNavigation)
+  var sh = popup.docShell
+                .QueryInterface(Ci.nsIWebNavigation)
                 .sessionHistory;
 
   return sh.legacySHistory.getEntryAtIndex(sh.index, false).URI.spec;
 }
 
 var wyciwygURL;
 function testContinue() {
   wyciwygURL = getPopupURL();
--- a/dom/html/test/test_bug460568.html
+++ b/dom/html/test/test_bug460568.html
@@ -31,20 +31,17 @@ function runTest()
 
   function isReallyEditable()
   {
     editor.focus();
     var range = document.createRange();
     range.selectNodeContents(editor);
     var prevStr = range.toString();
 
-    var docShell = SpecialPowers.wrap(window)
-            .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
-            .getInterface(SpecialPowers.Ci.nsIWebNavigation)
-            .QueryInterface(SpecialPowers.Ci.nsIDocShell);
+    var docShell = SpecialPowers.wrap(window).docShell;
     var controller =
           docShell.QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
                   .getInterface(SpecialPowers.Ci.nsISelectionDisplay)
                   .QueryInterface(SpecialPowers.Ci.nsISelectionController);
     var sel = controller.getSelection(controller.SELECTION_NORMAL);
     sel.collapse(anchorInEditor, 0);
     sendString("a");
     range.selectNodeContents(editor);
--- a/dom/html/test/test_bug512367.html
+++ b/dom/html/test/test_bug512367.html
@@ -19,22 +19,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 var frame = document.getElementById("i");
 
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(function() {
-  var viewer =
-    SpecialPowers.wrap(frame.contentWindow)
-                 .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
-                 .getInterface(SpecialPowers.Ci.nsIWebNavigation)
-                 .QueryInterface(SpecialPowers.Ci.nsIDocShell)
-                 .contentViewer;
+  var viewer = SpecialPowers.wrap(frame.contentWindow).docShell.contentViewer;
 
   viewer.fullZoom = 1.5;
 
   setTimeout(function() {
     synthesizeMouse(frame, 30, 30, {});
 
     is(viewer.fullZoom, 1.5, "Zoom in the image frame should not have been reset");
 
rename from dom/ipc/manifestMessages.js
rename to dom/ipc/ManifestMessages.jsm
--- a/dom/ipc/manifestMessages.js
+++ b/dom/ipc/ManifestMessages.jsm
@@ -6,109 +6,107 @@
  * http://www.w3.org/TR/appmanifest/#obtaining
  *
  * It searches a top-level browsing context for
  * a <link rel=manifest> element. Then fetches
  * and processes the linked manifest.
  *
  * BUG: https://bugzilla.mozilla.org/show_bug.cgi?id=1083410
  */
-/*globals Task, ManifestObtainer, ManifestFinder, content, sendAsyncMessage, addMessageListener, Components*/
 "use strict";
-const {
-  utils: Cu,
-} = Components;
+
+var EXPORTED_SYMBOLS = ["ManifestMessages"];
+
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 ChromeUtils.defineModuleGetter(this, "ManifestObtainer",
-				  "resource://gre/modules/ManifestObtainer.jsm");
+                               "resource://gre/modules/ManifestObtainer.jsm");
 ChromeUtils.defineModuleGetter(this, "ManifestFinder",
-				  "resource://gre/modules/ManifestFinder.jsm");
+                               "resource://gre/modules/ManifestFinder.jsm");
 ChromeUtils.defineModuleGetter(this, "ManifestIcons",
-				  "resource://gre/modules/ManifestIcons.jsm");
+                               "resource://gre/modules/ManifestIcons.jsm");
+
+class ManifestMessages {
+  constructor(mm) {
+    this.mm = mm;
+  }
 
-const MessageHandler = {
-  registerListeners() {
-    addMessageListener(
-      "DOM:WebManifest:hasManifestLink",
-      this.hasManifestLink.bind(this)
-    );
-    addMessageListener(
-      "DOM:ManifestObtainer:Obtain",
-      this.obtainManifest.bind(this)
-    );
-    addMessageListener(
-      "DOM:Manifest:FireAppInstalledEvent",
-      this.fireAppInstalledEvent.bind(this)
-    );
-    addMessageListener(
-      "DOM:WebManifest:fetchIcon",
-      this.fetchIcon.bind(this)
-    );
-  },
+  receiveMessage(message) {
+    switch (message.name) {
+    case "DOM:WebManifest:hasManifestLink":
+      return this.hasManifestLink(message);
+    case "DOM:ManifestObtainer:Obtain":
+      return this.obtainManifest(message);
+    case "DOM:Manifest:FireAppInstalledEvent":
+      return this.fireAppInstalledEvent(message);
+    case "DOM:WebManifest:fetchIcon":
+      return this.fetchIcon(message);
+    }
+    return undefined;
+  }
 
   /**
-   * Check if the content document includes a link to a web manifest.
+   * Check if the this.mm.content document includes a link to a web manifest.
    * @param {Object} aMsg The IPC message, which is destructured to just
    *                      get the id.
    */
   hasManifestLink({data: {id}}) {
     const response = makeMsgResponse(id);
-    response.result = ManifestFinder.contentHasManifestLink(content);
+    response.result = ManifestFinder.contentHasManifestLink(this.mm.content);
     response.success = true;
-    sendAsyncMessage("DOM:WebManifest:hasManifestLink", response);
-  },
+    this.mm.sendAsyncMessage("DOM:WebManifest:hasManifestLink", response);
+  }
 
   /**
-   * Asynchronously obtains a web manifest from content by using the
+   * Asynchronously obtains a web manifest from this.mm.content by using the
    * ManifestObtainer and messages back the result.
    * @param {Object} aMsg The IPC message, which is destructured to just
    *                      get the id.
    */
   async obtainManifest({data: {id}}) {
     const response = makeMsgResponse(id);
     try {
-      response.result = await ManifestObtainer.contentObtainManifest(content);
+      response.result = await ManifestObtainer.contentObtainManifest(this.mm.content);
       response.success = true;
     } catch (err) {
       response.result = serializeError(err);
     }
-    sendAsyncMessage("DOM:ManifestObtainer:Obtain", response);
-  },
+    this.mm.sendAsyncMessage("DOM:ManifestObtainer:Obtain", response);
+  }
 
-  fireAppInstalledEvent({data: {id}}){
+  fireAppInstalledEvent({data: {id}}) {
     const ev = new Event("appinstalled");
     const response = makeMsgResponse(id);
-    if (!content || content.top !== content) {
+    if (!this.mm.content || this.mm.content.top !== this.mm.content) {
       const msg = "Can only dispatch install event on top-level browsing contexts.";
       response.result = serializeError(new Error(msg));
     } else {
       response.success = true;
-      content.dispatchEvent(ev);
+      this.mm.content.dispatchEvent(ev);
     }
-    sendAsyncMessage("DOM:Manifest:FireAppInstalledEvent", response);
-  },
+    this.mm.sendAsyncMessage("DOM:Manifest:FireAppInstalledEvent", response);
+  }
 
   /**
    * Given a manifest and an expected icon size, ask ManifestIcons
    * to fetch the appropriate icon and send along result
    */
   async fetchIcon({data: {id, manifest, iconSize}}) {
     const response = makeMsgResponse(id);
     try {
       response.result =
-        await ManifestIcons.contentFetchIcon(content, manifest, iconSize);
+        await ManifestIcons.contentFetchIcon(this.mm.content, manifest, iconSize);
       response.success = true;
     } catch (err) {
       response.result = serializeError(err);
     }
-    sendAsyncMessage("DOM:WebManifest:fetchIcon", response);
-  },
+    this.mm.sendAsyncMessage("DOM:WebManifest:fetchIcon", response);
+  }
+}
 
-};
 /**
  * Utility function to Serializes an JS Error, so it can be transferred over
  * the message channel.
  * FIX ME: https://bugzilla.mozilla.org/show_bug.cgi?id=1172586
  * @param  {Error} aError The error to serialize.
  * @return {Object} The serialized object.
  */
 function serializeError(aError) {
@@ -119,16 +117,14 @@ function serializeError(aError) {
     "stack": aError.stack,
     "message": aError.message,
     "name": aError.name
   };
   return clone;
 }
 
 function makeMsgResponse(aId) {
-    return {
-      id: aId,
-      success: false,
-      result: undefined
-    };
-  }
-
-MessageHandler.registerListeners();
+  return {
+    id: aId,
+    success: false,
+    result: undefined
+  };
+}
--- a/dom/ipc/jar.mn
+++ b/dom/ipc/jar.mn
@@ -3,9 +3,8 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 toolkit.jar:
         content/global/test-ipc.xul (test.xul)
         content/global/remote-test-ipc.js (remote-test.js)
         content/global/BrowserElementChild.js (../browser-element/BrowserElementChild.js)
         content/global/BrowserElementChildPreload.js (../browser-element/BrowserElementChildPreload.js)
         content/global/BrowserElementCopyPaste.js (../browser-element/BrowserElementCopyPaste.js)
-        content/global/manifestMessages.js (manifestMessages.js)
--- a/dom/ipc/moz.build
+++ b/dom/ipc/moz.build
@@ -8,16 +8,20 @@ with Files("**"):
     BUG_COMPONENT = ("Core", "DOM: Content Processes")
 
 XPIDL_SOURCES += [
     'nsIHangReport.idl',
 ]
 
 XPIDL_MODULE = 'dom'
 
+EXTRA_JS_MODULES += [
+    'ManifestMessages.jsm',
+]
+
 EXPORTS.mozilla.dom.ipc += [
     'IdType.h',
     'MemMapSnapshot.h',
     'SharedMap.h',
     'SharedMapChangeEvent.h',
     'SharedStringMap.h',
     'StringTable.h',
     'StructuredCloneData.h',
--- a/dom/ipc/remote-test.js
+++ b/dom/ipc/remote-test.js
@@ -8,21 +8,17 @@ dump(content + "\n");
 var cpm = Cc["@mozilla.org/childprocessmessagemanager;1"].getService();
 cpm.addMessageListener("cpm-async",
   function(m) {
     cpm.sendSyncMessage("ppm-sync");
     dump(content.document.documentElement);
     cpm.sendAsyncMessage("ppm-async");
   });
 
-var dshell = content.QueryInterface(Ci.nsIInterfaceRequestor)
-                    .getInterface(Ci.nsIWebNavigation)
-                    .QueryInterface(Ci.nsIDocShellTreeItem)
-                    .rootTreeItem
-                    .QueryInterface(Ci.nsIDocShell);
+var dshell = content.docShell.rootTreeItem.QueryInterface(Ci.nsIDocShell);
 
 
 addEventListener("click",
   function(e) {
     dump(e.target + "\n");
     if (ChromeUtils.getClassName(e.target) === "HTMLAnchorElement" &&
         dshell == docShell) {
       var retval = docShell.QueryInterface(Ci.nsIInterfaceRequestor).
--- a/dom/ipc/tests/test_child_docshell.html
+++ b/dom/ipc/tests/test_child_docshell.html
@@ -61,19 +61,17 @@ SpecialPowers.pushPrefEnv({'set':[
       by creating a new window and listening for its DOMWindowCreated event
     */
     chromeEventHandler.addEventListener("DOMWindowCreated", function listener(evt) {
       if (evt.target == content.document) {
         return;
       }
       chromeEventHandler.removeEventListener("DOMWindowCreated", listener);
       let new_win = evt.target.defaultView;
-      let new_docShell = new_win.QueryInterface(Ci.nsIInterfaceRequestor)
-                                .getInterface(Ci.nsIWebNavigation)
-                                .QueryInterface(Ci.nsIDocShell);
+      let new_docShell = new_win.docShell;
       sendAsyncMessage("DOMWindowCreatedReceived", {
         stableChromeEventHandler: chromeEventHandler === docShell.chromeEventHandler,
         iframeHasNewDocShell: new_docShell !== docShell,
         iframeHasSameChromeEventHandler: new_docShell.chromeEventHandler === chromeEventHandler
       });
     });
 
     function go() {
--- a/dom/serviceworkers/test/test_devtools_bypass_serviceworker.html
+++ b/dom/serviceworkers/test/test_devtools_bypass_serviceworker.html
@@ -10,16 +10,18 @@
 </head>
 <body>
 <div id="content" style="display: none"></div>
 <script src="utils.js"></script>
 <script type="text/javascript">
 "use strict";
 
 async function testBypassSW () {
+  let Ci = SpecialPowers.Ci;
+
   // Bypass SW imitates the "Disable Cache" option in dev-tools.
   // Note: if we put the setter/getter into dev-tools, we should take care of
   // the implementation of enabling/disabling cache since it just overwrite the
   // defaultLoadFlags of docShell.
   function setBypassServiceWorker(aDocShell, aBypass) {
     if (aBypass) {
       aDocShell.defaultLoadFlags |= Ci.nsIChannel.LOAD_BYPASS_SERVICE_WORKER;
       return;
@@ -50,21 +52,17 @@ async function testBypassSW () {
       // Intercepted
       return true;
     }
 
     throw("Unexpected error");
     return;
   }
 
-  let Ci = SpecialPowers.Ci;
-  let docShell = SpecialPowers.wrap(window)
-                              .QueryInterface(Ci.nsIInterfaceRequestor)
-                              .getInterface(Ci.nsIWebNavigation)
-                              .QueryInterface(Ci.nsIDocShell);
+  let docShell = SpecialPowers.wrap(window).docShell;
 
   info("Test 1: Enable bypass service worker for the docShell");
 
   setBypassServiceWorker(docShell, true);
   ok(getBypassServiceWorker(docShell),
      "The loadFlags in docShell does bypass the serviceWorker by default");
 
   let intercepted = await fetchFakeDocAndCheckIfIntercepted(window);
--- a/dom/tests/mochitest/ajax/offline/460353_iframe_ownmanifest.html
+++ b/dom/tests/mochitest/ajax/offline/460353_iframe_ownmanifest.html
@@ -5,30 +5,16 @@
 <script type="text/javascript">
 
 applicationCache.onerror = function() {
   parent.frameOnUpdate("diff", false);
 }
 
 applicationCache.oncached = function() {
   parent.frameOnUpdate("diff", true, applicationCache.status);
-
-  /* This code tries to figure out what cache is really
-     associated to this document, but hangs on getter2.getInterface
-     from for now unknown reasons. Commenting this out.
-
-  netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
-
-  var getter1 = window.QueryInterface(Ci.nsIInterfaceRequestor);
-  var webnav = getter1.getInterface(Ci.nsIWebNavigation);
-  var getter2 = webnav.QueryInterface(Ci.nsIInterfaceRequestor);
-  var cacheCont = getter2.getInterface(Ci.nsIApplicationCacheContainer);
-  var cache = cacheCont.applicationCache;
-  dump(cache.groupID);
-  */
 }
 
 </script>
 
 </head>
 <body onload="parent.frameOnLoad('diff', applicationCache.status);">
   This is an iframe with a different manifest reference
 </body>
--- a/dom/tests/mochitest/ajax/offline/offlineTests.js
+++ b/dom/tests/mochitest/ajax/offline/offlineTests.js
@@ -301,20 +301,18 @@ manifestURL: function(overload)
             .getService(Ci.nsIIOService)
 
   var baseURI = ios.newURI(window.location.href);
   return ios.newURI(manifestURLspec, null, baseURI);
 },
 
 loadContext: function()
 {
-  return SpecialPowers.wrap(window).QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
-                                   .getInterface(SpecialPowers.Ci.nsIWebNavigation)
-                                   .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
-                                   .getInterface(SpecialPowers.Ci.nsILoadContext);
+  return SpecialPowers.wrap(window).docShell
+                                   .QueryInterface(SpecialPowers.Ci.nsILoadContext);
 },
 
 loadContextInfo: function()
 {
   return LoadContextInfo.fromLoadContext(this.loadContext(), false);
 },
 
 getActiveCache: function(overload)
--- a/dom/tests/mochitest/gamepad/test_gamepad_extensions_iframe.html
+++ b/dom/tests/mochitest/gamepad/test_gamepad_extensions_iframe.html
@@ -106,17 +106,17 @@ function posecheck() {
      "correct gamepadPose linearVelocity");
   is(checkValueInFloat32Array(pose.linearAcceleration, poseLinAcc), true,
      "correct gamepadPose linearAcceleration");
   pressButton();
 }
 
 function setFrameVisible(f, visible) {
   var Ci = SpecialPowers.Ci;
-  var docshell = SpecialPowers.wrap(f.contentWindow).QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation).QueryInterface(Ci.nsIDocShell);
+  var docshell = SpecialPowers.wrap(f.contentWindow).docShell;
   docshell.isActive = visible;
 }
 
 function haptictest() {
   var gamepads = navigator.getGamepads();
   var hapticActuators = gamepads[0].hapticActuators[0];
   hapticActuators.pulse(1, 100).then(function(result) {
     is(result, true, "gamepad hapticActuators test success.");
--- a/dom/tests/mochitest/gamepad/test_gamepad_frame_state_sync_iframe.html
+++ b/dom/tests/mochitest/gamepad/test_gamepad_frame_state_sync_iframe.html
@@ -14,17 +14,17 @@ let is = window.parent.is;
 let SimpleTest = window.parent.SimpleTest;
 let SpecialPowers = window.parent.SpecialPowers;
 
  // Add a gamepad
 var index;
 
 function setFrameVisible(f, visible) {
   var Ci = SpecialPowers.Ci;
-  var docshell = SpecialPowers.wrap(f.contentWindow).QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation).QueryInterface(Ci.nsIDocShell);
+  var docshell = SpecialPowers.wrap(f.contentWindow).docShell;
   docshell.isActive = visible;
 }
 
 var frames_loaded = 0;
 function startTest() {
   frames_loaded++;
   if (frames_loaded == 2) {
     GamepadService.addGamepad("test gamepad", // id
--- a/dom/tests/mochitest/gamepad/test_gamepad_hidden_frame_iframe.html
+++ b/dom/tests/mochitest/gamepad/test_gamepad_hidden_frame_iframe.html
@@ -21,17 +21,17 @@ window.addEventListener("gamepadbuttondo
 
 function pressButton() {
   GamepadService.newButtonEvent(index, 0, true, true);
   GamepadService.newButtonEvent(index, 0, false, false);
 }
 
 function setFrameVisible(f, visible) {
   var Ci = SpecialPowers.Ci;
-  var docshell = SpecialPowers.wrap(f.contentWindow).QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation).QueryInterface(Ci.nsIDocShell);
+  var docshell = SpecialPowers.wrap(f.contentWindow).docShell;
   docshell.isActive = visible;
 }
 
 var frames_loaded = 0;
 function startTest() {
   frames_loaded++;
   if (frames_loaded == 2) {
     GamepadService.addGamepad("test gamepad", // id
--- a/dom/tests/mochitest/general/test_innerScreen.xul
+++ b/dom/tests/mochitest/general/test_innerScreen.xul
@@ -44,17 +44,17 @@ function doTests()
   isRounded(window.mozInnerScreenX*devPxPerCSSPx, windowBO.screenX,
             "window screen X");
   isRounded(window.mozInnerScreenY*devPxPerCSSPx, windowBO.screenY,
             "window screen Y");
 
   var f = document.getElementById("f");
   var fBounds = f.getBoundingClientRect();
 
-  var fshell = f.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation).QueryInterface(Ci.nsIDocShell);
+  var fshell = f.contentWindow.docShell;
   var fmudv = fshell.contentViewer;
 
   isRounded(f.contentWindow.mozInnerScreenX,
             window.mozInnerScreenX + fBounds.left,
             "frame screen X");
   isRounded(f.contentWindow.mozInnerScreenY,
             window.mozInnerScreenY + fBounds.top,
             "frame screen Y");
--- a/dom/tests/mochitest/whatwg/test_bug500328.html
+++ b/dom/tests/mochitest/whatwg/test_bug500328.html
@@ -155,18 +155,18 @@ function popstateExpected(msg) {
 function getColor(elem) {
   var utils = SpecialPowers.getDOMWindowUtils(document.defaultView);
   return utils.getVisitedDependentComputedStyle(elem, "", "color");
 }
 
 function getSHistory(theWindow)
 {
   const Ci = SpecialPowers.Ci;
-  var sh = SpecialPowers.wrap(theWindow.QueryInterface(Ci.nsIInterfaceRequestor))
-                        .getInterface(Ci.nsIWebNavigation)
+  var sh = SpecialPowers.wrap(theWindow).docShell
+                        .QueryInterface(Ci.nsIWebNavigation)
                         .sessionHistory;
   if (!sh || sh == null)
     throw("Couldn't get shistory for window!");
 
   return sh;
 }
 
 function getSHTitle(sh, offset)
--- a/dom/websocket/tests/websocket_tests.js
+++ b/dom/websocket/tests/websocket_tests.js
@@ -986,18 +986,17 @@ function test41() {
         }
 
         wsc.onclose = function(e) {
           ok(true, "test 41c close");
 
           // clean up the STS state
           const Ci = SpecialPowers.Ci;
           var loadContext = SpecialPowers.wrap(window)
-                            .QueryInterface(Ci.nsIInterfaceRequestor)
-                            .getInterface(Ci.nsIWebNavigation)
+                            .docShell
                             .QueryInterface(Ci.nsILoadContext);
           var flags = 0;
           if (loadContext.usePrivateBrowsing)
             flags |= Ci.nsISocketProvider.NO_PERMANENT_STORAGE;
           SpecialPowers.cleanUpSTSData("http://example.com", flags);
           resolve();
          }
        }
--- a/editor/AsyncSpellCheckTestHelper.jsm
+++ b/editor/AsyncSpellCheckTestHelper.jsm
@@ -25,21 +25,17 @@ const SPELL_CHECK_STARTED_TOPIC = "inlin
  * @param callback         Called when spell check has completed or enough turns
  *                         of the event loop have passed to determine it has not
  *                         started.
  */
 function onSpellCheck(editableElement, callback) {
   let editor = editableElement.editor;
   if (!editor) {
     let win = editableElement.ownerGlobal;
-    editor = win.QueryInterface(Ci.nsIInterfaceRequestor).
-                 getInterface(Ci.nsIWebNavigation).
-                 QueryInterface(Ci.nsIInterfaceRequestor).
-                 getInterface(Ci.nsIEditingSession).
-                 getEditorForWindow(win);
+    editor = win.docShell.editingSession.getEditorForWindow(win);
   }
   if (!editor)
     throw new Error("Unable to find editor for element " + editableElement);
 
   try {
     // False is important here.  Pass false so that the inline spell checker
     // isn't created if it doesn't already exist.
     var isc = editor.getInlineSpellChecker(false);
--- a/editor/composer/test/test_bug1266815.html
+++ b/editor/composer/test/test_bug1266815.html
@@ -58,20 +58,17 @@ add_task(async function() {
     iframe.id = "testframe";
     iframe.src = "data:text/html,<div id=edit contenteditable=true>abc</div>";
     document.body.appendChild(iframe);
   });
 
   await promise;
 
   let iframe = document.getElementById("testframe");
-  let docShell = SpecialPowers.wrap(iframe.contentWindow)
-                 .QueryInterface(Ci.nsIInterfaceRequestor)
-                 .getInterface(Ci.nsIWebNavigation)
-                 .QueryInterface(Ci.nsIDocShell);
+  let docShell = SpecialPowers.wrap(iframe.contentWindow).docShell;
 
   ok(docShell.hasEditingSession, "Should have editing session");
 
   document.getElementById("testframe").src =
     "data:application/octet-stream,TESTCONTENT";
 
   await helperAppDlgPromise;
 
--- a/editor/composer/test/test_bug519928.html
+++ b/editor/composer/test/test_bug519928.html
@@ -17,21 +17,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 var iframe = document.getElementById("load-frame");
 
 function enableJS() { allowJS(true, iframe); }
 function disableJS() { allowJS(false, iframe); }
 function allowJS(allow, frame) {
-  SpecialPowers.wrap(frame.contentWindow)
-               .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
-               .getInterface(SpecialPowers.Ci.nsIWebNavigation)
-               .QueryInterface(SpecialPowers.Ci.nsIDocShell)
-               .allowJavascript = allow;
+  SpecialPowers.wrap(frame.contentWindow).docShell.allowJavascript = allow;
 }
 
 function expectJSAllowed(allowed, testCondition, callback) {
   window.ICanRunMyJS = false;
   var self_ = window;
   testCondition();
 
   var doc = iframe.contentDocument;
--- a/editor/libeditor/tests/test_CF_HTML_clipboard.html
+++ b/editor/libeditor/tests/test_CF_HTML_clipboard.html
@@ -22,18 +22,18 @@ https://bugzilla.mozilla.org/show_bug.cg
 /** Test for Bug 572642 **/
 
 function copyCF_HTML(cfhtml, success, failure) {
   const Cc = SpecialPowers.Cc;
   const Ci = SpecialPowers.Ci;
   const CF_HTML = "application/x-moz-nativehtml";
 
   function getLoadContext() {
-    return SpecialPowers.wrap(window).QueryInterface(Ci.nsIInterfaceRequestor)
-                 .getInterface(Ci.nsIWebNavigation)
+    return SpecialPowers.wrap(window)
+                 .docShell
                  .QueryInterface(Ci.nsILoadContext);
   }
 
   var cb = Cc["@mozilla.org/widget/clipboard;1"].
            getService(Ci.nsIClipboard);
 
   var counter = 0;
   function copyCF_HTML_worker(success, failure) {
--- a/editor/libeditor/tests/test_bug1100966.html
+++ b/editor/libeditor/tests/test_bug1100966.html
@@ -49,21 +49,17 @@ SimpleTest.waitForFocus(function() {
         });
       },0);
     },0);
   });
 });
 
 function getEditor() {
   var Ci = SpecialPowers.Ci;
-  var editingSession = SpecialPowers.wrap(window)
-    .QueryInterface(Ci.nsIInterfaceRequestor)
-    .getInterface(Ci.nsIWebNavigation)
-    .QueryInterface(Ci.nsIInterfaceRequestor)
-    .getInterface(Ci.nsIEditingSession);
+  var editingSession = SpecialPowers.wrap(window).docShell.editingSession;
   return editingSession.getEditorForWindow(window);
 }
 
 function getSpellChecker() {
   return getEditor().getInlineSpellChecker(false).spellChecker;
 }
 
 function getSpellCheckSelection() {
--- a/editor/libeditor/tests/test_bug1140105.html
+++ b/editor/libeditor/tests/test_bug1140105.html
@@ -48,21 +48,17 @@ SimpleTest.waitForFocus(function() {
   is(allHas.value, false, "Test for Courier: allHas: false expected");
 
   SimpleTest.finish();
 
 });
 
 function getEditor() {
   var Ci = SpecialPowers.Ci;
-  var editingSession = SpecialPowers.wrap(window)
-                             .QueryInterface(Ci.nsIInterfaceRequestor)
-                             .getInterface(Ci.nsIWebNavigation)
-                             .QueryInterface(Ci.nsIInterfaceRequestor)
-                             .getInterface(Ci.nsIEditingSession);
+  var editingSession = SpecialPowers.wrap(window).docShell.editingSession;
   var editor = editingSession.getEditorForWindow(window);
   editor.QueryInterface(Ci.nsIHTMLEditor);
   return editor;
 }
 
 </script>
 </body>
 
--- a/editor/libeditor/tests/test_bug1154791.html
+++ b/editor/libeditor/tests/test_bug1154791.html
@@ -49,21 +49,17 @@ SimpleTest.waitForFocus(function() {
         });
       },0);
     },0);
   });
 });
 
 function getEditor() {
   var Ci = SpecialPowers.Ci;
-  var editingSession = SpecialPowers.wrap(window)
-                             .QueryInterface(Ci.nsIInterfaceRequestor)
-                             .getInterface(Ci.nsIWebNavigation)
-                             .QueryInterface(Ci.nsIInterfaceRequestor)
-                             .getInterface(Ci.nsIEditingSession);
+  var editingSession = SpecialPowers.wrap(window).docShell.editingSession;
   return editingSession.getEditorForWindow(window);
 }
 
 function getSpellChecker() {
   return getEditor().getInlineSpellChecker(false).spellChecker;
 }
 
 function getSpellCheckSelection() {
--- a/editor/libeditor/tests/test_bug1186799.html
+++ b/editor/libeditor/tests/test_bug1186799.html
@@ -49,20 +49,18 @@ SimpleTest.waitForFocus(function() {
   ok(!isThereIMESelection(), "There should be no IME selection");
 
   SimpleTest.finish();
 });
 
 function isThereIMESelection()
 {
   var selCon = SpecialPowers.wrap(window).
-        QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor).
-        getInterface(SpecialPowers.Ci.nsIWebNavigation).
-        QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor).
-        getInterface(SpecialPowers.Ci.nsIEditingSession).
+        docShell.
+        editingSession.
         getEditorForWindow(window).
         selectionController;
   const kIMESelections = [
     SpecialPowers.Ci.nsISelectionController.SELECTION_IME_RAWINPUT,
     SpecialPowers.Ci.nsISelectionController.SELECTION_IME_SELECTEDRAWTEXT,
     SpecialPowers.Ci.nsISelectionController.SELECTION_IME_CONVERTEDTEXT,
     SpecialPowers.Ci.nsISelectionController.SELECTION_IME_SELECTEDCONVERTEDTEXT
   ];
--- a/editor/libeditor/tests/test_bug1230473.html
+++ b/editor/libeditor/tests/test_bug1230473.html
@@ -30,19 +30,17 @@ SimpleTest.waitForFocus(()=>{
     function value() {
       return isNSEditableElement() ? aEditor.value : aEditor.textContent;
     }
     function isComposing() {
       return isNSEditableElement() ?  SpecialPowers.wrap(aEditor)
                                                    .editor
                                                    .composing :
                                       SpecialPowers.wrap(window)
-                                                   .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
-                                                   .getInterface(SpecialPowers.Ci.nsIWebNavigation)
-                                                   .QueryInterface(SpecialPowers.Ci.nsIDocShell)
+                                                   .docShell
                                                    .editor
                                                    .composing;
     }
     function clear() {
       if (isNSEditableElement()) {
         aEditor.value = "";
       } else {
         aEditor.textContent = "";
--- a/editor/libeditor/tests/test_bug1268736.html
+++ b/editor/libeditor/tests/test_bug1268736.html
@@ -32,21 +32,17 @@ https://bugzilla.mozilla.org/show_bug.cg
  * Test for Bug 1268736
  *
  * Tests for editing a table cell's contents when the table cell is or isn't a child of a contenteditable node.
  *
  */
 
 function getEditor() {
   const Ci = SpecialPowers.Ci;
-  var editingSession = SpecialPowers.wrap(window)
-                                    .QueryInterface(Ci.nsIInterfaceRequestor)
-                                    .getInterface(Ci.nsIWebNavigation)
-                                    .QueryInterface(Ci.nsIInterfaceRequestor)
-                                    .getInterface(Ci.nsIEditingSession);
+  var editingSession = SpecialPowers.wrap(window).docShell.editingSession;
   return editingSession.getEditorForWindow(window).QueryInterface(Ci.nsITableEditor);
 }
 
 var table = document.getElementById("table");
 var tableHTML = table.innerHTML;
 var editor = getEditor();
 
 var cell = document.getElementById("cell_readonly");
--- a/editor/libeditor/tests/test_bug1330796.html
+++ b/editor/libeditor/tests/test_bug1330796.html
@@ -81,21 +81,17 @@ SimpleTest.waitForFocus(function() {
   }
 
   SimpleTest.finish();
 
 });
 
 function makeMailEditor() {
   var Ci = SpecialPowers.Ci;
-  var editingSession = SpecialPowers.wrap(window)
-    .QueryInterface(Ci.nsIInterfaceRequestor)
-    .getInterface(Ci.nsIWebNavigation)
-    .QueryInterface(Ci.nsIInterfaceRequestor)
-    .getInterface(Ci.nsIEditingSession);
+  var editingSession = SpecialPowers.wrap(window).docShell.editingSession;
   var editor = editingSession.getEditorForWindow(window);
   editor.flags |= Ci.nsIPlaintextEditor.eEditorMailMask;
 }
 </script>
 
 </pre>
 </body>
 </html>
--- a/editor/libeditor/tests/test_bug1397412.xul
+++ b/editor/libeditor/tests/test_bug1397412.xul
@@ -28,19 +28,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   </body>
   <script class="testbody" type="application/javascript">
   <![CDATA[
 function runTest() {
   var initialHTML1 = "xx<br><br>";
   var expectedHTML1 = "xx<br>t<br>";
   var initialHTML2 = "xx<br><br>yy<br>";
   var expectedHTML2 = "xx<br>t<br>yy<br>";
-  window.QueryInterface(Ci.nsIInterfaceRequestor)
-     .getInterface(Ci.nsIWebNavigation)
-     .QueryInterface(Ci.nsIDocShellTreeItem)
+  window.docShell
      .rootTreeItem
      .QueryInterface(Ci.nsIDocShell)
      .appType = Ci.nsIDocShell.APP_TYPE_EDITOR;
   var e = document.getElementById("editor");
   var doc = e.contentDocument;
   doc.designMode = "on";
   doc.defaultView.focus();
   var selection = doc.defaultView.getSelection();
--- a/editor/libeditor/tests/test_bug366682.html
+++ b/editor/libeditor/tests/test_bug366682.html
@@ -31,21 +31,17 @@ function getEdit() {
 
 function editDoc() {
   return getEdit().contentDocument;
 }
 
 function getEditor() {
   var Ci = SpecialPowers.Ci;
   var win = editDoc().defaultView;
-  var editingSession = SpecialPowers.wrap(win)
-    .QueryInterface(Ci.nsIInterfaceRequestor)
-    .getInterface(Ci.nsIWebNavigation)
-    .QueryInterface(Ci.nsIInterfaceRequestor)
-    .getInterface(Ci.nsIEditingSession);
+  var editingSession = SpecialPowers.wrap(win).docShell.editingSession;
   return editingSession.getEditorForWindow(win);
 }
 
 function runTest() {
   editDoc().body.innerHTML = "<div>errror and an other errror</div>";
   gMisspeltWords = ["errror", "errror"];
   editDoc().designMode = "on";
 
--- a/editor/libeditor/tests/test_bug432225.html
+++ b/editor/libeditor/tests/test_bug432225.html
@@ -34,21 +34,17 @@ function getEdit() {
 
 function editDoc() {
   return getEdit().contentDocument;
 }
 
 function getEditor() {
   var Ci = SpecialPowers.Ci;
   var win = editDoc().defaultView;
-  var editingSession = SpecialPowers.wrap(win)
-                                    .QueryInterface(Ci.nsIInterfaceRequestor)
-                                    .getInterface(Ci.nsIWebNavigation)
-                                    .QueryInterface(Ci.nsIInterfaceRequestor)
-                                    .getInterface(Ci.nsIEditingSession);
+  var editingSession = SpecialPowers.wrap(win).docShell.editingSession;
   return editingSession.getEditorForWindow(win);
 }
 
 function runTest() {
   editDoc().designMode = "on";
   setTimeout(function() { addWords(100); }, 0);
 }  
  
--- a/editor/libeditor/tests/test_bug484181.html
+++ b/editor/libeditor/tests/test_bug484181.html
@@ -24,20 +24,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(runTest);
 
 var gMisspeltWords;
 
 function getEditor() {
   var Ci = SpecialPowers.Ci;
   var win = window;
-  var editingSession = SpecialPowers.wrap(win).QueryInterface(Ci.nsIInterfaceRequestor)
-                          .getInterface(Ci.nsIWebNavigation)
-                          .QueryInterface(Ci.nsIInterfaceRequestor)
-                          .getInterface(Ci.nsIEditingSession);
+  var editingSession = SpecialPowers.wrap(win).docShell.editingSession;
   return editingSession.getEditorForWindow(win);
 }
 
 function append(str) {
   var edit = document.getElementById("edit");
   var editor = getEditor();
   var sel = editor.selection;
   sel.selectAllChildren(edit);
--- a/editor/libeditor/tests/test_bug489202.xul
+++ b/editor/libeditor/tests/test_bug489202.xul
@@ -26,35 +26,31 @@ https://bugzilla.mozilla.org/show_bug.cg
   <pre id="test">
   </pre>
   </body>
   <script class="testbody" type="application/javascript">
   <![CDATA[
   var utils = SpecialPowers.getDOMWindowUtils(window);
 
 function getLoadContext() {
-  return window.QueryInterface(Ci.nsIInterfaceRequestor)
-               .getInterface(Ci.nsIWebNavigation)
-               .QueryInterface(Ci.nsILoadContext);
+  return window.docShell.QueryInterface(Ci.nsILoadContext);
 }
 
 function runTest() {
   var trans = Cc["@mozilla.org/widget/transferable;1"]
       .createInstance(Ci.nsITransferable);
   trans.init(getLoadContext());
   trans.addDataFlavor("text/html");
   var test_data = '<meta/><a href="http://mozilla.org/">mozilla.org</a>';
   var cstr = Cc["@mozilla.org/supports-string;1"]
       .createInstance(Ci.nsISupportsString);
   cstr.data = test_data;
   trans.setTransferData("text/html", cstr, test_data.length*2);
 
-  window.QueryInterface(Ci.nsIInterfaceRequestor)
-     .getInterface(Ci.nsIWebNavigation)
-     .QueryInterface(Ci.nsIDocShellTreeItem)
+  window.docShell
      .rootTreeItem
      .QueryInterface(Ci.nsIDocShell)
      .appType = Ci.nsIDocShell.APP_TYPE_EDITOR; 
   var e = document.getElementById('i1');
   var doc = e.contentDocument;
   doc.designMode = "on";
   doc.body.innerHTML = "";
   doc.defaultView.focus();
--- a/editor/libeditor/tests/test_bug520189.html
+++ b/editor/libeditor/tests/test_bug520189.html
@@ -545,20 +545,17 @@ function doNextTest() {
 
   runTest(tests[testCounter]);
 
   doNextTest();
 }
 
 function getLoadContext() {
   const Ci = SpecialPowers.Ci;
-  return SpecialPowers.wrap(window)
-               .QueryInterface(Ci.nsIInterfaceRequestor)
-               .getInterface(Ci.nsIWebNavigation)
-               .QueryInterface(Ci.nsILoadContext);
+  return SpecialPowers.wrap(window).docShell.QueryInterface(Ci.nsILoadContext);
 }
 
 function runTest(test) {
   var elem = document.getElementById(test.id);
   if ("isIFrame" in test) {
     elem.contentDocument.designMode = "on";
     elem.contentWindow.focus();
   } else
@@ -576,20 +573,17 @@ function runTest(test) {
   if ("indirectPaste" in test) {
     var editor, win;
     if ("isIFrame" in test) {
       win = elem.contentDocument.defaultView;
     } else {
       getSelection().collapse(elem, 0);
       win = window;
     }
-    editor = SpecialPowers.wrap(win).QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
-                          .getInterface(SpecialPowers.Ci.nsIWebNavigation)
-                          .QueryInterface(SpecialPowers.Ci.nsIDocShell)
-                          .editor;
+    editor = SpecialPowers.wrap(win).docShell.editor;
     editor.pasteTransferable(trans);
   } else {
     var clipboard = SpecialPowers.Cc["@mozilla.org/widget/clipboard;1"]
                                  .getService(SpecialPowers.Ci.nsIClipboard);
 
     clipboard.setData(trans, null, SpecialPowers.Ci.nsIClipboard.kGlobalClipboard);
 
     synthesizeKey("V", {accelKey: true});
--- a/editor/libeditor/tests/test_bug525389.html
+++ b/editor/libeditor/tests/test_bug525389.html
@@ -8,20 +8,17 @@
 
 <script class="testbody" type="application/javascript">
 
   var utils = SpecialPowers.getDOMWindowUtils(window);
   var Cc = SpecialPowers.Cc;
   var Ci = SpecialPowers.Ci;
 
 function getLoadContext() {
-  return SpecialPowers.wrap(window)
-               .QueryInterface(Ci.nsIInterfaceRequestor)
-               .getInterface(Ci.nsIWebNavigation)
-               .QueryInterface(Ci.nsILoadContext);
+  return SpecialPowers.wrap(window).docShell.QueryInterface(Ci.nsILoadContext);
 }
 
 async function runTest() {
   var pasteCount = 0;
   var pasteFunc = function (event) { pasteCount++; };
 
   function verifyContent(s) {
     var e = document.getElementById('i1');
--- a/editor/libeditor/tests/test_bug596333.html
+++ b/editor/libeditor/tests/test_bug596333.html
@@ -35,20 +35,17 @@ function append(str) {
   var edit = document.getElementById("edit");
   edit.focus();
   edit.selectionStart = edit.selectionEnd = edit.value.length;
   var editor = getEditor();
   sendString(str);
 }
 
 function getLoadContext() {
-  return SpecialPowers.wrap(window)
-         .QueryInterface(Ci.nsIInterfaceRequestor)
-         .getInterface(Ci.nsIWebNavigation)
-         .QueryInterface(Ci.nsILoadContext);
+  return SpecialPowers.wrap(window).docShell.QueryInterface(Ci.nsILoadContext);
 }
 
 function paste(str) {
   var edit = document.getElementById("edit");
   var Cc = SpecialPowers.Cc, Ci = SpecialPowers.Ci;
   var trans = Cc["@mozilla.org/widget/transferable;1"].createInstance(Ci.nsITransferable);
   trans.init(getLoadContext());
   var s = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
--- a/editor/libeditor/tests/test_bug790475.html
+++ b/editor/libeditor/tests/test_bug790475.html
@@ -24,21 +24,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(runTest);
 
 var gMisspeltWords;
 
 function getEditor() {
   const Ci = SpecialPowers.Ci;
-  var editingSession = SpecialPowers.wrap(window)
-                                    .QueryInterface(Ci.nsIInterfaceRequestor)
-                                    .getInterface(Ci.nsIWebNavigation)
-                                    .QueryInterface(Ci.nsIInterfaceRequestor)
-                                    .getInterface(Ci.nsIEditingSession);
+  var editingSession = SpecialPowers.wrap(window).docShell.editingSession;
   return editingSession.getEditorForWindow(window);
 }
 
 function getSpellCheckSelection() {
   var editor = getEditor();
   var selcon = editor.selectionController;
   return selcon.getSelection(selcon.SELECTION_SPELLCHECK);
 }
--- a/editor/libeditor/tests/test_bug857487.html
+++ b/editor/libeditor/tests/test_bug857487.html
@@ -37,21 +37,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 /**
  * Test for Bug 857487
  *
  * Tests that removing a table row through nsIHTMLEditor works
  */
 
 function getEditor() {
   const Ci = SpecialPowers.Ci;
-  var editingSession = SpecialPowers.wrap(window)
-                                    .QueryInterface(Ci.nsIInterfaceRequestor)
-                                    .getInterface(Ci.nsIWebNavigation)
-                                    .QueryInterface(Ci.nsIInterfaceRequestor)
-                                    .getInterface(Ci.nsIEditingSession);
+  var editingSession = SpecialPowers.wrap(window).docShell.editingSession;
   return editingSession.getEditorForWindow(window).QueryInterface(Ci.nsITableEditor);
 }
 
 var cell = document.getElementById("cell");
 cell.focus();
 
 // place caret at end of center cell
 var sel = getSelection();
--- a/editor/libeditor/tests/test_bug974309.html
+++ b/editor/libeditor/tests/test_bug974309.html
@@ -39,21 +39,17 @@ https://bugzilla.mozilla.org/show_bug.cg
  * Test for Bug 974309
  *
  * Tests that editing a table row fails when the table or row is _not_ a child of a contenteditable node.
  * See bug 857487 for tests that cover when the table or row _is_ a child of a contenteditable node.
  */
 
 function getEditor() {
   const Ci = SpecialPowers.Ci;
-  var editingSession = SpecialPowers.wrap(window)
-                                    .QueryInterface(Ci.nsIInterfaceRequestor)
-                                    .getInterface(Ci.nsIWebNavigation)
-                                    .QueryInterface(Ci.nsIInterfaceRequestor)
-                                    .getInterface(Ci.nsIEditingSession);
+  var editingSession = SpecialPowers.wrap(window).docShell.editingSession;
   return editingSession.getEditorForWindow(window).QueryInterface(Ci.nsITableEditor);
 }
 
 var cell = document.getElementById("cell");
 cell.focus();
 
 // place caret at end of center cell
 var sel = getSelection();
--- a/editor/libeditor/tests/test_contenteditable_focus.html
+++ b/editor/libeditor/tests/test_contenteditable_focus.html
@@ -51,19 +51,17 @@ function runTests()
 
 function runTestsInternal()
 {
   var fm = SpecialPowers.Cc["@mozilla.org/focus-manager;1"].
              getService(SpecialPowers.Ci.nsIFocusManager);
   // XXX using selCon for checking the visibility of the caret, however,
   // selCon is shared in document, cannot get the element of owner of the
   // caret from javascript?
-  var selCon = SpecialPowers.wrap(window).
-        QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor).
-        getInterface(SpecialPowers.Ci.nsIWebNavigation).
+  var selCon = SpecialPowers.wrap(window).docShell.
         QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor).
         getInterface(SpecialPowers.Ci.nsISelectionDisplay).
         QueryInterface(SpecialPowers.Ci.nsISelectionController);
   var selection = window.getSelection();
 
   var inputText = document.getElementById("inputText");
   var inputTextReadonly = document.getElementById("inputTextReadonly");
   var inputButton = document.getElementById("inputButton");
--- a/editor/libeditor/tests/test_css_chrome_load_access.html
+++ b/editor/libeditor/tests/test_css_chrome_load_access.html
@@ -20,21 +20,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 const Ci = SpecialPowers.Ci;
 var styleSheets = null;
 
 function runTest() {
 
   var editframe = window.frames[0];
   var editdoc = editframe.document;
   editdoc.designMode = 'on';
-  var editor = SpecialPowers.wrap(editframe)
-                            .QueryInterface(Ci.nsIInterfaceRequestor)
-                            .getInterface(Ci.nsIWebNavigation)
-                            .QueryInterface(Ci.nsIInterfaceRequestor)
-                            .getInterface(Ci.nsIEditingSession)
+  var editor = SpecialPowers.wrap(editframe).docShell.editingSession
                             .getEditorForWindow(editframe);
 
   styleSheets = editor.QueryInterface(Ci.nsIEditorStyleSheets);
 
   // test 1: try to access chrome:// url that is accessible to content
   try
   {
     styleSheets.addOverrideStyleSheet("chrome://browser/content/pageinfo/pageInfo.css");
--- a/editor/libeditor/tests/test_documentCharacterSet.html
+++ b/editor/libeditor/tests/test_documentCharacterSet.html
@@ -13,21 +13,17 @@
 <iframe></iframe>
 
 <pre id="test">
 
 <script class="testbody" type="application/javascript">
 function getEditor() {
   const Ci = SpecialPowers.Ci;
   let editframe = window.frames[0];
-  return SpecialPowers.wrap(editframe)
-                      .QueryInterface(Ci.nsIInterfaceRequestor)
-                      .getInterface(Ci.nsIWebNavigation)
-                      .QueryInterface(Ci.nsIInterfaceRequestor)
-                      .getInterface(Ci.nsIEditingSession)
+  return SpecialPowers.wrap(editframe).docShell.editingSession
                       .getEditorForWindow(editframe);
 }
 
 SimpleTest.waitForExplicitFinish();
 SimpleTest.waitForFocus(function() {
   let editdoc = window.frames[0].document;
   editdoc.designMode = 'on';
   let editor = getEditor();
--- a/editor/libeditor/tests/test_htmleditor_keyevent_handling.html
+++ b/editor/libeditor/tests/test_htmleditor_keyevent_handling.html
@@ -604,20 +604,17 @@ function runTests()
     is(aElement.innerHTML,
        aIsReadonly ? "" : aIsPlaintext ? "Mozilla " : "Mozilla <br>",
        aDescription + "typed \"Mozilla \"");
   }
 
   doTest(htmlEditor, "contenteditable=\"true\"", false, true, false);
 
   const nsIPlaintextEditor = SpecialPowers.Ci.nsIPlaintextEditor;
-  var editor = SpecialPowers.wrap(window).
-      QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor).
-      getInterface(SpecialPowers.Ci.nsIWebNavigation).
-      QueryInterface(SpecialPowers.Ci.nsIDocShell).editor;
+  var editor = SpecialPowers.wrap(window).docShell.editor;
   var flags = editor.flags;
   // readonly
   editor.flags = flags | nsIPlaintextEditor.eEditorReadonlyMask;
   doTest(htmlEditor, "readonly HTML editor", true, true, false);
 
   // non-tabbable
   editor.flags = flags & ~(nsIPlaintextEditor.eEditorAllowInteraction);
   doTest(htmlEditor, "non-tabbable HTML editor", false, false, false);
--- a/editor/libeditor/tests/test_root_element_replacement.html
+++ b/editor/libeditor/tests/test_root_element_replacement.html
@@ -79,18 +79,17 @@ const kTests = [
 var gIFrame;
 var gSetFocusToIFrame = false;
 
 function onLoadIFrame()
 {
   var frameDoc = gIFrame.contentWindow.document;
 
   var selCon = SpecialPowers.wrap(gIFrame).contentWindow.
-    QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor).
-    getInterface(SpecialPowers.Ci.nsIWebNavigation).
+    docShell.
     QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor).
     getInterface(SpecialPowers.Ci.nsISelectionDisplay).
     QueryInterface(SpecialPowers.Ci.nsISelectionController);
   var utils = SpecialPowers.getDOMWindowUtils(window);
 
   // move focus to the HTML editor
   const kTest = kTests[gTestIndex];
   ok(true, "Running " + kTest.description);
--- a/editor/spellchecker/tests/test_bug1205983.html
+++ b/editor/spellchecker/tests/test_bug1205983.html
@@ -61,21 +61,17 @@ SimpleTest.waitForFocus(function() {
   document.getElementById('de-DE').focus();
 });
 
 function deFocus() {
   elem_de = document.getElementById('de-DE');
 
   onSpellCheck(elem_de, function () {
     var Ci = SpecialPowers.Ci;
-    var editingSession = SpecialPowers.wrap(window)
-                               .QueryInterface(Ci.nsIInterfaceRequestor)
-                               .getInterface(Ci.nsIWebNavigation)
-                               .QueryInterface(Ci.nsIInterfaceRequestor)
-                               .getInterface(Ci.nsIEditingSession);
+    var editingSession = SpecialPowers.wrap(window).docShell.editingSession;
     editor_de = editingSession.getEditorForWindow(window);
     selcon_de = editor_de.selectionController;
     var sel = selcon_de.getSelection(selcon_de.SELECTION_SPELLCHECK);
 
     // Check that we spelled in German, so there is only one misspelled word.
     is(sel.toString(), "German", "one misspelled word expected: German");
 
     // Now focus the textarea, which requires English spelling.
--- a/editor/spellchecker/tests/test_bug1219928.html
+++ b/editor/spellchecker/tests/test_bug1219928.html
@@ -32,21 +32,17 @@ SimpleTest.waitForFocus(function() {
   SpecialPowers.Cu.import(
     "resource://testing-common/AsyncSpellCheckTestHelper.jsm", window);
 
   var elem = document.getElementById('en-US');
   elem.focus();
 
   onSpellCheck(elem, function () {
     var Ci = SpecialPowers.Ci;
-    var editingSession = SpecialPowers.wrap(window)
-                                      .QueryInterface(Ci.nsIInterfaceRequestor)
-                                      .getInterface(Ci.nsIWebNavigation)
-                                      .QueryInterface(Ci.nsIInterfaceRequestor)
-                                      .getInterface(Ci.nsIEditingSession);
+    var editingSession = SpecialPowers.wrap(window).docShell.editingSession;
     var editor = editingSession.getEditorForWindow(window);
     var selcon = editor.selectionController;
     var sel = selcon.getSelection(selcon.SELECTION_SPELLCHECK);
 
     is(sel.toString(), "missspelled", "one misspelled word expected: missspelled");
 
     spellchecker = SpecialPowers.Cc['@mozilla.org/editor/editorspellchecker;1']
                                 .createInstance(Ci.nsIEditorSpellCheck);
--- a/gfx/2d/FontVariation.h
+++ b/gfx/2d/FontVariation.h
@@ -2,16 +2,18 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_GFX_FONTVARIATION_H_
 #define MOZILLA_GFX_FONTVARIATION_H_
 
+#include <stdint.h>
+
 namespace mozilla {
 namespace gfx {
 
 // An OpenType variation tag and value pair
 struct FontVariation
 {
   uint32_t mTag;
   float mValue;
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -128,16 +128,19 @@ typedef PlatformSpecificStateBase Platfo
  * \page APZCPrefs APZ preferences
  *
  * The following prefs are used to control the behaviour of the APZC.
  * The default values are provided in gfxPrefs.h.
  *
  * \li\b apz.allow_checkerboarding
  * Pref that allows or disallows checkerboarding
  *
+ * \li\b apz.allow_double_tap_zooming
+ * Pref that allows or disallows double tap to zoom
+ *
  * \li\b apz.allow_immediate_handoff
  * If set to true, scroll can be handed off from one APZC to another within
  * a single input block. If set to false, a single input block can only
  * scroll one APZC.
  *
  * \li\b apz.android.chrome_fling_physics.enabled
  * If set to true, APZ uses a fling physical model similar to Chrome's
  * on Android, rather than Android's StackScroller.
--- a/gfx/tests/browser/browser_windowless_troubleshoot_crash.js
+++ b/gfx/tests/browser/browser_windowless_troubleshoot_crash.js
@@ -3,19 +3,17 @@ let { Services } = ChromeUtils.import("r
 add_task(async function test_windowlessBrowserTroubleshootCrash() {
   let webNav = Services.appShell.createWindowlessBrowser(false);
 
   let onLoaded = new Promise((resolve, reject) => {
     let docShell = webNav.QueryInterface(Ci.nsIInterfaceRequestor)
                          .getInterface(Ci.nsIDocShell);
     let listener = {
       observe(contentWindow, topic, data) {
-        let observedDocShell = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                                            .getInterface(Ci.nsIWebNavigation)
-                                            .QueryInterface(Ci.nsIDocShellTreeItem)
+        let observedDocShell = contentWindow.docShell
                                             .sameTypeRootTreeItem
                                             .QueryInterface(Ci.nsIDocShell);
           if (docShell === observedDocShell) {
             Services.obs.removeObserver(listener, "content-document-global-created");
             resolve();
           }
         }
     }
--- a/gfx/thebes/gfxFT2FontBase.cpp
+++ b/gfx/thebes/gfxFT2FontBase.cpp
@@ -228,17 +228,17 @@ gfxFT2FontBase::InitMetrics()
         SanitizeMetrics(&mMetrics, false);
         return;
     }
 
     if (face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) {
         // Resolve variations from entry (descriptor) and style (property)
         AutoTArray<gfxFontVariation,8> settings;
         mFontEntry->GetVariationsForStyle(settings, mStyle);
-        SetupVarCoords(face, settings, &mCoords);
+        SetupVarCoords(mFontEntry->GetMMVar(), settings, &mCoords);
         if (!mCoords.IsEmpty()) {
 #if MOZ_TREE_FREETYPE
             FT_Set_Var_Design_Coordinates(face, mCoords.Length(), mCoords.Elements());
 #else
             typedef FT_Error (*SetCoordsFunc)(FT_Face, FT_UInt, FT_Fixed*);
             static SetCoordsFunc setCoords;
             static bool firstTime = true;
             if (firstTime) {
@@ -633,51 +633,30 @@ gfxFT2FontBase::SetupCairoFont(DrawTarge
     return true;
 }
 
 // For variation fonts, figure out the variation coordinates to be applied
 // for each axis, in freetype's order (which may not match the order of
 // axes in mStyle.variationSettings, so we need to search by axis tag).
 /*static*/
 void
-gfxFT2FontBase::SetupVarCoords(FT_Face aFace,
+gfxFT2FontBase::SetupVarCoords(FT_MM_Var* aMMVar,
                                const nsTArray<gfxFontVariation>& aVariations,
                                nsTArray<FT_Fixed>* aCoords)
 {
     aCoords->TruncateLength(0);
-    if (aFace->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) {
-        typedef FT_Error (*GetVarFunc)(FT_Face, FT_MM_Var**);
-        typedef FT_Error (*DoneVarFunc)(FT_Library, FT_MM_Var*);
-#if MOZ_TREE_FREETYPE
-        GetVarFunc getVar = &FT_Get_MM_Var;
-        DoneVarFunc doneVar = &FT_Done_MM_Var;
-#else
-        static GetVarFunc getVar;
-        static DoneVarFunc doneVar;
-        static bool firstTime = true;
-        if (firstTime) {
-            firstTime = false;
-            getVar = (GetVarFunc)dlsym(RTLD_DEFAULT, "FT_Get_MM_Var");
-            doneVar = (DoneVarFunc)dlsym(RTLD_DEFAULT, "FT_Done_MM_Var");
-        }
-#endif
-        FT_MM_Var* ftVar;
-        if (getVar && FT_Err_Ok == (*getVar)(aFace, &ftVar)) {
-            for (unsigned i = 0; i < ftVar->num_axis; ++i) {
-                aCoords->AppendElement(ftVar->axis[i].def);
-                for (const auto& v : aVariations) {
-                    if (ftVar->axis[i].tag == v.mTag) {
-                        FT_Fixed val = v.mValue * 0x10000;
-                        val = std::min(val, ftVar->axis[i].maximum);
-                        val = std::max(val, ftVar->axis[i].minimum);
-                        (*aCoords)[i] = val;
-                        break;
-                    }
-                }
-            }
-            if (doneVar) {
-                (*doneVar)(aFace->glyph->library, ftVar);
-            } else {
-                free(ftVar);
+    if (!aMMVar) {
+        return;
+    }
+
+    for (unsigned i = 0; i < aMMVar->num_axis; ++i) {
+        aCoords->AppendElement(aMMVar->axis[i].def);
+        for (const auto& v : aVariations) {
+            if (aMMVar->axis[i].tag == v.mTag) {
+                FT_Fixed val = v.mValue * 0x10000;
+                val = std::min(val, aMMVar->axis[i].maximum);
+                val = std::max(val, aMMVar->axis[i].minimum);
+                (*aCoords)[i] = val;
+                break;
             }
         }
     }
 }
--- a/gfx/thebes/gfxFT2FontBase.h
+++ b/gfx/thebes/gfxFT2FontBase.h
@@ -32,17 +32,17 @@ public:
     virtual bool ProvidesGlyphWidths() const override { return true; }
     virtual int32_t GetGlyphWidth(DrawTarget& aDrawTarget,
                                   uint16_t aGID) override;
 
     virtual bool SetupCairoFont(DrawTarget* aDrawTarget) override;
 
     virtual FontType GetType() const override { return FONT_TYPE_FT2; }
 
-    static void SetupVarCoords(FT_Face aFace,
+    static void SetupVarCoords(FT_MM_Var* aMMVar,
                                const nsTArray<gfxFontVariation>& aVariations,
                                nsTArray<FT_Fixed>* aCoords);
 
 private:
     uint32_t GetCharExtents(char aChar, cairo_text_extents_t* aExtents);
     uint32_t GetCharWidth(char aChar, gfxFloat* aWidth);
 
     // Get advance of a single glyph from FreeType, and return true;
--- a/gfx/thebes/gfxFT2FontList.cpp
+++ b/gfx/thebes/gfxFT2FontList.cpp
@@ -201,16 +201,20 @@ FT2FontEntry::CreateScaledFont(const gfx
     NS_ASSERTION(cairo_scaled_font_status(scaledFont) == CAIRO_STATUS_SUCCESS,
                  "Failed to make scaled font");
 
     return scaledFont;
 }
 
 FT2FontEntry::~FT2FontEntry()
 {
+    if (mMMVar) {
+        FT_Done_MM_Var(mFTFace->glyph->library, mMMVar);
+    }
+
     // Do nothing for mFTFace here since FTFontDestroyFunc is called by cairo.
     mFTFace = nullptr;
 
 #ifndef ANDROID
     if (mFontFace) {
         cairo_font_face_destroy(mFontFace);
         mFontFace = nullptr;
     }
@@ -468,17 +472,17 @@ FT2FontEntry::CairoFontFace(const gfxFon
         (mFTFace->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS)) {
         int flags = gfxPlatform::GetPlatform()->FontHintingEnabled() ?
                     FT_LOAD_DEFAULT :
                     (FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING);
         // Resolve variations from entry (descriptor) and style (property)
         AutoTArray<gfxFontVariation,8> settings;
         GetVariationsForStyle(settings, aStyle ? *aStyle : gfxFontStyle());
         AutoTArray<FT_Fixed,8> coords;
-        gfxFT2FontBase::SetupVarCoords(mFTFace, settings, &coords);
+        gfxFT2FontBase::SetupVarCoords(GetMMVar(), settings, &coords);
         // Create a separate FT_Face because we need to apply custom
         // variation settings to it.
         FT_Face ftFace;
         if (!mFilename.IsEmpty() && mFilename[0] == '/') {
             ftFace = Factory::NewFTFace(nullptr, mFilename.get(), mFTFontIndex);
         } else {
             auto ufd = reinterpret_cast<FTUserFontData*>(
                 cairo_font_face_get_user_data(mFontFace, &sFTUserFontDataKey));
@@ -641,45 +645,52 @@ FT2FontEntry::HasVariations()
 }
 
 void
 FT2FontEntry::GetVariationAxes(nsTArray<gfxFontVariationAxis>& aAxes)
 {
     if (!HasVariations()) {
         return;
     }
-    AutoFTFace face(this);
-    if (!face) {
-        return;
-    }
-    FT_MM_Var* mmVar;
-    if (FT_Err_Ok != (FT_Get_MM_Var(face, &mmVar))) {
+    FT_MM_Var* mmVar = GetMMVar();
+    if (!mmVar) {
         return;
     }
     gfxFT2Utils::GetVariationAxes(mmVar, aAxes);
-    FT_Done_MM_Var(FT_Face(face)->glyph->library, mmVar);
 }
 
 void
 FT2FontEntry::GetVariationInstances(
     nsTArray<gfxFontVariationInstance>& aInstances)
 {
     if (!HasVariations()) {
         return;
     }
-    AutoFTFace face(this);
-    if (!face) {
-        return;
-    }
-    FT_MM_Var* mmVar;
-    if (FT_Err_Ok != (FT_Get_MM_Var(face, &mmVar))) {
+    FT_MM_Var* mmVar = GetMMVar();
+    if (!mmVar) {
         return;
     }
     gfxFT2Utils::GetVariationInstances(this, mmVar, aInstances);
-    FT_Done_MM_Var(FT_Face(face)->glyph->library, mmVar);
+}
+
+FT_MM_Var*
+FT2FontEntry::GetMMVar()
+{
+    if (mMMVarInitialized) {
+        return mMMVar;
+    }
+    mMMVarInitialized = true;
+    AutoFTFace face(this);
+    if (!face) {
+        return nullptr;
+    }
+    if (FT_Err_Ok != FT_Get_MM_Var(face, &mMMVar)) {
+        mMMVar = nullptr;
+    }
+    return mMMVar;
 }
 
 void
 FT2FontEntry::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
                                      FontListSizes* aSizes) const
 {
     gfxFontEntry::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
     aSizes->mFontListSize +=
--- a/gfx/thebes/gfxFT2FontList.h
+++ b/gfx/thebes/gfxFT2FontList.h
@@ -89,31 +89,36 @@ public:
     bool HasVariations() override;
     void GetVariationAxes(nsTArray<gfxFontVariationAxis>& aVariationAxes) override;
     void GetVariationInstances(nsTArray<gfxFontVariationInstance>& aInstances) override;
 
     // Check for various kinds of brokenness, and set flags on the entry
     // accordingly so that we avoid using bad font tables
     void CheckForBrokenFont(gfxFontFamily *aFamily);
 
+    FT_MM_Var* GetMMVar() override;
+
     virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                         FontListSizes* aSizes) const override;
     virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                         FontListSizes* aSizes) const override;
 
     FT_Face mFTFace;
     cairo_font_face_t *mFontFace;
 
+    FT_MM_Var* mMMVar = nullptr;
+
     nsCString mFilename;
     uint8_t   mFTFontIndex;
 
     mozilla::ThreadSafeWeakPtr<mozilla::gfx::UnscaledFontFreeType> mUnscaledFont;
 
     bool mHasVariations = false;
     bool mHasVariationsInitialized = false;
+    bool mMMVarInitialized = false;
 };
 
 class FT2FontFamily : public gfxFontFamily
 {
 public:
     explicit FT2FontFamily(const nsAString& aName) :
         gfxFontFamily(aName) { }
 
--- a/gfx/thebes/gfxFcPlatformFontList.cpp
+++ b/gfx/thebes/gfxFcPlatformFontList.cpp
@@ -779,17 +779,17 @@ gfxFontconfigFontEntry::CreateScaledFont
     }
 
     AutoTArray<FT_Fixed,8> coords;
     if (HasVariations()) {
         FT_Face ftFace = GetFTFace();
         if (ftFace) {
             AutoTArray<gfxFontVariation,8> settings;
             GetVariationsForStyle(settings, *aStyle);
-            gfxFT2FontBase::SetupVarCoords(ftFace, settings, &coords);
+            gfxFT2FontBase::SetupVarCoords(GetMMVar(), settings, &coords);
         }
     }
 
     cairo_font_face_t *face =
         cairo_ft_font_face_create_for_pattern(aRenderPattern,
                                               coords.Elements(),
                                               coords.Length());
 
--- a/gfx/thebes/gfxFcPlatformFontList.h
+++ b/gfx/thebes/gfxFcPlatformFontList.h
@@ -112,17 +112,17 @@ public:
 
     FcPattern* GetPattern() { return mFontPattern; }
 
     nsresult ReadCMAP(FontInfoData *aFontInfoData = nullptr) override;
     bool TestCharacterMap(uint32_t aCh) override;
 
     FT_Face GetFTFace();
 
-    FT_MM_Var* GetMMVar();
+    FT_MM_Var* GetMMVar() override;
 
     bool HasVariations() override;
     void GetVariationAxes(nsTArray<gfxFontVariationAxis>& aAxes) override;
     void GetVariationInstances(nsTArray<gfxFontVariationInstance>& aInstances) override;
 
     hb_blob_t* GetFontTable(uint32_t aTableTag) override;
 
     void ForgetHBFace() override;
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -41,16 +41,20 @@
 #include "gfxMathTable.h"
 #include "gfxSVGGlyphs.h"
 #include "gfx2DGlue.h"
 #include "TextDrawTarget.h"
 
 #include "GreekCasing.h"
 
 #include "cairo.h"
+#ifdef XP_WIN
+#include "cairo-win32.h"
+#include "gfxWindowsPlatform.h"
+#endif
 
 #include "harfbuzz/hb.h"
 #include "harfbuzz/hb-ot.h"
 
 #include <algorithm>
 #include <limits>
 #include <cmath>
 
--- a/gfx/thebes/gfxFontEntry.h
+++ b/gfx/thebes/gfxFontEntry.h
@@ -22,16 +22,17 @@
 #include "harfbuzz/hb.h"
 #include "mozilla/FontPropertyTypes.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/WeakPtr.h"
 #include <math.h>
 
 typedef struct gr_face gr_face;
+typedef struct FT_MM_Var_ FT_MM_Var;
 
 #ifdef DEBUG
 #include <stdio.h>
 #endif
 
 struct gfxFontStyle;
 class gfxContext;
 class gfxFont;
@@ -392,16 +393,19 @@ public:
     // Get variation axis settings that should be used to implement a particular
     // font style using this resource.
     void GetVariationsForStyle(nsTArray<gfxFontVariation>& aResult,
                                const gfxFontStyle& aStyle);
 
     // Get the font's list of features (if any) for DevTools support.
     void GetFeatureInfo(nsTArray<gfxFontFeatureInfo>& aFeatureInfo);
 
+    // This is only called on platforms where we use FreeType.
+    virtual FT_MM_Var* GetMMVar() { return nullptr; }
+
     nsString         mName;
     nsString         mFamilyName;
 
     RefPtr<gfxCharacterMap> mCharacterMap;
 
     mozilla::UniquePtr<uint8_t[]> mUVSData;
     mozilla::UniquePtr<gfxUserFontData> mUserFontData;
     mozilla::UniquePtr<gfxSVGGlyphs> mSVGGlyphs;
--- a/gfx/thebes/gfxFontInfoLoader.cpp
+++ b/gfx/thebes/gfxFontInfoLoader.cpp
@@ -4,16 +4,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "gfxFontInfoLoader.h"
 #include "nsCRT.h"
 #include "nsIObserverService.h"
 #include "nsThreadUtils.h"              // for nsRunnable
 #include "gfxPlatformFontList.h"
 
+#ifdef XP_WIN
+#include <windows.h>
+#endif
+
 using namespace mozilla;
 using services::GetObserverService;
 
 #define LOG_FONTINIT(args) MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontinit), \
                                LogLevel::Debug, args)
 #define LOG_FONTINIT_ENABLED() MOZ_LOG_TEST( \
                                    gfxPlatform::GetLog(eGfxLog_fontinit), \
                                    LogLevel::Debug)
--- a/gfx/thebes/gfxFontMissingGlyphs.h
+++ b/gfx/thebes/gfxFontMissingGlyphs.h
@@ -2,16 +2,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/. */
 
 #ifndef GFX_FONTMISSINGGLYPHS_H
 #define GFX_FONTMISSINGGLYPHS_H
 
 #include "mozilla/Attributes.h"
+#include "mozilla/gfx/MatrixFwd.h"
 #include "mozilla/gfx/Rect.h"
 
 namespace mozilla {
 namespace gfx {
 class DrawTarget;
 class Pattern;
 } // namespace gfx
 } // namespace mozilla
@@ -19,16 +20,17 @@ class Pattern;
 /**
  * This class should not be instantiated. It's just a container
  * for some helper functions.
  */
 class gfxFontMissingGlyphs final
 {
     typedef mozilla::gfx::DrawTarget DrawTarget;
     typedef mozilla::gfx::Float Float;
+    typedef mozilla::gfx::Matrix Matrix;
     typedef mozilla::gfx::Pattern Pattern;
     typedef mozilla::gfx::Rect Rect;
 
     gfxFontMissingGlyphs() = delete; // prevent instantiation
 
 public:
     /**
      * Draw hexboxes for a missing glyph.
--- a/gfx/thebes/gfxPattern.cpp
+++ b/gfx/thebes/gfxPattern.cpp
@@ -177,30 +177,30 @@ gfxPattern::IsOpaque()
 
   if (static_cast<SurfacePattern*>(mGfxPattern.GetPattern())->mSurface->GetFormat() == SurfaceFormat::B8G8R8X8) {
     return true;
   }
   return false;
 }
 
 void
-gfxPattern::SetSamplingFilter(gfx::SamplingFilter filter)
+gfxPattern::SetSamplingFilter(mozilla::gfx::SamplingFilter filter)
 {
   if (mGfxPattern.GetPattern()->GetType() != PatternType::SURFACE) {
     return;
   }
 
   static_cast<SurfacePattern*>(mGfxPattern.GetPattern())->mSamplingFilter = filter;
 }
 
 SamplingFilter
 gfxPattern::SamplingFilter() const
 {
   if (mGfxPattern.GetPattern()->GetType() != PatternType::SURFACE) {
-    return gfx::SamplingFilter::GOOD;
+    return mozilla::gfx::SamplingFilter::GOOD;
   }
   return static_cast<const SurfacePattern*>(mGfxPattern.GetPattern())->mSamplingFilter;
 }
 
 bool
 gfxPattern::GetSolidColor(Color& aColorOut)
 {
   if (mGfxPattern.GetPattern()->GetType() == PatternType::COLOR) {
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -288,16 +288,17 @@ private:
   // This is where DECL_GFX_PREF for each of the preferences should go.
   // We will keep these in an alphabetical order to make it easier to see if
   // a method accessing a pref already exists. Just add yours in the list.
 
   DECL_GFX_PREF(Live, "accessibility.browsewithcaret", AccessibilityBrowseWithCaret, bool, false);
 
   // The apz prefs are explained in AsyncPanZoomController.cpp
   DECL_GFX_PREF(Live, "apz.allow_checkerboarding",             APZAllowCheckerboarding, bool, true);
+  DECL_GFX_PREF(Live, "apz.allow_double_tap_zooming",          APZAllowDoubleTapZooming, bool, true);
   DECL_GFX_PREF(Live, "apz.allow_immediate_handoff",           APZAllowImmediateHandoff, bool, true);
   DECL_GFX_PREF(Live, "apz.allow_zooming",                     APZAllowZooming, bool, false);
   DECL_GFX_PREF(Live, "apz.android.chrome_fling_physics.enabled", APZUseChromeFlingPhysics, bool, false);
   DECL_GFX_PREF(Live, "apz.android.chrome_fling_physics.friction", APZChromeFlingPhysicsFriction, float, 0.015f);
   DECL_GFX_PREF(Live, "apz.android.chrome_fling_physics.inflexion", APZChromeFlingPhysicsInflexion, float, 0.35f);
   DECL_GFX_PREF(Live, "apz.android.chrome_fling_physics.stop_threshold", APZChromeFlingPhysicsStopThreshold, float, 0.1f);
   DECL_GFX_PREF(Live, "apz.autoscroll.enabled",                APZAutoscrollEnabled, bool, false);
   DECL_GFX_PREF(Live, "apz.axis_lock.breakout_angle",          APZAxisBreakoutAngle, float, float(M_PI / 8.0) /* 22.5 degrees */);
--- a/gfx/thebes/gfxSVGGlyphs.cpp
+++ b/gfx/thebes/gfxSVGGlyphs.cpp
@@ -35,17 +35,17 @@
 
 #define SVG_CONTENT_TYPE NS_LITERAL_CSTRING("image/svg+xml")
 #define UTF8_CHARSET NS_LITERAL_CSTRING("utf-8")
 
 using namespace mozilla;
 
 typedef mozilla::dom::Element Element;
 
-/* static */ const Color SimpleTextContextPaint::sZero = Color();
+/* static */ const mozilla::gfx::Color SimpleTextContextPaint::sZero;
 
 gfxSVGGlyphs::gfxSVGGlyphs(hb_blob_t *aSVGTable, gfxFontEntry *aFontEntry)
     : mSVGData(aSVGTable)
     , mFontEntry(aFontEntry)
 {
     unsigned int length;
     const char* svgData = hb_blob_get_data(mSVGData, &length);
     mHeader = reinterpret_cast<const Header*>(svgData);
--- a/gfx/thebes/gfxSkipChars.cpp
+++ b/gfx/thebes/gfxSkipChars.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * 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 "gfxSkipChars.h"
 #include "mozilla/BinarySearch.h"
+#include "mozilla/gfx/Logging.h"
 
 struct SkippedRangeStartComparator
 {
     const uint32_t mOffset;
     explicit SkippedRangeStartComparator(const uint32_t aOffset) : mOffset(aOffset) {}
     int operator()(const gfxSkipChars::SkippedRange& aRange) const {
         return (mOffset < aRange.Start()) ? -1 : 1;
     }
--- a/gfx/thebes/gfxTextRun.cpp
+++ b/gfx/thebes/gfxTextRun.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* vim: set ts=4 et sw=4 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "gfxTextRun.h"
 #include "gfxGlyphExtents.h"
+#include "gfxHarfBuzzShaper.h"
 #include "gfxPlatformFontList.h"
 #include "gfxUserFontSet.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/PathHelpers.h"
 #include "mozilla/Sprintf.h"
 
 #include "gfxContext.h"
 #include "gfxFontConstants.h"
--- a/gfx/thebes/moz.build
+++ b/gfx/thebes/moz.build
@@ -171,34 +171,39 @@ if CONFIG['INTEL_ARCHITECTURE']:
 SOURCES += [
     # Includes mac system header conflicting with point/size,
     # and includes glxXlibSurface.h which drags in Xrender.h
     'gfxASurface.cpp',
     # on X11, gfxDrawable.cpp includes X headers for an old workaround which
     # we could consider removing soon (affects Ubuntus older than 10.04 LTS)
     # which currently prevent it from joining UNIFIED_SOURCES.
     'gfxDrawable.cpp',
+    # gfxFontInfoLoader.cpp needs to set no_pgo for clang-cl
+    'gfxFontInfoLoader.cpp',
     # gfxFontUtils.cpp and gfxPlatform.cpp include mac system header conflicting with point/size
     'gfxFontUtils.cpp',
     'gfxPlatform.cpp',
     'gfxPrefs.cpp',
     'PrintTarget.cpp',
     'PrintTargetThebes.cpp',
 ]
 
+if CONFIG['CC_TYPE'] == 'clang-cl':
+    # clang-cl crashes trying to PGO this file (Bug 1479842)
+    SOURCES['gfxFontInfoLoader.cpp'].no_pgo = True
+
 UNIFIED_SOURCES += [
     'CJKCompatSVS.cpp',
     'gfxAlphaRecovery.cpp',
     'gfxBaseSharedMemorySurface.cpp',
     'gfxBlur.cpp',
     'gfxContext.cpp',
     'gfxFont.cpp',
     'gfxFontEntry.cpp',
     'gfxFontFeatures.cpp',
-    'gfxFontInfoLoader.cpp',
     'gfxFontMissingGlyphs.cpp',
     'gfxFontSrcPrincipal.cpp',
     'gfxFontSrcURI.cpp',
     'gfxGlyphExtents.cpp',
     'gfxGradientCache.cpp',
     'gfxGraphiteShaper.cpp',
     'gfxHarfBuzzShaper.cpp',
     'gfxImageSurface.cpp',
--- a/ipc/ipdl/sync-messages.ini
+++ b/ipc/ipdl/sync-messages.ini
@@ -609,18 +609,16 @@ platform = win
 
 # CPOWs
 [PBrowser::RpcMessage]
 description =
 [PContent::RpcMessage]
 description =
 [PJavaScript::PreventExtensions]
 description =
-[PJavaScript::GetPropertyDescriptor]
-description =
 [PJavaScript::GetOwnPropertyDescriptor]
 description =
 [PJavaScript::DefineProperty]
 description =
 [PJavaScript::Delete]
 description =
 [PJavaScript::Has]
 description =
--- a/js/ipc/JavaScriptBase.h
+++ b/js/ipc/JavaScriptBase.h
@@ -32,25 +32,16 @@ class JavaScriptBase : public WrapperOwn
 
     mozilla::ipc::IPCResult RecvPreventExtensions(const uint64_t& objId, ReturnStatus* rs) override {
         Maybe<ObjectId> obj(ObjectId::deserialize(objId));
         if (obj.isNothing() || !Answer::RecvPreventExtensions(obj.value(), rs)) {
             return IPC_FAIL_NO_REASON(this);
         }
         return IPC_OK();
     }
-    mozilla::ipc::IPCResult RecvGetPropertyDescriptor(const uint64_t& objId, const JSIDVariant& id,
-                                                      ReturnStatus* rs,
-                                                      PPropertyDescriptor* out) override {
-        Maybe<ObjectId> obj(ObjectId::deserialize(objId));
-        if (obj.isNothing() || !Answer::RecvGetPropertyDescriptor(obj.value(), id, rs, out)) {
-            return IPC_FAIL_NO_REASON(this);
-        }
-        return IPC_OK();
-    }
     mozilla::ipc::IPCResult RecvGetOwnPropertyDescriptor(const uint64_t& objId,
                                                          const JSIDVariant& id,
                                                          ReturnStatus* rs,
                                                          PPropertyDescriptor* out) override {
         Maybe<ObjectId> obj(ObjectId::deserialize(objId));
         if (obj.isNothing() || !Answer::RecvGetOwnPropertyDescriptor(obj.value(), id, rs, out)) {
             return IPC_FAIL_NO_REASON(this);
         }
@@ -211,21 +202,16 @@ class JavaScriptBase : public WrapperOwn
     /*** Dummy call handlers ***/
 
     bool SendDropObject(const ObjectId& objId) override {
         return Base::SendDropObject(objId.serialize());
     }
     bool SendPreventExtensions(const ObjectId& objId, ReturnStatus* rs) override {
         return Base::SendPreventExtensions(objId.serialize(), rs);
     }
-    bool SendGetPropertyDescriptor(const ObjectId& objId, const JSIDVariant& id,
-                                   ReturnStatus* rs,
-                                   PPropertyDescriptor* out) override {
-        return Base::SendGetPropertyDescriptor(objId.serialize(), id, rs, out);
-    }
     bool SendGetOwnPropertyDescriptor(const ObjectId& objId,
                                       const JSIDVariant& id,
                                       ReturnStatus* rs,
                                       PPropertyDescriptor* out) override {
         return Base::SendGetOwnPropertyDescriptor(objId.serialize(), id, rs, out);
     }
     bool SendDefineProperty(const ObjectId& objId, const JSIDVariant& id,
                             const PPropertyDescriptor& flags,
--- a/js/ipc/PJavaScript.ipdl
+++ b/js/ipc/PJavaScript.ipdl
@@ -20,17 +20,16 @@ nested(upto inside_sync) sync protocol P
     manager PContent or PContentBridge;
 
 both:
     // Sent when a CPOW has been finalized and table entries can be freed up.
     async DropObject(uint64_t objId);
 
     // These roughly map to the ProxyHandler hooks that CPOWs need.
     nested(inside_sync) sync PreventExtensions(uint64_t objId) returns (ReturnStatus rs);
-    nested(inside_sync) sync GetPropertyDescriptor(uint64_t objId, JSIDVariant id) returns (ReturnStatus rs, PPropertyDescriptor result);
     nested(inside_sync) sync GetOwnPropertyDescriptor(uint64_t objId, JSIDVariant id) returns (ReturnStatus rs, PPropertyDescriptor result);
     nested(inside_sync) sync DefineProperty(uint64_t objId, JSIDVariant id, PPropertyDescriptor descriptor) returns (ReturnStatus rs);
     nested(inside_sync) sync Delete(uint64_t objId, JSIDVariant id) returns (ReturnStatus rs);
 
     nested(inside_sync) sync Has(uint64_t objId, JSIDVariant id) returns (ReturnStatus rs, bool has);
     nested(inside_sync) sync HasOwn(uint64_t objId, JSIDVariant id) returns (ReturnStatus rs, bool has);
     nested(inside_sync) sync Get(uint64_t objId, JSVariant receiver, JSIDVariant id) returns (ReturnStatus rs, JSVariant result);
     nested(inside_sync) sync Set(uint64_t objId, JSIDVariant id, JSVariant value, JSVariant receiver) returns (ReturnStatus rs);
--- a/js/ipc/WrapperAnswer.cpp
+++ b/js/ipc/WrapperAnswer.cpp
@@ -126,51 +126,16 @@ EmptyDesc(PPropertyDescriptor* desc)
     desc->obj() = LocalObject(0);
     desc->attrs() = 0;
     desc->value() = UndefinedVariant();
     desc->getter() = 0;
     desc->setter() = 0;
 }
 
 bool
-WrapperAnswer::RecvGetPropertyDescriptor(const ObjectId& objId, const JSIDVariant& idVar,
-                                         ReturnStatus* rs, PPropertyDescriptor* out)
-{
-    if (!IsInAutomation())
-        return false;
-
-    MaybeForceDebugGC();
-
-    AutoJSAPI jsapi;
-    if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects())))
-        return false;
-    JSContext* cx = jsapi.cx();
-    EmptyDesc(out);
-
-    RootedObject obj(cx, findObjectById(cx, objId));
-    if (!obj)
-        return deadCPOW(jsapi, rs);
-
-    LOG("%s.getPropertyDescriptor(%s)", ReceiverObj(objId), Identifier(idVar));
-
-    RootedId id(cx);
-    if (!fromJSIDVariant(cx, idVar, &id))
-        return fail(jsapi, rs);
-
-    Rooted<PropertyDescriptor> desc(cx);
-    if (!JS_GetPropertyDescriptorById(cx, obj, id, &desc))
-        return fail(jsapi, rs);
-
-    if (!fromDescriptor(cx, desc, out))
-        return fail(jsapi, rs);
-
-    return ok(rs);
-}
-
-bool
 WrapperAnswer::RecvGetOwnPropertyDescriptor(const ObjectId& objId, const JSIDVariant& idVar,
                                             ReturnStatus* rs, PPropertyDescriptor* out)
 {
     if (!IsInAutomation())
         return false;
 
     MaybeForceDebugGC();
 
--- a/js/ipc/WrapperAnswer.h
+++ b/js/ipc/WrapperAnswer.h
@@ -17,19 +17,16 @@ class AutoJSAPI;
 } // namespace dom
 
 namespace jsipc {
 
 class WrapperAnswer : public virtual JavaScriptShared
 {
   public:
     bool RecvPreventExtensions(const ObjectId& objId, ReturnStatus* rs);
-    bool RecvGetPropertyDescriptor(const ObjectId& objId, const JSIDVariant& id,
-                                   ReturnStatus* rs,
-                                   PPropertyDescriptor* out);
     bool RecvGetOwnPropertyDescriptor(const ObjectId& objId,
                                       const JSIDVariant& id,
                                       ReturnStatus* rs,
                                       PPropertyDescriptor* out);
     bool RecvDefineProperty(const ObjectId& objId, const JSIDVariant& id,
                             const PPropertyDescriptor& flags, ReturnStatus* rs);
     bool RecvDelete(const ObjectId& objId, const JSIDVariant& id, ReturnStatus* rs);
 
--- a/js/ipc/WrapperOwner.cpp
+++ b/js/ipc/WrapperOwner.cpp
@@ -113,18 +113,16 @@ class CPOWProxyHandler : public BaseProx
     virtual bool has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const override;
     virtual bool get(JSContext* cx, HandleObject proxy, HandleValue receiver,
                      HandleId id, MutableHandleValue vp) const override;
     virtual bool set(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleValue v,
                      JS::HandleValue receiver, JS::ObjectOpResult& result) const override;
     virtual bool call(JSContext* cx, HandleObject proxy, const CallArgs& args) const override;
     virtual bool construct(JSContext* cx, HandleObject proxy, const CallArgs& args) const override;
 
-    virtual bool getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
-                                       MutableHandle<PropertyDescriptor> desc) const override;
     virtual bool hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const override;
     virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy,
                                               AutoIdVector& props) const override;
     virtual bool hasInstance(JSContext* cx, HandleObject proxy,
                              MutableHandleValue v, bool* bp) const override;
     virtual bool getBuiltinClass(JSContext* cx, HandleObject obj, js::ESClass* cls) const override;
     virtual bool isArray(JSContext* cx, HandleObject obj,
                          IsArrayAnswer* answer) const override;
@@ -156,46 +154,16 @@ const CPOWProxyHandler CPOWProxyHandler:
         return failRetVal;                                              \
     }                                                                   \
     {                                                                   \
         CPOWTimer timer(cx);                                            \
         return owner->call args;                                        \
     }
 
 bool
-CPOWProxyHandler::getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
-                                        MutableHandle<PropertyDescriptor> desc) const
-{
-    FORWARD(getPropertyDescriptor, (cx, proxy, id, desc), false);
-}
-
-bool
-WrapperOwner::getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
-                                    MutableHandle<PropertyDescriptor> desc)
-{
-    ObjectId objId = idOf(proxy);
-
-    JSIDVariant idVar;
-    if (!toJSIDVariant(cx, id, &idVar))
-        return false;
-
-    ReturnStatus status;
-    PPropertyDescriptor result;
-    if (!SendGetPropertyDescriptor(objId, idVar, &status, &result))
-        return ipcfail(cx);
-
-    LOG_STACK();
-
-    if (!ok(cx, status))
-        return false;
-
-    return toDescriptor(cx, result, desc);
-}
-
-bool
 CPOWProxyHandler::getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
                                            MutableHandle<PropertyDescriptor> desc) const
 {
     FORWARD(getOwnPropertyDescriptor, (cx, proxy, id, desc), false);
 }
 
 bool
 WrapperOwner::getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
--- a/js/ipc/WrapperOwner.h
+++ b/js/ipc/WrapperOwner.h
@@ -42,18 +42,16 @@ class WrapperOwner : public virtual Java
     bool get(JSContext* cx, JS::HandleObject proxy, JS::HandleValue receiver,
              JS::HandleId id, JS::MutableHandleValue vp);
     bool set(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleValue v,
              JS::HandleValue receiver, JS::ObjectOpResult& result);
     bool callOrConstruct(JSContext* cx, JS::HandleObject proxy, const JS::CallArgs& args,
                          bool construct);
 
     // SpiderMonkey extensions.
-    bool getPropertyDescriptor(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
-                               JS::MutableHandle<JS::PropertyDescriptor> desc);
     bool hasOwn(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, bool* bp);
     bool getOwnEnumerablePropertyKeys(JSContext* cx, JS::HandleObject proxy,
                                       JS::AutoIdVector& props);
     bool hasInstance(JSContext* cx, JS::HandleObject proxy, JS::MutableHandleValue v, bool* bp);
     bool getBuiltinClass(JSContext* cx, JS::HandleObject proxy, js::ESClass* cls);
     bool isArray(JSContext* cx, JS::HandleObject proxy, JS::IsArrayAnswer* answer);
     const char* className(JSContext* cx, JS::HandleObject proxy);
     bool getPrototype(JSContext* cx, JS::HandleObject proxy, JS::MutableHandleObject protop);
@@ -107,19 +105,16 @@ class WrapperOwner : public virtual Java
     bool ok(JSContext* cx, const ReturnStatus& status);
 
     bool inactive_;
 
     /*** Dummy call handlers ***/
   public:
     virtual bool SendDropObject(const ObjectId& objId) = 0;
     virtual bool SendPreventExtensions(const ObjectId& objId, ReturnStatus* rs) = 0;
-    virtual bool SendGetPropertyDescriptor(const ObjectId& objId, const JSIDVariant& id,
-                                           ReturnStatus* rs,
-                                           PPropertyDescriptor* out) = 0;
     virtual bool SendGetOwnPropertyDescriptor(const ObjectId& objId,
                                               const JSIDVariant& id,
                                               ReturnStatus* rs,
                                               PPropertyDescriptor* out) = 0;
     virtual bool SendDefineProperty(const ObjectId& objId, const JSIDVariant& id,
                                     const PPropertyDescriptor& flags,
                                     ReturnStatus* rs) = 0;
     virtual bool SendDelete(const ObjectId& objId, const JSIDVariant& id,
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/typedarray-set.js
@@ -0,0 +1,29 @@
+// Based on work by André Bargull
+
+function f() {
+    var x = [1,2,3];
+    x[3] = 0xff;
+
+    // Should have been defined on typed array.
+    assertEq(x.length, 3);
+    assertEq(x[3], -1);
+
+    x[3] = 0;
+}
+
+Object.setPrototypeOf(Array.prototype, new Int8Array(4));
+f();
+f();
+
+function g() {
+    var x = [1,2,3,4];
+    x[4] = 0xff;
+
+    // OOB [[Set]] should have been ignored
+    assertEq(x.length, 4);
+    assertEq(x[4], undefined);
+}
+
+Object.setPrototypeOf(Array.prototype, new Int8Array(4));
+g();
+g();
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -3591,16 +3591,20 @@ CanAttachAddElement(NativeObject* obj, b
 
         JSObject* proto = obj->staticPrototype();
         if (!proto)
             break;
 
         if (!proto->isNative())
             return false;
 
+        // TypedArrayObjects [[Set]] has special behavior.
+        if (proto->is<TypedArrayObject>())
+            return false;
+
         // We have to make sure the proto has no non-writable (frozen) elements
         // because we're not allowed to shadow them. There are a few cases to
         // consider:
         //
         // * If the proto is extensible, its Shape will change when it's made
         //   non-extensible.
         //
         // * If the proto is already non-extensible, no new elements will be
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -1915,72 +1915,41 @@ js::NativeDefineDataProperty(JSContext* 
 bool
 js::NativeDefineDataProperty(JSContext* cx, HandleNativeObject obj, PropertyName* name,
                              HandleValue value, unsigned attrs)
 {
     RootedId id(cx, NameToId(name));
     return NativeDefineDataProperty(cx, obj, id, value, attrs);
 }
 
-
-// ES2019 draft rev e7dc63fb5d1c26beada9ffc12dc78aa6548f1fb5
-// 9.4.5.9 IntegerIndexedElementSet
-static bool
-DefineNonexistentTypedArrayElement(JSContext* cx, Handle<TypedArrayObject*> obj, uint64_t index,
-                                   HandleValue v, ObjectOpResult& result)
-{
-    // This method is only called for non-existent properties, which
-    // means any absent indexed property must be out of range.
-    MOZ_ASSERT(index >= obj->length());
-
-    // Steps 1-2 are enforced by the caller.
-
-    // Step 3.
-    // We still need to call ToNumber, because of its possible side
-    // effects.
-    double d;
-    if (!ToNumber(cx, v, &d))
-        return false;
-
-    // Steps 4-5.
-    // ToNumber may have detached the array buffer.
-    if (obj->hasDetachedBuffer())
-        return result.failSoft(JSMSG_TYPED_ARRAY_DETACHED);
-
-    // Steps 6-9.
-    // We (wrongly) ignore out of range defines.
-    return result.failSoft(JSMSG_BAD_INDEX);
-}
-
 static bool
 DefineNonexistentProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
                           HandleValue v, ObjectOpResult& result)
 {
     // Optimized NativeDefineProperty() version for known absent properties.
 
+#ifdef DEBUG
+    // Indexed properties of typed arrays should have been handled by SetTypedArrayElement.
+    uint64_t index;
+    MOZ_ASSERT_IF(obj->is<TypedArrayObject>(), !IsTypedArrayIndex(id, &index));
+#endif
+
     // Dispense with custom behavior of exotic native objects first.
     if (obj->is<ArrayObject>()) {
         // Array's length property is non-configurable, so we shouldn't
         // encounter it in this function.
         MOZ_ASSERT(id != NameToId(cx->names().length));
 
         // 9.4.2.1 step 3. Don't extend a fixed-length array.
         uint32_t index;
         if (IdIsIndex(id, &index)) {
             if (WouldDefinePastNonwritableLength(&obj->as<ArrayObject>(), index))
                 return result.fail(JSMSG_CANT_DEFINE_PAST_ARRAY_LENGTH);
         }
-    } else if (obj->is<TypedArrayObject>()) {
-        // 9.4.5.5 step 2. Indexed properties of typed arrays are special.
-        uint64_t index;
-        if (IsTypedArrayIndex(id, &index)) {
-            Rooted<TypedArrayObject*> tobj(cx, &obj->as<TypedArrayObject>());
-            return DefineNonexistentTypedArrayElement(cx, tobj, index, v, result);
-        }
-    } else if (obj->is<ArgumentsObject>()) {
+    }  else if (obj->is<ArgumentsObject>()) {
         // If this method is called with either |length| or |@@iterator|, the
         // property was previously deleted and hence should already be marked
         // as overridden.
         MOZ_ASSERT_IF(id == NameToId(cx->names().length),
                       obj->as<ArgumentsObject>().hasOverriddenLength());
         MOZ_ASSERT_IF(JSID_IS_SYMBOL(id) && JSID_TO_SYMBOL(id) == cx->wellKnownSymbols().iterator,
                       obj->as<ArgumentsObject>().hasOverriddenIterator());
 
@@ -2639,76 +2608,66 @@ SetNonexistentProperty(JSContext* cx, Ha
             if (!op(cx, obj, id, &desc))
                 return false;
 
             MOZ_ASSERT(!desc.object());
         }
 #endif
 
         // Step 5.e. Define the new data property.
-
         if (DefinePropertyOp op = obj->getOpsDefineProperty()) {
             // Purge the property cache of now-shadowed id in receiver's environment chain.
             if (!ReshapeForShadowedProp(cx, obj, id))
                 return false;
 
             Rooted<PropertyDescriptor> desc(cx);
             desc.initFields(nullptr, v, JSPROP_ENUMERATE, nullptr, nullptr);
 
             MOZ_ASSERT(!cx->helperThread());
             return op(cx, obj, id, desc, result);
         }
 
         return DefineNonexistentProperty(cx, obj, id, v, result);
     }
 
-    if (IsQualified && obj->is<TypedArrayObject>()) {
-        // 9.4.5.5 step 2. Indexed properties of typed arrays are special.
-        uint64_t index;
-        if (IsTypedArrayIndex(id, &index)) {
-            Rooted<TypedArrayObject*> tobj(cx, &obj->as<TypedArrayObject>());
-            return DefineNonexistentTypedArrayElement(cx, tobj, index, v, result);
-        }
-    }
-
     return SetPropertyByDefining(cx, id, v, receiver, result);
 }
 
 // ES2019 draft rev e7dc63fb5d1c26beada9ffc12dc78aa6548f1fb5
 // 9.4.5.9 IntegerIndexedElementSet
-// Set an existing own property obj[index] that's a typed array element.
 static bool
-SetTypedArrayElement(JSContext* cx, Handle<TypedArrayObject*> obj, uint32_t index, HandleValue v,
+SetTypedArrayElement(JSContext* cx, Handle<TypedArrayObject*> obj, uint64_t index, HandleValue v,
                      ObjectOpResult& result)
 {
-    // Steps 1-2 are enforced by the caller.
+    // Steps 1-2. Are enforced by the caller.
 
     // Step 3.
     double d;
     if (!ToNumber(cx, v, &d))
         return false;
 
-    // Steps 6-7 don't apply for existing typed array elements.
-
-    // Steps 8-16.
-    // Silently do nothing for out-of-bounds sets, for consistency with
-    // current behavior.  (ES6 currently says to throw for this in
-    // strict mode code, so we may eventually need to change.)
-    uint32_t len = obj->as<TypedArrayObject>().length();
-    if (index < len) {
-        TypedArrayObject::setElement(obj->as<TypedArrayObject>(), index, d);
-        return result.succeed();
-    }
-
-    // Steps 4-5.
-    // A previously existing typed array element can only be out-of-bounds
-    // if the above ToNumber call detached the typed array's buffer.
-    MOZ_ASSERT(obj->as<TypedArrayObject>().hasDetachedBuffer());
-
-    return result.failSoft(JSMSG_TYPED_ARRAY_DETACHED);
+    // Step 5.
+    if (obj->hasDetachedBuffer())
+        return result.failSoft(JSMSG_TYPED_ARRAY_DETACHED);
+
+    // Step 6. Right now we only allow integer indexes.
+    // Step 7.
+
+    // Step 8.
+    uint32_t length = obj->length();
+
+    // Step 9.
+    if (index >= length)
+        return result.failSoft(JSMSG_BAD_INDEX);
+
+    // Steps 4, 10-15.
+    TypedArrayObject::setElement(*obj, index, d);
+
+    // Step 16.
+    return result.succeed();
 }
 
 // Set an existing own property obj[index] that's a dense element.
 static bool
 SetDenseElement(JSContext* cx, HandleNativeObject obj, uint32_t index, HandleValue v,
                 ObjectOpResult& result)
 {
     MOZ_ASSERT(!obj->is<TypedArrayObject>());
@@ -2730,21 +2689,17 @@ SetDenseElement(JSContext* cx, HandleNat
  * dense or typed array element (i.e. not actually a pointer to a Shape).
  */
 static bool
 SetExistingProperty(JSContext* cx, HandleId id, HandleValue v, HandleValue receiver,
                     HandleNativeObject pobj, Handle<PropertyResult> prop, ObjectOpResult& result)
 {
     // Step 5 for dense elements.
     if (prop.isDenseOrTypedArrayElement()) {
-        // TypedArray [[Set]] ignores the receiver completely.
-        if (pobj->is<TypedArrayObject>()) {
-            Rooted<TypedArrayObject*> tobj(cx, &pobj->as<TypedArrayObject>());
-            return SetTypedArrayElement(cx, tobj, JSID_TO_INT(id), v, result);
-        }
+        MOZ_ASSERT(!pobj->is<TypedArrayObject>());
 
         // Step 5.a.
         if (pobj->denseElementsAreFrozen())
             return result.fail(JSMSG_READ_ONLY);
 
         // Pure optimization for the common case:
         if (receiver.isObject() && pobj == &receiver.toObject())
             return SetDenseElement(cx, pobj, JSID_TO_INT(id), v, result);
@@ -2803,16 +2758,27 @@ js::NativeSetProperty(JSContext* cx, Han
     // 4.c.i below. (There's a very similar loop in the NativeGetProperty
     // implementation, but unfortunately not similar enough to common up.)
     for (;;) {
         // Steps 2-3. ('done' is a SpiderMonkey-specific thing, used below.)
         bool done;
         if (!LookupOwnPropertyInline<CanGC>(cx, pobj, id, &prop, &done))
             return false;
 
+        if (pobj->is<TypedArrayObject>()) {
+            uint64_t index;
+            if (IsTypedArrayIndex(id, &index)) {
+                Rooted<TypedArrayObject*> tobj(cx, &pobj->as<TypedArrayObject>());
+                return SetTypedArrayElement(cx, tobj, index, v, result);
+            }
+
+            // This case should have been handled.
+            MOZ_ASSERT(!prop.isDenseOrTypedArrayElement());
+        }
+
         if (prop) {
             // Steps 5-6.
             return SetExistingProperty(cx, id, v, receiver, pobj, prop, result);
         }
 
         // Steps 4.a-b. The check for 'done' on this next line is tricky.
         // done can be true in exactly these unlikely-sounding cases:
         // - We're looking up an element, and pobj is a TypedArray that
--- a/layout/base/ZoomConstraintsClient.cpp
+++ b/layout/base/ZoomConstraintsClient.cpp
@@ -176,17 +176,17 @@ ZoomConstraintsClient::ScreenSizeChanged
   RefreshZoomConstraints();
 }
 
 static mozilla::layers::ZoomConstraints
 ComputeZoomConstraintsFromViewportInfo(const nsViewportInfo& aViewportInfo)
 {
   mozilla::layers::ZoomConstraints constraints;
   constraints.mAllowZoom = aViewportInfo.IsZoomAllowed() && gfxPrefs::APZAllowZooming();
-  constraints.mAllowDoubleTapZoom = constraints.mAllowZoom;
+  constraints.mAllowDoubleTapZoom = constraints.mAllowZoom && gfxPrefs::APZAllowDoubleTapZooming();
   if (constraints.mAllowZoom) {
     constraints.mMinZoom.scale = aViewportInfo.GetMinZoom().scale;
     constraints.mMaxZoom.scale = aViewportInfo.GetMaxZoom().scale;
   } else {
     constraints.mMinZoom.scale = aViewportInfo.GetDefaultZoom().scale;
     constraints.mMaxZoom.scale = aViewportInfo.GetDefaultZoom().scale;
   }
   return constraints;
--- a/layout/base/tests/chrome/bug1041200_window.html
+++ b/layout/base/tests/chrome/bug1041200_window.html
@@ -9,22 +9,17 @@
 <body>
 <iframe style="width:700px; height:500px; margin-top:200px;" id="ourFrame"></iframe>
 <script>
 var SpecialPowers = window.opener.wrappedJSObject.SpecialPowers;
 var SimpleTest = window.opener.wrappedJSObject.SimpleTest;
 var ok = window.opener.wrappedJSObject.ok;
 var info = window.opener.wrappedJSObject.info;
 
-var viewer =
-  SpecialPowers.wrap(ourFrame).contentWindow
-               .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
-               .getInterface(SpecialPowers.Ci.nsIWebNavigation)
-               .QueryInterface(SpecialPowers.Ci.nsIDocShell)
-               .contentViewer;
+var viewer = SpecialPowers.wrap(ourFrame).contentWindow.docShell.contentViewer;
 viewer.fullZoom = 2;
 
 SimpleTest.waitForExplicitFinish();
 
 window.onload = function() {
   window.waitForAllPaintsFlushed(function () {
     // Supply random key to ensure load actually happens
     ourFrame.src = "bug1041200_frame.html?" + Math.random();
--- a/layout/base/tests/chrome/file_bug1018265.xul
+++ b/layout/base/tests/chrome/file_bug1018265.xul
@@ -31,18 +31,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   function didGoBack(e) {
     testcontent.removeEventListener("pageshow", didGoBack, true);
     shouldHaveTwoNonHiddenContentViewers();
     opener.done();
     window.close();
   }
 
   function getContentViewer(win) {
-    return win.QueryInterface(Ci.nsIInterfaceRequestor)
-              .getInterface(Ci.nsIDocShell).contentViewer;
+    return win.docShell.contentViewer;
   }
 
   function shouldHaveTwoNonHiddenContentViewers() {
     opener.is(getContentViewer(testcontent.contentWindow).isHidden, false, "Top level ContentViewer should not be hidden.");
     opener.is(getContentViewer(testcontent.contentWindow.frames[0]).isHidden, false, " Iframe's ContentViewer should not be hidden.");
   }
   ]]>
   </script>
--- a/layout/base/tests/chrome/test_bug396367-1.html
+++ b/layout/base/tests/chrome/test_bug396367-1.html
@@ -13,18 +13,17 @@ https://bugzilla.mozilla.org/show_bug.cg
     function finish() {
       ok(true, "didn't crash");
       var docviewer = getdocviewer();
       docviewer.textZoom = 1;
       SimpleTest.finish();
     }
 
     function getdocviewer() {
-      var navigator1 = top.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation);
-      var docShell = navigator1.QueryInterface(Ci.nsIDocShell);
+      var docShell = top.docShell;
       var docviewer = docShell.contentViewer;
       return docviewer;
     }
   </script>
 </head>
 <body>
 
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=396367">Mozilla Bug 396367</a>
--- a/layout/base/tests/chrome/test_bug396367-2.html
+++ b/layout/base/tests/chrome/test_bug396367-2.html
@@ -14,18 +14,17 @@ https://bugzilla.mozilla.org/show_bug.cg
     function finish() {
       ok(true, "didn't crash");
       var docviewer = getdocviewer();
       docviewer.textZoom = 1;
       SimpleTest.finish();
     }
 
     function getdocviewer() {
-      var navigator1 = top.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation);
-      var docShell = navigator1.QueryInterface(Ci.nsIDocShell);
+      var docShell = top.docShell;
       var docviewer = docShell.contentViewer;
       return docviewer;
     }
   </script>
 </head>
 <body>
 
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=396367">Mozilla Bug 396367</a>
--- a/layout/base/tests/chrome/test_bug420499.xul
+++ b/layout/base/tests/chrome/test_bug420499.xul
@@ -62,20 +62,17 @@ https://bugzilla.mozilla.org/show_bug.cg
     function getSelectionController() {
       return window.docShell
         .QueryInterface(Ci.nsIInterfaceRequestor)
         .getInterface(Ci.nsISelectionDisplay)
         .QueryInterface(Ci.nsISelectionController);
     }
 
     function isCaretVisible() {
-      window.QueryInterface(Ci.nsIInterfaceRequestor);
-      var docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                            .getInterface(Ci.nsIWebNavigation)
-                            .QueryInterface(Ci.nsIDocShell);
+      var docShell = window.docShell;
       var selCon = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
         .getInterface(Ci.nsISelectionDisplay)
         .QueryInterface(Ci.nsISelectionController);
       return selCon.caretVisible;
     }
     
     function focusInput() {
       ok(!isCaretVisible(), "Caret shouldn't be visible");
--- a/layout/base/tests/chrome/test_bug514660.xul
+++ b/layout/base/tests/chrome/test_bug514660.xul
@@ -17,21 +17,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 </body>
   <!-- test code goes here -->
 <script type="application/javascript">
 <![CDATA[
 SimpleTest.waitForExplicitFinish();
 
 function doTest()
 {
-  var viewer = window
-                 .QueryInterface(Ci.nsIInterfaceRequestor)
-                 .getInterface(Ci.nsIWebNavigation)
-                 .QueryInterface(Ci.nsIDocShell)
-                 .contentViewer;
+  var viewer = window.docShell.contentViewer;
   viewer.authorStyleDisabled = true;
 
   document.documentElement.getBoundingClientRect();
   ok(true, "Didn't crash");
 
   viewer.authorStyleDisabled = false;
 
   SimpleTest.finish();
--- a/layout/base/tests/chrome/test_bug708062.html
+++ b/layout/base/tests/chrome/test_bug708062.html
@@ -23,18 +23,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 function isBoundingClientRect(e, r, msg) {
   var BCR = e.getBoundingClientRect();
   is([BCR.left, BCR.top, BCR.right, BCR.bottom].join(','), r, msg);
 }
 
 function doTest() {
   var f = document.getElementById('f');
 
-  var navigator1 = f.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation);
-  var docShell = navigator1.QueryInterface(Ci.nsIDocShell);
+  var docShell = f.contentWindow.docShell;
   var docviewer = docShell.contentViewer;
 
   var d = f.contentDocument.getElementById('d');
 
   isBoundingClientRect(d, "-70,0,100,1", "initial rect");
   docviewer.fullZoom = 2;
   isBoundingClientRect(d, "-120,0,50,1", "after zooming in");
   docviewer.fullZoom = 1;
--- a/layout/base/tests/test_bug449781.html
+++ b/layout/base/tests/test_bug449781.html
@@ -32,21 +32,17 @@ addLoadEvent(function() {
   s2 = snapshotWindow(window);
 
   var equal, str1, str2;
   [equal, str1, str2] = compareSnapshots(s1, s2, true);
   ok(equal, "Show/hide should have no effect",
      "got " + str1 + " but expected " + str2);
 
   var viewer =
-    SpecialPowers.wrap($("ourFrame")).contentWindow
-                 .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
-                 .getInterface(SpecialPowers.Ci.nsIWebNavigation)
-                 .QueryInterface(SpecialPowers.Ci.nsIDocShell)
-                 .contentViewer;
+    SpecialPowers.wrap($("ourFrame")).contentWindow.docShell.contentViewer;
   viewer.fullZoom = 2;
   
   s3 = snapshotWindow(window);
 
   [equal, str1, str2] = compareSnapshots(s1, s3, true);
   ok(equal, "Zoom should have no effect",
      "got " + str1 + " but expected " + str2);
 
--- a/layout/generic/test/frame_selection_underline-ref.xhtml
+++ b/layout/generic/test/frame_selection_underline-ref.xhtml
@@ -8,20 +8,17 @@
 
 function init(aTest)
 {
   var target = document.getElementById("target");
   var decoration = document.getElementById("decoration");
   var leftSpacer = document.getElementById("leftspacer");
   var rightSpacer = document.getElementById("rightspacer");
 
-  var docShell =
-    window.QueryInterface(Ci.nsIInterfaceRequestor)
-          .getInterface(Ci.nsIWebNavigation)
-          .QueryInterface(Ci.nsIDocShell);
+  var docShell = window.docShell;
   var controller =
     docShell.QueryInterface(Ci.nsIInterfaceRequestor)
             .getInterface(Ci.nsISelectionDisplay)
             .QueryInterface(Ci.nsISelectionController);
 
   const nsISelectionController = Ci.nsISelectionController;
   if (aTest.selection.isIME) {
     leftSpacer.style.display = rightSpacer.style.display = "inline-block";
--- a/layout/generic/test/frame_selection_underline.xhtml
+++ b/layout/generic/test/frame_selection_underline.xhtml
@@ -3,20 +3,17 @@
 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" class="willBeRemoved">
 <head>
 <link rel="stylesheet" type="text/css" href="frame_selection_underline.css"/>
 <script type="text/javascript">
 <![CDATA[
 
 function init(aTest)
 {
-  var docShell =
-    window.QueryInterface(Ci.nsIInterfaceRequestor)
-          .getInterface(Ci.nsIWebNavigation)
-          .QueryInterface(Ci.nsIDocShell);
+  var docShell = window.docShell;
   var controller =
     docShell.QueryInterface(Ci.nsIInterfaceRequestor)
             .getInterface(Ci.nsISelectionDisplay)
             .QueryInterface(Ci.nsISelectionController);
 
   var selections = [
     controller.SELECTION_SPELLCHECK,
     controller.SELECTION_IME_RAWINPUT,
@@ -43,9 +40,9 @@ function init(aTest)
 }
 
 ]]>
 </script>
 </head>
 <body class="test">
   <div id="target"><span id="decoration">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div>
 </body>
-</html>
\ No newline at end of file
+</html>
--- a/layout/generic/test/test_bug263683.html
+++ b/layout/generic/test/test_bug263683.html
@@ -47,18 +47,17 @@ https://bugzilla.mozilla.org/show_bug.cg
       function startTest() {
         var textToSelect = document.getElementById("selecttext");
 
         // Take a snapshot now. This will be used to check that removing the
         // ranges removes the highlighting correctly
         var noHighlight = snapshotWindow(window);
 
         var controller = SpecialPowers.wrap(window).
-           QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor).
-           getInterface(SpecialPowers.Ci.nsIWebNavigation).
+           docShell.
            QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor).
            getInterface(SpecialPowers.Ci.nsISelectionDisplay).
            QueryInterface(SpecialPowers.Ci.nsISelectionController);
 
         // Get selection
         var findSelection = controller.getSelection(controller.SELECTION_FIND);
 
         // Lastly add range
--- a/layout/generic/test/test_selection_touchevents.html
+++ b/layout/generic/test/test_selection_touchevents.html
@@ -25,19 +25,17 @@ function test()
   selection.removeAllRanges();
   var rect = div1.getBoundingClientRect();
 
   // Position the caret using a fake mouse click
   var Ci = SpecialPowers.Ci;
   var cwu = SpecialPowers.getDOMWindowUtils(window);
   cwu.sendMouseEventToWindow("mousedown", rect.left + rect.width/2, rect.top + rect.height/2, 0, 0, 0, true);
   cwu.sendMouseEventToWindow("mouseup",   rect.left + rect.width/2, rect.top + rect.height/2, 0, 0, 0, true);
-  var selectionController = SpecialPowers.wrap(window).
-                              QueryInterface(Ci.nsIInterfaceRequestor).
-                              getInterface(Ci.nsIWebNavigation).
+  var selectionController = SpecialPowers.wrap(window).docShell.
                               QueryInterface(Ci.nsIInterfaceRequestor).
                               getInterface(Ci.nsISelectionDisplay).
                               QueryInterface(Ci.nsISelectionController);
 
   selectionController.wordMove(false, false);
   selectionController.wordMove(true, true);
   isnot(selection.rangeCount, 0, "Something should be selected");
   var string = selection.toString();
@@ -49,9 +47,9 @@ function test()
 
   SimpleTest.finish();
 }
 window.onload = function() { setTimeout(test, 0); };
 SimpleTest.waitForExplicitFinish();
 </script>
 </pre>
 </body>
-</html>
\ No newline at end of file
+</html>
--- a/layout/mathml/crashtests/400157.xhtml
+++ b/layout/mathml/crashtests/400157.xhtml
@@ -1,17 +1,16 @@
 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:mathml="http://www.w3.org/1998/Math/MathML" class="reftest-wait">
 <mathml:mfenced/>
 
 
 <script><![CDATA[
 var docviewer;
 function do_onload() {
-  var navigator1 = SpecialPowers.wrap(parent).QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor).getInterface(SpecialPowers.Ci.nsIWebNavigation);
-  var docShell = navigator1.QueryInterface(SpecialPowers.Ci.nsIDocShell);
+  var docShell = SpecialPowers.wrap(parent).docShell;
   docviewer = docShell.contentViewer;
 
   setTimeout(function() {
     clearTimeout(timer);
     docviewer.textZoom = 1;
     document.documentElement.removeAttribute("class");
   }, 500);
   setTimeout(doe,50, 0.2);
--- a/layout/reftests/text-stroke/reftest.list
+++ b/layout/reftests/text-stroke/reftest.list
@@ -1,10 +1,10 @@
 # basic tests for webkit-text-stroke
 # fuzzy is needed here for platform dependent backends
 default-preferences pref(layout.css.prefixes.webkit,true)
 
 fuzzy-if(gtkWidget,255,20) fuzzy-if(winWidget,20,10) fails-if(skiaContent&&(gtkWidget||winWidget)) == webkit-text-stroke-property-001.html webkit-text-stroke-property-001-ref.html
-fuzzy-if(gtkWidget,255,20) fuzzy-if(winWidget,20,10) == webkit-text-stroke-property-002.html webkit-text-stroke-property-002-ref.html
+fuzzy-if(gtkWidget,255,20) fuzzy-if(winWidget,20,10) fails-if(skiaContent&&!webrender&&gtkWidget) == webkit-text-stroke-property-002.html webkit-text-stroke-property-002-ref.html
 fuzzy-if(gtkWidget,255,20) fuzzy-if(winWidget,20,10) fails-if(skiaContent&&gtkWidget) == webkit-text-stroke-property-003.html webkit-text-stroke-property-003-ref.html
 fuzzy-if(gtkWidget,255,20) fuzzy-if(winWidget,20,10) fails-if(skiaContent&&gtkWidget) == webkit-text-stroke-property-004.html webkit-text-stroke-property-004-ref.html
 fuzzy-if(gtkWidget,255,20) fuzzy-if(winWidget,20,10) fails-if(skiaContent&&(gtkWidget||winWidget)) == webkit-text-stroke-property-005.html webkit-text-stroke-property-005-ref.html
 fuzzy-if(gtkWidget,255,392) fuzzy-if(winWidget&&!d2d,48,372) fuzzy-if(winWidget&&d2d,71,10) == webkit-text-stroke-property-006.html webkit-text-stroke-property-006-ref.html
--- a/layout/style/test/chrome/test_display_mode.html
+++ b/layout/style/test/chrome/test_display_mode.html
@@ -47,19 +47,17 @@ add_task(async function() {
     ok(queryApplies(q), q + " should apply");
   }
 
   function shouldNotApply(q) {
     ok(!queryApplies(q), q + " should not apply");
   }
 
   function setDisplayMode(mode) {
-    win.QueryInterface(Ci.nsIInterfaceRequestor)
-                 .getInterface(Ci.nsIDocShell)
-                 .displayMode = mode;
+    win.docShell.displayMode = mode;
   }
 
   shouldApply("all and (display-mode: browser)");
   shouldNotApply("all and (display-mode: fullscreen)");
   shouldNotApply("all and (display-mode: standalone)");
   shouldNotApply("all and (display-mode: minimal-ui)");
 
   // Test entering the OS's fullscreen mode.
--- a/layout/style/test/test_restyles_in_smil_animation.html
+++ b/layout/style/test/test_restyles_in_smil_animation.html
@@ -28,20 +28,17 @@ function waitForAnimationFrames(frameCou
       }
     }
     window.requestAnimationFrame(handleFrame);
   });
 }
 
 function observeStyling(frameCount) {
   var Ci = SpecialPowers.Ci;
-  var docShell =
-    SpecialPowers.wrap(window).QueryInterface(Ci.nsIInterfaceRequestor)
-                              .getInterface(Ci.nsIWebNavigation)
-                              .QueryInterface(Ci.nsIDocShell);
+  var docShell = SpecialPowers.wrap(window).docShell;
 
   docShell.recordProfileTimelineMarkers = true;
   docShell.popProfileTimelineMarkers();
 
   return new Promise(function(resolve) {
     return waitForAnimationFrames(frameCount).then(function() {
       var markers = docShell.popProfileTimelineMarkers();
       docShell.recordProfileTimelineMarkers = false;
--- a/layout/xul/test/test_popupZoom.xul
+++ b/layout/xul/test/test_popupZoom.xul
@@ -14,20 +14,17 @@
   <script type="application/javascript"><![CDATA[
     SimpleTest.waitForExplicitFinish();
 
     var docviewer;
     var savedzoom;
 
     function openPopup()
     {
-      docviewer = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                        .getInterface(Ci.nsIWebNavigation)
-                        .QueryInterface(Ci.nsIDocShell)
-                        .contentViewer;
+      docviewer = window.docShell.contentViewer;
       savedzoom = docviewer.fullZoom;
       docviewer.fullZoom = 2;
 
       document.getElementById("panel").
         openPopup(document.getElementById("anchor"), "after_start", 0, 0, false, false, null);
     }
 
     function popupShown(event)
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -1834,17 +1834,18 @@ var BrowserApp = {
         break;
 
       case "Session:Forward":
         browser.goForward();
         break;
 
       case "Session:Navigate": {
         let index = data.index;
-        let webNav = BrowserApp.selectedTab.window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation);
+        let webNav = BrowserApp.selectedTab.window.docShell
+                               .QueryInterface(Ci.nsIWebNavigation);
         let historySize = webNav.sessionHistory.count;
 
         if (index < 0) {
           index = 0;
           Log.e("Browser", "Negative index truncated to zero");
         } else if (index >= historySize) {
           Log.e("Browser", "Incorrect index " + index + " truncated to " + historySize - 1);
           index = historySize - 1;
@@ -2217,17 +2218,18 @@ var BrowserApp = {
   getUITelemetryObserver: function() {
     return UITelemetry;
   },
 
   // This method will return a list of history items and toIndex based on the action provided from the fromIndex to toIndex,
   // optionally selecting selIndex (if fromIndex <= selIndex <= toIndex)
   getHistory: function(data) {
     let action = data.action;
-    let webNav = BrowserApp.getTabForId(data.tabId).window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation);
+    let webNav = BrowserApp.getTabForId(data.tabId).window.docShell
+                           .QueryInterface(Ci.nsIWebNavigation);
     let historyIndex = webNav.sessionHistory.index;
     let historySize = webNav.sessionHistory.count;
     let canGoBack = webNav.canGoBack;
     let canGoForward = webNav.canGoForward;
     let listitems = [];
     let fromIndex = 0;
     let toIndex = historySize - 1;
     let selIndex = historyIndex;
@@ -4497,17 +4499,17 @@ Tab.prototype = {
         success: success
       };
       GlobalEventDispatcher.sendRequest(message);
     }
   },
 
   onLocationChange: function(aWebProgress, aRequest, aLocationURI, aFlags) {
     let contentWin = aWebProgress.DOMWindow;
-    let webNav = contentWin.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation);
+    let webNav = contentWin.docShell.QueryInterface(Ci.nsIWebNavigation);
 
     // Browser webapps may load content inside iframes that can not reach across the app/frame boundary
     // i.e. even though the page is loaded in an iframe window.top != webapp
     // Make cure this window is a top level tab before moving on.
     if (BrowserApp.getBrowserForWindow(contentWin) == null) {
       // We still need to update the back/forward button state, though.
       let message = {
         type: "Content:SubframeNavigation",
--- a/mobile/android/chrome/geckoview/GeckoViewSelectionActionContent.js
+++ b/mobile/android/chrome/geckoview/GeckoViewSelectionActionContent.js
@@ -83,18 +83,17 @@ class GeckoViewSelectionActionContent ex
     let currentWindow = aEvent.target.defaultView;
     while (currentWindow.realFrameElement) {
       const currentRect = currentWindow.realFrameElement.getBoundingClientRect();
       currentWindow = currentWindow.realFrameElement.ownerGlobal;
 
       offset.left += currentRect.left;
       offset.top += currentRect.top;
 
-      let targetDocShell = currentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                                        .getInterface(Ci.nsIWebNavigation);
+      let targetDocShell = currentWindow.docShell;
       if (targetDocShell.isMozBrowser) {
         break;
       }
     }
 
     return offset;
   }
 
--- a/mobile/android/modules/ActionBarHandler.jsm
+++ b/mobile/android/modules/ActionBarHandler.jsm
@@ -781,30 +781,28 @@ var ActionBarHandler = {
   /**
    * Returns an nsEditor or nsHTMLEditor.
    */
   _getEditor: function(element = this._targetElement, win = this._contentWindow) {
     if (this._isElementEditable(element)) {
       return element.editor;
     }
 
-    return win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation).
-               QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIEditingSession).
-               getEditorForWindow(win);
+    return win.docShell.editingSession.getEditorForWindow(win);
   },
 
   /**
    * Returns a selection controller.
    */
   _getSelectionController: function(element = this._targetElement, win = this._contentWindow) {
     if (this._isElementEditable(element)) {
       return this._getEditor(element, win).selectionController;
     }
 
-    return win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation).
+    return win.docShell.
                QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsISelectionDisplay).
                QueryInterface(Ci.nsISelectionController);
   },
 
   /**
    * For selectAll(), provides the editor, or the default window selection Controller.
    */
   _getSelectAllController: function(element = this._targetElement, win = this._contentWindow) {
--- a/mobile/android/modules/geckoview/GeckoViewSettings.jsm
+++ b/mobile/android/modules/geckoview/GeckoViewSettings.jsm
@@ -96,19 +96,15 @@ class GeckoViewSettings extends GeckoVie
       Services.obs.removeObserver(this._userAgentObserver,
                                   "http-on-useragent-request");
       this._userAgentObserver = undefined;
     }
     this._useDesktopMode = aUse;
   }
 
   get displayMode() {
-    return this.window.QueryInterface(Ci.nsIInterfaceRequestor)
-                      .getInterface(Ci.nsIDocShell)
-                      .displayMode;
+    return this.window.docShell.displayMode;
   }
 
   set displayMode(aMode) {
-    this.window.QueryInterface(Ci.nsIInterfaceRequestor)
-               .getInterface(Ci.nsIDocShell)
-               .displayMode = aMode;
+    this.window.docShell.displayMode = aMode;
   }
 }
--- a/mobile/android/modules/geckoview/GeckoViewUtils.jsm
+++ b/mobile/android/modules/geckoview/GeckoViewUtils.jsm
@@ -239,20 +239,19 @@ var GeckoViewUtils = {
   getRootDocShell: function(aWin) {
     if (!aWin) {
       return null;
     }
     let docShell;
     try {
       docShell = aWin.QueryInterface(Ci.nsIDocShell);
     } catch (e) {
-      docShell = aWin.QueryInterface(Ci.nsIInterfaceRequestor)
-                     .getInterface(Ci.nsIDocShell);
+      docShell = aWin.docShell;
     }
-    return docShell.QueryInterface(Ci.nsIDocShellTreeItem).rootTreeItem
+    return docShell.rootTreeItem
                    .QueryInterface(Ci.nsIInterfaceRequestor);
   },
 
   /**
    * Return the outermost chrome DOM window (the XUL window) for a given DOM
    * window, in the parent process.
    *
    * @param aWin a DOM window.
--- a/mobile/android/tests/browser/robocop/robocop_input.html
+++ b/mobile/android/tests/browser/robocop/robocop_input.html
@@ -127,38 +127,32 @@
             textArea.setSelectionRange(pos, pos);
           };
           setValue(value);
           textArea.focus();
         },
 
         focus_content_editable: function(value) {
           getEditor = function() {
-            return SpecialPowers.wrap(window).QueryInterface(
-                SpecialPowers.Ci.nsIInterfaceRequestor).getInterface(
-                SpecialPowers.Ci.nsIWebNavigation).QueryInterface(
-                SpecialPowers.Ci.nsIDocShell).editor;
+            return SpecialPowers.wrap(window).docShell.editor;
           };
           setValue = function(val) {
             // eslint-disable-next-line no-unsanitized/property
             contentEditable.innerHTML = val;
           };
           setSelection = function(pos) {
             window.getSelection().collapse(contentEditable.firstChild, pos);
           };
           setValue(value);
           contentEditable.focus();
         },
 
         focus_design_mode: function(value) {
           getEditor = function() {
-            return SpecialPowers.wrap(designMode.contentWindow).QueryInterface(
-                SpecialPowers.Ci.nsIInterfaceRequestor).getInterface(
-                SpecialPowers.Ci.nsIWebNavigation).QueryInterface(
-                SpecialPowers.Ci.nsIDocShell).editor;
+            return SpecialPowers.wrap(designMode.contentWindow).docShell.editor;
           };
           setValue = function(val) {
           // eslint-disable-next-line no-unsanitized/property
             designMode.contentDocument.body.innerHTML = val;
           };
           setSelection = function(pos) {
             designMode.contentWindow.getSelection().collapse(
                 designMode.contentDocument.body.firstChild, pos);
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -648,16 +648,17 @@ pref("layers.geometry.opengl.enabled", t
 pref("layers.geometry.basic.enabled", true);
 
 // Whether to enable arbitrary layer geometry for DirectX compositor
 pref("layers.geometry.d3d11.enabled", true);
 
 // APZ preferences. For documentation/details on what these prefs do, check
 // gfx/layers/apz/src/AsyncPanZoomController.cpp.
 pref("apz.allow_checkerboarding", true);
+pref("apz.allow_double_tap_zooming", true);
 pref("apz.allow_immediate_handoff", true);
 pref("apz.allow_zooming", false);
 pref("apz.android.chrome_fling_physics.enabled", true);
 pref("apz.android.chrome_fling_physics.friction", "0.015");
 pref("apz.android.chrome_fling_physics.inflexion", "0.35");
 pref("apz.android.chrome_fling_physics.stop_threshold", "0.1");
 pref("apz.autoscroll.enabled", true);
 
@@ -4846,17 +4847,17 @@ pref("layers.child-process-shutdown", tr
 pref("layers.max-active", -1);
 
 // Compositor target frame rate. NOTE: If vsync is enabled the compositor
 // frame rate will still be capped.
 // -1 -> default (match layout.frame_rate or 60 FPS)
 // 0  -> full-tilt mode: Recomposite even if not transaction occured.
 pref("layers.offmainthreadcomposition.frame-rate", -1);
 
-#if defined(XP_MACOSX) || defined(MOZ_WIDGET_GTK)
+#if defined(XP_MACOSX)
 pref("layers.enable-tiles", true);
 #else
 pref("layers.enable-tiles", false);
 #endif
 #if defined(XP_WIN)
 pref("layers.enable-tiles-if-skia-pomtp", true);
 #else
 pref("layers.enable-tiles-if-skia-pomtp", false);
--- a/netwerk/dns/prepare_tlds.py
+++ b/netwerk/dns/prepare_tlds.py
@@ -3,16 +3,17 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 import codecs
 import encodings.idna
 import imp
 import os
 import re
 import sys
+from make_dafsa import words_to_cxx
 
 """
 Processes a file containing effective TLD data.  See the following URL for a
 description of effective TLDs and of the file format that this script
 processes (although for the latter you're better off just reading this file's
 short source code).
 
 http://wiki.mozilla.org/Gecko:Effective_TLD_Service
@@ -99,24 +100,16 @@ class EffectiveTLDEntry:
 
 def main(output, effective_tld_filename):
   """
   effective_tld_filename is the effective TLD file to parse.
   A C++ array of a binary representation of a DAFSA representing the
   eTLD file is then printed to output.
   """
 
-  # Find and load the `make_dafsa.py` script under xpcom/ds.
-  tld_dir = os.path.dirname(effective_tld_filename)
-  make_dafsa_py = os.path.join(tld_dir, '../../xpcom/ds/make_dafsa.py')
-  sys.path.append(os.path.dirname(make_dafsa_py))
-  with open(make_dafsa_py, 'r') as fh:
-    make_dafsa = imp.load_module('script', fh, make_dafsa_py,
-                                 ('.py', 'r', imp.PY_SOURCE))
-
   def typeEnum(etld):
     """
     Maps the flags to the DAFSA's enum types.
     """
     if etld.exception():
       return 1
     elif etld.wild():
       return 2
@@ -125,12 +118,12 @@ def main(output, effective_tld_filename)
 
   def dafsa_words():
     """
     make_dafsa expects lines of the form "<domain_name><enum_value>"
     """
     for etld in getEffectiveTLDs(effective_tld_filename):
       yield "%s%d" % (etld.domain(), typeEnum(etld))
 
-  output.write(make_dafsa.words_to_cxx(dafsa_words()))
+  output.write(words_to_cxx(dafsa_words()))
 
 if __name__ == '__main__':
     main(sys.stdout, sys.argv[1])
--- a/security/manager/ssl/moz.build
+++ b/security/manager/ssl/moz.build
@@ -185,17 +185,17 @@ LOCAL_INCLUDES += [
 LOCAL_INCLUDES += [
     '!/dist/public/nss',
 ]
 
 GENERATED_FILES = [
     'nsSTSPreloadList.h',
 ]
 dafsa_data = GENERATED_FILES['nsSTSPreloadList.h']
-dafsa_data.script = '../../../xpcom/ds/make_dafsa.py'
+dafsa_data.script = '../../../xpcom/ds/tools/make_dafsa.py'
 dafsa_data.inputs = ['nsSTSPreloadList.inc']
 
 if CONFIG['NSS_DISABLE_DBM']:
     DEFINES['NSS_DISABLE_DBM'] = '1'
 
 DEFINES['SSL_DISABLE_DEPRECATED_CIPHER_SUITE_NAMES'] = 'True'
 DEFINES['NSS_ENABLE_ECC'] = 'True'
 for var in ('DLL_PREFIX', 'DLL_SUFFIX'):
--- a/security/manager/ssl/tests/mochitest/mixedcontent/backward.html
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/backward.html
@@ -2,17 +2,17 @@
 <html>
 <head>
   <script type="text/javascript">
   "use strict";
   window.onload = function()
   {
     window.setTimeout(function()
     {
-      SpecialPowers.wrap(window).QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
-        .getInterface(SpecialPowers.Ci.nsIWebNavigation)
+      SpecialPowers.wrap(window).docShell
+        .QueryInterface(SpecialPowers.Ci.nsIWebNavigation)
         .goBack();
     }, 100);
   };
 
   </script>
 </head>
 </html>
--- a/security/manager/ssl/tests/mochitest/mixedcontent/mixedContentTest.js
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/mixedContentTest.js
@@ -134,21 +134,17 @@ function is(a, b, message) {
   }
 }
 
 function isSecurityState(expectedState, message, test) {
   if (!test) {
     test = ok;
   }
 
-  let ui = SpecialPowers.wrap(window)
-    .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
-    .getInterface(SpecialPowers.Ci.nsIWebNavigation)
-    .QueryInterface(SpecialPowers.Ci.nsIDocShell)
-    .securityUI;
+  let ui = SpecialPowers.wrap(window).docShell.securityUI;
 
   let isInsecure = !ui ||
     (ui.state & SpecialPowers.Ci.nsIWebProgressListener.STATE_IS_INSECURE);
   let isBroken = ui &&
     (ui.state & SpecialPowers.Ci.nsIWebProgressListener.STATE_IS_BROKEN);
   let isEV = ui &&
     (ui.state & SpecialPowers.Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL);
 
--- a/taskcluster/ci/test/talos.yml
+++ b/taskcluster/ci/test/talos.yml
@@ -88,20 +88,17 @@ talos-chrome-profiling:
         extra-options:
             - --suite=chromez
             - --geckoProfile
 
 talos-damp:
     description: "Talos devtools (damp)"
     try-name: damp
     treeherder-symbol: T(damp)
-    max-run-time:
-        by-test-platform:
-            linux64.*: 2700
-            default: 5400
+    max-run-time: 5400
     run-on-projects:
         by-test-platform:
             linux64-ccov/.*: ['try']  # Bug 1407593
             windows.*msvc/.*: ['mozilla-beta', 'mozilla-central', 'try']
             default: ['mozilla-beta', 'mozilla-central', 'mozilla-inbound', 'autoland', 'try']
     mozharness:
         extra-options:
             - --suite=damp
--- a/testing/geckodriver/src/capabilities.rs
+++ b/testing/geckodriver/src/capabilities.rs
@@ -136,16 +136,20 @@ impl<'a> BrowserCapabilities for Firefox
         let version_str = self.version();
         if let Some(x) = version_str {
             Ok(try!(Version::from_str(&*x).or_else(|x| Err(convert_version_error(x)))).major >= 52)
         } else {
             Ok(false)
         }
     }
 
+    fn set_window_rect(&mut self, _: &Capabilities) -> WebDriverResult<bool> {
+        Ok(true)
+    }
+
     fn compare_browser_version(&mut self,
                                version: &str,
                                comparison: &str)
                                -> WebDriverResult<bool> {
         try!(Version::from_str(version).or_else(|x| Err(convert_version_error(x))))
             .matches(comparison)
             .or_else(|x| Err(convert_version_error(x)))
     }
--- a/testing/marionette/capabilities.js
+++ b/testing/marionette/capabilities.js
@@ -390,16 +390,17 @@ class Capabilities extends Map {
       // webdriver
       ["browserName", appinfo.name],
       ["browserVersion", appinfo.version],
       ["platformName", getWebDriverPlatformName()],
       ["platformVersion", Services.sysinfo.getProperty("version")],
       ["acceptInsecureCerts", false],
       ["pageLoadStrategy", PageLoadStrategy.Normal],
       ["proxy", new Proxy()],
+      ["setWindowRect", appinfo.name == "firefox"],
       ["timeouts", new Timeouts()],
       ["unhandledPromptBehavior", UnhandledPromptBehavior.DismissAndNotify],
 
       // features
       ["rotatable", appinfo.name == "B2G"],
 
       // proprietary
       ["moz:accessibilityChecks", false],
@@ -460,67 +461,64 @@ class Capabilities extends Map {
   // Matches capabilities as described by WebDriver.
   static match_(json = {}) {
     let matched = new Capabilities();
 
     for (let [k, v] of Object.entries(json)) {
       switch (k) {
         case "acceptInsecureCerts":
           assert.boolean(v, pprint`Expected ${k} to be a boolean, got ${v}`);
-          matched.set("acceptInsecureCerts", v);
           break;
 
         case "pageLoadStrategy":
           assert.string(v, pprint`Expected ${k} to be a string, got ${v}`);
-
-          if (Object.values(PageLoadStrategy).includes(v)) {
-            matched.set("pageLoadStrategy", v);
-          } else {
+          if (!Object.values(PageLoadStrategy).includes(v)) {
             throw new InvalidArgumentError("Unknown page load strategy: " + v);
           }
-
           break;
 
         case "proxy":
-          let proxy = Proxy.fromJSON(v);
-          matched.set("proxy", proxy);
+          v = Proxy.fromJSON(v);
+          break;
+
+        case "setWindowRect":
+          assert.boolean(v, pprint`Expected ${k} to be boolean, got ${v}`);
+          if (appinfo.name == "firefox" && !v) {
+            throw new InvalidArgumentError("setWindowRect cannot be disabled");
+          } else if (appinfo.name != "firefox" && v) {
+            throw new InvalidArgumentError("setWindowRect is only supported in Firefox desktop");
+          }
           break;
 
         case "timeouts":
-          let timeouts = Timeouts.fromJSON(v);
-          matched.set("timeouts", timeouts);
+          v = Timeouts.fromJSON(v);
           break;
 
         case "unhandledPromptBehavior":
           assert.string(v, pprint`Expected ${k} to be a string, got ${v}`);
-
-          if (Object.values(UnhandledPromptBehavior).includes(v)) {
-            matched.set("unhandledPromptBehavior", v);
-          } else {
+          if (!Object.values(UnhandledPromptBehavior).includes(v)) {
             throw new InvalidArgumentError(
                 `Unknown unhandled prompt behavior: ${v}`);
           }
-
           break;
 
         case "moz:accessibilityChecks":
-          assert.boolean(v, pprint`Expected ${k} to be a boolean, got ${v}`);
-          matched.set("moz:accessibilityChecks", v);
+          assert.boolean(v, pprint`Expected ${k} to be boolean, got ${v}`);
           break;
 
         case "moz:useNonSpecCompliantPointerOrigin":
-          assert.boolean(v, pprint`Expected ${k} to be a boolean, got ${v}`);
-          matched.set("moz:useNonSpecCompliantPointerOrigin", v);
+          assert.boolean(v, pprint`Expected ${k} to be boolean, got ${v}`);
           break;
 
         case "moz:webdriverClick":
-          assert.boolean(v, pprint`Expected ${k} to be a boolean, got ${v}`);
-          matched.set("moz:webdriverClick", v);
+          assert.boolean(v, pprint`Expected ${k} to be boolean, got ${v}`);
           break;
       }
+
+      matched.set(k, v);
     }
 
     return matched;
   }
 }
 
 this.Capabilities = Capabilities;
 this.PageLoadStrategy = PageLoadStrategy;
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_capabilities.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_capabilities.py
@@ -55,23 +55,28 @@ class TestCapabilities(MarionetteTestCas
         return profile
 
     def test_mandated_capabilities(self):
         self.assertIn("browserName", self.caps)
         self.assertIn("browserVersion", self.caps)
         self.assertIn("platformName", self.caps)
         self.assertIn("platformVersion", self.caps)
         self.assertIn("acceptInsecureCerts", self.caps)
+        self.assertIn("setWindowRect", self.caps)
         self.assertIn("timeouts", self.caps)
 
         self.assertEqual(self.caps["browserName"], self.appinfo["name"].lower())
         self.assertEqual(self.caps["browserVersion"], self.appinfo["version"])
         self.assertEqual(self.caps["platformName"], self.os_name)
         self.assertEqual(self.caps["platformVersion"], self.os_version)
         self.assertFalse(self.caps["acceptInsecureCerts"])
+        if self.appinfo["name"] == "Firefox":
+            self.assertTrue(self.caps["setWindowRect"])
+        else:
+            self.assertFalse(self.caps["setWindowRect"])
         self.assertDictEqual(self.caps["timeouts"],
                              {"implicit": 0,
                               "pageLoad": 300000,
                               "script": 30000})
 
     def test_supported_features(self):
         self.assertIn("rotatable", self.caps)
 
@@ -115,16 +120,17 @@ class TestCapabilities(MarionetteTestCas
                          "Session ID has {{}} in it: {}".format(
                              self.marionette.session_id))
 
 
 class TestCapabilityMatching(MarionetteTestCase):
 
     def setUp(self):
         MarionetteTestCase.setUp(self)
+        self.browser_name = self.marionette.session_capabilities["browserName"]
         self.delete_session()
 
     def delete_session(self):
         if self.marionette.session is not None:
             self.marionette.delete_session()
 
     def test_accept_insecure_certs(self):
         for value in ["", 42, {}, []]:
@@ -143,16 +149,28 @@ class TestCapabilityMatching(MarionetteT
             self.marionette.start_session({"pageLoadStrategy": strategy})
             self.assertEqual(self.marionette.session_capabilities["pageLoadStrategy"], strategy)
 
         for value in ["", "EAGER", True, 42, {}, [], None]:
             print("invalid strategy {}".format(value))
             with self.assertRaisesRegexp(SessionNotCreatedException, "InvalidArgumentError"):
                 self.marionette.start_session({"pageLoadStrategy": value})
 
+    def test_set_window_rect(self):
+        if self.browser_name == "firefox":
+            self.marionette.start_session({"setWindowRect": True})
+            self.delete_session()
+            with self.assertRaisesRegexp(SessionNotCreatedException, "InvalidArgumentError"):
+                self.marionette.start_session({"setWindowRect": False})
+        else:
+            self.marionette.start_session({"setWindowRect": False})
+            self.delete_session()
+            with self.assertRaisesRegexp(SessionNotCreatedException, "InvalidArgumentError"):
+                self.marionette.start_session({"setWindowRect": True})
+
     def test_timeouts(self):
         timeouts = {u"implicit": 123, u"pageLoad": 456, u"script": 789}
         caps = {"timeouts": timeouts}
         self.marionette.start_session(caps)
         self.assertIn("timeouts", self.marionette.session_capabilities)
         self.assertDictEqual(self.marionette.session_capabilities["timeouts"], timeouts)
         self.assertDictEqual(self.marionette._send_message("WebDriver:GetTimeouts"), timeouts)
 
--- a/testing/marionette/listener.js
+++ b/testing/marionette/listener.js
@@ -674,19 +674,17 @@ async function executeInSandbox(script, 
 function emitTouchEvent(type, touch) {
   logger.info(`Emitting Touch event of type ${type} ` +
       `to element with id: ${touch.target.id} ` +
       `and tag name: ${touch.target.tagName} ` +
       `at coordinates (${touch.clientX}), ` +
       `${touch.clientY}) relative to the viewport`);
 
   const win = curContainer.frame;
-  let docShell = win.QueryInterface(Ci.nsIInterfaceRequestor)
-      .getInterface(Ci.nsIWebNavigation)
-      .QueryInterface(Ci.nsIDocShell);
+  let docShell = win.docShell;
   if (docShell.asyncPanZoomEnabled && legacyactions.scrolling) {
     let ev = {
       index: 0,
       type,
       id: touch.identifier,
       clientX: touch.clientX,
       clientY: touch.clientY,
       screenX: touch.screenX,
--- a/testing/marionette/test/unit/test_capabilities.js
+++ b/testing/marionette/test/unit/test_capabilities.js
@@ -383,16 +383,17 @@ add_test(function test_Capabilities_ctor
   ok(caps.has("browserVersion"));
   ok(caps.has("platformName"));
   ok(["linux", "mac", "windows", "android"].includes(caps.get("platformName")));
   ok(caps.has("platformVersion"));
   equal(PageLoadStrategy.Normal, caps.get("pageLoadStrategy"));
   equal(false, caps.get("acceptInsecureCerts"));
   ok(caps.get("timeouts") instanceof Timeouts);
   ok(caps.get("proxy") instanceof Proxy);
+  equal(caps.get("setWindowRect"), false); // xpcshell does not populate appinfo
 
   ok(caps.has("rotatable"));
 
   equal(false, caps.get("moz:accessibilityChecks"));
   ok(caps.has("moz:processID"));
   ok(caps.has("moz:profile"));
   equal(false, caps.get("moz:useNonSpecCompliantPointerOrigin"));
   equal(true, caps.get("moz:webdriverClick"));
@@ -413,16 +414,17 @@ add_test(function test_Capabilities_toJS
   equal(caps.get("browserName"), json.browserName);
   equal(caps.get("browserVersion"), json.browserVersion);
   equal(caps.get("platformName"), json.platformName);
   equal(caps.get("platformVersion"), json.platformVersion);
   equal(caps.get("pageLoadStrategy"), json.pageLoadStrategy);
   equal(caps.get("acceptInsecureCerts"), json.acceptInsecureCerts);
   deepEqual(caps.get("timeouts").toJSON(), json.timeouts);
   equal(undefined, json.proxy);
+  equal(caps.get("setWindowRect"), json.setWindowRect);
 
   equal(caps.get("rotatable"), json.rotatable);
 
   equal(caps.get("moz:accessibilityChecks"), json["moz:accessibilityChecks"]);
   equal(caps.get("moz:processID"), json["moz:processID"]);
   equal(caps.get("moz:profile"), json["moz:profile"]);
   equal(caps.get("moz:useNonSpecCompliantPointerOrigin"),
       json["moz:useNonSpecCompliantPointerOrigin"]);
@@ -461,16 +463,20 @@ add_test(function test_Capabilities_from
   let proxyConfig = {proxyType: "manual"};
   caps = fromJSON({proxy: proxyConfig});
   equal("manual", caps.get("proxy").proxyType);
 
   let timeoutsConfig = {implicit: 123};
   caps = fromJSON({timeouts: timeoutsConfig});
   equal(123, caps.get("timeouts").implicit);
 
+  caps = fromJSON({setWindowRect: false});
+  equal(false, caps.get("setWindowRect"));
+  Assert.throws(() => fromJSON({setWindowRect: true}), InvalidArgumentError);
+
   caps = fromJSON({"moz:accessibilityChecks": true});
   equal(true, caps.get("moz:accessibilityChecks"));
   caps = fromJSON({"moz:accessibilityChecks": false});
   equal(false, caps.get("moz:accessibilityChecks"));
   Assert.throws(() => fromJSON({"moz:accessibilityChecks": "foo"}), InvalidArgumentError);
   Assert.throws(() => fromJSON({"moz:accessibilityChecks": 1}), InvalidArgumentError);
 
   caps = fromJSON({"moz:useNonSpecCompliantPointerOrigin": false});
--- a/testing/mochitest/browser-test.js
+++ b/testing/mochitest/browser-test.js
@@ -90,18 +90,18 @@ function testInit() {
     let messageHandler = function(m) {
       // eslint-disable-next-line no-undef
       messageManager.removeMessageListener("chromeEvent", messageHandler);
       var url = m.json.data;
 
       // Window is the [ChromeWindow] for messageManager, so we need content.window
       // Currently chrome tests are run in a content window instead of a ChromeWindow
       // eslint-disable-next-line no-undef
-      var webNav = content.window.QueryInterface(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsIWebNavigation);
+      var webNav = content.window.docShell
+                          .QueryInterface(Ci.nsIWebNavigation);
       webNav.loadURI(url, null, null, null, null);
     };
 
     var listener = 'data:,function doLoad(e) { var data=e.detail&&e.detail.data;removeEventListener("contentEvent", function (e) { doLoad(e); }, false, true);sendAsyncMessage("chromeEvent", {"data":data}); };addEventListener("contentEvent", function (e) { doLoad(e); }, false, true);';
     // eslint-disable-next-line no-undef
     messageManager.addMessageListener("chromeEvent", messageHandler);
     // eslint-disable-next-line no-undef
     messageManager.loadFrameScript(listener, true);
--- a/testing/mochitest/tests/Harness_sanity/test_SpecialPowersExtension.html
+++ b/testing/mochitest/tests/Harness_sanity/test_SpecialPowersExtension.html
@@ -97,20 +97,18 @@ function starttest(){
   var testURI = SpecialPowers.Cc['@mozilla.org/network/standard-url-mutator;1']
                              .createInstance(SpecialPowers.Ci.nsIURIMutator)
                              .setSpec("http://www.foobar.org/")
                              .finalize();
   is(testURI.spec, "http://www.foobar.org/", "Getters/Setters should work correctly");
   is(SpecialPowers.wrap(document).getElementsByTagName('details').length, 0, "Should work with proxy-based DOM bindings.");
 
   // Play with the window object.
-  var webnav = SpecialPowers.wrap(window).QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
-                                         .getInterface(SpecialPowers.Ci.nsIWebNavigation);
-  webnav.QueryInterface(SpecialPowers.Ci.nsIDocShell);
-  ok(webnav.allowJavascript, "Able to pull properties off of docshell!");
+  var docShell = SpecialPowers.wrap(window).docShell;
+  ok(docShell.allowJavascript, "Able to pull properties off of docshell!");
 
   // Make sure Xray-wrapped functions work.
   try {
     SpecialPowers.wrap(SpecialPowers.Components).ID('{00000000-0000-0000-0000-000000000000}');
     ok(true, "Didn't throw");
   }
   catch (e) {
     ok(false, "Threw while trying to call Xray-wrapped function.");
--- a/testing/specialpowers/content/MockFilePicker.jsm
+++ b/testing/specialpowers/content/MockFilePicker.jsm
@@ -3,17 +3,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 var EXPORTED_SYMBOLS = ["MockFilePicker"];
 
 const Cm = Components.manager;
 
 const CONTRACT_ID = "@mozilla.org/filepicker;1";
 
-ChromeUtils.import("resource://gre/modules/FileUtils.jsm");
+ChromeUtils.defineModuleGetter(this, "FileUtils",
+                               "resource://gre/modules/FileUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 // Allow stuff from this scope to be accessed from non-privileged scopes. This
 // would crash if used outside of automation.
 Cu.forcePermissiveCOWs();
 
 var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
 var oldClassID, oldFactory;
--- a/testing/specialpowers/content/specialpowersAPI.js
+++ b/testing/specialpowers/content/specialpowersAPI.js
@@ -7,25 +7,33 @@
 
 "use strict";
 
 /* import-globals-from MozillaLogger.js */
 /* globals XPCNativeWrapper, content */
 
 var global = this;
 
-ChromeUtils.import("resource://specialpowers/MockFilePicker.jsm");
-ChromeUtils.import("resource://specialpowers/MockColorPicker.jsm");
-ChromeUtils.import("resource://specialpowers/MockPermissionPrompt.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
-ChromeUtils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
-ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
-ChromeUtils.import("resource://gre/modules/ServiceWorkerCleanUp.jsm");
+
+ChromeUtils.defineModuleGetter(this, "MockFilePicker",
+                               "resource://specialpowers/MockFilePicker.jsm");
+ChromeUtils.defineModuleGetter(this, "MockColorPicker",
+                               "resource://specialpowers/MockColorPicker.jsm");
+ChromeUtils.defineModuleGetter(this, "MockPermissionPrompt",
+                               "resource://specialpowers/MockPermissionPrompt.jsm");
+ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
+                               "resource://gre/modules/PrivateBrowsingUtils.jsm");
+ChromeUtils.defineModuleGetter(this, "NetUtil",
+                               "resource://gre/modules/NetUtil.jsm");
+ChromeUtils.defineModuleGetter(this, "AppConstants",
+                               "resource://gre/modules/AppConstants.jsm");
+ChromeUtils.defineModuleGetter(this, "ServiceWorkerCleanUp",
+                               "resource://gre/modules/ServiceWorkerCleanUp.jsm");
 
 ChromeUtils.defineModuleGetter(this, "PerTestCoverageUtils",
   "resource://testing-common/PerTestCoverageUtils.jsm");
 
 // We're loaded with "this" not set to the global in some cases, so we
 // have to play some games to get at the global object here.  Normally
 // we'd try "this" from a function called with undefined this value,
 // but this whole file is in strict mode.  So instead fall back on
@@ -1400,19 +1408,17 @@ SpecialPowersAPI.prototype = {
       prefType,
       iid, // Only used with complex prefs
       prefValue,
     };
     return this._sendSyncMessage("SPPrefService", msg)[0];
   },
 
   _getDocShell(window) {
-    return window.QueryInterface(Ci.nsIInterfaceRequestor)
-                 .getInterface(Ci.nsIWebNavigation)
-                 .QueryInterface(Ci.nsIDocShell);
+    return window.docShell;
   },
   _getMUDV(window) {
     return this._getDocShell(window).contentViewer;
   },
   // XXX: these APIs really ought to be removed, they're not e10s-safe.
   // (also they're pretty Firefox-specific)
   _getTopChromeWindow(window) {
     return window.docShell.rootTreeItem.domWindow
@@ -1744,20 +1750,19 @@ SpecialPowersAPI.prototype = {
   focus(aWindow) {
     // This is called inside TestRunner._makeIframe without aWindow, because of assertions in oop mochitests
     // With aWindow, it is called in SimpleTest.waitForFocus to allow popup window opener focus switching
     if (aWindow)
       aWindow.focus();
     var mm = global;
     if (aWindow) {
       try {
-        mm = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                                    .getInterface(Ci.nsIDocShell)
-                                    .QueryInterface(Ci.nsIInterfaceRequestor)
-                                    .getInterface(Ci.nsIContentFrameMessageManager);
+        mm = aWindow.docShell
+                    .QueryInterface(Ci.nsIInterfaceRequestor)
+                    .getInterface(Ci.nsIContentFrameMessageManager);
       } catch (ex) {
         /* Ignore exceptions for e.g. XUL chrome windows from mochitest-chrome
          * which won't have a message manager */
       }
     }
     mm.sendAsyncMessage("SpecialPowers.Focus", {});
   },
 
--- a/testing/talos/talos/tests/cpstartup/content/cpstartup.html
+++ b/testing/talos/talos/tests/cpstartup/content/cpstartup.html
@@ -1,16 +1,15 @@
 <html>
   <head>
     <script>
 
       function init() {
         if (document.location.hash.indexOf("#auto") == 0) {
-          let mm = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsIWebNavigation)
+          let mm = window.docShell
                          .QueryInterface(Ci.nsIInterfaceRequestor)
                          .getInterface(Ci.nsIContentFrameMessageManager);
 
           mm.addMessageListener("CPStartup:FinalResults", function onResults(msg) {
             mm.removeMessageListener("CPStartup:FinalResults", onResults);
             let results = msg.data;
 
             tpRecordTime(results, 0, "content-process-startup");
--- a/testing/talos/talos/tests/tabpaint/content/tabpaint.html
+++ b/testing/talos/talos/tests/tabpaint/content/tabpaint.html
@@ -1,16 +1,15 @@
 <html>
   <head>
     <script>
 
       function init() {
         if (document.location.hash.indexOf("#auto") == 0) {
-          let mm = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsIWebNavigation)
+          let mm = window.docShell
                          .QueryInterface(Ci.nsIInterfaceRequestor)
                          .getInterface(Ci.nsIContentFrameMessageManager);
 
           mm.addMessageListener("TabPaint:FinalResults", function onResults(msg) {
             mm.removeMessageListener("TabPaint:FinalResults", onResults);
 
             let { fromParent, fromContent } = msg.data;
 
--- a/testing/talos/talos/tests/tabpaint/content/target.html
+++ b/testing/talos/talos/tests/tabpaint/content/target.html
@@ -47,17 +47,16 @@
     // paint occurring within the tab.
     let fetchStart = window.performance.timing.fetchStart;
     let presented = fetchStart + e.paintTimeStamp;
     removeEventListener("MozAfterPaint", onPaint);
 
     let opened = parseInt(location.search.substring(1), 10);
     let delta = presented - opened;
 
-    let mm = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                   .getInterface(Ci.nsIWebNavigation)
+    let mm = window.docShell
                    .QueryInterface(Ci.nsIInterfaceRequestor)
                    .getInterface(Ci.nsIContentFrameMessageManager);
 
     mm.sendAsyncMessage("TabPaint:Painted", { delta });
   });
 </script>
 </html>
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -359058,24 +359058,16 @@
     ]
    ],
    "html/semantics/tabular-data/processing-model-1/col-span-limits.html": [
     [
      "/html/semantics/tabular-data/processing-model-1/col-span-limits.html",
      {}
     ]
    ],
-   "html/semantics/tabular-data/processing-model-1/span-limits-2.html": [
-    [
-     "/html/semantics/tabular-data/processing-model-1/span-limits-2.html",
-     {
-      "timeout": "long"
-     }
-    ]
-   ],
    "html/semantics/tabular-data/processing-model-1/span-limits.html": [
     [
      "/html/semantics/tabular-data/processing-model-1/span-limits.html",
      {
       "timeout": "long"
      }
     ]
    ],
@@ -418774,22 +418766,38 @@
    "webdriver/tests/new_session/merge.py": [
     [
      "/webdriver/tests/new_session/merge.py",
      {
       "timeout": "long"
      }
     ]
    ],
+   "webdriver/tests/new_session/page_load_strategy.py": [
+    [
+     "/webdriver/tests/new_session/page_load_strategy.py",
+     {}
+    ]
+   ],
+   "webdriver/tests/new_session/platform_name.py": [
+    [
+     "/webdriver/tests/new_session/platform_name.py",
+     {}
+    ]
+   ],
    "webdriver/tests/new_session/response.py": [
     [
      "/webdriver/tests/new_session/response.py",
-     {
-      "timeout": "long"
-     }
+     {}
+    ]
+   ],
+   "webdriver/tests/new_session/timeouts.py": [
+    [
+     "/webdriver/tests/new_session/timeouts.py",
+     {}
     ]
    ],
    "webdriver/tests/page_source/source.py": [
     [
      "/webdriver/tests/page_source/source.py",
      {}
     ]
    ],
@@ -598847,22 +598855,18 @@
   "html/semantics/tabular-data/processing-model-1/col-span-limits.html": [
    "a4a425b9c1f70926c77ad3eb1b8a8a87a4655de9",
    "testharness"
   ],
   "html/semantics/tabular-data/processing-model-1/contains.json": [
    "be5a8fdfdd488068db55c07dad8d716d94f88c57",
    "support"
   ],
-  "html/semantics/tabular-data/processing-model-1/span-limits-2.html": [
-   "84b508872aac52197a7d31c5a739fb1048ef25f6",
-   "testharness"
-  ],
   "html/semantics/tabular-data/processing-model-1/span-limits.html": [
-   "0bc986281a41997fc2c27b1ea0f727582a47ebb0",
+   "cdfa61bbcdc06ea62b80d042440d55fb0c89a186",
    "testharness"
   ],
   "html/semantics/tabular-data/the-caption-element/caption_001.html": [
    "ecb1bef85436167bd2329c380a4b1e9c45fe7649",
    "testharness"
   ],
   "html/semantics/tabular-data/the-table-element/caption-methods.html": [
    "ec95eab39fb26acc0831c287d55317075abead1a",
@@ -636904,17 +636908,17 @@
    "5bd5c609cbd5e799787c93bac673fb8e392a7c30",
    "support"
   ],
   "tools/wptrunner/wptrunner/executors/testharness_servodriver.js": [
    "d731cc04d7011759ce63079fbd1242fd6426603b",
    "support"
   ],
   "tools/wptrunner/wptrunner/executors/testharness_webdriver.js": [
-   "22675f0703d33b0395b58b6e2b8c70c2adc76439",
+   "7f00050fa7ed6daf89ce6af7a0e610d0e5ddc42b",
    "support"
   ],
   "tools/wptrunner/wptrunner/executors/testharness_webdriver_resume.js": [
    "7a2df98ed4e73bf4618b169aa297f0c075ff8a19",
    "support"
   ],
   "tools/wptrunner/wptrunner/expected.py": [
    "f06abb946af16f690d434715c7b933f12d5a3335",
@@ -641536,33 +641540,33 @@
    "7a3619829d10de99268b35968e8dec8985575564",
    "support"
   ],
   "webdriver/tests/element_click/support/input.html": [
    "e2c6dadd1218e0a7d8b7d243a1c49b7f47092d77",
    "support"
   ],
   "webdriver/tests/element_send_keys/__init__.py": [
-   "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
+   "a7facf6fcf70309251757e3fb90b89255e13133f",
    "support"
   ],
   "webdriver/tests/element_send_keys/conftest.py": [
-   "54fd9d9dc01771804c025cf3d771b3f2d9c4f758",
+   "17bdd162a772bb0a5a4d14a2c2627a25ded9eca3",
    "support"
   ],
   "webdriver/tests/element_send_keys/content_editable.py": [
    "16ebc2d018fb7ef18669c089375881ba68236d10",
    "wdspec"
   ],
   "webdriver/tests/element_send_keys/events.py": [
-   "01c6e0924a45fa8ef51b830b8122ad851cec0b0e",
+   "41b00ef37fa24154231a485bbc45da036f40e717",
    "wdspec"
   ],
   "webdriver/tests/element_send_keys/file_upload.py": [
-   "0f2d85c3e3f2c78f8f155928d4725a64f740c17d",
+   "d1a8b81da78e9f809d36fa68b4f2d5acee73c615",
    "wdspec"
   ],
   "webdriver/tests/element_send_keys/form_controls.py": [
    "e53d0610f1ed99cdd8c9b257759ba99effa93d36",
    "wdspec"
   ],
   "webdriver/tests/element_send_keys/interactability.py": [
    "3a809aac6939b54b76cc2badec176abb216dcdd8",
@@ -641832,17 +641836,17 @@
    "7f5414b2b0fd8df52174a784743c22362122a034",
    "wdspec"
   ],
   "webdriver/tests/new_session/__init__.py": [
    "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
    "support"
   ],
   "webdriver/tests/new_session/conftest.py": [
-   "74563f7b866165e67b870e47bb8707fcb4bc1cf4",
+   "bf974ab1c000a1d733664637faf9b39e7303d556",
    "support"
   ],
   "webdriver/tests/new_session/create_alwaysMatch.py": [
    "e5f498d54ae562cfbedf63fe93977d31c57d078d",
    "wdspec"
   ],
   "webdriver/tests/new_session/create_firstMatch.py": [
    "f00f6042b085dc09876f67ae650ff62ceb3eb76a",
@@ -641852,31 +641856,43 @@
    "61816812eb1e79e4cc96190e09e597a878bdebee",
    "wdspec"
   ],
   "webdriver/tests/new_session/invalid_capabilities.py": [
    "83f93ea22f7ed28fa28ab05d36387df828716026",
    "wdspec"
   ],
   "webdriver/tests/new_session/merge.py": [
-   "d4b4ee56388246d253fe156cf3d3836c1732faff",
+   "857d289fcaf054492e17ba730c6f530d55fe2640",
+   "wdspec"
+  ],
+  "webdriver/tests/new_session/page_load_strategy.py": [
+   "69288ef43335605e4c9d21cfa56bd2806f6e92b0",
+   "wdspec"
+  ],
+  "webdriver/tests/new_session/platform_name.py": [
+   "0504f04259d8e289e1581669ba9066a14bb4d529",
    "wdspec"
   ],
   "webdriver/tests/new_session/response.py": [
-   "e3b096abb9c0e0b3af5d44707c3cbf5f32121fee",
+   "3e8520718237e6d74fe368d68c4a7bf2ead08c9e",
    "wdspec"
   ],
   "webdriver/tests/new_session/support/__init__.py": [
    "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
    "support"
   ],
   "webdriver/tests/new_session/support/create.py": [
    "85ae1cd4ea85e0a1e0d712b1a7803d6066ab8739",
    "support"
   ],
+  "webdriver/tests/new_session/timeouts.py": [
+   "c5adb9396819beb03d4627ef9890958fb687b58d",
+   "wdspec"
+  ],
   "webdriver/tests/page_source/__init__.py": [
    "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
    "support"
   ],
   "webdriver/tests/page_source/source.py": [
    "97e4e1eff255c2209cf36bc7885022acbcf9963f",
    "wdspec"
   ],
@@ -641924,17 +641940,17 @@
    "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
    "support"
   ],
   "webdriver/tests/status/status.py": [
    "f0df16a1ee17d22e6293af049876810bb4073929",
    "wdspec"
   ],
   "webdriver/tests/support/__init__.py": [
-   "d37faf6da04807045bd3c9e11c76826693d48906",
+   "e5e43c4e655170d57d3de7a85d4ebb639c31aee0",
    "support"
   ],
   "webdriver/tests/support/asserts.py": [
    "1a7bbc2697c0cde846a59aaa08e656bd23cfaa43",
    "support"
   ],
   "webdriver/tests/support/fixtures.py": [
    "775b1735e7d4076402506e12d6ecf7b24899e9a0",
@@ -642740,17 +642756,17 @@
    "ad9cdff5fb0a4cf90e977fd0cdde7d3d685e9f4d",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-track-stats.https.html": [
    "682e7e57e465ccde77c3d8887ce80cb5ea01bf54",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-transceivers.https.html": [
-   "3f4e50800590c7054d0d316e1e9e66ba18a557d1",
+   "49f683911e61bf7e0c0466e6d6a91f2f70ab30e6",
    "testharness"
   ],
   "webrtc/RTCPeerConnectionIceEvent-constructor.html": [
    "07e9736441285536e0549c55b110a562b49276cc",
    "testharness"
   ],
   "webrtc/RTCRtpCapabilities-helper.js": [
    "fb297c35fb1126e8985ff2f2a0dd1dd824ca5c1d",
@@ -642824,17 +642840,17 @@
    "a1f7854e1a2f18036040f882889ff6758e9968c0",
    "testharness"
   ],
   "webrtc/RTCRtpTransceiver-setDirection.html": [
    "32cbff5d98586840ab9c37a1e1e05f262560848e",
    "testharness"
   ],
   "webrtc/RTCRtpTransceiver.https.html": [
-   "2bba7c4641c64b46b6e6a80c11e9ff8a6a30118e",
+   "f3b67632f2da94c8a01f95c829ffe129231835e0",
    "testharness"
   ],
   "webrtc/RTCSctpTransport-constructor.html": [
    "c415c3fe180b6718fa11c20b1bc9a1ecd88a4547",
    "testharness"
   ],
   "webrtc/RTCSctpTransport-maxMessageSize.html": [
    "28d17eeaccf3f26aaca93c9e1f1eab9edc643aaa",
deleted file mode 100644
--- a/testing/web-platform/meta/css/CSS2/backgrounds/background-position-001.xht.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[background-position-001.xht]
-  expected:
-    if (os == "linux") and not webrender: FAIL
deleted file mode 100644
--- a/testing/web-platform/meta/css/CSS2/backgrounds/background-position-002.xht.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[background-position-002.xht]
-  expected:
-    if (os == "linux") and not webrender: FAIL
--- a/testing/web-platform/meta/css/css-transforms/transform3d-preserve3d-013.html.ini
+++ b/testing/web-platform/meta/css/css-transforms/transform3d-preserve3d-013.html.ini
@@ -1,5 +1,3 @@
 [transform3d-preserve3d-013.html]
   expected:
     if webrender: FAIL
-  disabled:
-    if debug and not e10s and (os == "linux"): https://bugzilla.mozilla.org/show_bug.cgi?id=1471704
--- a/testing/web-platform/meta/mozilla-sync
+++ b/testing/web-platform/meta/mozilla-sync
@@ -1,2 +1,2 @@
-local: 38eaf263a00fa5313060ef98916ba095e285ccb7
-upstream: 24c4f1e5c434a5ad018aa7467b7bfcffca764b8d
+local: 635fc136b5a4e5a82fb4f57dc10ab6f3e77d159f
+upstream: 00d1189bc19907a981ee8ee9b90229121bf3ac51
--- a/testing/web-platform/meta/webdriver/tests/new_session/response.py.ini
+++ b/testing/web-platform/meta/webdriver/tests/new_session/response.py.ini
@@ -1,7 +1,6 @@
 [response.py]
-  [test_capabilites]
+  [test_capability_type\[proxy-dict\]]
     expected: FAIL
 
-  [test_data]
+  [test_capability_default_value\[proxy-default_value2\]]
     expected: FAIL
-
--- a/testing/web-platform/meta/webrtc/RTCPeerConnection-transceivers.https.html.ini
+++ b/testing/web-platform/meta/webrtc/RTCPeerConnection-transceivers.https.html.ini
@@ -1,85 +1,19 @@
 [RTCPeerConnection-transceivers.https.html]
-  [setLocalDescription(answer): transceiver.currentDirection is recvonly]
-    expected: FAIL
-
-  [setRemoteDescription(offer): ontrack fires with a track]
-    expected: FAIL
-
-  [Can setup two-way call using a single transceiver]
-    expected: FAIL
-
-  [setRemoteDescription(offer): transceiver.mid is the same on both ends]
-    expected: FAIL
-
   [addTransceiver(track, init): initialize sendEncodings[0\].active to false]
     expected: FAIL
 
-  [setLocalDescription(answer): transceiver.currentDirection is sendonly]
-    expected: FAIL
-
-  [addTrack: transceiver is not associated with an m-section]
-    expected: FAIL
-
-  [addTrack: transceiver.receiver has its own track]
-    expected: FAIL
-
-  [addTrack: "transceiver == {sender,receiver}"]
-    expected: FAIL
-
-  [setLocalDescription(offer): transceiver.mid matches the offer SDP]
-    expected: FAIL
-
-  [setRemoteDescription(offer): transceiver.direction is recvonly]
-    expected: FAIL
-
-  [addTrack: transceiver is not stopped]
-    expected: FAIL
-
   [addTrack(0 streams): ontrack fires with no stream]
     expected: FAIL
 
-  [addTrack: transceiver's direction is sendrecv]
-    expected: FAIL
-
   [addTrack(2 streams): ontrack fires with corresponding two streams]
     expected: FAIL
 
-  [setRemoteDescription(offer): transceiver.currentDirection is null]
-    expected: FAIL
-
-  [addTrack: transceiver.sender is associated with the track]
-    expected: FAIL
-
-  [setLocalDescription(offer): transceiver gets associated with an m-section]
-    expected: FAIL
-
-  [setRemoteDescription(offer): transceiver.stopped is false]
-    expected: FAIL
-
   [setRemoteDescription(offer): ontrack's track.id is the same as track.id]
     expected: FAIL
 
   [addTransceiver(2 streams): ontrack fires with corresponding two streams]
     expected: FAIL
 
-  [setRemoteDescription(offer): "transceiver == {sender,receiver}"]
-    expected: FAIL
-
-  [addTrack: transceiver's currentDirection is null]
-    expected: FAIL
-
-  [addTrack reuses reusable transceivers]
-    expected: FAIL
-
-  [addTrack: creates a transceiver for the sender]
-    expected: FAIL
-
   [Closing the PC stops the transceivers]
     expected: FAIL
 
-  [setRemoteDescription(offer): ontrack fires with a transceiver.]
-    expected: FAIL
-
-  [addTrack: transceiver.receiver's track is muted]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/tests/html/semantics/tabular-data/processing-model-1/span-limits-2.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<!DOCTYPE html>
-<title>Limits on rowSpan</title>
-<meta name="timeout" content="long">
-<script src=/resources/testharness.js></script>
-<script src=/resources/testharnessreport.js></script>
-<div id=log></div>
-
-<table border=1>
-  <!-- The first column must go one cell below the bottom -->
-  <tr><td rowspan=65535 id=d1>a<td>
-  <!-- We'll add another 65534 rows later -->
-</table>
-
-<script>
-var $ = document.querySelector.bind(document);
-
-test(() => {
-    var s = "";
-    for (var i = 0; i < 65532; i++) {
-      s += "<tr><td>";
-    }
-    s += "<tr><td id=d2><tr><td>a<td>";
-    document.querySelector("table").firstElementChild.innerHTML += s;
-    assert_equals($("#d1").getBoundingClientRect().bottom,
-                  $("#d2").getBoundingClientRect().bottom);
-}, "rowspan of 65535 must be treated as 65534");
-</script>
--- a/testing/web-platform/tests/html/semantics/tabular-data/processing-model-1/span-limits.html
+++ b/testing/web-platform/tests/html/semantics/tabular-data/processing-model-1/span-limits.html
@@ -18,16 +18,22 @@
 </table>
 
 <table border=1 style="float:left">
   <!-- The first column must go all the way down to the bottom -->
   <tr><td rowspan=65534 id=c1>a<td>
   <!-- We'll add another 65533 rows later -->
 </table>
 
+<table border=1>
+  <!-- The first column must go one cell below the bottom -->
+  <tr><td rowspan=65535 id=d1>a<td>
+  <!-- We'll add another 65534 rows later -->
+</table>
+
 <script>
 var $ = document.querySelector.bind(document);
 
 test(() => {
     assert_equals($("#a2").getBoundingClientRect().right,
                   $("#a1").getBoundingClientRect().right);
 }, "colspan of 1000 must work");
 
@@ -42,10 +48,19 @@ test(() => {
       s += "<tr><td>";
     }
     s += "<tr><td id=c2>";
     document.querySelectorAll("table")[2].firstElementChild.innerHTML += s;
     assert_equals($("#c1").getBoundingClientRect().bottom,
                   $("#c2").getBoundingClientRect().bottom);
 }, "rowspan of 65534 must work");
 
-// See span-limits-2.html too. This test was split into two due to slowness.
+test(() => {
+    var s = "";
+    for (var i = 0; i < 65532; i++) {
+      s += "<tr><td>";
+    }
+    s += "<tr><td id=d2><tr><td>a<td>";
+    document.querySelectorAll("table")[3].firstElementChild.innerHTML += s;
+    assert_equals($("#d1").getBoundingClientRect().bottom,
+                  $("#d2").getBoundingClientRect().bottom);
+}, "rowspan of 65535 must be treated as 65534");
 </script>
--- a/testing/web-platform/tests/webdriver/tests/new_session/conftest.py
+++ b/testing/web-platform/tests/webdriver/tests/new_session/conftest.py
@@ -1,10 +1,8 @@
-import sys
-
 import pytest
 
 from webdriver.transport import HTTPWireProtocol
 
 
 def product(a, b):
     return [(a, item) for item in b]
 
@@ -61,18 +59,8 @@ def fixture_new_session(request, configu
             custom_session["session"] = response.body["value"]
         return response, custom_session.get("session", None)
 
     yield new_session
 
     if custom_session.get("session") is not None:
         _delete_session(custom_session["session"]["sessionId"])
         custom_session = None
-
-
-@pytest.fixture(scope="session")
-def platform_name():
-    return {
-        "linux2": "linux",
-        "win32": "windows",
-        "cygwin": "windows",
-        "darwin": "mac"
-    }.get(sys.platform)
--- a/testing/web-platform/tests/webdriver/tests/new_session/merge.py
+++ b/testing/web-platform/tests/webdriver/tests/new_session/merge.py
@@ -1,20 +1,20 @@
 # META: timeout=long
 
 import pytest
 
 from tests.support.asserts import assert_error, assert_success
-from conftest import platform_name
+from tests.support import platform_name
 
 
-@pytest.mark.skipif(platform_name() is None, reason="Unsupported platform")
+@pytest.mark.skipif(platform_name is None, reason="Unsupported platform {}".format(platform_name))
 @pytest.mark.parametrize("body", [lambda key, value: {"alwaysMatch": {key: value}},
                                   lambda key, value: {"firstMatch": [{key: value}]}])
-def test_platform_name(new_session, add_browser_capabilities, platform_name, body):
+def test_platform_name(new_session, add_browser_capabilities, body):
     capabilities = body("platformName", platform_name)
     if "alwaysMatch" in capabilities:
         capabilities["alwaysMatch"] = add_browser_capabilities(capabilities["alwaysMatch"])
     else:
         capabilities["firstMatch"][0] = add_browser_capabilities(capabilities["firstMatch"][0])
 
     response, _ = new_session({"capabilities": capabilities})
     value = assert_success(response)
@@ -35,18 +35,18 @@ invalid_merge = [
 def test_merge_invalid(new_session, add_browser_capabilities, key, value):
     response, _ = new_session({"capabilities": {
         "alwaysMatch": add_browser_capabilities({key: value[0]}),
         "firstMatch": [{}, {key: value[1]}],
     }})
     assert_error(response, "invalid argument")
 
 
-@pytest.mark.skipif(platform_name() is None, reason="Unsupported platform")
-def test_merge_platformName(new_session, add_browser_capabilities, platform_name):
+@pytest.mark.skipif(platform_name is None, reason="Unsupported platform {}".format(platform_name))
+def test_merge_platformName(new_session, add_browser_capabilities):
     response, _ = new_session({"capabilities": {
         "alwaysMatch": add_browser_capabilities({"timeouts": {"script": 10}}),
         "firstMatch": [{
             "platformName": platform_name.upper(),
             "pageLoadStrategy": "none",
         }, {
             "platformName": platform_name,
             "pageLoadStrategy": "eager",
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webdriver/tests/new_session/page_load_strategy.py
@@ -0,0 +1,7 @@
+from tests.support.asserts import assert_success
+
+def test_pageLoadStrategy(new_session, add_browser_capabilities):
+    response, _ = new_session({"capabilities": {
+        "alwaysMatch": add_browser_capabilities({"pageLoadStrategy": "eager"})}})
+    value = assert_success(response)
+    assert value["capabilities"]["pageLoadStrategy"] == "eager"
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webdriver/tests/new_session/platform_name.py
@@ -0,0 +1,11 @@
+import pytest
+
+from tests.support import platform_name
+from tests.support.asserts import assert_success
+
+
+@pytest.mark.skip_if(platform_name is None, reason="Unsupported platform {}".format(platform_name))
+def test_corresponds_to_local_system(new_session, add_browser_capabilities):
+    response, _ = new_session({"capabilities": {"alwaysMatch": add_browser_capabilities({})}})
+    value = assert_success(response)
+    assert value["capabilities"]["platformName"] == platform_name
--- a/testing/web-platform/tests/webdriver/tests/new_session/response.py
+++ b/testing/web-platform/tests/webdriver/tests/new_session/response.py
@@ -1,74 +1,43 @@
-# META: timeout=long
+import uuid
 
-import uuid
+import pytest
 
 from tests.support.asserts import assert_success
 
 
 def test_sessionid(new_session, add_browser_capabilities):
     response, _ = new_session({"capabilities": {"alwaysMatch": add_browser_capabilities({})}})
     value = assert_success(response)
     assert isinstance(value["sessionId"], basestring)
     uuid.UUID(hex=value["sessionId"])
 
 
-def test_capabilites(new_session, add_browser_capabilities):
-    response, _ = new_session({"capabilities": {"alwaysMatch": add_browser_capabilities({})}})
-    value = assert_success(response)
-    assert isinstance(value["capabilities"], dict)
-
-    all_capabilities = set(value["capabilities"].keys())
-    expected_capabilities = {
-        "browserName",
-        "browserVersion",
-        "platformName",
-        "acceptInsecureCerts",
-        "setWindowRect",
-        "timeouts",
-        "proxy",
-        "pageLoadStrategy",
-    }
-
-    assert expected_capabilities.issubset(all_capabilities), (
-        "{0} cannot be found in {1}".format(
-            list(expected_capabilities - all_capabilities), all_capabilities))
+@pytest.mark.parametrize("capability, type", [
+    ("browserName", basestring),
+    ("browserVersion", basestring),
+    ("platformName", basestring),
+    ("acceptInsecureCerts", bool),
+    ("pageLoadStrategy", basestring),
+    ("proxy", dict),
+    ("setWindowRect", bool),
+    ("timeouts", dict),
+    ("unhandledPromptBehavior", basestring),
+])
+def test_capability_type(session, capability, type):
+    assert isinstance(session.capabilities, dict)
+    assert capability in session.capabilities
+    assert isinstance(session.capabilities[capability], type)
 
 
-def test_data(new_session, add_browser_capabilities, platform_name):
-    response, _ = new_session({"capabilities": {"alwaysMatch": add_browser_capabilities({})}})
-    value = assert_success(response)
-
-    assert isinstance(value["capabilities"]["browserName"], basestring)
-    assert isinstance(value["capabilities"]["browserVersion"], basestring)
-    if platform_name:
-        assert value["capabilities"]["platformName"] == platform_name
-    else:
-        assert "platformName" in value["capabilities"]
-    assert value["capabilities"]["acceptInsecureCerts"] is False
-    assert isinstance(value["capabilities"]["setWindowRect"], bool)
-    assert value["capabilities"]["timeouts"]["implicit"] == 0
-    assert value["capabilities"]["timeouts"]["pageLoad"] == 300000
-    assert value["capabilities"]["timeouts"]["script"] == 30000
-    assert value["capabilities"]["proxy"] == {}
-    assert value["capabilities"]["pageLoadStrategy"] == "normal"
-
-
-def test_timeouts(new_session, add_browser_capabilities, platform_name):
-    response, _ = new_session({"capabilities": {
-        "alwaysMatch": add_browser_capabilities({"timeouts": {"implicit": 1000}}),
-    }})
-    value = assert_success(response)
-
-    assert value["capabilities"]["timeouts"] == {
-        "implicit": 1000,
-        "pageLoad": 300000,
-        "script": 30000
-    }
-
-
-def test_pageLoadStrategy(new_session, add_browser_capabilities, platform_name):
-    response, _ = new_session({"capabilities": {
-        "alwaysMatch": add_browser_capabilities({"pageLoadStrategy": "eager"})}})
-    value = assert_success(response)
-
-    assert value["capabilities"]["pageLoadStrategy"] == "eager"
+@pytest.mark.parametrize("capability, default_value", [
+    ("acceptInsecureCerts", False),
+    ("pageLoadStrategy", "normal"),
+    ("proxy", {}),
+    ("setWindowRect", True),
+    ("timeouts", {"implicit": 0, "pageLoad": 300000, "script": 30000}),
+    ("unhandledPromptBehavior", "dismiss and notify"),
+])
+def test_capability_default_value(session, capability, default_value):
+    assert isinstance(session.capabilities, dict)
+    assert capability in session.capabilities
+    assert session.capabilities[capability] == default_value
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webdriver/tests/new_session/timeouts.py
@@ -0,0 +1,22 @@
+import pytest
+
+from tests.support.asserts import assert_success
+
+
+def test_default_values(session):
+    timeouts = session.capabilities["timeouts"]
+
+    assert timeouts["implicit"] == 0
+    assert timeouts["pageLoad"] == 300000
+    assert timeouts["script"] == 30000
+
+
+@pytest.mark.parametrize("timeouts", [
+    {"implicit": 444, "pageLoad": 300000,"script": 30000},
+    {"implicit": 0, "pageLoad": 444,"script": 30000},
+    {"implicit": 0, "pageLoad": 300000,"script": 444},
+])
+def test_timeouts(new_session, add_browser_capabilities, timeouts):
+    response, _ = new_session({"capabilities": {"alwaysMatch": add_browser_capabilities({"timeouts": timeouts})}})
+    value = assert_success(response)
+    assert value["capabilities"]["timeouts"] == timeouts
--- a/testing/web-platform/tests/webdriver/tests/support/__init__.py
+++ b/testing/web-platform/tests/webdriver/tests/support/__init__.py
@@ -1,1 +1,10 @@
+import sys
+
 from merge_dictionaries import merge_dictionaries
+
+platform_name = {
+    "linux2": "linux",
+    "win32": "windows",
+    "cygwin": "windows",
+    "darwin": "mac"
+}.get(sys.platform)
--- a/testing/web-platform/tests/webrtc/RTCPeerConnection-transceivers.https.html
+++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-transceivers.https.html
@@ -11,23 +11,23 @@
 //   addEventListenerPromise
 
 function createPeerConnectionWithCleanup(t) {
   const pc = new RTCPeerConnection();
   t.add_cleanup(() => pc.close());
   return pc;
 }
 
-async function createTrackWithCleanup(t, kind = 'audio') {
+async function createTrackAndStreamWithCleanup(t, kind = 'audio') {
   let constraints = {};
   constraints[kind] = true;
   const stream = await navigator.mediaDevices.getUserMedia(constraints);
   const [track] = stream.getTracks();
   t.add_cleanup(() => track.stop());
-  return track;
+  return [track, stream];
 }
 
 function findTransceiverForSender(pc, sender) {
   const transceivers = pc.getTransceivers();
   for (let i = 0; i < transceivers.length; ++i) {
     if (transceivers[i].sender == sender)
       return transceivers[i];
   }
@@ -59,254 +59,254 @@ async function exchangeAnswer(pc1, pc2) 
 async function exchangeAnswerAndListenToOntrack(t, pc1, pc2) {
   const ontrackPromise = addEventListenerPromise(t, pc1, 'track');
   await exchangeAnswer(pc1, pc2);
   return ontrackPromise;
 }
 
 promise_test(async t => {
   const pc = createPeerConnectionWithCleanup(t);
-  const track = await createTrackWithCleanup(t);
-  const sender = pc.addTrack(track);
+  const [track, stream] = await createTrackAndStreamWithCleanup(t);
+  const sender = pc.addTrack(track, stream);
   const transceiver = findTransceiverForSender(pc, sender);
   assert_true(transceiver instanceof RTCRtpTransceiver);
   assert_true(transceiver.sender instanceof RTCRtpSender);
   assert_equals(transceiver.sender, sender);
 }, 'addTrack: creates a transceiver for the sender');
 
 promise_test(async t => {
   const pc = createPeerConnectionWithCleanup(t);
-  const track = await createTrackWithCleanup(t);
-  const transceiver = findTransceiverForSender(pc, pc.addTrack(track));
+  const [track, stream] = await createTrackAndStreamWithCleanup(t);
+  const transceiver = findTransceiverForSender(pc, pc.addTrack(track, stream));
   assert_array_equals(pc.getTransceivers(), [transceiver],
                       'pc.getTransceivers() equals [transceiver]');
   assert_array_equals(pc.getSenders(), [transceiver.sender],
                       'pc.getSenders() equals [transceiver.sender]');
   assert_array_equals(pc.getReceivers(), [transceiver.receiver],
                       'pc.getReceivers() equals [transceiver.receiver]');
 }, 'addTrack: "transceiver == {sender,receiver}"');
 
 promise_test(async t => {
   const pc = createPeerConnectionWithCleanup(t);
-  const track = await createTrackWithCleanup(t);
-  const transceiver = findTransceiverForSender(pc, pc.addTrack(track));
+  const [track, stream] = await createTrackAndStreamWithCleanup(t);
+  const transceiver = findTransceiverForSender(pc, pc.addTrack(track, stream));
   assert_true(transceiver.sender.track instanceof MediaStreamTrack,
               'transceiver.sender.track instanceof MediaStreamTrack');
   assert_equals(transceiver.sender.track, track,
                 'transceiver.sender.track == track');
 }, 'addTrack: transceiver.sender is associated with the track');
 
 promise_test(async t => {
   const pc = createPeerConnectionWithCleanup(t);
-  const track = await createTrackWithCleanup(t);
-  const transceiver = findTransceiverForSender(pc, pc.addTrack(track));
+  const [track, stream] = await createTrackAndStreamWithCleanup(t);
+  const transceiver = findTransceiverForSender(pc, pc.addTrack(track, stream));
   assert_true(transceiver.receiver instanceof RTCRtpReceiver,
               'transceiver.receiver instanceof RTCRtpReceiver');
   assert_true(transceiver.receiver.track instanceof MediaStreamTrack,
               'transceiver.receiver.track instanceof MediaStreamTrack');
   assert_not_equals(transceiver.receiver.track, track,
                     'transceiver.receiver.track != track');
 }, 'addTrack: transceiver.receiver has its own track');
 
 promise_test(async t => {
   const pc = createPeerConnectionWithCleanup(t);
-  const track = await createTrackWithCleanup(t);
-  const transceiver = findTransceiverForSender(pc, pc.addTrack(track));
+  const [track, stream] = await createTrackAndStreamWithCleanup(t);
+  const transceiver = findTransceiverForSender(pc, pc.addTrack(track, stream));
   assert_true(transceiver.receiver.track.muted);
 }, 'addTrack: transceiver.receiver\'s track is muted');
 
 promise_test(async t => {
   const pc = createPeerConnectionWithCleanup(t);
-  const track = await createTrackWithCleanup(t);
-  const transceiver = findTransceiverForSender(pc, pc.addTrack(track));
+  const [track, stream] = await createTrackAndStreamWithCleanup(t);
+  const transceiver = findTransceiverForSender(pc, pc.addTrack(track, stream));
   assert_equals(transceiver.mid, null);
 }, 'addTrack: transceiver is not associated with an m-section');
 
 promise_test(async t => {
   const pc = createPeerConnectionWithCleanup(t);
-  const track = await createTrackWithCleanup(t);
-  const transceiver = findTransceiverForSender(pc, pc.addTrack(track));
+  const [track, stream] = await createTrackAndStreamWithCleanup(t);
+  const transceiver = findTransceiverForSender(pc, pc.addTrack(track, stream));
   assert_false(transceiver.stopped);
 }, 'addTrack: transceiver is not stopped');
 
 promise_test(async t => {
   const pc = createPeerConnectionWithCleanup(t);
-  const track = await createTrackWithCleanup(t);
-  const transceiver = findTransceiverForSender(pc, pc.addTrack(track));
+  const [track, stream] = await createTrackAndStreamWithCleanup(t);
+  const transceiver = findTransceiverForSender(pc, pc.addTrack(track, stream));
   assert_equals(transceiver.direction, 'sendrecv');
 }, 'addTrack: transceiver\'s direction is sendrecv');
 
 promise_test(async t => {
   const pc = createPeerConnectionWithCleanup(t);
-  const track = await createTrackWithCleanup(t);
-  const transceiver = findTransceiverForSender(pc, pc.addTrack(track));
+  const [track, stream] = await createTrackAndStreamWithCleanup(t);
+  const transceiver = findTransceiverForSender(pc, pc.addTrack(track, stream));
   assert_equals(transceiver.currentDirection, null);
 }, 'addTrack: transceiver\'s currentDirection is null');
 
 promise_test(async t => {
   const pc = createPeerConnectionWithCleanup(t);
-  const track = await createTrackWithCleanup(t);
-  const transceiver = findTransceiverForSender(pc, pc.addTrack(track));
+  const [track, stream] = await createTrackAndStreamWithCleanup(t);
+  const transceiver = findTransceiverForSender(pc, pc.addTrack(track, stream));
   await pc.setLocalDescription(await pc.createOffer());
   assert_not_equals(transceiver.mid, null, 'transceiver.mid != null');
 }, 'setLocalDescription(offer): transceiver gets associated with an m-section');
 
 promise_test(async t => {
   const pc = createPeerConnectionWithCleanup(t);
-  const track = await createTrackWithCleanup(t);
-  const transceiver = findTransceiverForSender(pc, pc.addTrack(track));
+  const [track, stream] = await createTrackAndStreamWithCleanup(t);
+  const transceiver = findTransceiverForSender(pc, pc.addTrack(track, stream));
   const offer = await pc.createOffer();
   await pc.setLocalDescription(offer);
   let sdp = offer.sdp;
   let sdpMidLineStart = sdp.indexOf('a=mid:');
   let sdpMidLineEnd = sdp.indexOf('\r\n', sdpMidLineStart);
   assert_true(sdpMidLineStart != -1 && sdpMidLineEnd != -1,
               'Failed to parse offer SDP for a=mid');
   let parsedMid = sdp.substring(sdpMidLineStart + 6, sdpMidLineEnd);
   assert_equals(transceiver.mid, parsedMid, 'transceiver.mid == parsedMid');
 }, 'setLocalDescription(offer): transceiver.mid matches the offer SDP');
 
 promise_test(async t => {
   const pc1 = createPeerConnectionWithCleanup(t);
-  pc1.addTrack(await createTrackWithCleanup(t));
+  pc1.addTrack(... await createTrackAndStreamWithCleanup(t));
   const pc2 = createPeerConnectionWithCleanup(t);
   const trackEvent = await exchangeOfferAndListenToOntrack(t, pc1, pc2);
   assert_true(trackEvent instanceof RTCTrackEvent,
               'trackEvent instanceof RTCTrackEvent');
   assert_true(trackEvent.track instanceof MediaStreamTrack,
               'trackEvent.track instanceof MediaStreamTrack');
 }, 'setRemoteDescription(offer): ontrack fires with a track');
 
 promise_test(async t => {
   const pc1 = createPeerConnectionWithCleanup(t);
-  const track = await createTrackWithCleanup(t);
-  pc1.addTrack(track);
+  const [track, stream] = await createTrackAndStreamWithCleanup(t);
+  pc1.addTrack(track, stream);
   const pc2 = createPeerConnectionWithCleanup(t);
   const trackEvent = await exchangeOfferAndListenToOntrack(t, pc1, pc2);
   assert_true(trackEvent.track instanceof MediaStreamTrack,
               'trackEvent.track instanceof MediaStreamTrack');
   assert_equals(trackEvent.track.id, track.id,
                 'trackEvent.track.id == track.id');
 }, 'setRemoteDescription(offer): ontrack\'s track.id is the same as track.id');
 
 promise_test(async t => {
   const pc1 = createPeerConnectionWithCleanup(t);
-  pc1.addTrack(await createTrackWithCleanup(t));
+  pc1.addTrack(... await createTrackAndStreamWithCleanup(t));
   const pc2 = createPeerConnectionWithCleanup(t);
   const trackEvent = await exchangeOfferAndListenToOntrack(t, pc1, pc2);
   assert_true(trackEvent.transceiver instanceof RTCRtpTransceiver,
               'trackEvent.transceiver instanceof RTCRtpTransceiver');
 }, 'setRemoteDescription(offer): ontrack fires with a transceiver.');
 
 promise_test(async t => {
   const pc1 = createPeerConnectionWithCleanup(t);
-  const track = await createTrackWithCleanup(t);
-  const transceiver = findTransceiverForSender(pc1, pc1.addTrack(track));
+  const [track, stream] = await createTrackAndStreamWithCleanup(t);
+  const transceiver = findTransceiverForSender(pc1, pc1.addTrack(track, stream));
   const pc2 = createPeerConnectionWithCleanup(t);
   const trackEvent = await exchangeOfferAndListenToOntrack(t, pc1, pc2);
   assert_equals(transceiver.mid, trackEvent.transceiver.mid);
 }, 'setRemoteDescription(offer): transceiver.mid is the same on both ends');
 
 promise_test(async t => {
   const pc1 = createPeerConnectionWithCleanup(t);
-  pc1.addTrack(await createTrackWithCleanup(t));
+  pc1.addTrack(... await createTrackAndStreamWithCleanup(t));
   const pc2 = createPeerConnectionWithCleanup(t);
   const trackEvent = await exchangeOfferAndListenToOntrack(t, pc1, pc2);
   const transceiver = trackEvent.transceiver;
   assert_array_equals(pc2.getTransceivers(), [transceiver],
                       'pc2.getTransceivers() equals [transceiver]');
   assert_array_equals(pc2.getSenders(), [transceiver.sender],
                       'pc2.getSenders() equals [transceiver.sender]');
   assert_array_equals(pc2.getReceivers(), [transceiver.receiver],
                       'pc2.getReceivers() equals [transceiver.receiver]');
 }, 'setRemoteDescription(offer): "transceiver == {sender,receiver}"');
 
 promise_test(async t => {
   const pc1 = createPeerConnectionWithCleanup(t);
-  pc1.addTrack(await createTrackWithCleanup(t));
+  pc1.addTrack(... await createTrackAndStreamWithCleanup(t));
   const pc2 = createPeerConnectionWithCleanup(t);
   const trackEvent = await exchangeOfferAndListenToOntrack(t, pc1, pc2);
   assert_equals(trackEvent.transceiver.direction, 'recvonly');
 }, 'setRemoteDescription(offer): transceiver.direction is recvonly');
 
 promise_test(async t => {
   const pc1 = createPeerConnectionWithCleanup(t);
-  pc1.addTrack(await createTrackWithCleanup(t));
+  pc1.addTrack(... await createTrackAndStreamWithCleanup(t));
   const pc2 = createPeerConnectionWithCleanup(t);
   const trackEvent = await exchangeOfferAndListenToOntrack(t, pc1, pc2);
   assert_equals(trackEvent.transceiver.currentDirection, null);
 }, 'setRemoteDescription(offer): transceiver.currentDirection is null');
 
 promise_test(async t => {
   const pc1 = createPeerConnectionWithCleanup(t);
-  pc1.addTrack(await createTrackWithCleanup(t));
+  pc1.addTrack(... await createTrackAndStreamWithCleanup(t));
   const pc2 = createPeerConnectionWithCleanup(t);
   const trackEvent = await exchangeOfferAndListenToOntrack(t, pc1, pc2);
   assert_false(trackEvent.transceiver.stopped);
 }, 'setRemoteDescription(offer): transceiver.stopped is false');
 
 promise_test(async t => {
   const pc1 = createPeerConnectionWithCleanup(t);
-  pc1.addTrack(await createTrackWithCleanup(t));
+  pc1.addTrack(... await createTrackAndStreamWithCleanup(t));
   const pc2 = createPeerConnectionWithCleanup(t);
   const trackEvent = await exchangeOfferAndListenToOntrack(t, pc1, pc2);
   const transceiver = trackEvent.transceiver;
   assert_equals(transceiver.currentDirection, null,
                 'SRD(offer): transciever.currentDirection is null');
   await pc2.setLocalDescription(await pc2.createAnswer());
   assert_equals(transceiver.currentDirection, 'recvonly',
                 'SLD(answer): transciever.currentDirection is recvonly');
 }, 'setLocalDescription(answer): transceiver.currentDirection is recvonly');
 
 promise_test(async t => {
   const pc1 = createPeerConnectionWithCleanup(t);
-  const track = await createTrackWithCleanup(t);
-  const transceiver = findTransceiverForSender(pc1, pc1.addTrack(track));
+  const [track, stream] = await createTrackAndStreamWithCleanup(t);
+  const transceiver = findTransceiverForSender(pc1, pc1.addTrack(track, stream));
   const pc2 = createPeerConnectionWithCleanup(t);
   await exchangeOffer(pc1, pc2);
   assert_equals(transceiver.currentDirection, null,
                 'SLD(offer): transciever.currentDirection is null');
   await exchangeAnswer(pc1, pc2);
   assert_equals(transceiver.currentDirection, 'sendonly',
                 'SRD(answer): transciever.currentDirection is sendonly');
 }, 'setLocalDescription(answer): transceiver.currentDirection is sendonly');
 
 promise_test(async t => {
   const pc = createPeerConnectionWithCleanup(t);
-  const track = await createTrackWithCleanup(t);
+  const [track, stream] = await createTrackAndStreamWithCleanup(t);
   const transceiver = pc.addTransceiver(track);
   assert_true(transceiver instanceof RTCRtpTransceiver);
   assert_true(transceiver.sender instanceof RTCRtpSender);
   assert_true(transceiver.receiver instanceof RTCRtpReceiver);
   assert_equals(transceiver.sender.track, track);
 }, 'addTransceiver(track): creates a transceiver for the track');
 
 promise_test(async t => {
   const pc = createPeerConnectionWithCleanup(t);
-  const track = await createTrackWithCleanup(t);
+  const [track, stream] = await createTrackAndStreamWithCleanup(t);
   const transceiver = pc.addTransceiver(track);
   assert_array_equals(pc.getTransceivers(), [transceiver],
                       'pc.getTransceivers() equals [transceiver]');
   assert_array_equals(pc.getSenders(), [transceiver.sender],
                       'pc.getSenders() equals [transceiver.sender]');
   assert_array_equals(pc.getReceivers(), [transceiver.receiver],
                       'pc.getReceivers() equals [transceiver.receiver]');
 }, 'addTransceiver(track): "transceiver == {sender,receiver}"');
 
 promise_test(async t => {
   const pc = createPeerConnectionWithCleanup(t);
-  const track = await createTrackWithCleanup(t);
+  const [track, stream] = await createTrackAndStreamWithCleanup(t);
   const transceiver = pc.addTransceiver(track, {direction:'inactive'});
   assert_equals(transceiver.direction, 'inactive');
 }, 'addTransceiver(track, init): initialize direction to inactive');
 
 promise_test(async t => {
   const pc = createPeerConnectionWithCleanup(t);
   const otherPc = createPeerConnectionWithCleanup(t);
-  const track = await createTrackWithCleanup(t);
+  const [track, stream] = await createTrackAndStreamWithCleanup(t);
   const transceiver = pc.addTransceiver(track, {
     sendEncodings: [{active:false}]
   });
 
   // Negotiate parameters.
   const offer = await pc.createOffer();
   await pc.setLocalDescription(offer);
   await otherPc.setRemoteDescription(offer);
@@ -316,178 +316,176 @@ promise_test(async t => {
 
   const params = transceiver.sender.getParameters();
   assert_false(params.encodings[0].active);
 }, 'addTransceiver(track, init): initialize sendEncodings[0].active to false');
 
 promise_test(async t => {
   const pc1 = createPeerConnectionWithCleanup(t);
   const pc2 = createPeerConnectionWithCleanup(t);
-  const track = await createTrackWithCleanup(t);
+  const [track, stream] = await createTrackAndStreamWithCleanup(t);
   pc1.addTransceiver(track, {streams:[]});
   const trackEvent = await exchangeOfferAndListenToOntrack(t, pc1, pc2);
   assert_equals(trackEvent.streams.length, 0, 'trackEvent.streams.length == 0');
 }, 'addTransceiver(0 streams): ontrack fires with no stream');
 
 promise_test(async t => {
   const pc1 = createPeerConnectionWithCleanup(t);
   const pc2 = createPeerConnectionWithCleanup(t);
-  const track = await createTrackWithCleanup(t);
-  const stream = new MediaStream();
+  const [track, stream] = await createTrackAndStreamWithCleanup(t);
   pc1.addTransceiver(track, {streams:[stream]});
   const trackEvent = await exchangeOfferAndListenToOntrack(t, pc1, pc2);
   assert_equals(trackEvent.streams.length, 1, 'trackEvent.streams.length == 1');
   assert_equals(trackEvent.streams[0].id, stream.id,
                 'trackEvent.streams[0].id == stream.id');
 }, 'addTransceiver(1 stream): ontrack fires with corresponding stream');
 
 promise_test(async t => {
   const pc1 = createPeerConnectionWithCleanup(t);
   const pc2 = createPeerConnectionWithCleanup(t);
-  const track = await createTrackWithCleanup(t);
+  const [track, stream] = await createTrackAndStreamWithCleanup(t);
   const stream0 = new MediaStream();
   const stream1 = new MediaStream();
   pc1.addTransceiver(track, {streams:[stream0, stream1]});
   const trackEvent = await exchangeOfferAndListenToOntrack(t, pc1, pc2);
   assert_equals(trackEvent.streams.length, 2, 'trackEvent.streams.length == 2');
   assert_equals(trackEvent.streams[0].id, stream0.id,
                 'trackEvent.streams[0].id == stream0.id');
   assert_equals(trackEvent.streams[1].id, stream1.id,
                 'trackEvent.streams[1].id == stream1.id');
 }, 'addTransceiver(2 streams): ontrack fires with corresponding two streams');
 
 promise_test(async t => {
   const pc1 = createPeerConnectionWithCleanup(t);
   const pc2 = createPeerConnectionWithCleanup(t);
-  const track = await createTrackWithCleanup(t);
-  pc1.addTrack(track);
+  const [track, stream] = await createTrackAndStreamWithCleanup(t);
+  pc1.addTrack(track, stream);
   const trackEvent = await exchangeOfferAndListenToOntrack(t, pc1, pc2);
   assert_equals(trackEvent.streams.length, 0, 'trackEvent.streams.length == 0');
 }, 'addTrack(0 streams): ontrack fires with no stream');
 
 promise_test(async t => {
   const pc1 = createPeerConnectionWithCleanup(t);
   const pc2 = createPeerConnectionWithCleanup(t);
-  const track = await createTrackWithCleanup(t);
-  const stream = new MediaStream();
+  const [track, stream] = await createTrackAndStreamWithCleanup(t);
   pc1.addTrack(track, stream);
   const trackEvent = await exchangeOfferAndListenToOntrack(t, pc1, pc2);
   assert_equals(trackEvent.streams.length, 1, 'trackEvent.streams.length == 1');
   assert_equals(trackEvent.streams[0].id, stream.id,
                 'trackEvent.streams[0].id == stream.id');
 }, 'addTrack(1 stream): ontrack fires with corresponding stream');
 
 promise_test(async t => {
   const pc1 = createPeerConnectionWithCleanup(t);
   const pc2 = createPeerConnectionWithCleanup(t);
-  const track = await createTrackWithCleanup(t);
+  const [track, stream] = await createTrackAndStreamWithCleanup(t);
   const stream0 = new MediaStream();
   const stream1 = new MediaStream();
   pc1.addTrack(track, stream0, stream1);
   const trackEvent = await exchangeOfferAndListenToOntrack(t, pc1, pc2);
   assert_equals(trackEvent.streams.length, 2, 'trackEvent.streams.length == 2');
   assert_equals(trackEvent.streams[0].id, stream0.id,
                 'trackEvent.streams[0].id == stream0.id');
   assert_equals(trackEvent.streams[1].id, stream1.id,
                 'trackEvent.streams[1].id == stream1.id');
 }, 'addTrack(2 streams): ontrack fires with corresponding two streams');
 
 promise_test(async t => {
   const pc = createPeerConnectionWithCleanup(t);
-  const track = await createTrackWithCleanup(t);
+  const [track, stream] = await createTrackAndStreamWithCleanup(t);
   const transceiver = pc.addTransceiver('audio');
   assert_equals(transceiver.direction, 'sendrecv');
 }, 'addTransceiver(\'audio\'): creates a transceiver with direction sendrecv');
 
 promise_test(async t => {
   const pc = createPeerConnectionWithCleanup(t);
-  const track = await createTrackWithCleanup(t);
+  const [track, stream] = await createTrackAndStreamWithCleanup(t);
   const transceiver = pc.addTransceiver('audio');
   assert_equals(transceiver.receiver.track.kind, 'audio');
 }, 'addTransceiver(\'audio\'): transceiver.receiver.track.kind == \'audio\'');
 
 promise_test(async t => {
   const pc = createPeerConnectionWithCleanup(t);
-  const track = await createTrackWithCleanup(t);
+  const [track, stream] = await createTrackAndStreamWithCleanup(t);
   const transceiver = pc.addTransceiver('video');
   assert_equals(transceiver.receiver.track.kind, 'video');
 }, 'addTransceiver(\'video\'): transceiver.receiver.track.kind == \'video\'');
 
 promise_test(async t => {
   const pc = createPeerConnectionWithCleanup(t);
-  const track = await createTrackWithCleanup(t);
+  const [track, stream] = await createTrackAndStreamWithCleanup(t);
   const transceiver = pc.addTransceiver('audio');
   assert_equals(transceiver.sender.track, null);
 }, 'addTransceiver(\'audio\'): transceiver.sender.track == null');
 
 promise_test(async t => {
   const pc = createPeerConnectionWithCleanup(t);
-  const track = await createTrackWithCleanup(t);
+  const [track, stream] = await createTrackAndStreamWithCleanup(t);
   const transceiver = pc.addTransceiver('audio');
   assert_equals(transceiver.currentDirection, null);
 }, 'addTransceiver(\'audio\'): transceiver.currentDirection is null');
 
 promise_test(async t => {
   const pc = createPeerConnectionWithCleanup(t);
-  const track = await createTrackWithCleanup(t);
+  const [track, stream] = await createTrackAndStreamWithCleanup(t);
   const transceiver = pc.addTransceiver('audio');
   assert_false(transceiver.stopped);
 }, 'addTransceiver(\'audio\'): transceiver.stopped is false');
 
 promise_test(async t => {
   const pc = createPeerConnectionWithCleanup(t);
-  const track = await createTrackWithCleanup(t, 'audio');
+  const [track, stream] = await createTrackAndStreamWithCleanup(t, 'audio');
   const transceiver = pc.addTransceiver('audio');
-  const sender = pc.addTrack(track);
+  const sender = pc.addTrack(track, stream);
   assert_equals(sender, transceiver.sender, 'sender == transceiver.sender');
   assert_equals(sender.track, track, 'sender.track == track');
 }, 'addTrack reuses reusable transceivers');
 
 promise_test(async t => {
   const pc = createPeerConnectionWithCleanup(t);
-  const track = await createTrackWithCleanup(t, 'audio');
+  const [track, stream] = await createTrackAndStreamWithCleanup(t, 'audio');
   const t1 = pc.addTransceiver('audio');
   const t2 = pc.addTransceiver(track);
   assert_not_equals(t2, t1, 't2 != t1');
   assert_equals(t2.sender.track, track, 't2.sender.track == track');
 }, 'addTransceiver does not reuse reusable transceivers');
 
 promise_test(async t => {
   const pc1 = createPeerConnectionWithCleanup(t);
   const pc2 = createPeerConnectionWithCleanup(t);
-  const track = await createTrackWithCleanup(t);
-  const pc1Transceiver = findTransceiverForSender(pc1, pc1.addTrack(track));
+  const [track, stream] = await createTrackAndStreamWithCleanup(t);
+  const pc1Transceiver = findTransceiverForSender(pc1, pc1.addTrack(track, stream));
   const pc2TrackEvent = await exchangeOfferAndListenToOntrack(t, pc1, pc2);
   const pc2Transceiver = pc2TrackEvent.transceiver;
   assert_equals(pc2Transceiver.direction, 'recvonly',
                 'pc2Transceiver.direction is recvonly after SRD(offer)');
-  const pc2Sender = pc2.addTrack(track);
+  const pc2Sender = pc2.addTrack(track, stream);
   assert_equals(pc2Transceiver.sender, pc2Sender,
                 'pc2Transceiver.sender == sender');
   assert_equals(pc2Transceiver.direction, 'sendrecv',
                 'pc2Transceiver.direction is sendrecv after addTrack()');
   assert_equals(pc2Transceiver.currentDirection, null,
                 'pc2Transceiver.currentDirection is null before answer');
   const pc1TrackEvent = await exchangeAnswerAndListenToOntrack(t, pc1, pc2);
   assert_equals(pc2Transceiver.currentDirection, 'sendrecv',
-      'pc2Transceiver.currentDirection is sendrecv after SLD(answer)');
+                'pc2Transceiver.currentDirection is sendrecv after SLD(answer)');
   assert_equals(pc1TrackEvent.transceiver, pc1Transceiver,
                 'Answer: pc1.ontrack fires with the existing transceiver.');
   assert_equals(pc1Transceiver.currentDirection, 'sendrecv',
                 'pc1Transceiver.currentDirection is sendrecv');
   assert_equals(pc2.getTransceivers().length, 1,
                 'pc2.getTransceivers().length == 1');
   assert_equals(pc1.getTransceivers().length, 1,
                 'pc1.getTransceivers().length == 1');
 }, 'Can setup two-way call using a single transceiver');
 
 promise_test(async t => {
   const pc1 = createPeerConnectionWithCleanup(t);
   const pc2 = createPeerConnectionWithCleanup(t);
-  const track = await createTrackWithCleanup(t, 'audio');
+  const [track, stream] = await createTrackAndStreamWithCleanup(t, 'audio');
   const transceiver = pc1.addTransceiver(track);
   await exchangeOffer(pc1, pc2);
   await exchangeAnswer(pc1, pc2);
   assert_equals(transceiver.currentDirection, 'sendonly');
   assert_false(transceiver.stopped);
   pc1.close();
   assert_equals(transceiver.currentDirection, null);
   assert_true(transceiver.stopped);
--- a/testing/webdriver/src/capabilities.rs
+++ b/testing/webdriver/src/capabilities.rs
@@ -4,54 +4,74 @@ use rustc_serialize::json::{Json, ToJson
 use std::collections::BTreeMap;
 use url::Url;
 
 pub type Capabilities = BTreeMap<String, Json>;
 
 /// Trait for objects that can be used to inspect browser capabilities
 ///
 /// The main methods in this trait are called with a Capabilites object
-/// resulting from a full set of potential capabilites for the session.
-/// Given those Capabilities they return a property of the browser instance
-/// that would be initiated. In many cases this will be independent of the
-/// input, but in the case of e.g. browser version, it might depend on a
-/// path to the binary provided as a capability.
+/// resulting from a full set of potential capabilites for the session.  Given
+/// those Capabilities they return a property of the browser instance that
+/// would be initiated. In many cases this will be independent of the input,
+/// but in the case of e.g. browser version, it might depend on a path to the
+/// binary provided as a capability.
 pub trait BrowserCapabilities {
     /// Set up the Capabilites object
     ///
     /// Typically used to create any internal caches
     fn init(&mut self, &Capabilities);
 
     /// Name of the browser
     fn browser_name(&mut self, &Capabilities) -> WebDriverResult<Option<String>>;
+
     /// Version number of the browser
     fn browser_version(&mut self, &Capabilities) -> WebDriverResult<Option<String>>;
+
     /// Compare actual browser version to that provided in a version specifier
     ///
     /// Parameters are the actual browser version and the comparison string,
-    /// respectively. The format of the comparison string is implementation-defined.
-    fn compare_browser_version(&mut self, version: &str, comparison: &str) -> WebDriverResult<bool>;
+    /// respectively. The format of the comparison string is
+    /// implementation-defined.
+    fn compare_browser_version(&mut self, version: &str, comparison: &str)
+        -> WebDriverResult<bool>;
+
     /// Name of the platform/OS
     fn platform_name(&mut self, &Capabilities) -> WebDriverResult<Option<String>>;
+
     /// Whether insecure certificates are supported
     fn accept_insecure_certs(&mut self, &Capabilities) -> WebDriverResult<bool>;
 
-    fn accept_proxy(&mut self, proxy_settings: &BTreeMap<String, Json>, &Capabilities) -> WebDriverResult<bool>;
+    /// Indicates whether driver supports all of the window resizing and
+    /// repositioning commands.
+    fn set_window_rect(&mut self, &Capabilities) -> WebDriverResult<bool>;
+
+    fn accept_proxy(
+        &mut self,
+        proxy_settings: &BTreeMap<String, Json>,
+        &Capabilities,
+    ) -> WebDriverResult<bool>;
 
     /// Type check custom properties
     ///
     /// Check that custom properties containing ":" have the correct data types.
     /// Properties that are unrecognised must be ignored i.e. return without
     /// error.
     fn validate_custom(&self, name: &str, value: &Json) -> WebDriverResult<()>;
+
     /// Check if custom properties are accepted capabilites
     ///
     /// Check that custom properties containing ":" are compatible with
     /// the implementation.
-    fn accept_custom(&mut self, name: &str, value: &Json, merged: &Capabilities) -> WebDriverResult<bool>;
+    fn accept_custom(
+        &mut self,
+        name: &str,
+        value: &Json,
+        merged: &Capabilities,
+    ) -> WebDriverResult<bool>;
 }
 
 /// Trait to abstract over various version of the new session parameters
 ///
 /// This trait is expected to be implemented on objects holding the capabilities
 /// from a new session command.
 pub trait CapabilitiesMatching {
     /// Match the BrowserCapabilities against some candidate capabilites
@@ -65,59 +85,64 @@ pub trait CapabilitiesMatching {
 
 #[derive(Debug, PartialEq)]
 pub struct SpecNewSessionParameters {
     pub alwaysMatch: Capabilities,
     pub firstMatch: Vec<Capabilities>,
 }
 
 impl SpecNewSessionParameters {
-    fn validate<T: BrowserCapabilities>(&self,
-                                        mut capabilities: Capabilities,
-                                        browser_capabilities: &T) -> WebDriverResult<Capabilities> {
+    fn validate<T: BrowserCapabilities>(
+        &self,
+        mut capabilities: Capabilities,
+        browser_capabilities: &T,
+    ) -> WebDriverResult<Capabilities> {
         // Filter out entries with the value `null`
         let null_entries = capabilities
             .iter()
             .filter(|&(_, ref value)| **value == Json::Null)
             .map(|(k, _)| k.clone())
             .collect::<Vec<String>>();
         for key in null_entries {
             capabilities.remove(&key);
         }
 
         for (key, value) in capabilities.iter() {
             match &**key {
-                "acceptInsecureCerts" => if !value.is_boolean() {
-                        return Err(WebDriverError::new(ErrorStatus::InvalidArgument,
-                                                       format!("acceptInsecureCerts is not boolean: {}", value)))
-                    },
-                x @ "browserName" |
-                x @ "browserVersion" |
-                x @ "platformName" => if !value.is_string() {
-                        return Err(WebDriverError::new(ErrorStatus::InvalidArgument,
-                                                       format!("{} is not a string: {}", x, value)))
-                    },
-                "pageLoadStrategy" => {
-                    try!(SpecNewSessionParameters::validate_page_load_strategy(value))
+                x @ "acceptInsecureCerts" | x @ "setWindowRect" => if !value.is_boolean() {
+                    return Err(WebDriverError::new(
+                        ErrorStatus::InvalidArgument,
+                        format!("{} is not boolean: {}", x, value),
+                    ));
+                },
+                x @ "browserName" | x @ "browserVersion" | x @ "platformName" => {
+                    if !value.is_string() {
+                        return Err(WebDriverError::new(
+                            ErrorStatus::InvalidArgument,
+                            format!("{} is not a string: {}", x, value),
+                        ));
+                    }
                 }
-                "proxy" => {
-                    try!(SpecNewSessionParameters::validate_proxy(value))
-                },
-                "timeouts" => {
-                    try!(SpecNewSessionParameters::validate_timeouts(value))
-                },
+                "pageLoadStrategy" => SpecNewSessionParameters::validate_page_load_strategy(value)?,
+                "proxy" => SpecNewSessionParameters::validate_proxy(value)?,
+                "timeouts" => SpecNewSessionParameters::validate_timeouts(value)?,
                 "unhandledPromptBehavior" => {
-                    try!(SpecNewSessionParameters::validate_unhandled_prompt_behaviour(value))
+                    SpecNewSessionParameters::validate_unhandled_prompt_behaviour(value)?
                 }
                 x => {
                     if !x.contains(":") {
-                        return Err(WebDriverError::new(ErrorStatus::InvalidArgument,
-                                                       format!("{} is not the name of a known capability or extension capability", x)))
+                        return Err(WebDriverError::new(
+                            ErrorStatus::InvalidArgument,
+                            format!(
+                                "{} is not the name of a known capability or extension capability",
+                                x
+                            ),
+                        ));
                     } else {
-                        try!(browser_capabilities.validate_custom(x, value));
+                        browser_capabilities.validate_custom(x, value)?
                     }
                 }
             }
         }
         Ok(capabilities)
     }
 
     fn validate_page_load_strategy(value: &Json) -> WebDriverResult<()> {
@@ -453,16 +478,25 @@ impl CapabilitiesMatching for SpecNewSes
                             if value.as_boolean().unwrap_or(false) &&
                                 !browser_capabilities
                                     .accept_insecure_certs(merged)
                                     .unwrap_or(false)
                             {
                                 return None;
                             }
                         }
+                        "setWindowRect" => {
+                            if value.as_boolean().unwrap_or(false) &&
+                                !browser_capabilities
+                                    .set_window_rect(merged)
+                                    .unwrap_or(false)
+                            {
+                                return None;
+                            }
+                        }
                         "proxy" => {
                             let default = BTreeMap::new();
                             let proxy = value.as_object().unwrap_or(&default);
                             if !browser_capabilities
                                 .accept_proxy(&proxy, merged)
                                 .unwrap_or(false)
                             {
                                 return None;
--- a/toolkit/components/normandy/content/AboutPages.jsm
+++ b/toolkit/components/normandy/content/AboutPages.jsm
@@ -16,24 +16,17 @@ ChromeUtils.defineModuleGetter(
 ChromeUtils.defineModuleGetter(
   this, "RecipeRunner", "resource://normandy/lib/RecipeRunner.jsm",
 );
 
 var EXPORTED_SYMBOLS = ["AboutPages"];
 
 const SHIELD_LEARN_MORE_URL_PREF = "app.normandy.shieldLearnMoreUrl";
 
-// Due to bug 1051238 frame scripts are cached forever, so we can't update them
-// as a restartless add-on. The Math.random() is the work around for this.
-const PROCESS_SCRIPT = (
-  `resource://normandy-content/shield-content-process.js?${Math.random()}`
-);
-const FRAME_SCRIPT = (
-  `resource://normandy-content/shield-content-frame.js?${Math.random()}`
-);
+const PROCESS_SCRIPT = "resource://normandy-content/shield-content-process.js";
 
 /**
  * Class for managing an about: page that Normandy provides. Adapted from
  * browser/extensions/pocket/content/AboutPocket.jsm.
  *
  * @implements nsIFactory
  * @implements nsIAboutModule
  */
@@ -94,27 +87,25 @@ AboutPage.prototype.QueryInterface = Chr
 
 /**
  * The module exported by this file.
  */
 var AboutPages = {
   async init() {
     // Load scripts in content processes and tabs
     Services.ppmm.loadProcessScript(PROCESS_SCRIPT, true);
-    Services.mm.loadFrameScript(FRAME_SCRIPT, true);
 
     // Register about: pages and their listeners
     this.aboutStudies.register();
     this.aboutStudies.registerParentListeners();
 
     CleanupManager.addCleanupHandler(() => {
       // Stop loading processs scripts and notify existing scripts to clean up.
       Services.ppmm.removeDelayedProcessScript(PROCESS_SCRIPT);
       Services.ppmm.broadcastAsyncMessage("Shield:ShuttingDown");
-      Services.mm.removeDelayedFrameScript(FRAME_SCRIPT);
       Services.mm.broadcastAsyncMessage("Shield:ShuttingDown");
 
       // Clean up about pages
       this.aboutStudies.unregisterParentListeners();
       this.aboutStudies.unregister();
     });
   },
 };
rename from toolkit/components/normandy/content/shield-content-frame.js
rename to toolkit/components/normandy/content/ShieldFrameListener.jsm
--- a/toolkit/components/normandy/content/shield-content-frame.js
+++ b/toolkit/components/normandy/content/ShieldFrameListener.jsm
@@ -1,28 +1,27 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
+var EXPORTED_SYMBOLS = ["ShieldFrameListener"];
+
 /**
  * Listen for DOM events bubbling up from the about:studies page, and perform
  * privileged actions in response to them. If we need to do anything that the
  * content process can't handle (such as reading IndexedDB), we send a message
  * to the parent process and handle it there.
  *
  * This file is loaded as a frame script. It will be loaded once per tab that
  * is opened.
  */
 
-/* global content addMessageListener removeMessageListener sendAsyncMessage */
-
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
 
 const frameGlobal = {};
 ChromeUtils.defineModuleGetter(
   frameGlobal, "AboutPages", "resource://normandy-content/AboutPages.jsm",
 );
 
 XPCOMUtils.defineLazyGetter(this, "gBrandBundle", function() {
   return Services.strings.createBundle("chrome://branding/locale/brand.properties");
@@ -33,41 +32,45 @@ XPCOMUtils.defineLazyGetter(this, "gStri
 });
 
 /**
  * Handles incoming events from the parent process and about:studies.
  * @implements nsIMessageListener
  * @implements EventListener
  */
 class ShieldFrameListener {
+  constructor(mm) {
+    this.mm = mm;
+  }
+
   handleEvent(event) {
     // Abort if the current page isn't about:studies.
     if (!this.ensureTrustedOrigin()) {
       return;
     }
 
     // We waited until after we received an event to register message listeners
     // in order to save resources for tabs that don't ever load about:studies.
-    addMessageListener("Shield:ShuttingDown", this);
-    addMessageListener("Shield:ReceiveStudyList", this);
-    addMessageListener("Shield:ReceiveStudiesEnabled", this);
+    this.mm.addMessageListener("Shield:ShuttingDown", this);
+    this.mm.addMessageListener("Shield:ReceiveStudyList", this);
+    this.mm.addMessageListener("Shield:ReceiveStudiesEnabled", this);
 
     switch (event.detail.action) {
       // Actions that require the parent process
       case "GetRemoteValue:StudyList":
-        sendAsyncMessage("Shield:GetStudyList");
+        this.mm.sendAsyncMessage("Shield:GetStudyList");
         break;
       case "RemoveStudy":
-        sendAsyncMessage("Shield:RemoveStudy", event.detail.data);
+        this.mm.sendAsyncMessage("Shield:RemoveStudy", event.detail.data);
         break;
       case "GetRemoteValue:StudiesEnabled":
-        sendAsyncMessage("Shield:GetStudiesEnabled");
+        this.mm.sendAsyncMessage("Shield:GetStudiesEnabled");
         break;
       case "NavigateToDataPreferences":
-        sendAsyncMessage("Shield:OpenDataPreferences");
+        this.mm.sendAsyncMessage("Shield:OpenDataPreferences");
         break;
       // Actions that can be performed in the content process
       case "GetRemoteValue:ShieldLearnMoreHref":
         this.triggerPageCallback(
           "ReceiveRemoteValue:ShieldLearnMoreHref",
           frameGlobal.AboutPages.aboutStudies.getShieldLearnMoreHref()
         );
         break;
@@ -89,17 +92,17 @@ class ShieldFrameListener {
     }
   }
 
   /**
    * Check that the current webpage's origin is about:studies.
    * @return {Boolean}
    */
   ensureTrustedOrigin() {
-    return content.document.documentURI.startsWith("about:studies");
+    return this.mm.content.document.documentURI.startsWith("about:studies");
   }
 
   /**
    * Handle messages from the parent process.
    * @param {Object} message
    *   See the nsIMessageListener docs.
    */
   receiveMessage(message) {
@@ -122,24 +125,24 @@ class ShieldFrameListener {
    * @param {Object} detail
    */
   triggerPageCallback(type, detail) {
     // Do not communicate with untrusted pages.
     if (!this.ensureTrustedOrigin()) {
       return;
     }
 
+    let {content} = this.mm;
+
     // Clone details and use the event class from the unprivileged context.
     const event = new content.document.defaultView.CustomEvent(type, {
       bubbles: true,
       detail: Cu.cloneInto(detail, content.document.defaultView),
     });
     content.document.dispatchEvent(event);
   }
 
   onShutdown() {
-    removeMessageListener("Shield:SendStudyList", this);
-    removeMessageListener("Shield:ShuttingDown", this);
-    removeEventListener("Shield", this);
+    this.mm.removeMessageListener("Shield:SendStudyList", this);
+    this.mm.removeMessageListener("Shield:ShuttingDown", this);
+    this.mm.removeEventListener("Shield", this);
   }
 }
-
-addEventListener("ShieldPageEvent", new ShieldFrameListener(), false, true);
--- a/toolkit/components/passwordmgr/LoginManagerContent.jsm
+++ b/toolkit/components/passwordmgr/LoginManagerContent.jsm
@@ -149,19 +149,17 @@ var observer = {
 Services.obs.addObserver(observer, "earlyformsubmit");
 var prefBranch = Services.prefs.getBranch("signon.");
 prefBranch.addObserver("", observer.onPrefChange);
 
 observer.onPrefChange(); // read initial values
 
 
 function messageManagerFromWindow(win) {
-  return win.QueryInterface(Ci.nsIInterfaceRequestor)
-            .getInterface(Ci.nsIWebNavigation)
-            .QueryInterface(Ci.nsIDocShell)
+  return win.docShell
             .QueryInterface(Ci.nsIInterfaceRequestor)
             .getInterface(Ci.nsIContentFrameMessageManager);
 }
 
 // This object maps to the "child" process (even in the single-process case).
 var LoginManagerContent = {
   __formFillService: null, // FormFillController, for username autocompleting
   get _formFillService() {
@@ -357,19 +355,17 @@ var LoginManagerContent = {
   },
 
   setupProgressListener(window) {
     if (!LoginHelper.formlessCaptureEnabled) {
       return;
     }
 
     try {
-      let webProgress = window.QueryInterface(Ci.nsIInterfaceRequestor).
-                        getInterface(Ci.nsIWebNavigation).
-                        QueryInterface(Ci.nsIDocShell).
+      let webProgress = window.docShell.
                         QueryInterface(Ci.nsIInterfaceRequestor).
                         getInterface(Ci.nsIWebProgress);
       webProgress.addProgressListener(observer,
                                       Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT |
                                       Ci.nsIWebProgress.NOTIFY_LOCATION);
     } catch (ex) {
       // Ignore NS_ERROR_FAILURE if the progress listener was already added
     }
--- a/toolkit/components/passwordmgr/LoginRecipes.jsm
+++ b/toolkit/components/passwordmgr/LoginRecipes.jsm
@@ -229,19 +229,17 @@ var LoginRecipesContent = {
     if (recipeMap) {
       recipes = recipeMap.get(aHost);
 
       if (recipes) {
         return recipes;
       }
     }
 
-    let mm = win.QueryInterface(Ci.nsIInterfaceRequestor)
-                .getInterface(Ci.nsIWebNavigation)
-                .QueryInterface(Ci.nsIDocShell)
+    let mm = win.docShell
                 .QueryInterface(Ci.nsIInterfaceRequestor)
                 .getInterface(Ci.nsIContentFrameMessageManager);
 
     log.warn("getRecipes: falling back to a synchronous message for:", aHost);
     recipes