Merge inbound to mozilla-central. a=merge
authorBrindusan Cristian <cbrindusan@mozilla.com>
Wed, 08 Aug 2018 00:51:43 +0300
changeset 485525 d9e6ce390607
parent 485474 7d78ffbf4c4a (current diff)
parent 485524 332b27e88d11 (diff)
child 485526 ec96693b39be
child 485539 50c9581c458a
child 485585 37be1d2d1647
push id9719
push userffxbld-merge
push dateFri, 24 Aug 2018 17:49:46 +0000
treeherdermozilla-beta@719ec98fba77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone63.0a1
first release with
nightly linux32
d9e6ce390607 / 63.0a1 / 20180807220134 / files
nightly linux64
d9e6ce390607 / 63.0a1 / 20180807220134 / files
nightly mac
d9e6ce390607 / 63.0a1 / 20180807220134 / files
nightly win32
d9e6ce390607 / 63.0a1 / 20180807220134 / files
nightly win64
d9e6ce390607 / 63.0a1 / 20180807220134 / 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/components/uitour/content-UITour.js
browser/components/uitour/jar.mn
dom/base/nsContentUtils.cpp
dom/ipc/manifestMessages.js
gfx/layers/BufferEdgePad.cpp
gfx/layers/BufferEdgePad.h
gfx/layers/BufferUnrotate.cpp
gfx/layers/BufferUnrotate.h
testing/talos/talos/tests/svgx/images/kyoto_1.jpg
testing/talos/talos/tests/svgx/images/kyoto_2.jpg
toolkit/components/normandy/content/shield-content-frame.js
toolkit/components/normandy/content/shield-content-process.js
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1314,19 +1314,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);
     }
--- 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
@@ -459,32 +459,28 @@ window._gBrowser = {
   },
 
   get userTypedValue() {
     return this.selectedBrowser.userTypedValue;
   },
 
   _setFindbarData() {
     // Ensure we know what the find bar key is in the content process:
-    let initialProcessData = Services.ppmm.initialProcessData;
-    if (!initialProcessData.findBarShortcutData) {
+    let {sharedData} = Services.ppmm;
+    if (!sharedData.has("Findbar:Shortcut")) {
       let keyEl = document.getElementById("key_find");
       let mods = keyEl.getAttribute("modifiers")
         .replace(/accel/i, AppConstants.platform == "macosx" ? "meta" : "control");
-      initialProcessData.findBarShortcutData = {
+      sharedData.set("Findbar:Shortcut", {
         key: keyEl.getAttribute("key"),
-        modifiers: {
-          shiftKey: mods.includes("shift"),
-          ctrlKey: mods.includes("control"),
-          altKey: mods.includes("alt"),
-          metaKey: mods.includes("meta"),
-        },
-      };
-      Services.ppmm.broadcastAsyncMessage("Findbar:ShortcutData",
-        initialProcessData.findBarShortcutData);
+        shiftKey: mods.includes("shift"),
+        ctrlKey: mods.includes("control"),
+        altKey: mods.includes("alt"),
+        metaKey: mods.includes("meta"),
+      });
     }
   },
 
   isFindBarInitialized(aTab) {
     return (aTab || this.selectedTab)._findBar != undefined;
   },
 
   /**
--- 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",
 
@@ -54,34 +48,30 @@ const whitelist = {
     // Browser front-end
     "resource:///modules/ContentLinkHandler.jsm",
     "resource:///modules/ContentMetaHandler.jsm",
     "resource:///modules/PageStyleHandler.jsm",
     "resource://gre/modules/BrowserUtils.jsm",
     "resource://gre/modules/E10SUtils.jsm",
     "resource://gre/modules/PrivateBrowsingUtils.jsm",
     "resource://gre/modules/ReaderMode.jsm",
+    "resource://gre/modules/WebProgressChild.jsm",
+    "resource://gre/modules/WebNavigationChild.jsm",
 
     // Pocket
     "chrome://pocket/content/AboutPocket.jsm",
 
     // Telemetry
     "resource://gre/modules/TelemetryController.jsm", // bug 1470339
     "resource://gre/modules/TelemetrySession.jsm", // bug 1470339
     "resource://gre/modules/TelemetryUtils.jsm", // bug 1470339
 
     // 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.
 const intermittently_loaded_whitelist = {
   components: new Set([
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/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -365,16 +365,20 @@
 @RESPATH@/components/mozDOMLocalization.js
 @RESPATH@/components/mozDOMLocalization.manifest
 
 ; [Extensions]
 @RESPATH@/components/extensions-toolkit.manifest
 @RESPATH@/components/extension-process-script.js
 @RESPATH@/browser/components/extensions-browser.manifest
 
+; [Normandy]
+@RESPATH@/components/shield.manifest
+@RESPATH@/components/shield-content-process.js
+
 ; [PDF Viewer]
 @RESPATH@/browser/components/pdfjs.manifest
 @RESPATH@/browser/components/pdfjs.js
 
 ; Modules
 @RESPATH@/browser/modules/*
 @RESPATH@/modules/*
 
--- a/build/mobile/remoteautomation.py
+++ b/build/mobile/remoteautomation.py
@@ -398,16 +398,17 @@ class RemoteAutomation(Automation):
                     break
                 if not hasOutput:
                     top = self.device.get_top_activity(timeout=60)
                     if top is None:
                         print("Failed to get top activity, retrying, once...")
                         top = self.device.get_top_activity(timeout=60)
             # Flush anything added to stdout during the sleep
             self.read_stdout()
+            print("wait for %s complete; top activity=%s" % (self.procName, top))
             return status
 
         def kill(self, stagedShutdown=False):
             if self.utilityPath:
                 # Take a screenshot to capture the screen state just before
                 # the application is killed. There are on-device screenshot
                 # options but they rarely work well with Firefox on the
                 # Android emulator. dump_screen provides an effective
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -294,16 +294,17 @@ UNIFIED_SOURCES += [
     'nsAttrValueOrString.cpp',
     'nsCCUncollectableMarker.cpp',
     'nsContentAreaDragDrop.cpp',
     'nsContentIterator.cpp',
     'nsContentList.cpp',
     'nsContentPermissionHelper.cpp',
     'nsContentPolicy.cpp',
     'nsContentSink.cpp',
+    'nsContentTypeParser.cpp',
     'nsCopySupport.cpp',
     'nsDataDocumentContentPolicy.cpp',
     'nsDocument.cpp',
     'nsDocumentEncoder.cpp',
     'nsDOMAttributeMap.cpp',
     'nsDOMCaretPosition.cpp',
     'nsDOMMutationObserver.cpp',
     'nsDOMNavigationTiming.cpp',
new file mode 100644
--- /dev/null
+++ b/dom/base/nsContentTypeParser.cpp
@@ -0,0 +1,31 @@
+/* -*- 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 "nsContentTypeParser.h"
+#include "nsNetUtil.h"
+
+nsContentTypeParser::nsContentTypeParser(const nsAString& aString)
+  : mString(aString)
+{
+}
+
+nsresult
+nsContentTypeParser::GetParameter(const char* aParameterName,
+                                  nsAString& aResult) const
+{
+  return net::GetParameterHTTP(mString, aParameterName, aResult);
+}
+
+nsresult
+nsContentTypeParser::GetType(nsAString& aResult) const
+{
+  nsresult rv = GetParameter(nullptr, aResult);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  nsContentUtils::ASCIIToLower(aResult);
+  return NS_OK;
+}
--- a/dom/base/nsContentTypeParser.h
+++ b/dom/base/nsContentTypeParser.h
@@ -4,25 +4,22 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsContentTypeParser_h
 #define nsContentTypeParser_h
 
 #include "nsAString.h"
 
-class nsIMIMEHeaderParam;
-
-class nsContentTypeParser {
+class nsContentTypeParser
+{
 public:
   explicit nsContentTypeParser(const nsAString& aString);
-  ~nsContentTypeParser();
 
   nsresult GetParameter(const char* aParameterName, nsAString& aResult) const;
   nsresult GetType(nsAString& aResult) const;
 
 private:
   NS_ConvertUTF16toUTF8 mString;
-  nsIMIMEHeaderParam*   mService;
 };
 
 #endif
 
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -6419,50 +6419,17 @@ nsContentUtils::CheckMayLoad(nsIPrincipa
 {
   nsCOMPtr<nsIURI> channelURI;
   nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
   NS_ENSURE_SUCCESS(rv, false);
 
   return NS_SUCCEEDED(aPrincipal->CheckMayLoad(channelURI, false, aAllowIfInheritsPrincipal));
 }
 
-nsContentTypeParser::nsContentTypeParser(const nsAString& aString)
-  : mString(aString), mService(nullptr)
-{
-  CallGetService("@mozilla.org/network/mime-hdrparam;1", &mService);
-}
-
-nsContentTypeParser::~nsContentTypeParser()
-{
-  NS_IF_RELEASE(mService);
-}
-
-nsresult
-nsContentTypeParser::GetParameter(const char* aParameterName,
-                                  nsAString& aResult) const
-{
-  NS_ENSURE_TRUE(mService, NS_ERROR_FAILURE);
-  return mService->GetParameterHTTP(mString, aParameterName,
-                                    EmptyCString(), false, nullptr,
-                                    aResult);
-}
-
-nsresult
-nsContentTypeParser::GetType(nsAString& aResult) const
-{
-  nsresult rv = GetParameter(nullptr, aResult);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-  nsContentUtils::ASCIIToLower(aResult);
-  return NS_OK;
-}
-
-/* static */
-
+/* static */
 bool
 nsContentUtils::CanAccessNativeAnon()
 {
   return LegacyIsCallerChromeOrNativeCode() || IsCallerContentXBL();
 }
 
 /* static */ nsresult
 nsContentUtils::DispatchXULCommand(nsIContent* aTarget,
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/media/ChannelMediaResource.cpp
+++ b/dom/media/ChannelMediaResource.cpp
@@ -6,16 +6,17 @@
 #include "ChannelMediaResource.h"
 
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsICachingChannel.h"
 #include "nsIClassOfService.h"
 #include "nsIInputStream.h"
 #include "nsIThreadRetargetableRequest.h"
+#include "nsHttp.h"
 #include "nsNetUtil.h"
 
 static const uint32_t HTTP_PARTIAL_RESPONSE_CODE = 206;
 static const uint32_t HTTP_OK_CODE = 200;
 static const uint32_t HTTP_REQUESTED_RANGE_NOT_SATISFIABLE_CODE = 416;
 
 mozilla::LazyLogModule gMediaResourceLog("MediaResource");
 // Debug logging macro with object pointer and class name.
@@ -217,17 +218,18 @@ ChannelMediaResource::OnStartRequest(nsI
       // certainly don't want an error page to end up in our cache!
       CloseChannel();
       return NS_OK;
     }
 
     nsAutoCString ranges;
     Unused << hc->GetResponseHeader(NS_LITERAL_CSTRING("Accept-Ranges"),
                                     ranges);
-    bool acceptsRanges = ranges.EqualsLiteral("bytes");
+    bool acceptsRanges =
+      net::nsHttp::FindToken(ranges.get(), "bytes", HTTP_HEADER_VALUE_SEPS);
 
     int64_t contentLength = -1;
     const bool isCompressed = IsPayloadCompressed(hc);
     if (!isCompressed) {
       hc->GetContentLength(&contentLength);
     }
 
     // Check response code for byte-range requests (seeking, chunk requests).
--- a/dom/media/DecoderTraits.cpp
+++ b/dom/media/DecoderTraits.cpp
@@ -149,18 +149,16 @@ CanHandleCodecsType(const MediaContainer
   return CANPLAY_MAYBE;
 }
 
 static
 CanPlayStatus
 CanHandleMediaType(const MediaContainerType& aType,
                    DecoderDoctorDiagnostics* aDiagnostics)
 {
-  MOZ_ASSERT(NS_IsMainThread());
-
 #ifdef MOZ_ANDROID_HLS_SUPPORT
   if (HLSDecoder::IsSupportedType(aType)) {
     return CANPLAY_MAYBE;
   }
 #endif
 
   if (DecoderTraits::IsHttpLiveStreamingType(aType)) {
     Telemetry::Accumulate(Telemetry::MEDIA_HLS_CANPLAY_REQUESTED, true);
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -13,16 +13,17 @@
 #include "MediaResource.h"
 #include "MediaShutdownManager.h"
 #include "VideoFrameContainer.h"
 #include "VideoUtils.h"
 #include "mozilla/AbstractThread.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/StaticPrefs.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/Telemetry.h"
 #include "Visibility.h"
 #include "mozilla/Unused.h"
 #include "nsComponentManagerUtils.h"
 #include "nsContentUtils.h"
 #include "nsError.h"
 #include "nsIMemoryReporter.h"
@@ -1260,35 +1261,35 @@ MediaDecoder::SetCDMProxy(CDMProxy* aPro
                                        __func__,
                                        &MediaFormatReader::SetCDMProxy,
                                        aProxy);
 }
 
 bool
 MediaDecoder::IsOpusEnabled()
 {
-  return Preferences::GetBool("media.opus.enabled");
+  return StaticPrefs::MediaOpusEnabled();
 }
 
 bool
 MediaDecoder::IsOggEnabled()
 {
-  return Preferences::GetBool("media.ogg.enabled");
+  return StaticPrefs::MediaOggEnabled();
 }
 
 bool
 MediaDecoder::IsWaveEnabled()
 {
-  return Preferences::GetBool("media.wave.enabled");
+  return StaticPrefs::MediaWaveEnabled();
 }
 
 bool
 MediaDecoder::IsWebMEnabled()
 {
-  return Preferences::GetBool("media.webm.enabled");
+  return StaticPrefs::MediaWebMEnabled();
 }
 
 NS_IMETHODIMP
 MediaMemoryTracker::CollectReports(nsIHandleReportCallback* aHandleReport,
                                    nsISupports* aData, bool aAnonymize)
 {
   // NB: When resourceSizes' ref count goes to 0 the promise will report the
   //     resources memory and finish the asynchronous memory report.
--- a/dom/media/mp4/MP4Decoder.cpp
+++ b/dom/media/mp4/MP4Decoder.cpp
@@ -194,17 +194,17 @@ MP4Decoder::IsAAC(const nsACString& aMim
 {
   return aMimeType.EqualsLiteral("audio/mp4a-latm");
 }
 
 /* static */
 bool
 MP4Decoder::IsEnabled()
 {
-  return StaticPrefs::mediaMp4Enabled();
+  return StaticPrefs::MediaMp4Enabled();
 }
 
 /* static */ nsTArray<UniquePtr<TrackInfo>>
 MP4Decoder::GetTracksInfo(const MediaContainerType& aType)
 {
   MediaResult rv = NS_OK;
   return GetTracksInfo(aType, rv);
 }
--- a/dom/media/ogg/OggDecoder.cpp
+++ b/dom/media/ogg/OggDecoder.cpp
@@ -2,18 +2,18 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "OggDecoder.h"
 #include "MediaContainerType.h"
 #include "MediaDecoder.h"
+#include "mozilla/StaticPrefs.h"
 #include "nsMimeTypes.h"
-#include "mozilla/StaticPrefs.h"
 
 namespace mozilla {
 
 /* static */
 bool
 OggDecoder::IsSupportedType(const MediaContainerType& aContainerType)
 {
   if (!StaticPrefs::MediaOggEnabled()) {
--- a/dom/media/webm/WebMDecoder.cpp
+++ b/dom/media/webm/WebMDecoder.cpp
@@ -80,17 +80,17 @@ WebMDecoder::GetTracksInfo(const MediaCo
   }
   return tracks;
 }
 
 /* static */
 bool
 WebMDecoder::IsSupportedType(const MediaContainerType& aContainerType)
 {
-  if (!Preferences::GetBool("media.webm.enabled")) {
+  if (!StaticPrefs::MediaWebMEnabled()) {
     return false;
   }
 
   MediaResult rv = NS_OK;
   auto tracks = GetTracksInfo(aContainerType, rv);
 
   if (NS_FAILED(rv)) {
     return false;
--- a/js/src/jit/arm64/vixl/Globals-vixl.h
+++ b/js/src/jit/arm64/vixl/Globals-vixl.h
@@ -71,17 +71,17 @@ const int MBytes = 1024 * KBytes;
   #define VIXL_UNIMPLEMENTED() \
     do { fprintf(stderr, "UNIMPLEMENTED\t"); VIXL_ABORT(); } while (false)
   #define VIXL_UNREACHABLE() \
     do { fprintf(stderr, "UNREACHABLE\t"); VIXL_ABORT(); } while (false)
 #else
   #define VIXL_ASSERT(condition) ((void) 0)
   #define VIXL_CHECK(condition) ((void) 0)
   #define VIXL_UNIMPLEMENTED() ((void) 0)
-  #define VIXL_UNREACHABLE() MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE()
+  #define VIXL_UNREACHABLE() MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("vixl unreachable")
 #endif
 // This is not as powerful as template based assertions, but it is simple.
 // It assumes that the descriptions are unique. If this starts being a problem,
 // we can switch to a different implemention.
 #define VIXL_S(x) #x
 #define VIXL_STATIC_ASSERT_LINE(line, condition) \
     static_assert(condition, "STATIC_ASSERT_LINE_" VIXL_S(line))
 #define VIXL_STATIC_ASSERT(condition) \
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -7857,20 +7857,21 @@ if (IsCSSPropertyPrefEnabled("layout.css
     type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
     alias_for: "mask-size",
     subproperties: [ "mask-size" ],
   };
 }
 
 if (IsCSSPropertyPrefEnabled("layout.css.webkit-appearance.enabled")) {
   gCSSProperties["-webkit-appearance"] = {
-    domProp: "WebkitAppearance",
-    inherited: false,
-    type: CSS_TYPE_LONGHAND,
+    domProp: "webkitAppearance",
+    inherited: false,
+    type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
     alias_for: "-moz-appearance",
+    subproperties: [ "-moz-appearance" ],
   };
 }
 
 if (IsCSSPropertyPrefEnabled("layout.css.offset-logical-properties.enabled")) {
   gCSSProperties["offset-block-start"] = {
     domProp: "offsetBlockStart",
     inherited: false,
     type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
--- a/modules/libpref/init/StaticPrefList.h
+++ b/modules/libpref/init/StaticPrefList.h
@@ -875,23 +875,44 @@ VARCACHE_PREF(
 #endif
 VARCACHE_PREF(
   "media.video-max-decode-error",
    MediaVideoMaxDecodeError,
   uint32_t, PREF_VALUE
 )
 #undef PREF_VALUE
 
+// Opus
+VARCACHE_PREF(
+  "media.opus.enabled",
+   MediaOpusEnabled,
+  RelaxedAtomicBool, true
+)
+
+// Wave
+VARCACHE_PREF(
+  "media.wave.enabled",
+   MediaWaveEnabled,
+  RelaxedAtomicBool, true
+)
+
 // Ogg
 VARCACHE_PREF(
   "media.ogg.enabled",
    MediaOggEnabled,
   RelaxedAtomicBool, true
 )
 
+// WebM
+VARCACHE_PREF(
+  "media.webm.enabled",
+   MediaWebMEnabled,
+  RelaxedAtomicBool, true
+)
+
 // AV1
 VARCACHE_PREF(
   "media.av1.enabled",
    MediaAv1Enabled,
   RelaxedAtomicBool, false
 )
 
 VARCACHE_PREF(
@@ -915,17 +936,17 @@ VARCACHE_PREF(
 
 #ifdef MOZ_FMP4
 # define PREF_VALUE true
 #else
 # define PREF_VALUE false
 #endif
 VARCACHE_PREF(
   "media.mp4.enabled",
-   mediaMp4Enabled,
+   MediaMp4Enabled,
   RelaxedAtomicBool, PREF_VALUE
 )
 #undef PREF_VALUE
 
 // Error/warning handling, Decoder Doctor.
 //
 // Set to true to force demux/decode warnings to be treated as errors.
 VARCACHE_PREF(
--- a/netwerk/base/nsNetUtil.cpp
+++ b/netwerk/base/nsNetUtil.cpp
@@ -75,16 +75,17 @@
 #include "nsISiteSecurityService.h"
 #include "nsHttpHandler.h"
 #include "nsNSSComponent.h"
 #include "nsIRedirectHistoryEntry.h"
 #include "nsICertBlocklist.h"
 #include "nsICertOverrideService.h"
 #include "nsQueryObject.h"
 #include "mozIThirdPartyUtil.h"
+#include "../mime/nsMIMEHeaderParamImpl.h"
 
 #include <limits>
 
 using namespace mozilla;
 using namespace mozilla::net;
 using mozilla::dom::BlobURLProtocolHandler;
 using mozilla::dom::ClientInfo;
 using mozilla::dom::PerformanceStorage;
@@ -3254,10 +3255,19 @@ InScriptableRange(int64_t val)
 }
 
 bool
 InScriptableRange(uint64_t val)
 {
     return val <= kJS_MAX_SAFE_UINTEGER;
 }
 
+nsresult
+GetParameterHTTP(const nsACString& aHeaderVal,
+                 const char* aParamName,
+                 nsAString& aResult)
+{
+  return nsMIMEHeaderParamImpl::GetParameterHTTP(
+    aHeaderVal, aParamName, aResult);
+}
+
 } // namespace net
 } // namespace mozilla
--- a/netwerk/base/nsNetUtil.h
+++ b/netwerk/base/nsNetUtil.h
@@ -986,12 +986,30 @@ const static  int64_t kJS_MIN_SAFE_INTEG
 const static  int64_t kJS_MAX_SAFE_INTEGER  = +9007199254740991LL;
 
 // Make sure a 64bit value can be captured by JS MAX_SAFE_INTEGER
 bool InScriptableRange(int64_t val);
 
 // Make sure a 64bit value can be captured by JS MAX_SAFE_INTEGER
 bool InScriptableRange(uint64_t val);
 
+/**
+ * Given the value of a single header field  (such as
+ * Content-Disposition and Content-Type) and the name of a parameter
+ * (e.g. filename, name, charset), returns the value of the parameter.
+ * See nsIMIMEHeaderParam.idl for more information.
+ *
+ * @param  aHeaderVal        a header string to get the value of a parameter
+ *                           from.
+ * @param  aParamName        the name of a MIME header parameter (e.g.
+ *                           filename, name, charset). If empty or nullptr,
+ *                           returns the first (possibly) _unnamed_ 'parameter'.
+ * @return the value of <code>aParamName</code> in Unichar(UTF-16).
+ */
+nsresult
+GetParameterHTTP(const nsACString& aHeaderVal,
+                 const char* aParamName,
+                 nsAString& aResult);
+
 } // namespace net
 } // namespace mozilla
 
 #endif // !nsNetUtil_h__
--- a/netwerk/mime/nsMIMEHeaderParamImpl.cpp
+++ b/netwerk/mime/nsMIMEHeaderParamImpl.cpp
@@ -59,17 +59,33 @@ nsMIMEHeaderParamImpl::GetParameterHTTP(
                                         const nsACString& aFallbackCharset,
                                         bool aTryLocaleCharset,
                                         char **aLang, nsAString& aResult)
 {
   return DoGetParameter(aHeaderVal, aParamName, HTTP_FIELD_ENCODING,
                         aFallbackCharset, aTryLocaleCharset, aLang, aResult);
 }
 
+/* static */
+nsresult
+nsMIMEHeaderParamImpl::GetParameterHTTP(const nsACString& aHeaderVal,
+                                        const char *aParamName,
+                                        nsAString& aResult)
+{
+  return DoGetParameter(aHeaderVal,
+                        aParamName,
+                        HTTP_FIELD_ENCODING,
+                        EmptyCString(),
+                        false,
+                        nullptr,
+                        aResult);
+}
+
 // XXX : aTryLocaleCharset is not yet effective.
+/* static */
 nsresult
 nsMIMEHeaderParamImpl::DoGetParameter(const nsACString& aHeaderVal,
                                       const char *aParamName,
                                       ParamDecoding aDecoding,
                                       const nsACString& aFallbackCharset,
                                       bool aTryLocaleCharset,
                                       char **aLang, nsAString& aResult)
 {
@@ -350,16 +366,17 @@ nsMIMEHeaderParamImpl::GetParameterInter
                                             char **aLang,
                                             char **aResult)
 {
   return DoParameterInternal(aHeaderValue, aParamName, MIME_FIELD_ENCODING,
                              aCharset, aLang, aResult);
 }
 
 
+/* static */
 nsresult
 nsMIMEHeaderParamImpl::DoParameterInternal(const char *aHeaderValue,
                                            const char *aParamName,
                                            ParamDecoding aDecoding,
                                            char **aCharset,
                                            char **aLang,
                                            char **aResult)
 {
--- a/netwerk/mime/nsMIMEHeaderParamImpl.h
+++ b/netwerk/mime/nsMIMEHeaderParamImpl.h
@@ -9,34 +9,43 @@
 #define __nsmimeheaderparamimpl_h___
 class nsMIMEHeaderParamImpl : public nsIMIMEHeaderParam
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIMIMEHEADERPARAM
 
   nsMIMEHeaderParamImpl() = default;
+
+  /**
+   * Identical to calling
+   * GetParameterHTTP(aHeaderVal, aParameterName, EmptyCString(), false, nullptr, aResult)
+   * See nsIMIMEHeaderParam.idl for more information.
+   */
+  static nsresult GetParameterHTTP(const nsACString& aHeaderVal,
+                                   const char *aParamName,
+                                   nsAString &aResult);
+
 private:
   virtual ~nsMIMEHeaderParamImpl() = default;
   enum ParamDecoding {
     MIME_FIELD_ENCODING = 1,
     HTTP_FIELD_ENCODING
   };
 
-  nsresult DoGetParameter(const nsACString& aHeaderVal,
-                          const char *aParamName,
-                          ParamDecoding aDecoding,
-                          const nsACString& aFallbackCharset,
-                          bool aTryLocaleCharset,
-                          char **aLang,
-                          nsAString& aResult);
+  static nsresult DoGetParameter(const nsACString& aHeaderVal,
+                                 const char *aParamName,
+                                 ParamDecoding aDecoding,
+                                 const nsACString& aFallbackCharset,
+                                 bool aTryLocaleCharset,
+                                 char **aLang,
+                                 nsAString& aResult);
 
-  nsresult DoParameterInternal(const char *aHeaderValue,
-                               const char *aParamName,
-                               ParamDecoding aDecoding,
-                               char **aCharset,
-                               char **aLang,
-                               char **aResult);
-
+  static nsresult DoParameterInternal(const char *aHeaderValue,
+                                      const char *aParamName,
+                                      ParamDecoding aDecoding,
+                                      char **aCharset,
+                                      char **aLang,
+                                      char **aResult);
 };
 
 #endif
 
--- a/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp
+++ b/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp
@@ -1114,21 +1114,21 @@ nsHttpChannelAuthProvider::GetIdentityFr
     nsAutoString userBuf;
     nsAutoString passBuf;
 
     // XXX i18n
     nsAutoCString buf;
     mURI->GetUsername(buf);
     if (!buf.IsEmpty()) {
         NS_UnescapeURL(buf);
-        CopyASCIItoUTF16(buf, userBuf);
+        CopyUTF8toUTF16(buf, userBuf);
         mURI->GetPassword(buf);
         if (!buf.IsEmpty()) {
             NS_UnescapeURL(buf);
-            CopyASCIItoUTF16(buf, passBuf);
+            CopyUTF8toUTF16(buf, passBuf);
         }
     }
 
     if (!userBuf.IsEmpty()) {
         SetIdent(ident, authFlags, (char16_t *) userBuf.get(),
                  (char16_t *) passBuf.get());
     }
 }
--- a/netwerk/test/unit/test_authentication.js
+++ b/netwerk/test/unit/test_authentication.js
@@ -1,14 +1,16 @@
 // This file tests authentication prompt callbacks
 // TODO NIT use do_check_eq(expected, actual) consistently, not sometimes eq(actual, expected)
 
 ChromeUtils.import("resource://testing-common/httpd.js");
 ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
 
+Cu.importGlobalProperties(["XMLHttpRequest"]);
+
 // Turn off the authentication dialog blocking for this test.
 var prefs = Cc["@mozilla.org/preferences-service;1"].
               getService(Ci.nsIPrefBranch);
 prefs.setIntPref("network.auth.subresource-http-auth-allow", 2);
 
 XPCOMUtils.defineLazyGetter(this, "URL", function() {
   return "http://localhost:" + httpserv.identity.primaryPort;
 });
@@ -278,30 +280,17 @@ var listener = {
 
   onDataAvailable: function test_ODA() {
     do_throw("Should not get any data!");
   },
 
   onStopRequest: function test_onStopR(request, ctx, status) {
     Assert.equal(status, Cr.NS_ERROR_ABORT);
 
-    if (current_test < (tests.length - 1)) {
-      // First, gotta clear the auth cache
-      Cc["@mozilla.org/network/http-auth-manager;1"]
-        .getService(Ci.nsIHttpAuthManager)
-        .clearAll();
-
-      current_test++;
-      tests[current_test]();
-    } else {
-      do_test_pending();
-      httpserv.stop(do_test_finished);
-    }
-
-    do_test_finished();
+    moveToNextTest();
   }
 };
 
 function makeChan(url, loadingUrl) {
   var ios = Cc["@mozilla.org/network/io-service;1"].
               getService(Ci.nsIIOService);
   var ssm = Cc["@mozilla.org/scriptsecuritymanager;1"]
               .getService(Ci.nsIScriptSecurityManager);
@@ -313,22 +302,39 @@ function makeChan(url, loadingUrl) {
     });
 }
 
 var tests = [test_noauth, test_returnfalse1, test_wrongpw1, test_prompt1,
              test_prompt1CrossOrigin, test_prompt2CrossOrigin,
              test_returnfalse2, test_wrongpw2, test_prompt2, test_ntlm,
              test_basicrealm, test_nonascii, test_digest_noauth, test_digest,
              test_digest_bogus_user, test_short_digest, test_large_realm,
-             test_large_domain];
+             test_large_domain, test_nonascii_xhr];
 
 var current_test = 0;
 
 var httpserv = null;
 
+function moveToNextTest() {
+  if (current_test < (tests.length - 1)) {
+    // First, gotta clear the auth cache
+    Cc["@mozilla.org/network/http-auth-manager;1"]
+      .getService(Ci.nsIHttpAuthManager)
+      .clearAll();
+
+    current_test++;
+    tests[current_test]();
+  } else {
+    do_test_pending();
+    httpserv.stop(do_test_finished);
+  }
+
+  do_test_finished();
+}
+
 function run_test() {
   httpserv = new HttpServer();
 
   httpserv.registerPathHandler("/auth", authHandler);
   httpserv.registerPathHandler("/auth/ntlm/simple", authNtlmSimple);
   httpserv.registerPathHandler("/auth/realm", authRealm);
   httpserv.registerPathHandler("/auth/non_ascii", authNonascii);
   httpserv.registerPathHandler("/auth/digest", authDigest);
@@ -455,16 +461,31 @@ function test_nonascii() {
 
   chan.notificationCallbacks = new Requestor(FLAG_NON_ASCII_USER_PASSWORD, 2);
   listener.expectedCode = 200; // OK
   chan.asyncOpen2(listener);
 
   do_test_pending();
 }
 
+function test_nonascii_xhr() {
+  var xhr = new XMLHttpRequest();
+  xhr.open("GET", URL + "/auth/non_ascii", true, "é", "é");
+  xhr.onreadystatechange = function(event) {
+    if (xhr.readyState == 4) {
+      Assert.equal(xhr.status, 200);
+      moveToNextTest();
+      xhr.onreadystatechange = null;
+    }
+  };
+  xhr.send(null);
+
+  do_test_pending();
+}
+
 function test_digest_noauth() {
   var chan = makeChan(URL + "/auth/digest", URL);
 
   //chan.notificationCallbacks = new Requestor(FLAG_RETURN_FALSE, 2);
   listener.expectedCode = 401; // Unauthorized
   chan.asyncOpen2(listener);
 
   do_test_pending();
@@ -556,25 +577,26 @@ function authNonascii(metadata, response
 
   var body;
   if (metadata.hasHeader("Authorization") &&
       metadata.getHeader("Authorization") == expectedHeader)
   {
     response.setStatusLine(metadata.httpVersion, 200, "OK, authorized");
     response.setHeader("WWW-Authenticate", 'Basic realm="secret"', false);
 
-    body = "success";
+    // Use correct XML syntax since this function is also used for testing XHR.
+    body = "<?xml version='1.0' ?><root>success</root>";
   }
   else
   {
     // didn't know é:é, failure
     response.setStatusLine(metadata.httpVersion, 401, "Unauthorized");
     response.setHeader("WWW-Authenticate", 'Basic realm="secret"', false);
 
-    body = "failed";
+    body = "<?xml version='1.0' ?><root>failed</root>";
   }
 
   response.bodyOutputStream.write(body, body.length);
 }
 
 //
 // Digest functions
 // 
--- a/taskcluster/taskgraph/transforms/job/mozharness_test.py
+++ b/taskcluster/taskgraph/transforms/job/mozharness_test.py
@@ -65,16 +65,18 @@ def mozharness_test_on_docker(config, jo
 
     # apply some defaults
     worker['docker-image'] = test['docker-image']
     worker['allow-ptrace'] = True  # required for all tests, for crashreporter
     worker['loopback-video'] = test['loopback-video']
     worker['loopback-audio'] = test['loopback-audio']
     worker['max-run-time'] = test['max-run-time']
     worker['retry-exit-status'] = test['retry-exit-status']
+    if 'android-em-7.0-x86' in test['test-platform']:
+        worker['privileged'] = True
 
     artifacts = [
         # (artifact name prefix, in-image path)
         ("public/logs/", "{workdir}/workspace/build/upload/logs/".format(**run)),
         ("public/test", "{workdir}/artifacts/".format(**run)),
         ("public/test_info/", "{workdir}/workspace/build/blobber_upload_dir/".format(**run)),
     ]
 
--- a/taskcluster/taskgraph/transforms/task.py
+++ b/taskcluster/taskgraph/transforms/task.py
@@ -234,16 +234,17 @@ task_description_schema = Schema({
         # worker features that should be enabled
         Required('relengapi-proxy'): bool,
         Required('chain-of-trust'): bool,
         Required('taskcluster-proxy'): bool,
         Required('allow-ptrace'): bool,
         Required('loopback-video'): bool,
         Required('loopback-audio'): bool,
         Required('docker-in-docker'): bool,  # (aka 'dind')
+        Required('privileged'): bool,
 
         # Paths to Docker volumes.
         #
         # For in-tree Docker images, volumes can be parsed from Dockerfile.
         # This only works for the Dockerfile itself: if a volume is defined in
         # a base image, it will need to be declared here. Out-of-tree Docker
         # images will also require explicit volume annotation.
         #
@@ -812,16 +813,20 @@ def build_docker_worker_payload(config, 
 
     for lo in 'audio', 'video':
         if worker.get('loopback-' + lo):
             capitalized = 'loopback' + lo.capitalize()
             devices = capabilities.setdefault('devices', {})
             devices[capitalized] = True
             task_def['scopes'].append('docker-worker:capability:device:' + capitalized)
 
+    if worker.get('privileged'):
+        capabilities['privileged'] = True
+        task_def['scopes'].append('docker-worker:capability:privileged')
+
     task_def['payload'] = payload = {
         'image': image,
         'env': worker['env'],
     }
     if 'command' in worker:
         payload['command'] = worker['command']
 
     if 'max-run-time' in worker:
@@ -1293,16 +1298,17 @@ def set_defaults(config, tasks):
         if worker['implementation'] in ('docker-worker', 'docker-engine'):
             worker.setdefault('relengapi-proxy', False)
             worker.setdefault('chain-of-trust', False)
             worker.setdefault('taskcluster-proxy', False)
             worker.setdefault('allow-ptrace', False)
             worker.setdefault('loopback-video', False)
             worker.setdefault('loopback-audio', False)
             worker.setdefault('docker-in-docker', False)
+            worker.setdefault('privileged', False)
             worker.setdefault('volumes', [])
             worker.setdefault('env', {})
             if 'caches' in worker:
                 for c in worker['caches']:
                     c.setdefault('skip-untrusted', False)
         elif worker['implementation'] == 'generic-worker':
             worker.setdefault('env', {})
             worker.setdefault('os-groups', [])
--- a/taskcluster/taskgraph/transforms/tests.py
+++ b/taskcluster/taskgraph/transforms/tests.py
@@ -572,16 +572,18 @@ def set_treeherder_machine_platform(conf
         # conflict in `verify_task_graph_symbol` once you add a new test
         # platform based on regular macOS builds, such as for QR.
         # Since it's unclear if the regular macOS builds can be removed from
         # the table, workaround the issue for QR.
         if '-qr' in test['test-platform']:
             test['treeherder-machine-platform'] = test['test-platform']
         elif 'android-hw' in test['test-platform']:
             test['treeherder-machine-platform'] = test['test-platform']
+        elif 'android-em-7.0-x86' in test['test-platform']:
+            test['treeherder-machine-platform'] = 'android-em-7-0-x86/opt'
         else:
             test['treeherder-machine-platform'] = translation.get(
                 test['build-platform'], test['test-platform'])
         yield test
 
 
 @transforms.add
 def set_tier(config, tests):
@@ -981,16 +983,18 @@ def set_worker_type(config, tests):
                 test['worker-type'] = 'proj-autophone/gecko-t-ap-perf-g5'
             else:
                 test['worker-type'] = 'proj-autophone/gecko-t-ap-unit-g5'
         elif test_platform.startswith('android-hw-p2'):
             if test['suite'] == 'raptor':
                 test['worker-type'] = 'proj-autophone/gecko-t-ap-perf-p2'
             else:
                 test['worker-type'] = 'proj-autophone/gecko-t-ap-unit-p2'
+        elif test_platform.startswith('android-em-7.0-x86'):
+            test['worker-type'] = 'terraform-packet/gecko-t-linux'
         elif test_platform.startswith('linux') or test_platform.startswith('android'):
             if test.get('suite', '') == 'talos' and \
                  not test['build-platform'].startswith('linux64-ccov'):
                 test['worker-type'] = 'releng-hardware/gecko-t-linux-talos'
             else:
                 test['worker-type'] = LINUX_WORKER_TYPES[test['instance-size']]
         else:
             raise Exception("unknown test_platform {}".format(test_platform))
--- a/taskcluster/taskgraph/util/workertypes.py
+++ b/taskcluster/taskgraph/util/workertypes.py
@@ -45,16 +45,17 @@ WORKER_TYPES = {
     "scriptworker-prov-v1/shipit": ('shipit', None),
     "scriptworker-prov-v1/shipit-dev": ('shipit', None),
     "scriptworker-prov-v1/treescript-v1": ('treescript', None),
     'releng-hardware/gecko-t-osx-1010': ('generic-worker', 'macosx'),
     'proj-autophone/gecko-t-ap-perf-g5': ('script-engine-autophone', 'linux'),
     'proj-autophone/gecko-t-ap-unit-g5': ('script-engine-autophone', 'linux'),
     'proj-autophone/gecko-t-ap-perf-p2': ('script-engine-autophone', 'linux'),
     'proj-autophone/gecko-t-ap-unit-p2': ('script-engine-autophone', 'linux'),
+    'terraform-packet/gecko-t-linux': ('docker-worker', 'linux'),
 }
 
 
 def worker_type_implementation(worker_type):
     """Get the worker implementation and OS for the given workerType, where the
     OS represents the host system, not the target OS, in the case of
     cross-compiles."""
     # assume that worker types for all levels are the same implementation
--- a/testing/mach_commands.py
+++ b/testing/mach_commands.py
@@ -13,16 +13,17 @@ import tempfile
 import subprocess
 import shutil
 
 from mach.decorators import (
     CommandArgument,
     CommandProvider,
     Command,
     SettingsProvider,
+    SubCommand,
 )
 
 from mozbuild.base import (
     BuildEnvironmentNotFoundException,
     MachCommandBase,
     MachCommandConditions as conditions,
 )
 from moztest.resolve import TEST_SUITES
@@ -981,8 +982,104 @@ class TestInfoCommand(MachCommandBase):
         response.raise_for_status()
         json_response = response.json()
         print("\nBugzilla quick search for '%s':" % search)
         if 'bugs' in json_response:
             for bug in json_response['bugs']:
                 print("Bug %s: %s" % (bug['id'], bug['summary']))
         else:
             print("No bugs found.")
+
+    @SubCommand('test-info', 'long-tasks',
+                description='Find tasks approaching their taskcluster max-run-time.')
+    @CommandArgument('--branches',
+                     default='mozilla-central,mozilla-inbound,autoland',
+                     help='Report for named branches '
+                          '(default: mozilla-central,mozilla-inbound,autoland)')
+    @CommandArgument('--start',
+                     default=(date.today() - timedelta(7)
+                              ).strftime("%Y-%m-%d"),
+                     help='Start date (YYYY-MM-DD)')
+    @CommandArgument('--end',
+                     default=date.today().strftime("%Y-%m-%d"),
+                     help='End date (YYYY-MM-DD)')
+    @CommandArgument('--max-threshold-pct',
+                     default=90.0,
+                     help='Count tasks exceeding this percentage of max-run-time.')
+    @CommandArgument('--filter-threshold-pct',
+                     default=0.5,
+                     help='Report tasks exceeding this percentage of long tasks.')
+    @CommandArgument('--verbose', action='store_true',
+                     help='Enable debug logging.')
+    def report_long_running_tasks(self, **params):
+        def get_long_running_ratio(record):
+            count = record['count']
+            tasks_gt_pct = record['tasks_gt_pct']
+            return count / tasks_gt_pct
+
+        branches = params['branches']
+        start = params['start']
+        end = params['end']
+        self.verbose = params['verbose']
+        threshold_pct = float(params['max_threshold_pct'])
+        filter_threshold_pct = float(params['filter_threshold_pct'])
+
+        # Search test durations in ActiveData for long-running tests
+        query = {
+            "from": "task",
+            "format": "list",
+            "groupby": ["run.name"],
+            "limit": 1000,
+            "select": [
+                {
+                    "value": "task.maxRunTime",
+                    "aggregate": "median",
+                    "name": "max_run_time"
+                },
+                {
+                    "aggregate": "count"
+                },
+                {
+                    "value": {
+                        "when": {
+                            "gt": [
+                                {
+                                    "div": ["action.duration", "task.maxRunTime"]
+                                }, threshold_pct/100.0
+                            ]
+                        },
+                        "then": 1
+                    },
+                    "aggregate": "sum",
+                    "name": "tasks_gt_pct"
+                },
+            ],
+            "where": {"and": [
+                {"in": {"build.branch": branches.split(',')}},
+                {"gt": {"task.run.start_time": {"date": start}}},
+                {"lte": {"task.run.start_time": {"date": end}}},
+                {"eq": {"state": "completed"}},
+            ]}
+        }
+        data = self.submit(query)
+        print("\nTasks nearing their max-run-time on %s between %s and %s" %
+              (branches, start, end))
+        if data and len(data) > 0:
+            filtered = []
+            for record in data:
+                if 'tasks_gt_pct' in record:
+                    count = record['count']
+                    tasks_gt_pct = record['tasks_gt_pct']
+                    if tasks_gt_pct / count > filter_threshold_pct / 100.0:
+                        filtered.append(record)
+            filtered.sort(key=get_long_running_ratio)
+            if not filtered:
+                print("No long running tasks found.")
+            for record in filtered:
+                name = record['run']['name']
+                count = record['count']
+                max_run_time = record['max_run_time']
+                tasks_gt_pct = record['tasks_gt_pct']
+                print("%-55s: %d of %d runs (%.1f%%) exceeded %d%% of max-run-time (%d s)" %
+                      (name, tasks_gt_pct, count, tasks_gt_pct * 100 / count,
+                       threshold_pct, max_run_time))
+        else:
+            print("No tasks found.")
--- a/testing/marionette/doc/internals/sync.rst
+++ b/testing/marionette/doc/internals/sync.rst
@@ -1,10 +1,16 @@
 sync module
 ===========
 
 Provides an assortment of synchronisation primitives.
 
+.. js:autoclass:: MessageManagerDestroyedPromise
+  :members:
+
 .. js:autoclass:: PollPromise
   :members:
 
+.. js:autoclass:: Sleep
+  :members:
+
 .. js:autoclass:: TimedPromise
   :members:
--- a/testing/marionette/sync.js
+++ b/testing/marionette/sync.js
@@ -11,22 +11,20 @@ const {
   error,
   TimeoutError,
 } = ChromeUtils.import("chrome://marionette/content/error.js", {});
 const {Log} = ChromeUtils.import("chrome://marionette/content/log.js", {});
 
 XPCOMUtils.defineLazyGetter(this, "logger", Log.get);
 
 this.EXPORTED_SYMBOLS = [
-  /* exported PollPromise, TimedPromise */
+  "MessageManagerDestroyedPromise",
   "PollPromise",
+  "Sleep",
   "TimedPromise",
-
-  /* exported MessageManagerDestroyedPromise */
-  "MessageManagerDestroyedPromise",
 ];
 
 const {TYPE_ONE_SHOT, TYPE_REPEATING_SLACK} = Ci.nsITimer;
 
 /**
  * @callback Condition
  *
  * @param {function(*)} resolve
@@ -81,20 +79,35 @@ const {TYPE_ONE_SHOT, TYPE_REPEATING_SLA
  *     Defaults to 10 milliseconds.
  *
  * @return {Promise.<*>}
  *     Yields the value passed to ``func``'s
  *     ``resolve`` or ``reject`` callbacks.
  *
  * @throws {*}
  *     If ``func`` throws, its error is propagated.
+ * @throws {TypeError}
+ *     If `timeout` or `interval`` are not numbers.
+ * @throws {RangeError}
+ *     If `timeout` or `interval` are not unsigned integers.
  */
 function PollPromise(func, {timeout = 2000, interval = 10} = {}) {
   const timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
 
+  if (typeof func != "function") {
+    throw new TypeError();
+  }
+  if (!(typeof timeout == "number" && typeof interval == "number")) {
+    throw new TypeError();
+  }
+  if ((!Number.isInteger(timeout) || timeout < 0) ||
+      (!Number.isInteger(interval) || interval < 0)) {
+    throw new RangeError();
+  }
+
   return new Promise((resolve, reject) => {
     const start = new Date().getTime();
     const end = start + timeout;
 
     let evalFn = () => {
       new Promise(func).then(resolve, rejected => {
         if (error.isError(rejected)) {
           throw rejected;
@@ -131,28 +144,43 @@ function PollPromise(func, {timeout = 20
  *
  * @param {Condition} func
  *     Function to run, which will have its ``reject``
  *     callback invoked after the ``timeout`` duration is reached.
  *     It is given two callbacks: ``resolve(value)`` and
  *     ``reject(error)``.
  * @param {timeout=} [timeout=1500] timeout
  *     ``condition``'s ``reject`` callback will be called
- *     after this timeout.
+ *     after this timeout, given in milliseconds.
  * @param {Error=} [throws=TimeoutError] throws
  *     When the ``timeout`` is hit, this error class will be
  *     thrown.  If it is null, no error is thrown and the promise is
  *     instead resolved on timeout.
  *
  * @return {Promise.<*>}
  *     Timed promise.
+ *
+ * @throws {TypeError}
+ *     If `timeout` is not a number.
+ * @throws {RangeError}
+ *     If `timeout` is not an unsigned integer.
  */
 function TimedPromise(fn, {timeout = 1500, throws = TimeoutError} = {}) {
   const timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
 
+  if (typeof fn != "function") {
+    throw new TypeError();
+  }
+  if (typeof timeout != "number") {
+    throw new TypeError();
+  }
+  if (!Number.isInteger(timeout) || timeout < 0) {
+    throw new RangeError();
+  }
+
   return new Promise((resolve, reject) => {
     // Reject only if |throws| is given.  Otherwise it is assumed that
     // the user is OK with the promise timing out.
     let bail = () => {
       if (throws !== null) {
         let err = new throws();
         reject(err);
       } else {
@@ -173,16 +201,37 @@ function TimedPromise(fn, {timeout = 150
     return res;
   }, err => {
     timer.cancel();
     throw err;
   });
 }
 
 /**
+ * Pauses for the given duration.
+ *
+ * @param {number} timeout
+ *     Duration to wait before fulfilling promise in milliseconds.
+ *
+ * @return {Promise}
+ *     Promise that fulfills when the `timeout` is elapsed.
+ *
+ * @throws {TypeError}
+ *     If `timeout` is not a number.
+ * @throws {RangeError}
+ *     If `timeout` is not an unsigned integer.
+ */
+function Sleep(timeout) {
+  if (typeof timeout != "number") {
+    throw new TypeError();
+  }
+  return new TimedPromise(() => {}, {timeout, throws: null});
+}
+
+/**
  * Detects when the specified message manager has been destroyed.
  *
  * One can observe the removal and detachment of a content browser
  * (`<xul:browser>`) or a chrome window by its message manager
  * disconnecting.
  *
  * When a browser is associated with a tab, this is safer than only
  * relying on the event `TabClose` which signalises the _intent to_
--- a/testing/marionette/test/unit/test_sync.js
+++ b/testing/marionette/test/unit/test_sync.js
@@ -1,17 +1,55 @@
 /* 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/. */
 
-const {PollPromise} = ChromeUtils.import("chrome://marionette/content/sync.js", {});
+const {
+  PollPromise,
+  Sleep,
+  TimedPromise,
+} = ChromeUtils.import("chrome://marionette/content/sync.js", {});
 
 const DEFAULT_TIMEOUT = 2000;
 
-add_task(async function test_PollPromise_types() {
+add_test(function test_PollPromise_funcTypes() {
+  for (let type of ["foo", 42, null, undefined, true, [], {}]) {
+    Assert.throws(() => new PollPromise(type), /TypeError/);
+  }
+  new PollPromise(() => {});
+  new PollPromise(function() {});
+
+  run_next_test();
+});
+
+add_test(function test_PollPromise_timeoutTypes() {
+  for (let timeout of ["foo", null, true, [], {}]) {
+    Assert.throws(() => new PollPromise(() => {}, {timeout}), /TypeError/);
+  }
+  for (let timeout of [1.2, -1]) {
+    Assert.throws(() => new PollPromise(() => {}, {timeout}), /RangeError/);
+  }
+  new PollPromise(() => {}, {timeout: 42});
+
+  run_next_test();
+});
+
+add_test(function test_PollPromise_intervalTypes() {
+  for (let interval of ["foo", null, true, [], {}]) {
+    Assert.throws(() => new PollPromise(() => {}, {interval}), /TypeError/);
+  }
+  for (let interval of [1.2, -1]) {
+    Assert.throws(() => new PollPromise(() => {}, {interval}), /RangeError/);
+  }
+  new PollPromise(() => {}, {interval: 42});
+
+  run_next_test();
+});
+
+add_task(async function test_PollPromise_retvalTypes() {
   for (let typ of [true, false, "foo", 42, [], {}]) {
     strictEqual(typ, await new PollPromise(resolve => resolve(typ)));
   }
 });
 
 add_task(async function test_PollPromise_timeoutElapse() {
   let nevals = 0;
   let start = new Date().getTime();
@@ -67,8 +105,39 @@ add_task(async function test_PollPromise
 add_task(async function test_PollPromise_interval() {
   let nevals = 0;
   await new PollPromise((resolve, reject) => {
     ++nevals;
     reject();
   }, {timeout: 100, interval: 100});
   equal(2, nevals);
 });
+
+add_test(function test_TimedPromise_funcTypes() {
+  for (let type of ["foo", 42, null, undefined, true, [], {}]) {
+    Assert.throws(() => new TimedPromise(type), /TypeError/);
+  }
+  new TimedPromise(resolve => resolve());
+  new TimedPromise(function(resolve) { resolve(); });
+
+  run_next_test();
+});
+
+add_test(function test_TimedPromise_timeoutTypes() {
+  for (let timeout of ["foo", null, true, [], {}]) {
+    Assert.throws(() => new TimedPromise(resolve => resolve(), {timeout}), /TypeError/);
+  }
+  for (let timeout of [1.2, -1]) {
+    Assert.throws(() => new TimedPromise(resolve => resolve(), {timeout}), /RangeError/);
+  }
+  new TimedPromise(resolve => resolve(), {timeout: 42});
+
+  run_next_test();
+});
+
+add_task(async function test_Sleep() {
+  await Sleep(0);
+  for (let type of ["foo", true, null, undefined]) {
+    Assert.throws(() => new Sleep(type), /TypeError/);
+  }
+  Assert.throws(() => new Sleep(1.2), /RangeError/);
+  Assert.throws(() => new Sleep(-1), /RangeError/);
+});
--- a/testing/mozharness/configs/android/androidx86_7_0.py
+++ b/testing/mozharness/configs/android/androidx86_7_0.py
@@ -1,16 +1,15 @@
 # mozharness configuration for Android x86 7.0 unit tests
 #
 # This configuration should be combined with suite definitions and other
 # mozharness configuration from android_common.py, or similar.
 
 config = {
     "tooltool_manifest_path": "testing/config/tooltool-manifests/androidx86_7_0/releng.manifest",
-    "tooltool_servers": ['http://taskcluster/relengapi/tooltool/'],
     "emulator_manifest": """
         [
         {
         "size": 135064025,
         "digest": "125678c5b0d93ead8bbf01ba94253e532909417b40637460624cfca34e92f431534fc77a0225e9c4728dcbcf2884a8f7fa1ee059efdfa82d827ca20477d41705",
         "algorithm": "sha512",
         "filename": "android-sdk_r27.1.12-linux-x86emu.tar.gz",
         "unpack": "True"
--- 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
rename from testing/talos/talos/tests/svgx/images/kyoto_1.jpg
rename to testing/talos/talos/tests/svg_static/images/kyoto_1.jpg
deleted file mode 100644
index 577e2220c11814a8463535331d28bd7d29c6bd62..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/testing/web-platform/meta/media-capabilities/__dir__.ini
+++ b/testing/web-platform/meta/media-capabilities/__dir__.ini
@@ -1,1 +1,2 @@
+prefs: [media.media-capabilities.enabled:true]
 lsan-allowed: [Alloc, XPCWrappedNative::GetNewOrUsed, js_new, js_pod_calloc, js_pod_malloc, js_pod_realloc, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/testing/web-platform/meta/media-capabilities/decodingInfo.html.ini
+++ b/testing/web-platform/meta/media-capabilities/decodingInfo.html.ini
@@ -1,8 +1,7 @@
 [decodingInfo.html]
-  prefs: [media.media-capabilities.enabled:true]
   [Test that decodingInfo rejects if the video configuration contentType has more than one parameter]
     expected: FAIL
 
   [Test that decodingInfo rejects if the audio configuration contentType has more than one parameters]
     expected: FAIL
 
--- a/testing/web-platform/meta/media-capabilities/idlharness.any.js.ini
+++ b/testing/web-platform/meta/media-capabilities/idlharness.any.js.ini
@@ -1,239 +1,50 @@
 [idlharness.any.html]
-  [MediaCapabilitiesInfo interface: existence and properties of interface object]
-    expected: FAIL
-
-  [MediaCapabilitiesInfo interface object length]
-    expected: FAIL
-
-  [MediaCapabilitiesInfo interface object name]
-    expected: FAIL
-
-  [MediaCapabilitiesInfo interface: existence and properties of interface prototype object]
-    expected: FAIL
-
-  [MediaCapabilitiesInfo interface: existence and properties of interface prototype object's "constructor" property]
-    expected: FAIL
-
-  [MediaCapabilitiesInfo interface: existence and properties of interface prototype object's @@unscopables property]
-    expected: FAIL
-
-  [MediaCapabilitiesInfo interface: attribute supported]
-    expected: FAIL
-
-  [MediaCapabilitiesInfo interface: attribute smooth]
-    expected: FAIL
-
-  [MediaCapabilitiesInfo interface: attribute powerEfficient]
-    expected: FAIL
-
-  [MediaCapabilities interface: existence and properties of interface object]
-    expected: FAIL
-
-  [MediaCapabilities interface object length]
-    expected: FAIL
-
-  [MediaCapabilities interface object name]
-    expected: FAIL
-
-  [MediaCapabilities interface: existence and properties of interface prototype object]
-    expected: FAIL
-
-  [MediaCapabilities interface: existence and properties of interface prototype object's "constructor" property]
-    expected: FAIL
-
-  [MediaCapabilities interface: existence and properties of interface prototype object's @@unscopables property]
-    expected: FAIL
-
-  [MediaCapabilities interface: operation decodingInfo(MediaDecodingConfiguration)]
-    expected: FAIL
-
-  [MediaCapabilities interface: operation encodingInfo(MediaEncodingConfiguration)]
-    expected: FAIL
-
-  [ScreenLuminance interface: existence and properties of interface object]
-    expected: FAIL
-
-  [ScreenLuminance interface object length]
-    expected: FAIL
-
-  [ScreenLuminance interface object name]
-    expected: FAIL
-
-  [ScreenLuminance interface: existence and properties of interface prototype object]
-    expected: FAIL
-
-  [ScreenLuminance interface: existence and properties of interface prototype object's "constructor" property]
-    expected: FAIL
-
-  [ScreenLuminance interface: existence and properties of interface prototype object's @@unscopables property]
-    expected: FAIL
-
-  [ScreenLuminance interface: attribute min]
-    expected: FAIL
-
-  [ScreenLuminance interface: attribute max]
-    expected: FAIL
-
-  [ScreenLuminance interface: attribute maxAverage]
-    expected: FAIL
-
-  [Navigator interface: attribute mediaCapabilities]
-    expected: FAIL
-
-  [Navigator interface: navigator must inherit property "mediaCapabilities" with the proper type]
-    expected: FAIL
-
-  [Screen interface: attribute colorGamut]
-    expected: FAIL
-
-  [Screen interface: attribute luminance]
-    expected: FAIL
-
-  [Screen interface: attribute onchange]
-    expected: FAIL
-
-  [MediaCapabilitiesInfo interface: encodingInfo must inherit property "smooth" with the proper type]
-    expected: FAIL
-
   [MediaCapabilities interface: calling decodingInfo(MediaDecodingConfiguration) on navigatior.mediaCapabilities with too few arguments must throw TypeError]
     expected: FAIL
 
   [MediaCapabilities interface: navigatior.mediaCapabilities must inherit property "decodingInfo(MediaDecodingConfiguration)" with the proper type]
     expected: FAIL
 
-  [Stringification of decodingInfo]
-    expected: FAIL
-
-  [MediaCapabilitiesInfo interface: decodingInfo must inherit property "smooth" with the proper type]
-    expected: FAIL
-
   [ScreenLuminance interface: screen.luminance must inherit property "min" with the proper type]
     expected: FAIL
 
   [Stringification of navigatior.mediaCapabilities]
     expected: FAIL
 
-  [Screen interface: screen must inherit property "colorGamut" with the proper type]
-    expected: FAIL
-
   [Stringification of screen.luminance]
     expected: FAIL
 
   [ScreenLuminance interface: screen.luminance must inherit property "maxAverage" with the proper type]
     expected: FAIL
 
   [ScreenLuminance interface: screen.luminance must inherit property "max" with the proper type]
     expected: FAIL
 
-  [MediaCapabilitiesInfo interface: decodingInfo must inherit property "supported" with the proper type]
-    expected: FAIL
-
-  [MediaCapabilitiesInfo interface: decodingInfo must inherit property "powerEfficient" with the proper type]
-    expected: FAIL
-
-  [Screen interface: screen must inherit property "luminance" with the proper type]
-    expected: FAIL
-
-  [Stringification of encodingInfo]
-    expected: FAIL
-
-  [MediaCapabilitiesInfo interface: encodingInfo must inherit property "powerEfficient" with the proper type]
-    expected: FAIL
-
   [ScreenLuminance must be primary interface of screen.luminance]
     expected: FAIL
 
-  [MediaCapabilitiesInfo must be primary interface of encodingInfo]
-    expected: FAIL
-
   [MediaCapabilities interface: calling encodingInfo(MediaEncodingConfiguration) on navigatior.mediaCapabilities with too few arguments must throw TypeError]
     expected: FAIL
 
-  [MediaCapabilitiesInfo must be primary interface of decodingInfo]
-    expected: FAIL
-
-  [Screen interface: screen must inherit property "onchange" with the proper type]
-    expected: FAIL
-
   [MediaCapabilities interface: navigatior.mediaCapabilities must inherit property "encodingInfo(MediaEncodingConfiguration)" with the proper type]
     expected: FAIL
 
   [MediaCapabilities must be primary interface of navigatior.mediaCapabilities]
     expected: FAIL
 
-  [MediaCapabilitiesInfo interface: encodingInfo must inherit property "supported" with the proper type]
-    expected: FAIL
-
 
 [idlharness.any.worker.html]
-  [MediaCapabilities interface: existence and properties of interface object]
-    expected: FAIL
-
-  [MediaCapabilities interface object length]
-    expected: FAIL
-
-  [MediaCapabilities interface object name]
-    expected: FAIL
-
-  [MediaCapabilities interface: existence and properties of interface prototype object]
-    expected: FAIL
-
-  [MediaCapabilities interface: existence and properties of interface prototype object's "constructor" property]
-    expected: FAIL
-
-  [MediaCapabilities interface: existence and properties of interface prototype object's @@unscopables property]
-    expected: FAIL
-
-  [MediaCapabilities interface: operation decodingInfo(MediaDecodingConfiguration)]
-    expected: FAIL
-
-  [MediaCapabilities interface: operation encodingInfo(MediaEncodingConfiguration)]
-    expected: FAIL
-
-  [WorkerNavigator interface: attribute mediaCapabilities]
-    expected: FAIL
-
-  [MediaCapabilitiesInfo interface: existence and properties of interface object]
-    expected: FAIL
-
-  [MediaCapabilitiesInfo interface object length]
-    expected: FAIL
-
-  [MediaCapabilitiesInfo interface object name]
-    expected: FAIL
-
-  [MediaCapabilitiesInfo interface: existence and properties of interface prototype object]
-    expected: FAIL
-
-  [MediaCapabilitiesInfo interface: existence and properties of interface prototype object's "constructor" property]
-    expected: FAIL
-
-  [MediaCapabilitiesInfo interface: existence and properties of interface prototype object's @@unscopables property]
-    expected: FAIL
-
-  [MediaCapabilitiesInfo interface: attribute supported]
-    expected: FAIL
-
-  [MediaCapabilitiesInfo interface: attribute smooth]
-    expected: FAIL
-
-  [MediaCapabilitiesInfo interface: attribute powerEfficient]
-    expected: FAIL
-
   [MediaCapabilitiesInfo interface: encodingInfo must inherit property "smooth" with the proper type]
     expected: FAIL
 
   [MediaCapabilities interface: calling decodingInfo(MediaDecodingConfiguration) on navigatior.mediaCapabilities with too few arguments must throw TypeError]
     expected: FAIL
 
-  [WorkerNavigator interface: navigator must inherit property "mediaCapabilities" with the proper type]
-    expected: FAIL
-
   [MediaCapabilities interface: navigatior.mediaCapabilities must inherit property "decodingInfo(MediaDecodingConfiguration)" with the proper type]
     expected: FAIL
 
   [Stringification of decodingInfo]
     expected: FAIL
 
   [MediaCapabilitiesInfo interface: decodingInfo must inherit property "smooth" with the proper type]
     expected: FAIL
--- a/toolkit/components/normandy/content/AboutPages.jsm
+++ b/toolkit/components/normandy/content/AboutPages.jsm
@@ -1,14 +1,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
-const Cm = Components.manager;
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 ChromeUtils.defineModuleGetter(
   this, "CleanupManager", "resource://normandy/lib/CleanupManager.jsm",
 );
 ChromeUtils.defineModuleGetter(
   this, "AddonStudies", "resource://normandy/lib/AddonStudies.jsm",
@@ -16,37 +15,29 @@ 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()}`
-);
 
 /**
  * Class for managing an about: page that Normandy provides. Adapted from
  * browser/extensions/pocket/content/AboutPocket.jsm.
  *
  * @implements nsIFactory
  * @implements nsIAboutModule
  */
 class AboutPage {
-  constructor({chromeUrl, aboutHost, classId, description, uriFlags}) {
+  constructor({chromeUrl, aboutHost, classID, description, uriFlags}) {
     this.chromeUrl = chromeUrl;
     this.aboutHost = aboutHost;
-    this.classId = Components.ID(classId);
+    this.classID = Components.ID(classID);
     this.description = description;
     this.uriFlags = uriFlags;
   }
 
   getURIFlags() {
     return this.uriFlags;
   }
 
@@ -56,84 +47,50 @@ class AboutPage {
     channel.originalURI = uri;
 
     if (this.uriFlags & Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT) {
       const principal = Services.scriptSecurityManager.createCodebasePrincipal(uri, {});
       channel.owner = principal;
     }
     return channel;
   }
-
-  createInstance(outer, iid) {
-    if (outer !== null) {
-      throw Cr.NS_ERROR_NO_AGGREGATION;
-    }
-    return this.QueryInterface(iid);
-  }
-
-  /**
-   * Register this about: page with XPCOM. This must be called once in each
-   * process (parent and content) to correctly initialize the page.
-   */
-  register() {
-    Cm.QueryInterface(Ci.nsIComponentRegistrar).registerFactory(
-      this.classId,
-      this.description,
-      `@mozilla.org/network/protocol/about;1?what=${this.aboutHost}`,
-      this,
-    );
-  }
-
-  /**
-   * Unregister this about: page with XPCOM. This must be called before the
-   * add-on is cleaned up if the page has been registered.
-   */
-  unregister() {
-    Cm.QueryInterface(Ci.nsIComponentRegistrar).unregisterFactory(this.classId, this);
-  }
 }
 AboutPage.prototype.QueryInterface = ChromeUtils.generateQI([Ci.nsIAboutModule]);
 
 /**
  * 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();
     });
   },
 };
 
 /**
  * about:studies page for displaying in-progress and past Shield studies.
  * @type {AboutPage}
  * @implements {nsIMessageListener}
  */
 XPCOMUtils.defineLazyGetter(this.AboutPages, "aboutStudies", () => {
   const aboutStudies = new AboutPage({
     chromeUrl: "resource://normandy-content/about-studies/about-studies.html",
     aboutHost: "studies",
-    classId: "{6ab96943-a163-482c-9622-4faedc0e827f}",
+    classID: "{6ab96943-a163-482c-9622-4faedc0e827f}",
     description: "Shield Study Listing",
     uriFlags: (
       Ci.nsIAboutModule.ALLOW_SCRIPT
       | Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT
       | Ci.nsIAboutModule.URI_MUST_LOAD_IN_CHILD
     ),
   });
 
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/normandy/moz.build
+++ b/toolkit/components/normandy/moz.build
@@ -4,12 +4,17 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 with Files('**'):
     BUG_COMPONENT = ('Firefox', 'Normandy Client')
 
 JAR_MANIFESTS += ['jar.mn']
 
+EXTRA_COMPONENTS += [
+    'shield-content-process.js',
+    'shield.manifest',
+]
+
 SPHINX_TREES['normandy'] = 'docs'
 
 BROWSER_CHROME_MANIFESTS += ['test/browser/browser.ini']
 XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']
rename from toolkit/components/normandy/content/shield-content-process.js
rename to toolkit/components/normandy/shield-content-process.js
--- a/toolkit/components/normandy/content/shield-content-process.js
+++ b/toolkit/components/normandy/shield-content-process.js
@@ -7,41 +7,20 @@
  * Registers about: pages provided by Shield, and listens for a shutdown event
  * from the add-on before un-registering them.
  *
  * This file is loaded as a process script. It is executed once for each
  * process, including the parent one.
  */
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://normandy-content/AboutPages.jsm");
 
-class ShieldChildListener {
-  onStartup() {
-    Services.cpmm.addMessageListener("Shield:ShuttingDown", this, true);
-    AboutPages.aboutStudies.register();
-  }
-
-  onShutdown() {
-    AboutPages.aboutStudies.unregister();
-    Services.cpmm.removeMessageListener("Shield:ShuttingDown", this);
-
-    // Unload AboutPages.jsm in case the add-on is reinstalled and we need to
-    // load a new version of it.
-    Cu.unload("resource://normandy-content/AboutPages.jsm");
-  }
+// generateNSGetFactory only knows how to register factory classes, with
+// classID properties on their prototype, and a constructor that returns
+// an instance. It can't handle singletons directly. So wrap the
+// aboutStudies singleton in a trivial constructor function.
+function AboutStudies() {
+  return AboutStudies.prototype;
+}
+AboutStudies.prototype = AboutPages.aboutStudies;
 
-  receiveMessage(message) {
-    switch (message.name) {
-      case "Shield:ShuttingDown":
-        this.onShutdown();
-        break;
-    }
-  }
-}
-
-// Only register in content processes; the parent process handles registration
-// separately.
-if (Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_CONTENT) {
-  const listener = new ShieldChildListener();
-  listener.onStartup();
-}
+var NSGetFactory = XPCOMUtils.generateNSGetFactory([AboutStudies]);
new file mode 100644
--- /dev/null
+++ b/toolkit/components/normandy/shield.manifest
@@ -0,0 +1,3 @@
+
+component {6ab96943-a163-482c-9622-4faedc0e827f} shield-content-process.js
+contract @mozilla.org/network/protocol/about;1?what=studies {6ab96943-a163-482c-9622-4faedc0e827f}
--- a/toolkit/components/telemetry/TelemetryHistogram.cpp
+++ b/toolkit/components/telemetry/TelemetryHistogram.cpp
@@ -179,17 +179,17 @@ typedef mozilla::Vector<KeyedHistogramSn
 
 class KeyedHistogram {
 public:
   KeyedHistogram(HistogramID id, const HistogramInfo& info, bool expired);
   ~KeyedHistogram();
   nsresult GetHistogram(const nsCString& name, Histogram** histogram);
   Histogram* GetHistogram(const nsCString& name);
   uint32_t GetHistogramType() const { return mHistogramInfo.histogramType; }
-  nsresult GetJSKeys(JSContext* cx, JS::CallArgs& args);
+  nsresult GetKeys(const StaticMutexAutoLock& aLock, nsTArray<nsCString>& aKeys);
   // Note: unlike other methods, GetJSSnapshot is thread safe.
   nsresult GetJSSnapshot(JSContext* cx, JS::Handle<JSObject*> obj,
                          bool clearSubsession);
   nsresult GetSnapshot(const StaticMutexAutoLock& aLock,
                        KeyedHistogramSnapshotData& aSnapshot, bool aClearSubsession);
 
   nsresult Add(const nsCString& key, uint32_t aSample, ProcessID aProcessType);
   void Clear();
@@ -1045,37 +1045,28 @@ KeyedHistogram::SizeOfIncludingThis(mozi
 {
   size_t n = 0;
   n += aMallocSizeOf(this);
   n += mHistogramMap.SizeOfIncludingThis(aMallocSizeOf);
   return n;
 }
 
 nsresult
-KeyedHistogram::GetJSKeys(JSContext* cx, JS::CallArgs& args)
+KeyedHistogram::GetKeys(const StaticMutexAutoLock& aLock, nsTArray<nsCString>& aKeys)
 {
-  JS::AutoValueVector keys(cx);
-  if (!keys.reserve(mHistogramMap.Count())) {
+  if (!aKeys.SetCapacity(mHistogramMap.Count(), mozilla::fallible)) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   for (auto iter = mHistogramMap.Iter(); !iter.Done(); iter.Next()) {
-    JS::RootedValue jsKey(cx);
-    jsKey.setString(ToJSString(cx, iter.Get()->GetKey()));
-    if (!keys.append(jsKey)) {
+    if (!aKeys.AppendElement(iter.Get()->GetKey(), mozilla::fallible)) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
   }
 
-  JS::RootedObject jsKeys(cx, JS_NewArrayObject(cx, keys));
-  if (!jsKeys) {
-    return NS_ERROR_FAILURE;
-  }
-
-  args.rval().setObject(*jsKeys);
   return NS_OK;
 }
 
 nsresult
 KeyedHistogram::GetJSSnapshot(JSContext* cx, JS::Handle<JSObject*> obj, bool clearSubsession)
 {
   // Get a snapshot of the data.
   KeyedHistogramSnapshotData dataSnapshot;
@@ -1834,33 +1825,57 @@ internal_JSKeyedHistogram_Keys(JSContext
     return false;
   }
 
   JSObject *obj = &args.thisv().toObject();
   JSHistogramData* data = static_cast<JSHistogramData*>(JS_GetPrivate(obj));
   MOZ_ASSERT(data);
   HistogramID id = data->histogramId;
 
-  KeyedHistogram* keyed = nullptr;
+  nsTArray<nsCString> keys;
   {
     StaticMutexAutoLock locker(gTelemetryHistogramMutex);
     MOZ_ASSERT(internal_IsHistogramEnumId(id));
 
     // This is not good standard behavior given that we have histogram instances
     // covering multiple processes.
     // However, changing this requires some broader changes to callers.
-    keyed = internal_GetKeyedHistogramById(id, ProcessID::Parent);
+    KeyedHistogram* keyed = internal_GetKeyedHistogramById(id, ProcessID::Parent);
+
+    MOZ_ASSERT(keyed);
+    if (!keyed) {
+      return false;
+    }
+
+    if (NS_FAILED(keyed->GetKeys(locker, keys))) {
+      return false;
+    }
   }
 
-  MOZ_ASSERT(keyed);
-  if (!keyed) {
+  // Convert keys from nsTArray<nsCString> to JS array.
+  JS::AutoValueVector autoKeys(cx);
+  if (!autoKeys.reserve(keys.Length())) {
     return false;
   }
 
-  return NS_SUCCEEDED(keyed->GetJSKeys(cx, args));
+  for (const auto& key : keys) {
+    JS::RootedValue jsKey(cx);
+    jsKey.setString(ToJSString(cx, key));
+    if (!autoKeys.append(jsKey)) {
+      return false;
+    }
+  }
+
+  JS::RootedObject jsKeys(cx, JS_NewArrayObject(cx, autoKeys));
+  if (!jsKeys) {
+    return false;
+  }
+
+  args.rval().setObject(*jsKeys);
+  return true;
 }
 
 bool
 internal_JSKeyedHistogram_Snapshot(JSContext *cx, unsigned argc, JS::Value *vp)
 {
   return internal_KeyedHistogram_SnapshotImpl(cx, argc, vp, false);
 }
 
--- a/toolkit/content/browser-child.js
+++ b/toolkit/content/browser-child.js
@@ -1,400 +1,26 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* eslint-env mozilla/frame-script */
 
-ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
 ChromeUtils.import("resource://gre/modules/BrowserUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
-ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Timer.jsm");
+ChromeUtils.import("resource://gre/modules/WebNavigationChild.jsm");
+ChromeUtils.import("resource://gre/modules/WebProgressChild.jsm");
 
 ChromeUtils.defineModuleGetter(this, "PageThumbUtils",
   "resource://gre/modules/PageThumbUtils.jsm");
 
-ChromeUtils.defineModuleGetter(this, "Utils",
-  "resource://gre/modules/sessionstore/Utils.jsm");
-
-if (AppConstants.MOZ_CRASHREPORTER) {
-  XPCOMUtils.defineLazyServiceGetter(this, "CrashReporter",
-                                     "@mozilla.org/xre/app-info;1",
-                                     "nsICrashReporter");
-}
-
-var WebProgressListener = {
-  init() {
-    this._filter = Cc["@mozilla.org/appshell/component/browser-status-filter;1"]
-                     .createInstance(Ci.nsIWebProgress);
-    this._filter.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_ALL);
-    this._filter.target = tabEventTarget;
-
-    let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
-                              .getInterface(Ci.nsIWebProgress);
-    webProgress.addProgressListener(this._filter, Ci.nsIWebProgress.NOTIFY_ALL);
-    this.init = null;
-  },
-
-  uninit() {
-    let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
-                              .getInterface(Ci.nsIWebProgress);
-    webProgress.removeProgressListener(this._filter);
-
-    this._filter.removeProgressListener(this);
-    this._filter = null;
-  },
-
-  _requestSpec(aRequest, aPropertyName) {
-    if (!aRequest || !(aRequest instanceof Ci.nsIChannel))
-      return null;
-    return aRequest.QueryInterface(Ci.nsIChannel)[aPropertyName].spec;
-  },
-
-  _setupJSON: function setupJSON(aWebProgress, aRequest, aStateFlags) {
-    // Avoid accessing content.document when being called from onStateChange
-    // unless if we are in STATE_STOP, because otherwise the getter will
-    // instantiate an about:blank document for us.
-    let contentDocument = null;
-    if (aStateFlags) {
-      // We're being called from onStateChange
-      if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
-        contentDocument = content.document;
-      }
-    } else {
-      contentDocument = content.document;
-    }
-
-    let innerWindowID = null;
-    if (aWebProgress) {
-      let domWindowID = null;
-      try {
-        domWindowID = aWebProgress.DOMWindowID;
-        innerWindowID = aWebProgress.innerDOMWindowID;
-      } catch (e) {
-        // The DOM Window ID getters above may throw if the inner or outer
-        // windows aren't created yet or are destroyed at the time we're making
-        // this call but that isn't fatal so ignore the exceptions here.
-      }
-
-      aWebProgress = {
-        isTopLevel: aWebProgress.isTopLevel,
-        isLoadingDocument: aWebProgress.isLoadingDocument,
-        loadType: aWebProgress.loadType,
-        DOMWindowID: domWindowID
-      };
-    }
-
-    return {
-      webProgress: aWebProgress || null,
-      requestURI: this._requestSpec(aRequest, "URI"),
-      originalRequestURI: this._requestSpec(aRequest, "originalURI"),
-      documentContentType: contentDocument ? contentDocument.contentType : null,
-      innerWindowID,
-    };
-  },
-
-  _setupObjects: function setupObjects(aWebProgress, aRequest) {
-    let domWindow;
-    try {
-      domWindow = aWebProgress && aWebProgress.DOMWindow;
-    } catch (e) {
-      // If nsDocShell::Destroy has already been called, then we'll
-      // get NS_NOINTERFACE when trying to get the DOM window. Ignore
-      // that here.
-      domWindow = null;
-    }
-
-    return {
-      contentWindow: content,
-      contentDocument: content.document,
-      // DOMWindow is not necessarily the content-window with subframes.
-      DOMWindow: domWindow,
-      webProgress: aWebProgress,
-      request: aRequest,
-    };
-  },
-
-  _send(name, data, objects) {
-    sendAsyncMessage(name, data, objects);
-  },
-
-  onStateChange: function onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
-    let json = this._setupJSON(aWebProgress, aRequest, aStateFlags);
-    let objects = this._setupObjects(aWebProgress, aRequest);
-
-    json.stateFlags = aStateFlags;
-    json.status = aStatus;
-
-    // It's possible that this state change was triggered by
-    // loading an internal error page, for which the parent
-    // will want to know some details, so we'll update it with
-    // the documentURI.
-    if (aWebProgress && aWebProgress.isTopLevel) {
-      json.documentURI = content.document.documentURIObject.spec;
-      json.charset = content.document.characterSet;
-      json.mayEnableCharacterEncodingMenu = docShell.mayEnableCharacterEncodingMenu;
-      json.inLoadURI = WebNavigation.inLoadURI;
-    }
-
-    this._send("Content:StateChange", json, objects);
-  },
-
-  // Note: Because the nsBrowserStatusFilter timeout runnable is
-  // SystemGroup-labeled, this method should not modify content DOM or
-  // run content JS.
-  onProgressChange: function onProgressChange(aWebProgress, aRequest, aCurSelf, aMaxSelf, aCurTotal, aMaxTotal) {
-    let json = this._setupJSON(aWebProgress, aRequest);
-    let objects = this._setupObjects(aWebProgress, aRequest);
-
-    json.curSelf = aCurSelf;
-    json.maxSelf = aMaxSelf;
-    json.curTotal = aCurTotal;
-    json.maxTotal = aMaxTotal;
-
-    this._send("Content:ProgressChange", json, objects);
-  },
-
-  onProgressChange64: function onProgressChange(aWebProgress, aRequest, aCurSelf, aMaxSelf, aCurTotal, aMaxTotal) {
-    this.onProgressChange(aWebProgress, aRequest, aCurSelf, aMaxSelf, aCurTotal, aMaxTotal);
-  },
-
-  onLocationChange: function onLocationChange(aWebProgress, aRequest, aLocationURI, aFlags) {
-    let json = this._setupJSON(aWebProgress, aRequest);
-    let objects = this._setupObjects(aWebProgress, aRequest);
-
-    json.location = aLocationURI ? aLocationURI.spec : "";
-    json.flags = aFlags;
-
-    // These properties can change even for a sub-frame navigation.
-    let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
-    json.canGoBack = webNav.canGoBack;
-    json.canGoForward = webNav.canGoForward;
-
-    if (aWebProgress && aWebProgress.isTopLevel) {
-      json.documentURI = content.document.documentURIObject.spec;
-      json.title = content.document.title;
-      json.charset = content.document.characterSet;
-      json.mayEnableCharacterEncodingMenu = docShell.mayEnableCharacterEncodingMenu;
-      json.principal = content.document.nodePrincipal;
-      json.synthetic = content.document.mozSyntheticDocument;
-      json.inLoadURI = WebNavigation.inLoadURI;
-      json.requestContextID = content.document.documentLoadGroup
-        ? content.document.documentLoadGroup.requestContextID
-        : null;
-
-      if (AppConstants.MOZ_CRASHREPORTER && CrashReporter.enabled) {
-        let uri = aLocationURI;
-        try {
-          // If the current URI contains a username/password, remove it.
-          uri = uri.mutate()
-                   .setUserPass("")
-                   .finalize();
-        } catch (ex) { /* Ignore failures on about: URIs. */ }
-        CrashReporter.annotateCrashReport("URL", uri.spec);
-      }
-    }
-
-    this._send("Content:LocationChange", json, objects);
-  },
+this.WebProgress = new WebProgressChild(this);
+this.WebNavigation = new WebNavigationChild(this);
 
-  // Note: Because the nsBrowserStatusFilter timeout runnable is
-  // SystemGroup-labeled, this method should not modify content DOM or
-  // run content JS.
-  onStatusChange: function onStatusChange(aWebProgress, aRequest, aStatus, aMessage) {
-    let json = this._setupJSON(aWebProgress, aRequest);
-    let objects = this._setupObjects(aWebProgress, aRequest);
-
-    json.status = aStatus;
-    json.message = aMessage;
-
-    this._send("Content:StatusChange", json, objects);
-  },
-
-  onSecurityChange: function onSecurityChange(aWebProgress, aRequest, aState) {
-    let json = this._setupJSON(aWebProgress, aRequest);
-    let objects = this._setupObjects(aWebProgress, aRequest);
-
-    json.state = aState;
-    json.secInfo = SecurityUI.getSecInfoAsString();
-
-    json.matchedList = null;
-    if (aRequest && aRequest instanceof Ci.nsIClassifiedChannel) {
-      json.matchedList = aRequest.matchedList;
-    }
-
-    this._send("Content:SecurityChange", json, objects);
-  },
-
-  onRefreshAttempted: function onRefreshAttempted(aWebProgress, aURI, aDelay, aSameURI) {
-    return true;
-  },
-
-  sendLoadCallResult() {
-    sendAsyncMessage("Content:LoadURIResult");
-  },
-
-  QueryInterface: ChromeUtils.generateQI(["nsIWebProgressListener",
-                                          "nsIWebProgressListener2",
-                                          "nsISupportsWeakReference"]),
-};
-
-WebProgressListener.init();
-addEventListener("unload", () => {
-  WebProgressListener.uninit();
-});
-
-var WebNavigation =  {
-  init() {
-    addMessageListener("WebNavigation:GoBack", this);
-    addMessageListener("WebNavigation:GoForward", this);
-    addMessageListener("WebNavigation:GotoIndex", this);
-    addMessageListener("WebNavigation:LoadURI", this);
-    addMessageListener("WebNavigation:SetOriginAttributes", this);
-    addMessageListener("WebNavigation:Reload", this);
-    addMessageListener("WebNavigation:Stop", this);
-    // This message is used for measuring content process startup performance.
-    sendAsyncMessage("Content:BrowserChildReady", { time: Services.telemetry.msSystemNow() });
-    this.init = null;
-  },
-
-  get webNavigation() {
-    return docShell.QueryInterface(Ci.nsIWebNavigation);
-  },
-
-  _inLoadURI: false,
-
-  get inLoadURI() {
-    return this._inLoadURI;
-  },
-
-  receiveMessage(message) {
-    switch (message.name) {
-      case "WebNavigation:GoBack":
-        this.goBack();
-        break;
-      case "WebNavigation:GoForward":
-        this.goForward();
-        break;
-      case "WebNavigation:GotoIndex":
-        this.gotoIndex(message.data.index);
-        break;
-      case "WebNavigation:LoadURI":
-        let histogram = Services.telemetry.getKeyedHistogramById("FX_TAB_REMOTE_NAVIGATION_DELAY_MS");
-        histogram.add("WebNavigation:LoadURI",
-                      Services.telemetry.msSystemNow() - message.data.requestTime);
-
-        this.loadURI(message.data.uri, message.data.flags,
-                     message.data.referrer, message.data.referrerPolicy,
-                     message.data.postData, message.data.headers,
-                     message.data.baseURI, message.data.triggeringPrincipal);
-        break;
-      case "WebNavigation:SetOriginAttributes":
-        this.setOriginAttributes(message.data.originAttributes);
-        break;
-      case "WebNavigation:Reload":
-        this.reload(message.data.flags);
-        break;
-      case "WebNavigation:Stop":
-        this.stop(message.data.flags);
-        break;
-    }
-  },
-
-  _wrapURIChangeCall(fn) {
-    this._inLoadURI = true;
-    try {
-      fn();
-    } finally {
-      this._inLoadURI = false;
-      WebProgressListener.sendLoadCallResult();
-    }
-  },
-
-  goBack() {
-    if (this.webNavigation.canGoBack) {
-      this._wrapURIChangeCall(() => this.webNavigation.goBack());
-    }
-  },
-
-  goForward() {
-    if (this.webNavigation.canGoForward) {
-      this._wrapURIChangeCall(() => this.webNavigation.goForward());
-    }
-  },
-
-  gotoIndex(index) {
-    this._wrapURIChangeCall(() => this.webNavigation.gotoIndex(index));
-  },
-
-  loadURI(uri, flags, referrer, referrerPolicy, postData, headers, baseURI, triggeringPrincipal) {
-    if (AppConstants.MOZ_CRASHREPORTER && CrashReporter.enabled) {
-      let annotation = uri;
-      try {
-        let url = Services.io.newURI(uri);
-        // If the current URI contains a username/password, remove it.
-        url = url.mutate()
-                 .setUserPass("")
-                 .finalize();
-        annotation = url.spec;
-      } catch (ex) { /* Ignore failures to parse and failures
-                      on about: URIs. */ }
-      CrashReporter.annotateCrashReport("URL", annotation);
-    }
-    if (referrer)
-      referrer = Services.io.newURI(referrer);
-    if (postData)
-      postData = Utils.makeInputStream(postData);
-    if (headers)
-      headers = Utils.makeInputStream(headers);
-    if (baseURI)
-      baseURI = Services.io.newURI(baseURI);
-    if (triggeringPrincipal)
-      triggeringPrincipal = Utils.deserializePrincipal(triggeringPrincipal);
-    this._wrapURIChangeCall(() => {
-      return this.webNavigation.loadURIWithOptions(uri, flags, referrer, referrerPolicy,
-                                                   postData, headers, baseURI, triggeringPrincipal);
-    });
-  },
-
-  setOriginAttributes(originAttributes) {
-    if (originAttributes) {
-      this.webNavigation.setOriginAttributesBeforeLoading(originAttributes);
-    }
-  },
-
-  reload(flags) {
-    this.webNavigation.reload(flags);
-  },
-
-  stop(flags) {
-    this.webNavigation.stop(flags);
-  }
-};
-
-WebNavigation.init();
-
-var SecurityUI = {
-  getSecInfoAsString() {
-    let secInfo = docShell.securityUI.secInfo;
-
-    if (secInfo) {
-      if (secInfo) {
-        let helper = Cc["@mozilla.org/network/serialization-helper;1"]
-                        .getService(Ci.nsISerializationHelper);
-
-        secInfo.QueryInterface(Ci.nsISerializable);
-        return helper.serializeToString(secInfo);
-      }
-    }
-
-    return null;
-  }
-};
 
 var ControllerCommands = {
   init() {
     addMessageListener("ControllerCommands:Do", this);
     addMessageListener("ControllerCommands:DoWithParams", this);
     this.init = null;
   },
 
--- a/toolkit/content/browser-content.js
+++ b/toolkit/content/browser-content.js
@@ -32,28 +32,46 @@ XPCOMUtils.defineLazyServiceGetter(this,
 var global = this;
 
 XPCOMUtils.defineLazyProxy(this, "PopupBlocking", () => {
   let tmp = {};
   ChromeUtils.import("resource://gre/modules/PopupBlocking.jsm", tmp);
   return new tmp.PopupBlocking(global);
 });
 
+XPCOMUtils.defineLazyProxy(this, "ShieldFrameListener", () => {
+  let tmp = {};
+  ChromeUtils.import("resource://normandy-content/ShieldFrameListener.jsm", tmp);
+  return new tmp.ShieldFrameListener(global);
+});
+
+XPCOMUtils.defineLazyProxy(this, "UITourListener", () => {
+  let tmp = {};
+  ChromeUtils.import("resource:///modules/ContentUITour.jsm", tmp);
+  return new tmp.UITourListener(global);
+});
+
 XPCOMUtils.defineLazyProxy(this, "SelectionSourceContent",
   "resource://gre/modules/SelectionSourceContent.jsm");
 
 XPCOMUtils.defineLazyProxy(this, "WebChannelContent",
   "resource://gre/modules/WebChannelContent.jsm");
 
 XPCOMUtils.defineLazyProxy(this, "DateTimePickerContent", () => {
   let tmp = {};
   ChromeUtils.import("resource://gre/modules/DateTimePickerContent.jsm", tmp);
   return new tmp.DateTimePickerContent(this);
 });
 
+XPCOMUtils.defineLazyProxy(this, "FindBarChild", () => {
+  let tmp = {};
+  ChromeUtils.import("resource://gre/modules/FindBarChild.jsm", tmp);
+  return new tmp.FindBarChild(this);
+}, {inQuickFind: false, inPassThrough: false});
+
 
 // Lazily load the finder code
 addMessageListener("Finder:Initialize", function() {
   let {RemoteFinderListener} = ChromeUtils.import("resource://gre/modules/RemoteFinder.jsm", {});
   new RemoteFinderListener(global);
 });
 
 var AutoScrollListener = {
@@ -112,183 +130,76 @@ function SwitchDocumentDirection(aWindow
   }
 }
 
 addMessageListener("SwitchDocumentDirection", () => {
   SwitchDocumentDirection(content.window);
 });
 
 var FindBar = {
-  /* Please keep in sync with toolkit/content/widgets/findbar.xml */
-  FIND_NORMAL: 0,
-  FIND_TYPEAHEAD: 1,
-  FIND_LINKS: 2,
-
-  _findMode: 0,
-
   /**
    * _findKey and _findModifiers are used to determine whether a keypress
    * is a user attempting to use the find shortcut, after which we'll
    * route keypresses to the parent until we know the findbar has focus
    * there. To do this, we need shortcut data from the parent.
    */
   _findKey: null,
-  _findModifiers: null,
 
   init() {
-    addMessageListener("Findbar:UpdateState", this);
-    Services.els.addSystemEventListener(global, "keypress", this, false);
-    Services.els.addSystemEventListener(global, "mouseup", this, false);
-    this._initShortcutData();
+    Services.els.addSystemEventListener(global, "keypress",
+                                        this.onKeypress.bind(this), false);
     this.init = null;
   },
 
-  receiveMessage(msg) {
-    switch (msg.name) {
-      case "Findbar:UpdateState":
-        this._findMode = msg.data.findMode;
-        this._quickFindTimeout = msg.data.hasQuickFindTimeout;
-        if (msg.data.isOpenAndFocused) {
-          this._keepPassingUntilToldOtherwise = false;
-        }
-        break;
-      case "Findbar:ShortcutData":
-        // Set us up to never need this again for the lifetime of this process,
-        // and remove the listener.
-        Services.cpmm.initialProcessData.findBarShortcutData = msg.data;
-        Services.cpmm.removeMessageListener("Findbar:ShortcutData", this);
-        this._initShortcutData(msg.data);
-        break;
-    }
-  },
-
-  handleEvent(event) {
-    switch (event.type) {
-      case "keypress":
-        this._onKeypress(event);
-        break;
-      case "mouseup":
-        this._onMouseup(event);
-        break;
-    }
-  },
-
-  /**
-   * Use initial process data for find key/modifier data if we have it.
-   * Otherwise, add a listener so we get the data when the parent process has
-   * it.
-   */
-  _initShortcutData(data = Services.cpmm.initialProcessData.findBarShortcutData) {
-    if (data) {
-      this._findKey = data.key;
-      this._findModifiers = data.modifiers;
-    } else {
-      Services.cpmm.addMessageListener("Findbar:ShortcutData", this);
-    }
-  },
-
   /**
    * Check whether this key event will start the findbar in the parent,
    * in which case we should pass any further key events to the parent to avoid
    * them being lost.
    * @param aEvent the key event to check.
    */
-  _eventMatchesFindShortcut(aEvent) {
-    let modifiers = this._findModifiers;
-    if (!modifiers) {
-      return false;
+  eventMatchesFindShortcut(aEvent) {
+    if (!this._findKey) {
+      this._findKey = Services.cpmm.sharedData.get("Findbar:Shortcut");
+      if (!this._findKey) {
+          return false;
+      }
     }
-    return aEvent.ctrlKey == modifiers.ctrlKey && aEvent.altKey == modifiers.altKey &&
-      aEvent.shiftKey == modifiers.shiftKey && aEvent.metaKey == modifiers.metaKey &&
-      aEvent.key == this._findKey;
+    for (let k in this._findKey) {
+      if (this._findKey[k] != aEvent[k]) {
+        return false;
+      }
+    }
+    return true;
   },
 
-  /**
-   * Returns whether FAYT can be used for the given event in
-   * the current content state.
-   */
-  _canAndShouldFastFind() {
-    let should = false;
-    let can = BrowserUtils.canFastFind(content);
-    if (can) {
-      // XXXgijs: why all these shenanigans? Why not use the event's target?
-      let focusedWindow = {};
-      let elt = Services.focus.getFocusedElementForWindow(content, true, focusedWindow);
-      let win = focusedWindow.value;
-      should = BrowserUtils.shouldFastFind(elt, win);
-    }
-    return { can, should };
-  },
-
-  _onKeypress(event) {
-    const FAYT_LINKS_KEY = "'";
-    const FAYT_TEXT_KEY = "/";
-    if (this._eventMatchesFindShortcut(event)) {
-      this._keepPassingUntilToldOtherwise = true;
-    }
-    // Useless keys:
-    if (event.ctrlKey || event.altKey || event.metaKey || event.defaultPrevented) {
-      return;
+  onKeypress(event) {
+    if (!FindBarChild.inPassThrough &&
+        this.eventMatchesFindShortcut(event)) {
+      return FindBarChild.start(event);
     }
 
-    // Check the focused element etc.
-    let fastFind = this._canAndShouldFastFind();
-
-    // Can we even use find in this page at all?
-    if (!fastFind.can) {
-      return;
+    if (event.ctrlKey || event.altKey || event.metaKey || event.defaultPrevented ||
+        !BrowserUtils.canFastFind(content)) {
+      return null;
     }
-    if (this._keepPassingUntilToldOtherwise) {
-      this._passKeyToParent(event);
-      return;
-    }
-    if (!fastFind.should) {
-      return;
+
+    if (FindBarChild.inPassThrough || FindBarChild.inQuickFind) {
+      return FindBarChild.onKeypress(event);
     }
 
-    let charCode = event.charCode;
-    // If the find bar is open and quick find is on, send the key to the parent.
-    if (this._findMode != this.FIND_NORMAL && this._quickFindTimeout) {
-      if (!charCode)
-        return;
-      this._passKeyToParent(event);
-    } else {
-      let key = charCode ? String.fromCharCode(charCode) : null;
-      let manualstartFAYT = (key == FAYT_LINKS_KEY || key == FAYT_TEXT_KEY) && RemoteFinder._manualFAYT;
-      let autostartFAYT = !manualstartFAYT && RemoteFinder._findAsYouType && key && key != " ";
-      if (manualstartFAYT || autostartFAYT) {
-        let mode = (key == FAYT_LINKS_KEY || (autostartFAYT && RemoteFinder._typeAheadLinksOnly)) ?
-          this.FIND_LINKS : this.FIND_TYPEAHEAD;
-        // Set _findMode immediately (without waiting for child->parent->child roundtrip)
-        // to ensure we pass any further keypresses, too.
-        this._findMode = mode;
-        this._passKeyToParent(event);
+    if (event.charCode && BrowserUtils.shouldFastFind(event.target)) {
+      let key = String.fromCharCode(event.charCode);
+      if ((key == "/" || key == "'") && RemoteFinder._manualFAYT) {
+        return FindBarChild.startQuickFind(event);
+      }
+      if (key != " " && RemoteFinder._findAsYouType) {
+        return FindBarChild.startQuickFind(event, true);
       }
     }
-  },
-
-  _passKeyToParent(event) {
-    event.preventDefault();
-    // These are the properties required to dispatch another 'real' event
-    // to the findbar in the parent in _dispatchKeypressEvent in findbar.xml .
-    // If you make changes here, verify that that method can still do its job.
-    const kRequiredProps = [
-      "type", "bubbles", "cancelable", "ctrlKey", "altKey", "shiftKey",
-      "metaKey", "keyCode", "charCode",
-    ];
-    let fakeEvent = {};
-    for (let prop of kRequiredProps) {
-      fakeEvent[prop] = event[prop];
-    }
-    sendAsyncMessage("Findbar:Keypress", fakeEvent);
-  },
-
-  _onMouseup(event) {
-    if (this._findMode != this.FIND_NORMAL)
-      sendAsyncMessage("Findbar:Mouseup");
+    return null;
   },
 };
 FindBar.init();
 
 addEventListener("WebChannelMessageToChrome", WebChannelContent,
                  true, true);
 addMessageListener("WebChannelMessageToContent", WebChannelContent);
 
@@ -529,8 +440,12 @@ let ExtFind = {
       case "ext-Finder:clearHighlighting":
         this._findContent.highlighter.highlight(false);
         break;
     }
   },
 };
 
 ExtFind.init();
+
+addEventListener("ShieldPageEvent", ShieldFrameListener, false, true);
+
+addEventListener("mozUITour", UITourListener, false, true);
--- a/toolkit/content/widgets/findbar.xml
+++ b/toolkit/content/widgets/findbar.xml
@@ -215,17 +215,17 @@
     </xul:hbox>
     <xul:toolbarbutton anonid="find-closebutton"
                        class="findbar-closebutton close-icon"
                        tooltiptext="&findCloseButton.tooltip;"
                        oncommand="close();"/>
     </content>
 
     <implementation>
-      <!-- Please keep in sync with toolkit/content/browser-content.js -->
+      <!-- Please keep in sync with toolkit/modules/FindBarChild.jsm -->
       <field name="FIND_NORMAL">0</field>
       <field name="FIND_TYPEAHEAD">1</field>
       <field name="FIND_LINKS">2</field>
 
       <field name="__findMode">0</field>
       <property name="_findMode" onget="return this.__findMode;"
                 onset="this.__findMode = val; this._updateBrowserWithState(); return val;"/>
 
--- a/toolkit/modules/BrowserUtils.jsm
+++ b/toolkit/modules/BrowserUtils.jsm
@@ -285,22 +285,20 @@ var BrowserUtils = {
            mimeType == "mozilla.application/cached-xul";
   },
 
   /**
    * Return true if we should FAYT for this node + window (could be CPOW):
    *
    * @param elt
    *        The element that is focused
-   * @param win
-   *        The window that is focused
-   *
    */
-  shouldFastFind(elt, win) {
+  shouldFastFind(elt) {
     if (elt) {
+      let win = elt.ownerGlobal;
       if (elt instanceof win.HTMLInputElement && elt.mozIsTextField(false))
         return false;
 
       if (elt.isContentEditable || win.document.designMode == "on")
         return false;
 
       if (elt instanceof win.HTMLTextAreaElement ||
           elt instanceof win.HTMLSelectElement ||
copy from toolkit/content/browser-content.js
copy to toolkit/modules/FindBarChild.jsm
--- a/toolkit/content/browser-content.js
+++ b/toolkit/modules/FindBarChild.jsm
@@ -1,536 +1,99 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-/* eslint-env mozilla/frame-script */
-/* eslint no-unused-vars: ["error", {args: "none"}] */
-/* global sendAsyncMessage */
-
-ChromeUtils.import("resource://gre/modules/Services.jsm");
-ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+// vim: set ts=2 sw=2 sts=2 tw=80:
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+"use strict";
 
-ChromeUtils.defineModuleGetter(this, "AutoCompletePopup",
-  "resource://gre/modules/AutoCompletePopupContent.jsm");
-ChromeUtils.defineModuleGetter(this, "AutoScrollController",
-  "resource://gre/modules/AutoScrollController.jsm");
-ChromeUtils.defineModuleGetter(this, "BrowserUtils",
-  "resource://gre/modules/BrowserUtils.jsm");
-ChromeUtils.defineModuleGetter(this, "SelectContentHelper",
-  "resource://gre/modules/SelectContentHelper.jsm");
-ChromeUtils.defineModuleGetter(this, "FindContent",
-  "resource://gre/modules/FindContent.jsm");
-ChromeUtils.defineModuleGetter(this, "PrintingContent",
-  "resource://gre/modules/PrintingContent.jsm");
+var EXPORTED_SYMBOLS = ["FindBarChild"];
+
 ChromeUtils.defineModuleGetter(this, "RemoteFinder",
-  "resource://gre/modules/RemoteFinder.jsm");
-
-XPCOMUtils.defineLazyServiceGetter(this, "formFill",
-                                   "@mozilla.org/satchel/form-fill-controller;1",
-                                   "nsIFormFillController");
-
-var global = this;
+                               "resource://gre/modules/RemoteFinder.jsm");
+ChromeUtils.defineModuleGetter(this, "Services",
+                               "resource://gre/modules/Services.jsm");
 
-XPCOMUtils.defineLazyProxy(this, "PopupBlocking", () => {
-  let tmp = {};
-  ChromeUtils.import("resource://gre/modules/PopupBlocking.jsm", tmp);
-  return new tmp.PopupBlocking(global);
-});
-
-XPCOMUtils.defineLazyProxy(this, "SelectionSourceContent",
-  "resource://gre/modules/SelectionSourceContent.jsm");
-
-XPCOMUtils.defineLazyProxy(this, "WebChannelContent",
-  "resource://gre/modules/WebChannelContent.jsm");
-
-XPCOMUtils.defineLazyProxy(this, "DateTimePickerContent", () => {
-  let tmp = {};
-  ChromeUtils.import("resource://gre/modules/DateTimePickerContent.jsm", tmp);
-  return new tmp.DateTimePickerContent(this);
-});
+/* Please keep in sync with toolkit/this.mm.content/widgets/findbar.xml */
+const FIND_NORMAL = 0;
+const FIND_TYPEAHEAD = 1;
+const FIND_LINKS = 2;
 
-
-// Lazily load the finder code
-addMessageListener("Finder:Initialize", function() {
-  let {RemoteFinderListener} = ChromeUtils.import("resource://gre/modules/RemoteFinder.jsm", {});
-  new RemoteFinderListener(global);
-});
+class FindBarChild {
+  constructor(mm) {
+    this.mm = mm;
 
-var AutoScrollListener = {
-  handleEvent(event) {
-    if (event.isTrusted &
-        !event.defaultPrevented &&
-        event.button == 1) {
-      if (!this._controller) {
-        this._controller = new AutoScrollController(global);
-      }
-      this._controller.handleEvent(event);
-    }
+    this.findMode = 0;
+    this.inQuickFind = false;
+
+    this.mm.addMessageListener("Findbar:UpdateState", this);
+
+    Services.els.addSystemEventListener(this.mm, "mouseup", this, false);
   }
-};
-Services.els.addSystemEventListener(global, "mousedown", AutoScrollListener, true);
 
-addEventListener("MozOpenDateTimePicker", DateTimePickerContent);
-
-addEventListener("DOMPopupBlocked", PopupBlocking, true);
-
-var Printing = {
-  MESSAGES: [
-    "Printing:Preview:Enter",
-    "Printing:Preview:Exit",
-    "Printing:Preview:Navigate",
-    "Printing:Preview:ParseDocument",
-    "Printing:Print",
-  ],
-
-  init() {
-    this.MESSAGES.forEach(msgName => addMessageListener(msgName, this));
-    addEventListener("PrintingError", this, true);
-    addEventListener("printPreviewUpdate", this, true);
-    this.init = null;
-  },
-
-  handleEvent(event) {
-    return PrintingContent.handleEvent(global, event);
-  },
-
-  receiveMessage(message) {
-    return PrintingContent.receiveMessage(global, message);
-  },
-};
-Printing.init();
+  start(event) {
+    this.inPassThrough = true;
+  }
 
-function SwitchDocumentDirection(aWindow) {
- // document.dir can also be "auto", in which case it won't change
-  if (aWindow.document.dir == "ltr" || aWindow.document.dir == "") {
-    aWindow.document.dir = "rtl";
-  } else if (aWindow.document.dir == "rtl") {
-    aWindow.document.dir = "ltr";
-  }
-  for (let run = 0; run < aWindow.frames.length; run++) {
-    SwitchDocumentDirection(aWindow.frames[run]);
-  }
-}
-
-addMessageListener("SwitchDocumentDirection", () => {
-  SwitchDocumentDirection(content.window);
-});
+  startQuickFind(event, autostart = false) {
+    let mode = FIND_TYPEAHEAD;
+    if (event.charCode == "'".charAt(0) ||
+        autostart && RemoteFinder._typeAheadLinksOnly) {
+      mode = FIND_LINKS;
+    }
 
-var FindBar = {
-  /* Please keep in sync with toolkit/content/widgets/findbar.xml */
-  FIND_NORMAL: 0,
-  FIND_TYPEAHEAD: 1,
-  FIND_LINKS: 2,
-
-  _findMode: 0,
-
-  /**
-   * _findKey and _findModifiers are used to determine whether a keypress
-   * is a user attempting to use the find shortcut, after which we'll
-   * route keypresses to the parent until we know the findbar has focus
-   * there. To do this, we need shortcut data from the parent.
-   */
-  _findKey: null,
-  _findModifiers: null,
-
-  init() {
-    addMessageListener("Findbar:UpdateState", this);
-    Services.els.addSystemEventListener(global, "keypress", this, false);
-    Services.els.addSystemEventListener(global, "mouseup", this, false);
-    this._initShortcutData();
-    this.init = null;
-  },
+    // Set findMode immediately (without waiting for child->parent->child roundtrip)
+    // to ensure we pass any further keypresses, too.
+    this.findMode = mode;
+    this.passKeyToParent(event);
+  }
 
   receiveMessage(msg) {
     switch (msg.name) {
       case "Findbar:UpdateState":
-        this._findMode = msg.data.findMode;
-        this._quickFindTimeout = msg.data.hasQuickFindTimeout;
+        this.findMode = msg.data.findMode;
+        this.inQuickFind = msg.data.hasQuickFindTimeout;
         if (msg.data.isOpenAndFocused) {
-          this._keepPassingUntilToldOtherwise = false;
+          this.inPassThrough = false;
         }
         break;
-      case "Findbar:ShortcutData":
-        // Set us up to never need this again for the lifetime of this process,
-        // and remove the listener.
-        Services.cpmm.initialProcessData.findBarShortcutData = msg.data;
-        Services.cpmm.removeMessageListener("Findbar:ShortcutData", this);
-        this._initShortcutData(msg.data);
-        break;
     }
-  },
+  }
 
   handleEvent(event) {
     switch (event.type) {
       case "keypress":
-        this._onKeypress(event);
+        this.onKeypress(event);
         break;
       case "mouseup":
-        this._onMouseup(event);
+        this.onMouseup(event);
         break;
     }
-  },
-
-  /**
-   * Use initial process data for find key/modifier data if we have it.
-   * Otherwise, add a listener so we get the data when the parent process has
-   * it.
-   */
-  _initShortcutData(data = Services.cpmm.initialProcessData.findBarShortcutData) {
-    if (data) {
-      this._findKey = data.key;
-      this._findModifiers = data.modifiers;
-    } else {
-      Services.cpmm.addMessageListener("Findbar:ShortcutData", this);
-    }
-  },
-
-  /**
-   * Check whether this key event will start the findbar in the parent,
-   * in which case we should pass any further key events to the parent to avoid
-   * them being lost.
-   * @param aEvent the key event to check.
-   */
-  _eventMatchesFindShortcut(aEvent) {
-    let modifiers = this._findModifiers;
-    if (!modifiers) {
-      return false;
-    }
-    return aEvent.ctrlKey == modifiers.ctrlKey && aEvent.altKey == modifiers.altKey &&
-      aEvent.shiftKey == modifiers.shiftKey && aEvent.metaKey == modifiers.metaKey &&
-      aEvent.key == this._findKey;
-  },
-
-  /**
-   * Returns whether FAYT can be used for the given event in
-   * the current content state.
-   */
-  _canAndShouldFastFind() {
-    let should = false;
-    let can = BrowserUtils.canFastFind(content);
-    if (can) {
-      // XXXgijs: why all these shenanigans? Why not use the event's target?
-      let focusedWindow = {};
-      let elt = Services.focus.getFocusedElementForWindow(content, true, focusedWindow);
-      let win = focusedWindow.value;
-      should = BrowserUtils.shouldFastFind(elt, win);
-    }
-    return { can, should };
-  },
+  }
 
-  _onKeypress(event) {
-    const FAYT_LINKS_KEY = "'";
-    const FAYT_TEXT_KEY = "/";
-    if (this._eventMatchesFindShortcut(event)) {
-      this._keepPassingUntilToldOtherwise = true;
-    }
-    // Useless keys:
-    if (event.ctrlKey || event.altKey || event.metaKey || event.defaultPrevented) {
-      return;
-    }
-
-    // Check the focused element etc.
-    let fastFind = this._canAndShouldFastFind();
-
-    // Can we even use find in this page at all?
-    if (!fastFind.can) {
-      return;
-    }
-    if (this._keepPassingUntilToldOtherwise) {
-      this._passKeyToParent(event);
-      return;
+  onKeypress(event) {
+    if (this.inPassThrough) {
+      this.passKeyToParent(event);
+    } else if (this.findMode != FIND_NORMAL && this.inQuickFind && event.charCode) {
+      this.passKeyToParent(event);
     }
-    if (!fastFind.should) {
-      return;
-    }
+  }
 
-    let charCode = event.charCode;
-    // If the find bar is open and quick find is on, send the key to the parent.
-    if (this._findMode != this.FIND_NORMAL && this._quickFindTimeout) {
-      if (!charCode)
-        return;
-      this._passKeyToParent(event);
-    } else {
-      let key = charCode ? String.fromCharCode(charCode) : null;
-      let manualstartFAYT = (key == FAYT_LINKS_KEY || key == FAYT_TEXT_KEY) && RemoteFinder._manualFAYT;
-      let autostartFAYT = !manualstartFAYT && RemoteFinder._findAsYouType && key && key != " ";
-      if (manualstartFAYT || autostartFAYT) {
-        let mode = (key == FAYT_LINKS_KEY || (autostartFAYT && RemoteFinder._typeAheadLinksOnly)) ?
-          this.FIND_LINKS : this.FIND_TYPEAHEAD;
-        // Set _findMode immediately (without waiting for child->parent->child roundtrip)
-        // to ensure we pass any further keypresses, too.
-        this._findMode = mode;
-        this._passKeyToParent(event);
-      }
-    }
-  },
-
-  _passKeyToParent(event) {
+  passKeyToParent(event) {
     event.preventDefault();
     // These are the properties required to dispatch another 'real' event
     // to the findbar in the parent in _dispatchKeypressEvent in findbar.xml .
     // If you make changes here, verify that that method can still do its job.
     const kRequiredProps = [
       "type", "bubbles", "cancelable", "ctrlKey", "altKey", "shiftKey",
       "metaKey", "keyCode", "charCode",
     ];
     let fakeEvent = {};
     for (let prop of kRequiredProps) {
       fakeEvent[prop] = event[prop];
     }
-    sendAsyncMessage("Findbar:Keypress", fakeEvent);
-  },
-
-  _onMouseup(event) {
-    if (this._findMode != this.FIND_NORMAL)
-      sendAsyncMessage("Findbar:Mouseup");
-  },
-};
-FindBar.init();
-
-addEventListener("WebChannelMessageToChrome", WebChannelContent,
-                 true, true);
-addMessageListener("WebChannelMessageToContent", WebChannelContent);
-
-var AudioPlaybackListener = {
-  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]),
-
-  init() {
-    Services.obs.addObserver(this, "audio-playback");
-
-    addMessageListener("AudioPlayback", this);
-    addEventListener("unload", () => {
-      AudioPlaybackListener.uninit();
-    });
-    this.init = null;
-  },
-
-  uninit() {
-    Services.obs.removeObserver(this, "audio-playback");
-
-    removeMessageListener("AudioPlayback", this);
-  },
-
-  handleMediaControlMessage(msg) {
-    let utils = global.content.windowUtils;
-    let suspendTypes = Ci.nsISuspendedTypes;
-    switch (msg) {
-      case "mute":
-        utils.audioMuted = true;
-        break;
-      case "unmute":
-        utils.audioMuted = false;
-        break;
-      case "lostAudioFocus":
-        utils.mediaSuspend = suspendTypes.SUSPENDED_PAUSE_DISPOSABLE;
-        break;
-      case "lostAudioFocusTransiently":
-        utils.mediaSuspend = suspendTypes.SUSPENDED_PAUSE;
-        break;
-      case "gainAudioFocus":
-        utils.mediaSuspend = suspendTypes.NONE_SUSPENDED;
-        break;
-      case "mediaControlPaused":
-        utils.mediaSuspend = suspendTypes.SUSPENDED_PAUSE_DISPOSABLE;
-        break;
-      case "mediaControlStopped":
-        utils.mediaSuspend = suspendTypes.SUSPENDED_STOP_DISPOSABLE;
-        break;
-      case "resumeMedia":
-        // User has clicked the tab audio indicator to play a delayed
-        // media. That's clear user intent to play, so gesture activate
-        // the content document tree so that the block-autoplay logic
-        // allows the media to autoplay.
-        content.document.notifyUserGestureActivation();
-        utils.mediaSuspend = suspendTypes.NONE_SUSPENDED;
-        break;
-      default:
-        dump("Error : wrong media control msg!\n");
-        break;
-    }
-  },
-
-  observe(subject, topic, data) {
-    if (topic === "audio-playback") {
-      if (subject && subject.top == global.content) {
-        let name = "AudioPlayback:";
-        if (data === "activeMediaBlockStart") {
-          name += "ActiveMediaBlockStart";
-        } else if (data === "activeMediaBlockStop") {
-          name += "ActiveMediaBlockStop";
-        } else {
-          name += (data === "active") ? "Start" : "Stop";
-        }
-        sendAsyncMessage(name);
-      }
-    }
-  },
-
-  receiveMessage(msg) {
-    if (msg.name == "AudioPlayback") {
-      this.handleMediaControlMessage(msg.data.type);
-    }
-  },
-};
-AudioPlaybackListener.init();
-
-var UnselectedTabHoverObserver = {
-  init() {
-    addMessageListener("Browser:UnselectedTabHover", this);
-    addEventListener("UnselectedTabHover:Enable", this);
-    addEventListener("UnselectedTabHover:Disable", this);
-    this.init = null;
-  },
-  receiveMessage(message) {
-    Services.obs.notifyObservers(content.window, "unselected-tab-hover",
-                                 message.data.hovered);
-  },
-  handleEvent(event) {
-    sendAsyncMessage("UnselectedTabHover:Toggle",
-                     { enable: event.type == "UnselectedTabHover:Enable" });
-  }
-};
-UnselectedTabHoverObserver.init();
-
-
-var AudibleAutoplayObserver = {
-  init() {
-    addEventListener("AudibleAutoplayMediaOccurred", this);
-  },
-  handleEvent(event) {
-    sendAsyncMessage("AudibleAutoplayMediaOccurred");
-  }
-};
-AudibleAutoplayObserver.init();
-
-addMessageListener("Browser:PurgeSessionHistory", function BrowserPurgeHistory() {
-  let sessionHistory = docShell.QueryInterface(Ci.nsIWebNavigation).sessionHistory;
-  if (!sessionHistory) {
-    return;
+    this.mm.sendAsyncMessage("Findbar:Keypress", fakeEvent);
   }
 
-  // place the entry at current index at the end of the history list, so it won't get removed
-  if (sessionHistory.index < sessionHistory.count - 1) {
-    let legacy = sessionHistory.legacySHistory;
-    legacy.QueryInterface(Ci.nsISHistoryInternal);
-    let indexEntry = legacy.getEntryAtIndex(sessionHistory.index, false);
-    indexEntry.QueryInterface(Ci.nsISHEntry);
-    legacy.addEntry(indexEntry, true);
-  }
-
-  let purge = sessionHistory.count;
-  if (global.content.location.href != "about:blank") {
-    --purge; // Don't remove the page the user's staring at from shistory
-  }
-
-  if (purge > 0) {
-    sessionHistory.legacySHistory.PurgeHistory(purge);
+  onMouseup(event) {
+    if (this.findMode != FIND_NORMAL)
+      this.mm.sendAsyncMessage("Findbar:Mouseup");
   }
-});
-
-addMessageListener("ViewSource:GetSelection", SelectionSourceContent);
-
-addEventListener("MozApplicationManifest", function(e) {
-  let doc = e.target;
-  let info = {
-    uri: doc.documentURI,
-    characterSet: doc.characterSet,
-    manifest: doc.documentElement.getAttribute("manifest"),
-    principal: doc.nodePrincipal,
-  };
-  sendAsyncMessage("MozApplicationManifest", info);
-}, false);
-
-let AutoComplete = {
-  _connected: false,
-
-  init() {
-    addEventListener("unload", this, {once: true});
-    addEventListener("DOMContentLoaded", this, {once: true});
-    // WebExtension browserAction is preloaded and does not receive DCL, wait
-    // on pageshow so we can hookup the formfill controller.
-    addEventListener("pageshow", this, {capture: true, once: true});
-
-    XPCOMUtils.defineLazyProxy(this, "popup", () => new AutoCompletePopup(global),
-                               {QueryInterface: null});
-    this.init = null;
-  },
-
-  handleEvent(event) {
-    switch (event.type) {
-    case "DOMContentLoaded":
-    case "pageshow":
-      // We need to wait for a content viewer to be available
-      // before we can attach our AutoCompletePopup handler,
-      // since nsFormFillController assumes one will exist
-      // when we call attachToBrowser.
-      if (!this._connected) {
-        formFill.attachToBrowser(docShell, this.popup);
-        this._connected = true;
-      }
-      break;
-
-    case "unload":
-      if (this._connected) {
-        formFill.detachFromBrowser(docShell);
-        this._connected = false;
-      }
-      break;
-    }
-  },
-};
-
-AutoComplete.init();
-
-addEventListener("mozshowdropdown", event => {
-  if (!event.isTrusted)
-    return;
-
-  if (!SelectContentHelper.open) {
-    new SelectContentHelper(event.target, {isOpenedViaTouch: false}, this);
-  }
-});
-
-addEventListener("mozshowdropdown-sourcetouch", event => {
-  if (!event.isTrusted)
-    return;
-
-  if (!SelectContentHelper.open) {
-    new SelectContentHelper(event.target, {isOpenedViaTouch: true}, this);
-  }
-});
-
-let ExtFind = {
-  init() {
-    addMessageListener("ext-Finder:CollectResults", this);
-    addMessageListener("ext-Finder:HighlightResults", this);
-    addMessageListener("ext-Finder:clearHighlighting", this);
-    this.init = null;
-  },
-
-  _findContent: null,
-
-  async receiveMessage(message) {
-    if (!this._findContent) {
-      this._findContent = new FindContent(docShell);
-    }
-
-    let data;
-    switch (message.name) {
-      case "ext-Finder:CollectResults":
-        this.finderInited = true;
-        data = await this._findContent.findRanges(message.data);
-        sendAsyncMessage("ext-Finder:CollectResultsFinished", data);
-        break;
-      case "ext-Finder:HighlightResults":
-        data = this._findContent.highlightResults(message.data);
-        sendAsyncMessage("ext-Finder:HighlightResultsFinished", data);
-        break;
-      case "ext-Finder:clearHighlighting":
-        this._findContent.highlighter.highlight(false);
-        break;
-    }
-  },
-};
-
-ExtFind.init();
+}
copy from toolkit/content/browser-child.js
copy to toolkit/modules/WebNavigationChild.jsm
--- a/toolkit/content/browser-child.js
+++ b/toolkit/modules/WebNavigationChild.jsm
@@ -1,276 +1,48 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-/* eslint-env mozilla/frame-script */
+"use strict";
 
-ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
-ChromeUtils.import("resource://gre/modules/BrowserUtils.jsm");
+var EXPORTED_SYMBOLS = ["WebNavigationChild"];
+
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-ChromeUtils.import("resource://gre/modules/Timer.jsm");
-
-ChromeUtils.defineModuleGetter(this, "PageThumbUtils",
-  "resource://gre/modules/PageThumbUtils.jsm");
-
-ChromeUtils.defineModuleGetter(this, "Utils",
-  "resource://gre/modules/sessionstore/Utils.jsm");
-
-if (AppConstants.MOZ_CRASHREPORTER) {
-  XPCOMUtils.defineLazyServiceGetter(this, "CrashReporter",
-                                     "@mozilla.org/xre/app-info;1",
-                                     "nsICrashReporter");
-}
-
-var WebProgressListener = {
-  init() {
-    this._filter = Cc["@mozilla.org/appshell/component/browser-status-filter;1"]
-                     .createInstance(Ci.nsIWebProgress);
-    this._filter.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_ALL);
-    this._filter.target = tabEventTarget;
-
-    let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
-                              .getInterface(Ci.nsIWebProgress);
-    webProgress.addProgressListener(this._filter, Ci.nsIWebProgress.NOTIFY_ALL);
-    this.init = null;
-  },
-
-  uninit() {
-    let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
-                              .getInterface(Ci.nsIWebProgress);
-    webProgress.removeProgressListener(this._filter);
-
-    this._filter.removeProgressListener(this);
-    this._filter = null;
-  },
-
-  _requestSpec(aRequest, aPropertyName) {
-    if (!aRequest || !(aRequest instanceof Ci.nsIChannel))
-      return null;
-    return aRequest.QueryInterface(Ci.nsIChannel)[aPropertyName].spec;
-  },
-
-  _setupJSON: function setupJSON(aWebProgress, aRequest, aStateFlags) {
-    // Avoid accessing content.document when being called from onStateChange
-    // unless if we are in STATE_STOP, because otherwise the getter will
-    // instantiate an about:blank document for us.
-    let contentDocument = null;
-    if (aStateFlags) {
-      // We're being called from onStateChange
-      if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
-        contentDocument = content.document;
-      }
-    } else {
-      contentDocument = content.document;
-    }
 
-    let innerWindowID = null;
-    if (aWebProgress) {
-      let domWindowID = null;
-      try {
-        domWindowID = aWebProgress.DOMWindowID;
-        innerWindowID = aWebProgress.innerDOMWindowID;
-      } catch (e) {
-        // The DOM Window ID getters above may throw if the inner or outer
-        // windows aren't created yet or are destroyed at the time we're making
-        // this call but that isn't fatal so ignore the exceptions here.
-      }
-
-      aWebProgress = {
-        isTopLevel: aWebProgress.isTopLevel,
-        isLoadingDocument: aWebProgress.isLoadingDocument,
-        loadType: aWebProgress.loadType,
-        DOMWindowID: domWindowID
-      };
-    }
-
-    return {
-      webProgress: aWebProgress || null,
-      requestURI: this._requestSpec(aRequest, "URI"),
-      originalRequestURI: this._requestSpec(aRequest, "originalURI"),
-      documentContentType: contentDocument ? contentDocument.contentType : null,
-      innerWindowID,
-    };
-  },
+ChromeUtils.defineModuleGetter(this, "AppConstants",
+                               "resource://gre/modules/AppConstants.jsm");
+ChromeUtils.defineModuleGetter(this, "Utils",
+                               "resource://gre/modules/sessionstore/Utils.jsm");
 
-  _setupObjects: function setupObjects(aWebProgress, aRequest) {
-    let domWindow;
-    try {
-      domWindow = aWebProgress && aWebProgress.DOMWindow;
-    } catch (e) {
-      // If nsDocShell::Destroy has already been called, then we'll
-      // get NS_NOINTERFACE when trying to get the DOM window. Ignore
-      // that here.
-      domWindow = null;
-    }
-
-    return {
-      contentWindow: content,
-      contentDocument: content.document,
-      // DOMWindow is not necessarily the content-window with subframes.
-      DOMWindow: domWindow,
-      webProgress: aWebProgress,
-      request: aRequest,
-    };
-  },
-
-  _send(name, data, objects) {
-    sendAsyncMessage(name, data, objects);
-  },
-
-  onStateChange: function onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
-    let json = this._setupJSON(aWebProgress, aRequest, aStateFlags);
-    let objects = this._setupObjects(aWebProgress, aRequest);
-
-    json.stateFlags = aStateFlags;
-    json.status = aStatus;
+XPCOMUtils.defineLazyServiceGetter(this, "CrashReporter",
+                                   "@mozilla.org/xre/app-info;1",
+                                   "nsICrashReporter");
 
-    // It's possible that this state change was triggered by
-    // loading an internal error page, for which the parent
-    // will want to know some details, so we'll update it with
-    // the documentURI.
-    if (aWebProgress && aWebProgress.isTopLevel) {
-      json.documentURI = content.document.documentURIObject.spec;
-      json.charset = content.document.characterSet;
-      json.mayEnableCharacterEncodingMenu = docShell.mayEnableCharacterEncodingMenu;
-      json.inLoadURI = WebNavigation.inLoadURI;
-    }
-
-    this._send("Content:StateChange", json, objects);
-  },
-
-  // Note: Because the nsBrowserStatusFilter timeout runnable is
-  // SystemGroup-labeled, this method should not modify content DOM or
-  // run content JS.
-  onProgressChange: function onProgressChange(aWebProgress, aRequest, aCurSelf, aMaxSelf, aCurTotal, aMaxTotal) {
-    let json = this._setupJSON(aWebProgress, aRequest);
-    let objects = this._setupObjects(aWebProgress, aRequest);
-
-    json.curSelf = aCurSelf;
-    json.maxSelf = aMaxSelf;
-    json.curTotal = aCurTotal;
-    json.maxTotal = aMaxTotal;
-
-    this._send("Content:ProgressChange", json, objects);
-  },
-
-  onProgressChange64: function onProgressChange(aWebProgress, aRequest, aCurSelf, aMaxSelf, aCurTotal, aMaxTotal) {
-    this.onProgressChange(aWebProgress, aRequest, aCurSelf, aMaxSelf, aCurTotal, aMaxTotal);
-  },
-
-  onLocationChange: function onLocationChange(aWebProgress, aRequest, aLocationURI, aFlags) {
-    let json = this._setupJSON(aWebProgress, aRequest);
-    let objects = this._setupObjects(aWebProgress, aRequest);
-
-    json.location = aLocationURI ? aLocationURI.spec : "";
-    json.flags = aFlags;
-
-    // These properties can change even for a sub-frame navigation.
-    let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
-    json.canGoBack = webNav.canGoBack;
-    json.canGoForward = webNav.canGoForward;
-
-    if (aWebProgress && aWebProgress.isTopLevel) {
-      json.documentURI = content.document.documentURIObject.spec;
-      json.title = content.document.title;
-      json.charset = content.document.characterSet;
-      json.mayEnableCharacterEncodingMenu = docShell.mayEnableCharacterEncodingMenu;
-      json.principal = content.document.nodePrincipal;
-      json.synthetic = content.document.mozSyntheticDocument;
-      json.inLoadURI = WebNavigation.inLoadURI;
-      json.requestContextID = content.document.documentLoadGroup
-        ? content.document.documentLoadGroup.requestContextID
-        : null;
+class WebNavigationChild {
+  constructor(mm) {
+    this.mm = mm;
 
-      if (AppConstants.MOZ_CRASHREPORTER && CrashReporter.enabled) {
-        let uri = aLocationURI;
-        try {
-          // If the current URI contains a username/password, remove it.
-          uri = uri.mutate()
-                   .setUserPass("")
-                   .finalize();
-        } catch (ex) { /* Ignore failures on about: URIs. */ }
-        CrashReporter.annotateCrashReport("URL", uri.spec);
-      }
-    }
-
-    this._send("Content:LocationChange", json, objects);
-  },
-
-  // Note: Because the nsBrowserStatusFilter timeout runnable is
-  // SystemGroup-labeled, this method should not modify content DOM or
-  // run content JS.
-  onStatusChange: function onStatusChange(aWebProgress, aRequest, aStatus, aMessage) {
-    let json = this._setupJSON(aWebProgress, aRequest);
-    let objects = this._setupObjects(aWebProgress, aRequest);
-
-    json.status = aStatus;
-    json.message = aMessage;
-
-    this._send("Content:StatusChange", json, objects);
-  },
-
-  onSecurityChange: function onSecurityChange(aWebProgress, aRequest, aState) {
-    let json = this._setupJSON(aWebProgress, aRequest);
-    let objects = this._setupObjects(aWebProgress, aRequest);
-
-    json.state = aState;
-    json.secInfo = SecurityUI.getSecInfoAsString();
+    this.mm.addMessageListener("WebNavigation:GoBack", this);
+    this.mm.addMessageListener("WebNavigation:GoForward", this);
+    this.mm.addMessageListener("WebNavigation:GotoIndex", this);
+    this.mm.addMessageListener("WebNavigation:LoadURI", this);
+    this.mm.addMessageListener("WebNavigation:SetOriginAttributes", this);
+    this.mm.addMessageListener("WebNavigation:Reload", this);
+    this.mm.addMessageListener("WebNavigation:Stop", this);
+    // This message is used for measuring this.mm.content process startup performance.
+    this.mm.sendAsyncMessage("Content:BrowserChildReady", { time: Services.telemetry.msSystemNow() });
 
-    json.matchedList = null;
-    if (aRequest && aRequest instanceof Ci.nsIClassifiedChannel) {
-      json.matchedList = aRequest.matchedList;
-    }
-
-    this._send("Content:SecurityChange", json, objects);
-  },
-
-  onRefreshAttempted: function onRefreshAttempted(aWebProgress, aURI, aDelay, aSameURI) {
-    return true;
-  },
-
-  sendLoadCallResult() {
-    sendAsyncMessage("Content:LoadURIResult");
-  },
-
-  QueryInterface: ChromeUtils.generateQI(["nsIWebProgressListener",
-                                          "nsIWebProgressListener2",
-                                          "nsISupportsWeakReference"]),
-};
-
-WebProgressListener.init();
-addEventListener("unload", () => {
-  WebProgressListener.uninit();
-});
-
-var WebNavigation =  {
-  init() {
-    addMessageListener("WebNavigation:GoBack", this);
-    addMessageListener("WebNavigation:GoForward", this);
-    addMessageListener("WebNavigation:GotoIndex", this);
-    addMessageListener("WebNavigation:LoadURI", this);
-    addMessageListener("WebNavigation:SetOriginAttributes", this);
-    addMessageListener("WebNavigation:Reload", this);
-    addMessageListener("WebNavigation:Stop", this);
-    // This message is used for measuring content process startup performance.
-    sendAsyncMessage("Content:BrowserChildReady", { time: Services.telemetry.msSystemNow() });
-    this.init = null;
-  },
+    this.inLoadURI = false;
+  }
 
   get webNavigation() {
-    return docShell.QueryInterface(Ci.nsIWebNavigation);
-  },
-
-  _inLoadURI: false,
-
-  get inLoadURI() {
-    return this._inLoadURI;
-  },
+    return this.mm.docShell.QueryInterface(Ci.nsIWebNavigation);
+  }
 
   receiveMessage(message) {
     switch (message.name) {
       case "WebNavigation:GoBack":
         this.goBack();
         break;
       case "WebNavigation:GoForward":
         this.goForward();
@@ -293,43 +65,43 @@ var WebNavigation =  {
         break;
       case "WebNavigation:Reload":
         this.reload(message.data.flags);
         break;
       case "WebNavigation:Stop":
         this.stop(message.data.flags);
         break;
     }
-  },
+  }
 
   _wrapURIChangeCall(fn) {
-    this._inLoadURI = true;
+    this.inLoadURI = true;
     try {
       fn();
     } finally {
-      this._inLoadURI = false;
-      WebProgressListener.sendLoadCallResult();
+      this.inLoadURI = false;
+      this.mm.WebProgress.sendLoadCallResult();
     }
-  },
+  }
 
   goBack() {
     if (this.webNavigation.canGoBack) {
       this._wrapURIChangeCall(() => this.webNavigation.goBack());
     }
-  },
+  }
 
   goForward() {
     if (this.webNavigation.canGoForward) {
       this._wrapURIChangeCall(() => this.webNavigation.goForward());
     }
-  },
+  }
 
   gotoIndex(index) {
     this._wrapURIChangeCall(() => this.webNavigation.gotoIndex(index));
-  },
+  }
 
   loadURI(uri, flags, referrer, referrerPolicy, postData, headers, baseURI, triggeringPrincipal) {
     if (AppConstants.MOZ_CRASHREPORTER && CrashReporter.enabled) {
       let annotation = uri;
       try {
         let url = Services.io.newURI(uri);
         // If the current URI contains a username/password, remove it.
         url = url.mutate()
@@ -349,272 +121,24 @@ var WebNavigation =  {
     if (baseURI)
       baseURI = Services.io.newURI(baseURI);
     if (triggeringPrincipal)
       triggeringPrincipal = Utils.deserializePrincipal(triggeringPrincipal);
     this._wrapURIChangeCall(() => {
       return this.webNavigation.loadURIWithOptions(uri, flags, referrer, referrerPolicy,
                                                    postData, headers, baseURI, triggeringPrincipal);
     });
-  },
+  }
 
   setOriginAttributes(originAttributes) {
     if (originAttributes) {
       this.webNavigation.setOriginAttributesBeforeLoading(originAttributes);
     }
-  },
+  }
 
   reload(flags) {
     this.webNavigation.reload(flags);
-  },
+  }
 
   stop(flags) {
     this.webNavigation.stop(flags);
   }
-};
-
-WebNavigation.init();
-
-var SecurityUI = {
-  getSecInfoAsString() {
-    let secInfo = docShell.securityUI.secInfo;
-
-    if (secInfo) {
-      if (secInfo) {
-        let helper = Cc["@mozilla.org/network/serialization-helper;1"]
-                        .getService(Ci.nsISerializationHelper);
-
-        secInfo.QueryInterface(Ci.nsISerializable);
-        return helper.serializeToString(secInfo);
-      }
-    }
-
-    return null;
-  }
-};
-
-var ControllerCommands = {
-  init() {
-    addMessageListener("ControllerCommands:Do", this);
-    addMessageListener("ControllerCommands:DoWithParams", this);
-    this.init = null;
-  },
-
-  receiveMessage(message) {
-    switch (message.name) {
-      case "ControllerCommands:Do":
-        if (docShell.isCommandEnabled(message.data))
-          docShell.doCommand(message.data);
-        break;
-
-      case "ControllerCommands:DoWithParams":
-        var data = message.data;
-        if (docShell.isCommandEnabled(data.cmd)) {
-          var params = Cc["@mozilla.org/embedcomp/command-params;1"].
-                       createInstance(Ci.nsICommandParams);
-          for (var name in data.params) {
-            var value = data.params[name];
-            if (value.type == "long") {
-              params.setLongValue(name, parseInt(value.value));
-            } else {
-              throw Cr.NS_ERROR_NOT_IMPLEMENTED;
-            }
-          }
-          docShell.doCommandWithParams(data.cmd, params);
-        }
-        break;
-    }
-  }
-};
-
-ControllerCommands.init();
-
-addEventListener("DOMTitleChanged", function(aEvent) {
-  if (!aEvent.isTrusted || aEvent.target.defaultView != content)
-    return;
-  sendAsyncMessage("DOMTitleChanged", { title: content.document.title });
-}, false);
-
-addEventListener("DOMWindowClose", function(aEvent) {
-  if (!aEvent.isTrusted)
-    return;
-  sendAsyncMessage("DOMWindowClose");
-}, false);
-
-addEventListener("ImageContentLoaded", function(aEvent) {
-  if (content.document instanceof Ci.nsIImageDocument) {
-    let req = content.document.imageRequest;
-    if (!req.image)
-      return;
-    sendAsyncMessage("ImageDocumentLoaded", { width: req.image.width,
-                                              height: req.image.height });
-  }
-}, false);
-
-const ZoomManager = {
-  get fullZoom() {
-    return this._cache.fullZoom;
-  },
-
-  get textZoom() {
-    return this._cache.textZoom;
-  },
-
-  set fullZoom(value) {
-    this._cache.fullZoom = value;
-    this._markupViewer.fullZoom = value;
-  },
-
-  set textZoom(value) {
-    this._cache.textZoom = value;
-    this._markupViewer.textZoom = value;
-  },
-
-  refreshFullZoom() {
-    return this._refreshZoomValue("fullZoom");
-  },
-
-  refreshTextZoom() {
-    return this._refreshZoomValue("textZoom");
-  },
-
-  /**
-   * Retrieves specified zoom property value from markupViewer and refreshes
-   * cache if needed.
-   * @param valueName Either 'fullZoom' or 'textZoom'.
-   * @returns Returns true if cached value was actually refreshed.
-   * @private
-   */
-  _refreshZoomValue(valueName) {
-    let actualZoomValue = this._markupViewer[valueName];
-    // Round to remove any floating-point error.
-    actualZoomValue = Number(actualZoomValue.toFixed(2));
-    if (actualZoomValue != this._cache[valueName]) {
-      this._cache[valueName] = actualZoomValue;
-      return true;
-    }
-    return false;
-  },
-
-  get _markupViewer() {
-    return docShell.contentViewer;
-  },
-
-  _cache: {
-    fullZoom: NaN,
-    textZoom: NaN
-  }
-};
-
-addMessageListener("FullZoom", function(aMessage) {
-  ZoomManager.fullZoom = aMessage.data.value;
-});
-
-addMessageListener("TextZoom", function(aMessage) {
-  ZoomManager.textZoom = aMessage.data.value;
-});
-
-addEventListener("FullZoomChange", function() {
-  if (ZoomManager.refreshFullZoom()) {
-    sendAsyncMessage("FullZoomChange", { value: ZoomManager.fullZoom });
-  }
-}, false);
-
-addEventListener("TextZoomChange", function(aEvent) {
-  if (ZoomManager.refreshTextZoom()) {
-    sendAsyncMessage("TextZoomChange", { value: ZoomManager.textZoom });
-  }
-}, false);
-
-addEventListener("ZoomChangeUsingMouseWheel", function() {
-  sendAsyncMessage("ZoomChangeUsingMouseWheel", {});
-}, false);
-
-addMessageListener("UpdateCharacterSet", function(aMessage) {
-  docShell.charset = aMessage.data.value;
-  docShell.gatherCharsetMenuTelemetry();
-});
-
-/**
- * Remote thumbnail request handler for PageThumbs thumbnails.
- */
-addMessageListener("Browser:Thumbnail:Request", function(aMessage) {
-  let snapshot;
-  let args = aMessage.data.additionalArgs;
-  let fullScale = args ? args.fullScale : false;
-  if (fullScale) {
-    snapshot = PageThumbUtils.createSnapshotThumbnail(content, null, args);
-  } else {
-    let snapshotWidth = aMessage.data.canvasWidth;
-    let snapshotHeight = aMessage.data.canvasHeight;
-    snapshot =
-      PageThumbUtils.createCanvas(content, snapshotWidth, snapshotHeight);
-    PageThumbUtils.createSnapshotThumbnail(content, snapshot, args);
-  }
-
-  snapshot.toBlob(function(aBlob) {
-    sendAsyncMessage("Browser:Thumbnail:Response", {
-      thumbnail: aBlob,
-      id: aMessage.data.id
-    });
-  });
-});
-
-/**
- * Remote isSafeForCapture request handler for PageThumbs.
- */
-addMessageListener("Browser:Thumbnail:CheckState", function(aMessage) {
-  Services.tm.idleDispatchToMainThread(() => {
-    let result = PageThumbUtils.shouldStoreContentThumbnail(content, docShell);
-    sendAsyncMessage("Browser:Thumbnail:CheckState:Response", {
-      result
-    });
-  });
-});
-
-/**
- * Remote GetOriginalURL request handler for PageThumbs.
- */
-addMessageListener("Browser:Thumbnail:GetOriginalURL", function(aMessage) {
-  let channel = docShell.currentDocumentChannel;
-  let channelError = PageThumbUtils.isChannelErrorResponse(channel);
-  let originalURL;
-  try {
-    originalURL = channel.originalURI.spec;
-  } catch (ex) {}
-  sendAsyncMessage("Browser:Thumbnail:GetOriginalURL:Response", {
-    channelError,
-    originalURL,
-  });
-});
-
-/**
- * Remote createAboutBlankContentViewer request handler.
- */
-addMessageListener("Browser:CreateAboutBlank", function(aMessage) {
-  if (!content.document || content.document.documentURI != "about:blank") {
-    throw new Error("Can't create a content viewer unless on about:blank");
-  }
-  let principal = aMessage.data;
-  principal = BrowserUtils.principalWithMatchingOA(principal, content.document.nodePrincipal);
-  docShell.createAboutBlankContentViewer(principal);
-});
-
-addMessageListener("InPermitUnload", msg => {
-  let inPermitUnload = docShell.contentViewer && docShell.contentViewer.inPermitUnload;
-  sendAsyncMessage("InPermitUnload", {id: msg.data.id, inPermitUnload});
-});
-
-addMessageListener("PermitUnload", msg => {
-  sendAsyncMessage("PermitUnload", {id: msg.data.id, kind: "start"});
-
-  let permitUnload = true;
-  if (docShell && docShell.contentViewer) {
-    permitUnload = docShell.contentViewer.permitUnload(msg.data.aPermitUnloadFlags);
-  }
-
-  sendAsyncMessage("PermitUnload", {id: msg.data.id, kind: "end", permitUnload});
-});
-
-// We may not get any responses to Browser:Init if the browser element
-// is torn down too quickly.
-var outerWindowID = content.windowUtils.outerWindowID;
-sendAsyncMessage("Browser:Init", {outerWindowID});
+}
copy from toolkit/content/browser-child.js
copy to toolkit/modules/WebProgressChild.jsm
--- a/toolkit/content/browser-child.js
+++ b/toolkit/modules/WebProgressChild.jsm
@@ -1,72 +1,60 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
 
-/* eslint-env mozilla/frame-script */
+var EXPORTED_SYMBOLS = ["WebProgressChild"];
 
-ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
-ChromeUtils.import("resource://gre/modules/BrowserUtils.jsm");
-ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-ChromeUtils.import("resource://gre/modules/Timer.jsm");
 
-ChromeUtils.defineModuleGetter(this, "PageThumbUtils",
-  "resource://gre/modules/PageThumbUtils.jsm");
+ChromeUtils.defineModuleGetter(this, "AppConstants",
+                               "resource://gre/modules/AppConstants.jsm");
 
-ChromeUtils.defineModuleGetter(this, "Utils",
-  "resource://gre/modules/sessionstore/Utils.jsm");
+XPCOMUtils.defineLazyServiceGetter(this, "CrashReporter",
+                                   "@mozilla.org/xre/app-info;1",
+                                   "nsICrashReporter");
+XPCOMUtils.defineLazyServiceGetter(this, "serializationHelper",
+                                   "@mozilla.org/network/serialization-helper;1",
+                                   "nsISerializationHelper");
 
-if (AppConstants.MOZ_CRASHREPORTER) {
-  XPCOMUtils.defineLazyServiceGetter(this, "CrashReporter",
-                                     "@mozilla.org/xre/app-info;1",
-                                     "nsICrashReporter");
-}
+class WebProgressChild {
+  constructor(mm) {
+    this.mm = mm;
 
-var WebProgressListener = {
-  init() {
     this._filter = Cc["@mozilla.org/appshell/component/browser-status-filter;1"]
                      .createInstance(Ci.nsIWebProgress);
     this._filter.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_ALL);
-    this._filter.target = tabEventTarget;
+    this._filter.target = this.mm.tabEventTarget;
 
-    let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
+    let webProgress = this.mm.docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                               .getInterface(Ci.nsIWebProgress);
     webProgress.addProgressListener(this._filter, Ci.nsIWebProgress.NOTIFY_ALL);
-    this.init = null;
-  },
-
-  uninit() {
-    let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
-                              .getInterface(Ci.nsIWebProgress);
-    webProgress.removeProgressListener(this._filter);
-
-    this._filter.removeProgressListener(this);
-    this._filter = null;
-  },
+  }
 
   _requestSpec(aRequest, aPropertyName) {
     if (!aRequest || !(aRequest instanceof Ci.nsIChannel))
       return null;
-    return aRequest.QueryInterface(Ci.nsIChannel)[aPropertyName].spec;
-  },
+    return aRequest[aPropertyName].spec;
+  }
 
-  _setupJSON: function setupJSON(aWebProgress, aRequest, aStateFlags) {
-    // Avoid accessing content.document when being called from onStateChange
+  _setupJSON(aWebProgress, aRequest, aStateFlags) {
+    // Avoid accessing this.mm.content.document when being called from onStateChange
     // unless if we are in STATE_STOP, because otherwise the getter will
     // instantiate an about:blank document for us.
     let contentDocument = null;
     if (aStateFlags) {
       // We're being called from onStateChange
       if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
-        contentDocument = content.document;
+        contentDocument = this.mm.content.document;
       }
     } else {
-      contentDocument = content.document;
+      contentDocument = this.mm.content.document;
     }
 
     let innerWindowID = null;
     if (aWebProgress) {
       let domWindowID = null;
       try {
         domWindowID = aWebProgress.DOMWindowID;
         innerWindowID = aWebProgress.innerDOMWindowID;
@@ -86,535 +74,164 @@ var WebProgressListener = {
 
     return {
       webProgress: aWebProgress || null,
       requestURI: this._requestSpec(aRequest, "URI"),
       originalRequestURI: this._requestSpec(aRequest, "originalURI"),
       documentContentType: contentDocument ? contentDocument.contentType : null,
       innerWindowID,
     };
-  },
+  }
 
-  _setupObjects: function setupObjects(aWebProgress, aRequest) {
+  _setupObjects(aWebProgress, aRequest) {
     let domWindow;
     try {
       domWindow = aWebProgress && aWebProgress.DOMWindow;
     } catch (e) {
       // If nsDocShell::Destroy has already been called, then we'll
       // get NS_NOINTERFACE when trying to get the DOM window. Ignore
       // that here.
       domWindow = null;
     }
 
     return {
-      contentWindow: content,
-      contentDocument: content.document,
-      // DOMWindow is not necessarily the content-window with subframes.
+      contentWindow: this.mm.content,
+      contentDocument: this.mm.content.document,
+      // DOMWindow is not necessarily the this.mm.content-window with subframes.
       DOMWindow: domWindow,
       webProgress: aWebProgress,
       request: aRequest,
     };
-  },
+  }
 
   _send(name, data, objects) {
-    sendAsyncMessage(name, data, objects);
-  },
+    this.mm.sendAsyncMessage(name, data, objects);
+  }
 
-  onStateChange: function onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
+  onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
     let json = this._setupJSON(aWebProgress, aRequest, aStateFlags);
     let objects = this._setupObjects(aWebProgress, aRequest);
 
     json.stateFlags = aStateFlags;
     json.status = aStatus;
 
     // It's possible that this state change was triggered by
     // loading an internal error page, for which the parent
     // will want to know some details, so we'll update it with
     // the documentURI.
     if (aWebProgress && aWebProgress.isTopLevel) {
-      json.documentURI = content.document.documentURIObject.spec;
-      json.charset = content.document.characterSet;
-      json.mayEnableCharacterEncodingMenu = docShell.mayEnableCharacterEncodingMenu;
-      json.inLoadURI = WebNavigation.inLoadURI;
+      json.documentURI = this.mm.content.document.documentURIObject.spec;
+      json.charset = this.mm.content.document.characterSet;
+      json.mayEnableCharacterEncodingMenu = this.mm.docShell.mayEnableCharacterEncodingMenu;
+      json.inLoadURI = this.mm.WebNavigation.inLoadURI;
     }
 
     this._send("Content:StateChange", json, objects);
-  },
+  }
 
   // Note: Because the nsBrowserStatusFilter timeout runnable is
-  // SystemGroup-labeled, this method should not modify content DOM or
-  // run content JS.
-  onProgressChange: function onProgressChange(aWebProgress, aRequest, aCurSelf, aMaxSelf, aCurTotal, aMaxTotal) {
+  // SystemGroup-labeled, this method should not modify this.mm.content DOM or
+  // run this.mm.content JS.
+  onProgressChange(aWebProgress, aRequest, aCurSelf, aMaxSelf, aCurTotal, aMaxTotal) {
     let json = this._setupJSON(aWebProgress, aRequest);
     let objects = this._setupObjects(aWebProgress, aRequest);
 
     json.curSelf = aCurSelf;
     json.maxSelf = aMaxSelf;
     json.curTotal = aCurTotal;
     json.maxTotal = aMaxTotal;
 
     this._send("Content:ProgressChange", json, objects);
-  },
+  }
 
-  onProgressChange64: function onProgressChange(aWebProgress, aRequest, aCurSelf, aMaxSelf, aCurTotal, aMaxTotal) {
+  onProgressChange64(aWebProgress, aRequest, aCurSelf, aMaxSelf, aCurTotal, aMaxTotal) {
     this.onProgressChange(aWebProgress, aRequest, aCurSelf, aMaxSelf, aCurTotal, aMaxTotal);
-  },
+  }
 
-  onLocationChange: function onLocationChange(aWebProgress, aRequest, aLocationURI, aFlags) {
+  onLocationChange(aWebProgress, aRequest, aLocationURI, aFlags) {
     let json = this._setupJSON(aWebProgress, aRequest);
     let objects = this._setupObjects(aWebProgress, aRequest);
 
     json.location = aLocationURI ? aLocationURI.spec : "";
     json.flags = aFlags;
 
     // These properties can change even for a sub-frame navigation.
-    let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
+    let webNav = this.mm.docShell.QueryInterface(Ci.nsIWebNavigation);
     json.canGoBack = webNav.canGoBack;
     json.canGoForward = webNav.canGoForward;
 
     if (aWebProgress && aWebProgress.isTopLevel) {
-      json.documentURI = content.document.documentURIObject.spec;
-      json.title = content.document.title;
-      json.charset = content.document.characterSet;
-      json.mayEnableCharacterEncodingMenu = docShell.mayEnableCharacterEncodingMenu;
-      json.principal = content.document.nodePrincipal;
-      json.synthetic = content.document.mozSyntheticDocument;
-      json.inLoadURI = WebNavigation.inLoadURI;
-      json.requestContextID = content.document.documentLoadGroup
-        ? content.document.documentLoadGroup.requestContextID
+      json.documentURI = this.mm.content.document.documentURIObject.spec;
+      json.title = this.mm.content.document.title;
+      json.charset = this.mm.content.document.characterSet;
+      json.mayEnableCharacterEncodingMenu = this.mm.docShell.mayEnableCharacterEncodingMenu;
+      json.principal = this.mm.content.document.nodePrincipal;
+      json.synthetic = this.mm.content.document.mozSyntheticDocument;
+      json.inLoadURI = this.mm.WebNavigation.inLoadURI;
+      json.requestContextID = this.mm.content.document.documentLoadGroup
+        ? this.mm.content.document.documentLoadGroup.requestContextID
         : null;
 
       if (AppConstants.MOZ_CRASHREPORTER && CrashReporter.enabled) {
         let uri = aLocationURI;
         try {
           // If the current URI contains a username/password, remove it.
           uri = uri.mutate()
                    .setUserPass("")
                    .finalize();
         } catch (ex) { /* Ignore failures on about: URIs. */ }
         CrashReporter.annotateCrashReport("URL", uri.spec);
       }
     }
 
     this._send("Content:LocationChange", json, objects);
-  },
+  }
 
   // Note: Because the nsBrowserStatusFilter timeout runnable is
-  // SystemGroup-labeled, this method should not modify content DOM or
-  // run content JS.
-  onStatusChange: function onStatusChange(aWebProgress, aRequest, aStatus, aMessage) {
+  // SystemGroup-labeled, this method should not modify this.mm.content DOM or
+  // run this.mm.content JS.
+  onStatusChange(aWebProgress, aRequest, aStatus, aMessage) {
     let json = this._setupJSON(aWebProgress, aRequest);
     let objects = this._setupObjects(aWebProgress, aRequest);
 
     json.status = aStatus;
     json.message = aMessage;
 
     this._send("Content:StatusChange", json, objects);
-  },
+  }
 
-  onSecurityChange: function onSecurityChange(aWebProgress, aRequest, aState) {
+  getSecInfoAsString() {
+    let secInfo = this.mm.docShell.securityUI.secInfo;
+    if (secInfo) {
+      return serializationHelper.serializeToString(secInfo);
+    }
+
+    return null;
+  }
+
+  onSecurityChange(aWebProgress, aRequest, aState) {
     let json = this._setupJSON(aWebProgress, aRequest);
     let objects = this._setupObjects(aWebProgress, aRequest);
 
     json.state = aState;
-    json.secInfo = SecurityUI.getSecInfoAsString();
+    json.secInfo = this.getSecInfoAsString();
 
     json.matchedList = null;
     if (aRequest && aRequest instanceof Ci.nsIClassifiedChannel) {
       json.matchedList = aRequest.matchedList;
     }
 
     this._send("Content:SecurityChange", json, objects);
-  },
+  }
 
-  onRefreshAttempted: function onRefreshAttempted(aWebProgress, aURI, aDelay, aSameURI) {
+  onRefreshAttempted(aWebProgress, aURI, aDelay, aSameURI) {
     return true;
-  },
+  }
 
   sendLoadCallResult() {
-    sendAsyncMessage("Content:LoadURIResult");
-  },
-
-  QueryInterface: ChromeUtils.generateQI(["nsIWebProgressListener",
-                                          "nsIWebProgressListener2",
-                                          "nsISupportsWeakReference"]),
-};
-
-WebProgressListener.init();
-addEventListener("unload", () => {
-  WebProgressListener.uninit();
-});
-
-var WebNavigation =  {
-  init() {
-    addMessageListener("WebNavigation:GoBack", this);
-    addMessageListener("WebNavigation:GoForward", this);
-    addMessageListener("WebNavigation:GotoIndex", this);
-    addMessageListener("WebNavigation:LoadURI", this);
-    addMessageListener("WebNavigation:SetOriginAttributes", this);
-    addMessageListener("WebNavigation:Reload", this);
-    addMessageListener("WebNavigation:Stop", this);
-    // This message is used for measuring content process startup performance.
-    sendAsyncMessage("Content:BrowserChildReady", { time: Services.telemetry.msSystemNow() });
-    this.init = null;
-  },
-
-  get webNavigation() {
-    return docShell.QueryInterface(Ci.nsIWebNavigation);
-  },
-
-  _inLoadURI: false,
-
-  get inLoadURI() {
-    return this._inLoadURI;
-  },
-
-  receiveMessage(message) {
-    switch (message.name) {
-      case "WebNavigation:GoBack":
-        this.goBack();
-        break;
-      case "WebNavigation:GoForward":
-        this.goForward();
-        break;
-      case "WebNavigation:GotoIndex":
-        this.gotoIndex(message.data.index);
-        break;
-      case "WebNavigation:LoadURI":
-        let histogram = Services.telemetry.getKeyedHistogramById("FX_TAB_REMOTE_NAVIGATION_DELAY_MS");
-        histogram.add("WebNavigation:LoadURI",
-                      Services.telemetry.msSystemNow() - message.data.requestTime);
-
-        this.loadURI(message.data.uri, message.data.flags,
-                     message.data.referrer, message.data.referrerPolicy,
-                     message.data.postData, message.data.headers,
-                     message.data.baseURI, message.data.triggeringPrincipal);
-        break;
-      case "WebNavigation:SetOriginAttributes":
-        this.setOriginAttributes(message.data.originAttributes);
-        break;
-      case "WebNavigation:Reload":
-        this.reload(message.data.flags);
-        break;
-      case "WebNavigation:Stop":
-        this.stop(message.data.flags);
-        break;
-    }
-  },
-
-  _wrapURIChangeCall(fn) {
-    this._inLoadURI = true;
-    try {
-      fn();
-    } finally {
-      this._inLoadURI = false;
-      WebProgressListener.sendLoadCallResult();
-    }
-  },
-
-  goBack() {
-    if (this.webNavigation.canGoBack) {
-      this._wrapURIChangeCall(() => this.webNavigation.goBack());
-    }
-  },
-
-  goForward() {
-    if (this.webNavigation.canGoForward) {
-      this._wrapURIChangeCall(() => this.webNavigation.goForward());
-    }
-  },
-
-  gotoIndex(index) {
-    this._wrapURIChangeCall(() => this.webNavigation.gotoIndex(index));
-  },
-
-  loadURI(uri, flags, referrer, referrerPolicy, postData, headers, baseURI, triggeringPrincipal) {
-    if (AppConstants.MOZ_CRASHREPORTER && CrashReporter.enabled) {
-      let annotation = uri;
-      try {
-        let url = Services.io.newURI(uri);
-        // If the current URI contains a username/password, remove it.
-        url = url.mutate()
-                 .setUserPass("")
-                 .finalize();
-        annotation = url.spec;
-      } catch (ex) { /* Ignore failures to parse and failures
-                      on about: URIs. */ }
-      CrashReporter.annotateCrashReport("URL", annotation);
-    }
-    if (referrer)
-      referrer = Services.io.newURI(referrer);
-    if (postData)
-      postData = Utils.makeInputStream(postData);
-    if (headers)
-      headers = Utils.makeInputStream(headers);
-    if (baseURI)
-      baseURI = Services.io.newURI(baseURI);
-    if (triggeringPrincipal)
-      triggeringPrincipal = Utils.deserializePrincipal(triggeringPrincipal);
-    this._wrapURIChangeCall(() => {
-      return this.webNavigation.loadURIWithOptions(uri, flags, referrer, referrerPolicy,
-                                                   postData, headers, baseURI, triggeringPrincipal);
-    });
-  },
-
-  setOriginAttributes(originAttributes) {
-    if (originAttributes) {
-      this.webNavigation.setOriginAttributesBeforeLoading(originAttributes);
-    }
-  },
-
-  reload(flags) {
-    this.webNavigation.reload(flags);
-  },
-
-  stop(flags) {
-    this.webNavigation.stop(flags);
-  }
-};
-
-WebNavigation.init();
-
-var SecurityUI = {
-  getSecInfoAsString() {
-    let secInfo = docShell.securityUI.secInfo;
-
-    if (secInfo) {
-      if (secInfo) {
-        let helper = Cc["@mozilla.org/network/serialization-helper;1"]
-                        .getService(Ci.nsISerializationHelper);
-
-        secInfo.QueryInterface(Ci.nsISerializable);
-        return helper.serializeToString(secInfo);
-      }
-    }
-
-    return null;
-  }
-};
-
-var ControllerCommands = {
-  init() {
-    addMessageListener("ControllerCommands:Do", this);
-    addMessageListener("ControllerCommands:DoWithParams", this);
-    this.init = null;
-  },
-
-  receiveMessage(message) {
-    switch (message.name) {
-      case "ControllerCommands:Do":
-        if (docShell.isCommandEnabled(message.data))
-          docShell.doCommand(message.data);
-        break;
-
-      case "ControllerCommands:DoWithParams":
-        var data = message.data;
-        if (docShell.isCommandEnabled(data.cmd)) {
-          var params = Cc["@mozilla.org/embedcomp/command-params;1"].
-                       createInstance(Ci.nsICommandParams);
-          for (var name in data.params) {
-            var value = data.params[name];
-            if (value.type == "long") {
-              params.setLongValue(name, parseInt(value.value));
-            } else {
-              throw Cr.NS_ERROR_NOT_IMPLEMENTED;
-            }
-          }
-          docShell.doCommandWithParams(data.cmd, params);
-        }
-        break;
-    }
+    this.mm.sendAsyncMessage("Content:LoadURIResult");
   }
-};
-
-ControllerCommands.init();
-
-addEventListener("DOMTitleChanged", function(aEvent) {
-  if (!aEvent.isTrusted || aEvent.target.defaultView != content)
-    return;
-  sendAsyncMessage("DOMTitleChanged", { title: content.document.title });
-}, false);
-
-addEventListener("DOMWindowClose", function(aEvent) {
-  if (!aEvent.isTrusted)
-    return;
-  sendAsyncMessage("DOMWindowClose");
-}, false);
-
-addEventListener("ImageContentLoaded", function(aEvent) {
-  if (content.document instanceof Ci.nsIImageDocument) {
-    let req = content.document.imageRequest;
-    if (!req.image)
-      return;
-    sendAsyncMessage("ImageDocumentLoaded", { width: req.image.width,
-                                              height: req.image.height });
-  }
-}, false);
-
-const ZoomManager = {
-  get fullZoom() {
-    return this._cache.fullZoom;
-  },
-
-  get textZoom() {
-    return this._cache.textZoom;
-  },
-
-  set fullZoom(value) {
-    this._cache.fullZoom = value;
-    this._markupViewer.fullZoom = value;
-  },
-
-  set textZoom(value) {
-    this._cache.textZoom = value;
-    this._markupViewer.textZoom = value;
-  },
-
-  refreshFullZoom() {
-    return this._refreshZoomValue("fullZoom");
-  },
-
-  refreshTextZoom() {
-    return this._refreshZoomValue("textZoom");
-  },
-
-  /**
-   * Retrieves specified zoom property value from markupViewer and refreshes
-   * cache if needed.
-   * @param valueName Either 'fullZoom' or 'textZoom'.
-   * @returns Returns true if cached value was actually refreshed.
-   * @private
-   */
-  _refreshZoomValue(valueName) {
-    let actualZoomValue = this._markupViewer[valueName];
-    // Round to remove any floating-point error.
-    actualZoomValue = Number(actualZoomValue.toFixed(2));
-    if (actualZoomValue != this._cache[valueName]) {
-      this._cache[valueName] = actualZoomValue;
-      return true;
-    }
-    return false;
-  },
-
-  get _markupViewer() {
-    return docShell.contentViewer;
-  },
-
-  _cache: {
-    fullZoom: NaN,
-    textZoom: NaN
-  }
-};
-
-addMessageListener("FullZoom", function(aMessage) {
-  ZoomManager.fullZoom = aMessage.data.value;
-});
-
-addMessageListener("TextZoom", function(aMessage) {
-  ZoomManager.textZoom = aMessage.data.value;
-});
-
-addEventListener("FullZoomChange", function() {
-  if (ZoomManager.refreshFullZoom()) {
-    sendAsyncMessage("FullZoomChange", { value: ZoomManager.fullZoom });
-  }
-}, false);
+}
 
-addEventListener("TextZoomChange", function(aEvent) {
-  if (ZoomManager.refreshTextZoom()) {
-    sendAsyncMessage("TextZoomChange", { value: ZoomManager.textZoom });
-  }
-}, false);
-
-addEventListener("ZoomChangeUsingMouseWheel", function() {
-  sendAsyncMessage("ZoomChangeUsingMouseWheel", {});
-}, false);
-
-addMessageListener("UpdateCharacterSet", function(aMessage) {
-  docShell.charset = aMessage.data.value;
-  docShell.gatherCharsetMenuTelemetry();
-});
-
-/**
- * Remote thumbnail request handler for PageThumbs thumbnails.
- */
-addMessageListener("Browser:Thumbnail:Request", function(aMessage) {
-  let snapshot;
-  let args = aMessage.data.additionalArgs;
-  let fullScale = args ? args.fullScale : false;
-  if (fullScale) {
-    snapshot = PageThumbUtils.createSnapshotThumbnail(content, null, args);
-  } else {
-    let snapshotWidth = aMessage.data.canvasWidth;
-    let snapshotHeight = aMessage.data.canvasHeight;
-    snapshot =
-      PageThumbUtils.createCanvas(content, snapshotWidth, snapshotHeight);
-    PageThumbUtils.createSnapshotThumbnail(content, snapshot, args);
-  }
-
-  snapshot.toBlob(function(aBlob) {
-    sendAsyncMessage("Browser:Thumbnail:Response", {
-      thumbnail: aBlob,
-      id: aMessage.data.id
-    });
-  });
-});
-
-/**
- * Remote isSafeForCapture request handler for PageThumbs.
- */
-addMessageListener("Browser:Thumbnail:CheckState", function(aMessage) {
-  Services.tm.idleDispatchToMainThread(() => {
-    let result = PageThumbUtils.shouldStoreContentThumbnail(content, docShell);
-    sendAsyncMessage("Browser:Thumbnail:CheckState:Response", {
-      result
-    });
-  });
-});
-
-/**
- * Remote GetOriginalURL request handler for PageThumbs.
- */
-addMessageListener("Browser:Thumbnail:GetOriginalURL", function(aMessage) {
-  let channel = docShell.currentDocumentChannel;
-  let channelError = PageThumbUtils.isChannelErrorResponse(channel);
-  let originalURL;
-  try {
-    originalURL = channel.originalURI.spec;
-  } catch (ex) {}
-  sendAsyncMessage("Browser:Thumbnail:GetOriginalURL:Response", {
-    channelError,
-    originalURL,
-  });
-});
-
-/**
- * Remote createAboutBlankContentViewer request handler.
- */
-addMessageListener("Browser:CreateAboutBlank", function(aMessage) {
-  if (!content.document || content.document.documentURI != "about:blank") {
-    throw new Error("Can't create a content viewer unless on about:blank");
-  }
-  let principal = aMessage.data;
-  principal = BrowserUtils.principalWithMatchingOA(principal, content.document.nodePrincipal);
-  docShell.createAboutBlankContentViewer(principal);
-});
-
-addMessageListener("InPermitUnload", msg => {
-  let inPermitUnload = docShell.contentViewer && docShell.contentViewer.inPermitUnload;
-  sendAsyncMessage("InPermitUnload", {id: msg.data.id, inPermitUnload});
-});
-
-addMessageListener("PermitUnload", msg => {
-  sendAsyncMessage("PermitUnload", {id: msg.data.id, kind: "start"});
-
-  let permitUnload = true;
-  if (docShell && docShell.contentViewer) {
-    permitUnload = docShell.contentViewer.permitUnload(msg.data.aPermitUnloadFlags);
-  }
-
-  sendAsyncMessage("PermitUnload", {id: msg.data.id, kind: "end", permitUnload});
-});
-
-// We may not get any responses to Browser:Init if the browser element
-// is torn down too quickly.
-var outerWindowID = content.windowUtils.outerWindowID;
-sendAsyncMessage("Browser:Init", {outerWindowID});
+WebProgressChild.prototype.QueryInterface =
+  ChromeUtils.generateQI(["nsIWebProgressListener",
+                          "nsIWebProgressListener2",
+                          "nsISupportsWeakReference"]);
--- a/toolkit/modules/moz.build
+++ b/toolkit/modules/moz.build
@@ -195,16 +195,17 @@ EXTRA_JS_MODULES += [
     'css-selector.js',
     'DateTimePickerContent.jsm',
     'DateTimePickerParent.jsm',
     'DeferredTask.jsm',
     'Deprecated.jsm',
     'E10SUtils.jsm',
     'EventEmitter.jsm',
     'FileUtils.jsm',
+    'FindBarChild.jsm',
     'Finder.jsm',
     'FinderHighlighter.jsm',
     'FinderIterator.jsm',
     'FormLikeFactory.jsm',
     'Geometry.jsm',
     'GMPExtractorWorker.js',
     'GMPInstallManager.jsm',
     'GMPUtils.jsm',
@@ -248,16 +249,18 @@ EXTRA_JS_MODULES += [
     'ShortcutUtils.jsm',
     'Sqlite.jsm',
     'Task.jsm',
     'Timer.jsm',
     'Troubleshoot.jsm',
     'UpdateUtils.jsm',
     'WebChannel.jsm',
     'WebChannelContent.jsm',
+    'WebNavigationChild.jsm',
+    'WebProgressChild.jsm',
     'WindowDraggingUtils.jsm',
     'ZipUtils.jsm',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
     EXTRA_JS_MODULES += [
         'PropertyListUtils.jsm',
     ]
--- a/toolkit/profile/content/profileSelection.js
+++ b/toolkit/profile/content/profileSelection.js
@@ -192,17 +192,17 @@ function RenameProfile() {
       selectedProfile.name = newName;
     } catch (e) {
       var alTitle = gProfileManagerBundle.getString("profileNameInvalidTitle");
       var alMsg = gProfileManagerBundle.getFormattedString("profileNameInvalid", [newName]);
       Services.prompt.alert(window, alTitle, alMsg);
       return false;
     }
 
-    selectedItem.label = newName;
+    selectedItem.firstChild.setAttribute("value", newName);
     var tiptext = gProfileManagerBundle.
                   getFormattedString("profileTooltip",
                                      [newName, selectedProfile.rootDir.path]);
     selectedItem.setAttribute("tooltiptext", tiptext);
 
     return true;
   }
 
--- a/xpcom/components/ManifestParser.cpp
+++ b/xpcom/components/ManifestParser.cpp
@@ -41,81 +41,67 @@
 
 using namespace mozilla;
 
 struct ManifestDirective
 {
   const char* directive;
   int argc;
 
-  // Binary components are only allowed for APP locations.
-  bool apponly;
-
-  // Some directives should only be delivered for APP or EXTENSION locations.
-  bool componentonly;
-
   bool ischrome;
 
-  bool allowbootstrap;
-
   // The contentaccessible flags only apply to content/resource directives.
   bool contentflags;
 
   // Function to handle this directive. This isn't a union because C++ still
   // hasn't learned how to initialize unions in a sane way.
   void (nsComponentManagerImpl::*mgrfunc)(
     nsComponentManagerImpl::ManifestProcessingContext& aCx,
     int aLineNo, char* const* aArgv);
   void (nsChromeRegistry::*regfunc)(
     nsChromeRegistry::ManifestProcessingContext& aCx,
     int aLineNo, char* const* aArgv, int aFlags);
-
-  bool isContract;
 };
 static const ManifestDirective kParsingTable[] = {
   {
-    "manifest",         1, false, false, true, true, false,
+    "manifest",         1, true, false,
     &nsComponentManagerImpl::ManifestManifest, nullptr,
   },
   {
-    "binary-component", 1, true, true, false, false, false,
-    &nsComponentManagerImpl::ManifestBinaryComponent, nullptr,
-  },
-  {
-    "component",        2, false, true, false, false, false,
+    "component",        2, false, false,
     &nsComponentManagerImpl::ManifestComponent, nullptr,
   },
   {
-    "contract",         2, false, true, false, false, false,
+    "contract",         2, false, false,
     &nsComponentManagerImpl::ManifestContract, nullptr,
   },
   {
-    "category",         3, false, true, false, false, false,
+    "category",         3, false, false,
     &nsComponentManagerImpl::ManifestCategory, nullptr,
   },
   {
-    "content",          2, false, true, true, true,  true,
+    "content",          2, true,  true,
     nullptr, &nsChromeRegistry::ManifestContent,
   },
   {
-    "locale",           3, false, true, true, true, false,
+    "locale",           3, true, false,
     nullptr, &nsChromeRegistry::ManifestLocale,
   },
   {
-    "skin",             3, false, false, true, true, false,
+    "skin",             3, true, false,
     nullptr, &nsChromeRegistry::ManifestSkin,
   },
   {
     // NB: note that while skin manifests can use this, they are only allowed
     // to use it for chrome://../skin/ URLs
-    "override",         2, false, false, true, true, false,
+    "override",         2, true, false,
     nullptr, &nsChromeRegistry::ManifestOverride,
   },
   {
-    "resource",         2, false, true, true, false, true,
+    "resource",         2, false, true,
     nullptr, &nsChromeRegistry::ManifestResource,
   }
 };
 
 static const char kWhitespace[] = "\t ";
 
 static bool
 IsNewline(char aChar)
@@ -580,38 +566,23 @@ ParseManifest(NSLocationType aType, File
 
     if (!directive) {
       LogMessageWithContext(aFile, line,
                             "Ignoring unrecognized chrome manifest directive '%s'.",
                             token);
       continue;
     }
 
-    if (!directive->allowbootstrap && NS_BOOTSTRAPPED_LOCATION == aType) {
+    if (!directive->ischrome && NS_BOOTSTRAPPED_LOCATION == aType) {
       LogMessageWithContext(aFile, line,
                             "Bootstrapped manifest not allowed to use '%s' directive.",
                             token);
       continue;
     }
 
-#ifndef MOZ_BINARY_EXTENSIONS
-    if (directive->apponly && NS_APP_LOCATION != aType) {
-      LogMessageWithContext(aFile, line,
-                            "Only application manifests may use the '%s' directive.", token);
-      continue;
-    }
-#endif
-
-    if (directive->componentonly && NS_SKIN_LOCATION == aType) {
-      LogMessageWithContext(aFile, line,
-                            "Skin manifest not allowed to use '%s' directive.",
-                            token);
-      continue;
-    }
-
     NS_ASSERTION(directive->argc < 4, "Need to reset argv array length");
     char* argv[4];
     for (int i = 0; i < directive->argc; ++i) {
       argv[i] = nsCRT::strtok(whitespace, kWhitespace, &whitespace);
     }
 
     if (!argv[directive->argc - 1]) {
       LogMessageWithContext(aFile, line,
@@ -716,25 +687,18 @@ ParseManifest(NSLocationType aType, File
                                 "Chrome registry isn't available yet.");
           continue;
         }
       }
 
       (nsChromeRegistry::gChromeRegistry->*(directive->regfunc))(
         chromecx, line, argv, flags);
     } else if (directive->ischrome || !aChromeOnly) {
-      if (directive->isContract) {
-        CachedDirective* cd = contracts.AppendElement();
-        cd->lineno = line;
-        cd->argv[0] = argv[0];
-        cd->argv[1] = argv[1];
-      } else {
-        (nsComponentManagerImpl::gComponentManager->*(directive->mgrfunc))(
-          mgrcx, line, argv);
-      }
+      (nsComponentManagerImpl::gComponentManager->*(directive->mgrfunc))(
+        mgrcx, line, argv);
     }
   }
 
   for (uint32_t i = 0; i < contracts.Length(); ++i) {
     CachedDirective& d = contracts[i];
     nsComponentManagerImpl::gComponentManager->ManifestContract(mgrcx,
                                                                 d.lineno,
                                                                 d.argv);
--- a/xpcom/components/nsComponentManager.cpp
+++ b/xpcom/components/nsComponentManager.cpp
@@ -661,25 +661,16 @@ nsComponentManagerImpl::ManifestManifest
                                          int aLineNo, char* const* aArgv)
 {
   char* file = aArgv[0];
   FileLocation f(aCx.mFile, file);
   RegisterManifest(aCx.mType, f, aCx.mChromeOnly);
 }
 
 void
-nsComponentManagerImpl::ManifestBinaryComponent(ManifestProcessingContext& aCx,
-                                                int aLineNo,
-                                                char* const* aArgv)
-{
-  LogMessageWithContext(aCx.mFile, aLineNo,
-                        "Binary XPCOM components are no longer supported.");
-}
-
-void
 nsComponentManagerImpl::ManifestComponent(ManifestProcessingContext& aCx,
                                           int aLineNo, char* const* aArgv)
 {
   mLock.AssertNotCurrentThreadOwns();
 
   char* id = aArgv[0];
   char* file = aArgv[1];
 
--- a/xpcom/components/nsComponentManager.h
+++ b/xpcom/components/nsComponentManager.h
@@ -282,18 +282,16 @@ public:
 
     NSLocationType mType;
     mozilla::FileLocation mFile;
     bool mChromeOnly;
   };
 
   void ManifestManifest(ManifestProcessingContext& aCx, int aLineNo,
                         char* const* aArgv);
-  void ManifestBinaryComponent(ManifestProcessingContext& aCx, int aLineNo,
-                               char* const* aArgv);
   void ManifestComponent(ManifestProcessingContext& aCx, int aLineNo,
                          char* const* aArgv);
   void ManifestContract(ManifestProcessingContext& aCx, int aLineNo,
                         char* const* aArgv);
   void ManifestCategory(ManifestProcessingContext& aCx, int aLineNo,
                         char* const* aArgv);
 
   void RereadChromeManifests(bool aChromeOnly = true);