Merge mozilla-central to mozilla-inbound
authorNoemi Erli <nerli@mozilla.com>
Tue, 26 Feb 2019 06:24:02 +0200
changeset 518995 c4ce50209f1966dfd7b10c6060b5a625395e0dca
parent 518994 13457088df2c75726f901023a31c61d63cf3bcb5 (current diff)
parent 518901 7c89a561baee4bf8a5b726a30032d40a5986180f (diff)
child 518996 2bf33f573505edb8ca5f5048dc054f472bd7436e
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to mozilla-inbound
devtools/client/debugger/new/src/components/PrimaryPanes/tests/__snapshots__/SourcesTree.spec.js.snap
devtools/client/debugger/new/src/reducers/sources.js
dom/ipc/ContentBridgeChild.cpp
dom/ipc/ContentBridgeChild.h
dom/ipc/ContentBridgeParent.cpp
dom/ipc/ContentBridgeParent.h
dom/ipc/PContentBridge.ipdl
dom/ipc/nsIContentChild.cpp
dom/ipc/nsIContentChild.h
dom/ipc/nsIContentParent.cpp
dom/ipc/nsIContentParent.h
gfx/wr/webrender/src/lib.rs
gfx/wr/webrender/src/picture.rs
gfx/wr/wrench/reftests/filters/reftest.list
gfx/wr/wrench/src/yaml_frame_reader.rs
testing/web-platform/meta/css/css-shapes/shape-outside/values/shape-outside-inset-006.html.ini
tools/lint/test/files/flake8/subdir/exclude/bad.py
tools/tryselect/util/dicttools.py
--- a/.flake8
+++ b/.flake8
@@ -1,44 +1,97 @@
 [flake8]
 max-line-length = 99
 exclude =
+    # These paths should be triaged and either fixed or moved to the list below.
+    client.py,
+    devtools/shared,
+    dom/base,
+    dom/bindings,
+    dom/browser-element,
+    dom/canvas,
+    dom/encoding,
+    dom/imptests,
+    dom/security,
+    dom/websocket,
+    embedding/ios/moz.configure,
+    gfx/tests,
+    layout/base/tests/marionette,
+    layout/reftests/border-image,
+    layout/reftests/fonts,
+    layout/reftests/w3c-css,
+    layout/style,
+    media/libdav1d/generate_source.py,
+    moz.configure,
+    netwerk/dns/prepare_tlds.py,
+    netwerk/protocol/http/make_incoming_tables.py,
+    python/devtools/migrate-l10n/migrate/main.py,
+    python/l10n/fluent_migrations,
+    python/mozbuild,
+    servo/components/style,
+    testing/jsshell/benchmark.py,
+    testing/marionette/mach_commands.py,
+    testing/mozharness/docs,
+    testing/mozharness/examples,
+    testing/mozharness/external_tools,
+    testing/mozharness/mach_commands.py,
+    testing/mozharness/manifestparser,
+    testing/mozharness/mozprocess,
+    testing/mozharness/setup.py,
+    testing/parse_build_tests_ccov.py,
+    testing/runtimes/writeruntimes.py,
+    testing/tools/iceserver/iceserver.py,
+    testing/tools/view_gecko_profile/view_gecko_profile.py,
+    testing/tools/websocketprocessbridge/websocketprocessbridge.py,
+    testing/web-platform,
+    toolkit/components/featuregates,
+    toolkit/content/tests/chrome/file_about_networking_wsh.py,
+    toolkit/crashreporter/tools/symbolstore.py,
+    toolkit/crashreporter/tools/unit-symbolstore.py,
+    toolkit/library/dependentlibs.py,
+    toolkit/locales/generate_update_locale.py,
+    toolkit/mozapps,
+    toolkit/moz.configure,
+    toolkit/nss.configure,
+
+    # These paths are intentionally excluded (not necessarily for good reason).
     build/build-infer/build-infer.py,
     build/moz.configure/*.configure,
     build/pymake/,
     browser/extensions/mortar/ppapi/,
     browser/moz.configure,
     dom/canvas/test/webgl-conf/checkout/closure-library/,
     editor/libeditor/tests/browserscope/,
     intl/icu/,
     ipc/chromium/src/third_party/,
     js/*.configure,
     gfx/angle/,
     gfx/harfbuzz,
-    glx/skia/,
+    gfx/skia/,
     memory/moz.configure,
     mobile/android/*.configure,
     node_modules,
     security/nss/,
-    testing/firefox-ui/**/__init__.py,
-    testing/marionette/**/__init__.py,
     testing/marionette/harness/marionette_harness/runner/mixins,
     testing/marionette/harness/marionette_harness/tests,
     testing/mochitest/pywebsocket,
     testing/mozharness/configs/test/test_malformed.py,
     tools/lint/test/files,
     tools/infer/test/*.configure,
     tools/crashreporter/*.configure,
+    .ycm_extra_conf.py,
 
 # See:
 #   - http://flake8.pycqa.org/en/latest/user/error-codes.html
 #   - http://pep8.readthedocs.io/en/latest/intro.html#configuration
 ignore =
     # These should be triaged and either fixed or moved to the list below.
     F632, F633, F811, E117, W504, W605, W606,
     # These are intentionally disabled (not necessarily for good reason).
     #   F723: syntax error in type comment
     #       text contains quotes which breaks our custom JSON formatter
     F723, E121, E123, E126, E129, E133, E226, E241, E242, E402, E704, E741, W503,
 
 per-file-ignores =
     ipc/ipdl/*: F403, F405
+    testing/firefox-ui/**/__init__.py: F401
+    testing/marionette/**/__init__.py: F401
     testing/mozharness/configs/*: E124, E127, E128, E131, E231, E261, E265, E266, E501, W391
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 1524688: Deleting preprocessed manifests requires clobber.
+Bug 1521879: Adding PMediaTransport.ipdl requires a clobber.
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1158,17 +1158,17 @@ source = "registry+https://github.com/ru
 dependencies = [
  "khronos_api 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "gleam"
-version = "0.6.8"
+version = "0.6.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "gl_generator 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "glob"
 version = "0.2.11"
@@ -3089,17 +3089,17 @@ dependencies = [
  "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-foundation 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-graphics 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-text 13.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "cstr 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "dwrote 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "freetype 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "gleam 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gleam 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "malloc_size_of_derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "plane-split 0.13.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "rayon 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "ron 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -3140,17 +3140,17 @@ dependencies = [
  "app_units 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-foundation 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-graphics 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "dwrote 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.19.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "foreign-types 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "gleam 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gleam 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "nsstring 0.1.0",
  "rayon 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "webrender 0.60.0",
 ]
 
@@ -3393,17 +3393,17 @@ dependencies = [
 "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
 "checksum futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)" = "884dbe32a6ae4cd7da5c6db9b78114449df9953b8d490c9d7e1b51720b922c62"
 "checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4"
 "checksum fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
 "checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb"
 "checksum generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c0f28c2f5bfb5960175af447a2da7c18900693738343dc896ffbcabd9839592"
 "checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d"
 "checksum gl_generator 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a0ffaf173cf76c73a73e080366bf556b4776ece104b06961766ff11449f38604"
-"checksum gleam 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "4b47f5b15742aee359c7895ab98cf2cceecc89bb4feb6f4e42f802d7899877da"
+"checksum gleam 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "369e326d40628f4013f5754fbcf4b01eb999b9d0f13795a1b9d20f3288ab799f"
 "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
 "checksum goblin 0.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "5911d7df7b8f65ab676c5327b50acea29d3c6a1a4ad05e444cf5dce321b26db2"
 "checksum h2 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "a27e7ed946e8335bdf9a191bc1b9b14a03ba822d013d2f58437f4fabcbd7fc2c"
 "checksum http 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "dca621d0fa606a5ff2850b6e337b57ad6137ee4d67e940449643ff45af6874c6"
 "checksum httparse 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "af2f2dd97457e8fb1ae7c5a420db346af389926e36f43768b96f101546b04a07"
 "checksum humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0484fda3e7007f2a4a0d9c3a703ca38c71c54c55602ce4660c419fd32e188c9e"
 "checksum hyper 0.12.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c087746de95e20e4dabe86606c3a019964a8fde2d5f386152939063c116c5971"
 "checksum ident_case 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c9826188e666f2ed92071d2dadef6edc430b11b158b5b2b3f4babbcc891eaaa"
--- a/browser/app/blocklist.xml
+++ b/browser/app/blocklist.xml
@@ -1,10 +1,10 @@
 <?xml version='1.0' encoding='UTF-8'?>
-<blocklist lastupdate="1550684288501" xmlns="http://www.mozilla.org/2006/addons-blocklist">
+<blocklist lastupdate="1550866974800" xmlns="http://www.mozilla.org/2006/addons-blocklist">
   <emItems>
     <emItem blockID="i334" id="{0F827075-B026-42F3-885D-98981EE7B1AE}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i1211" id="flvto@hotger.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
@@ -2542,22 +2542,22 @@
     <pluginItem blockID="p1055">
       <match exp="DirectorShockwave\.plugin" name="filename"/>
       <infoURL>https://get.adobe.com/shockwave/</infoURL>
       <versionRange maxVersion="12.2.0.162" minVersion="0" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
     <pluginItem blockID="832dc9ff-3314-4df2-abcf-7bd65a645371">
       <match exp="(NPSWF32.*\.dll)|(NPSWF64.*\.dll)|(Flash\ Player\.plugin)" name="filename"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
-      <versionRange maxVersion="31.0.0.153" minVersion="0" severity="0" vulnerabilitystatus="1"/>
+      <versionRange maxVersion="32.0.0.114" minVersion="0" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
     <pluginItem blockID="49b843cc-a8fc-4ede-be0c-a0da56d0214f" os="Linux">
       <match exp="libflashplayer\.so" name="filename"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
-      <versionRange maxVersion="31.0.0.153" minVersion="0" severity="0" vulnerabilitystatus="1"/>
+      <versionRange maxVersion="32.0.0.114" minVersion="0" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
   </pluginItems>
   <gfxItems>
     <gfxBlacklistEntry blockID="g194">
       <os>WINNT 6.2</os>
       <vendor>0x1022</vendor>
       <feature>DIRECT2D</feature>
       <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>
--- a/browser/app/moz.build
+++ b/browser/app/moz.build
@@ -77,16 +77,18 @@ if CONFIG['OS_ARCH'] == 'WINNT':
         'winlauncher',
     ]
     LOCAL_INCLUDES += [
         '/browser/app/winlauncher',
     ]
     DELAYLOAD_DLLS += [
         'oleaut32.dll',
         'ole32.dll',
+        'rpcrt4.dll',
+        'version.dll',
     ]
 
 if CONFIG['MOZ_SANDBOX'] and CONFIG['OS_ARCH'] == 'Darwin':
     USE_LIBS += [
         'mozsandbox',
     ]
 
 if CONFIG['MOZ_SANDBOX'] and CONFIG['OS_ARCH'] == 'WINNT':
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -140,23 +140,17 @@ pref("app.update.auto", true);
 
 // If set to true, the Update Service will present no UI for any event.
 pref("app.update.silent", false);
 
 // app.update.badgeWaitTime is in branding section
 
 // If set to true, the Update Service will apply updates in the background
 // when it finishes downloading them.
-#if defined(XP_WIN) || defined(XP_MACOSX)
 pref("app.update.staging.enabled", true);
-#elif defined(EARLY_BETA_OR_EARLIER)
-pref("app.update.staging.enabled", true);
-#else
-pref("app.update.staging.enabled", false);
-#endif
 
 // Update service URL:
 pref("app.update.url", "https://aus5.mozilla.org/update/6/%PRODUCT%/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%SYSTEM_CAPABILITIES%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml");
 // app.update.url.manual is in branding section
 // app.update.url.details is in branding section
 
 // app.update.interval is in branding section
 // app.update.promptWaitTime is in branding section
@@ -1236,16 +1230,18 @@ pref("services.sync.prefs.sync.privacy.c
 pref("services.sync.prefs.sync.privacy.clearOnShutdown.offlineApps", true);
 pref("services.sync.prefs.sync.privacy.clearOnShutdown.sessions", true);
 pref("services.sync.prefs.sync.privacy.clearOnShutdown.siteSettings", true);
 pref("services.sync.prefs.sync.privacy.donottrackheader.enabled", true);
 pref("services.sync.prefs.sync.privacy.fuzzyfox.enabled", false);
 pref("services.sync.prefs.sync.privacy.fuzzyfox.clockgrainus", false);
 pref("services.sync.prefs.sync.privacy.sanitize.sanitizeOnShutdown", true);
 pref("services.sync.prefs.sync.privacy.trackingprotection.enabled", true);
+pref("services.sync.prefs.sync.privacy.trackingprotection.cryptomining.enabled", true);
+pref("services.sync.prefs.sync.privacy.trackingprotection.fingerprinting.enabled", true);
 pref("services.sync.prefs.sync.privacy.trackingprotection.pbmode.enabled", true);
 pref("services.sync.prefs.sync.privacy.resistFingerprinting", true);
 pref("services.sync.prefs.sync.privacy.reduceTimerPrecision", true);
 pref("services.sync.prefs.sync.privacy.resistFingerprinting.reduceTimerPrecision.microseconds", true);
 pref("services.sync.prefs.sync.privacy.resistFingerprinting.reduceTimerPrecision.jitter", true);
 pref("services.sync.prefs.sync.security.OCSP.enabled", true);
 pref("services.sync.prefs.sync.security.OCSP.require", true);
 pref("services.sync.prefs.sync.security.default_personal_cert", true);
--- a/browser/app/winlauncher/ErrorHandler.cpp
+++ b/browser/app/winlauncher/ErrorHandler.cpp
@@ -1,33 +1,743 @@
 /* -*- 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 https://mozilla.org/MPL/2.0/. */
 
 #include "ErrorHandler.h"
 
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/CmdLineAndEnvUtils.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/glue/WindowsDllServices.h"
+#include "mozilla/JSONWriter.h"
+#include "mozilla/Move.h"
+#include "mozilla/mscom/ProcessRuntime.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Unused.h"
+#include "mozilla/WindowsVersion.h"
+#include "mozilla/XREAppData.h"
+#include "nsWindowsHelpers.h"
+
 #if defined(MOZ_LAUNCHER_PROCESS)
 #  include "mozilla/LauncherRegistryInfo.h"
-#  include "mozilla/Unused.h"
 #endif  // defined(MOZ_LAUNCHER_PROCESS)
 
+#include <algorithm>
+#include <process.h>
+#include <sstream>
+#include <string>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+#include <objbase.h>
+#include <rpc.h>
+#if !defined(__MINGW32__)
+#  include <comutil.h>
+#  include <iwscapi.h>
+#  include <wscapi.h>
+#endif  // !defined(__MINGW32__)
+#include <tlhelp32.h>
+
+#if !defined(RRF_SUBKEY_WOW6464KEY)
+#  define RRF_SUBKEY_WOW6464KEY 0x00010000
+#endif  // !defined(RRF_SUBKEY_WOW6464KEY)
+
+#define QUOTE_ME2(x) #x
+#define QUOTE_ME(x) QUOTE_ME2(x)
+
+#define TELEMETRY_BASE_URL L"https://incoming.telemetry.mozilla.org/submit"
+#define TELEMETRY_NAMESPACE L"/firefox-launcher-process"
+#define TELEMETRY_LAUNCHER_PING_DOCTYPE L"/launcher-process-failure"
+#define TELEMETRY_LAUNCHER_PING_VERSION L"/1"
+
+static const wchar_t kUrl[] = TELEMETRY_BASE_URL TELEMETRY_NAMESPACE
+    TELEMETRY_LAUNCHER_PING_DOCTYPE TELEMETRY_LAUNCHER_PING_VERSION L"/";
+static const uint32_t kGuidCharLenWithNul = 39;
+static const uint32_t kGuidCharLenNoBracesNoNul = 36;
+static const mozilla::StaticXREAppData* gAppData;
+static bool gForceEventLog = false;
+
+namespace {
+
+struct EventSourceDeleter {
+  using pointer = HANDLE;
+
+  void operator()(pointer aEvtSrc) { ::DeregisterEventSource(aEvtSrc); }
+};
+
+using EventLog = mozilla::UniquePtr<HANDLE, EventSourceDeleter>;
+
+struct SerializedEventData {
+  HRESULT mHr;
+  uint32_t mLine;
+  char mFile[1];
+};
+
+}  // anonymous namespace
+
+static void PostErrorToLog(const mozilla::LauncherError& aError) {
+  // This is very bare-bones; just enough to spit out an HRESULT to the
+  // Application event log.
+  EventLog log(::RegisterEventSourceW(nullptr, L"Firefox"));
+  if (!log) {
+    return;
+  }
+
+  size_t fileLen = strlen(aError.mFile);
+  size_t dataLen = sizeof(HRESULT) + sizeof(uint32_t) + fileLen;
+  auto evtDataBuf = mozilla::MakeUnique<char[]>(dataLen);
+  SerializedEventData& evtData =
+      *reinterpret_cast<SerializedEventData*>(evtDataBuf.get());
+  evtData.mHr = aError.mError.AsHResult();
+  evtData.mLine = aError.mLine;
+  // Since this is binary data, we're not concerning ourselves with null
+  // terminators.
+  memcpy(evtData.mFile, aError.mFile, fileLen);
+
+  ::ReportEventW(log.get(), EVENTLOG_ERROR_TYPE, 0, aError.mError.AsHResult(),
+                 nullptr, 0, dataLen, nullptr, evtDataBuf.get());
+}
+
+#if defined(MOZ_TELEMETRY_REPORTING)
+
+namespace {
+
+// This JSONWriteFunc writes directly to a temp file. By creating this file
+// with the FILE_ATTRIBUTE_TEMPORARY attribute, we hint to the OS that this
+// file is short-lived. The OS will try to avoid flushing it to disk if at
+// all possible.
+class TempFileWriter final : public mozilla::JSONWriteFunc {
+ public:
+  TempFileWriter() : mFailed(false), mSuccessfulHandoff(false) {
+    wchar_t name[MAX_PATH + 1] = {};
+    if (_wtmpnam_s(name)) {
+      mFailed = true;
+      return;
+    }
+
+    mTempFileName = name;
+
+    mTempFile.own(::CreateFileW(name, GENERIC_WRITE, FILE_SHARE_READ, nullptr,
+                                CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY, nullptr));
+    if (mTempFile.get() == INVALID_HANDLE_VALUE) {
+      mFailed = true;
+    }
+  }
+
+  ~TempFileWriter() {
+    if (mSuccessfulHandoff) {
+      // It is no longer our responsibility to delete the temp file if we have
+      // successfully handed it off to pingsender.
+      return;
+    }
+
+    mTempFile.reset();
+    ::DeleteFileW(mTempFileName.c_str());
+  }
+
+  explicit operator bool() const { return !mFailed; }
+
+  void Write(const char* aStr) override {
+    if (mFailed) {
+      return;
+    }
+
+    size_t len = strlen(aStr);
+    DWORD bytesWritten = 0;
+    if (!::WriteFile(mTempFile, aStr, len, &bytesWritten, nullptr) ||
+        bytesWritten != len) {
+      mFailed = true;
+    }
+  }
+
+  const std::wstring& GetFileName() const { return mTempFileName; }
+
+  void SetSuccessfulHandoff() { mSuccessfulHandoff = true; }
+
+ private:
+  bool mFailed;
+  bool mSuccessfulHandoff;
+  std::wstring mTempFileName;
+  nsAutoHandle mTempFile;
+};
+
+using SigMap = mozilla::Vector<std::wstring, 0, InfallibleAllocPolicy>;
+
+}  // anonymous namespace
+
+// This is the guideline for maximum string length for telemetry intake
+static const size_t kMaxStrLen = 80;
+
+static mozilla::UniquePtr<char[]> WideToUTF8(const wchar_t* aStr,
+                                             const size_t aStrLenExclNul) {
+  // Yes, this might not handle surrogate pairs correctly. Let's just let
+  // WideCharToMultiByte fail in that unlikely case.
+  size_t cvtLen = std::min(aStrLenExclNul, kMaxStrLen);
+
+  int numConv = ::WideCharToMultiByte(CP_UTF8, 0, aStr, cvtLen, nullptr,
+                                      0, nullptr, nullptr);
+  if (!numConv) {
+    return nullptr;
+  }
+
+  // Include room for the null terminator by adding one
+  auto buf = mozilla::MakeUnique<char[]>(numConv + 1);
+
+  numConv = ::WideCharToMultiByte(CP_UTF8, 0, aStr, cvtLen, buf.get(),
+                                  numConv, nullptr, nullptr);
+  if (!numConv) {
+    return nullptr;
+  }
+
+  // Add null termination. numConv does not include the terminator, so we don't
+  // subtract 1 when indexing into buf.
+  buf[numConv] = 0;
+
+  return buf;
+}
+
+static mozilla::UniquePtr<char[]> WideToUTF8(const wchar_t* aStr) {
+  return WideToUTF8(aStr, wcslen(aStr));
+}
+
+static mozilla::UniquePtr<char[]> WideToUTF8(const std::wstring& aStr) {
+  return WideToUTF8(aStr.c_str(), aStr.length());
+}
+
+// MinGW does not support the Windows Security Center APIs.
+#if !defined(__MINGW32__)
+
+static mozilla::UniquePtr<char[]> WideToUTF8(const _bstr_t& aStr) {
+  return WideToUTF8(static_cast<const wchar_t*>(aStr), aStr.length());
+}
+
+namespace {
+
+struct ProviderKey {
+  WSC_SECURITY_PROVIDER mProviderType;
+  const char* mKey;
+};
+
+}  // anonymous namespace
+
+static bool EnumWSCProductList(RefPtr<IWSCProductList>& aProdList,
+                               mozilla::JSONWriter& aJson) {
+  LONG count;
+  HRESULT hr = aProdList->get_Count(&count);
+  if (FAILED(hr)) {
+    return false;
+  }
+
+  // Unlikely, but put a bound on the max length of the output array for the
+  // purposes of telemetry intake.
+  count = std::min(count, 1000L);
+
+  // Record the name(s) of each active registered product in this category
+  for (LONG index = 0; index < count; ++index) {
+    RefPtr<IWscProduct> product;
+    hr = aProdList->get_Item(index, getter_AddRefs(product));
+    if (FAILED(hr)) {
+      return false;
+    }
+
+    WSC_SECURITY_PRODUCT_STATE state;
+    hr = product->get_ProductState(&state);
+    if (FAILED(hr)) {
+      return false;
+    }
+
+    // We only care about products that are active
+    if (state == WSC_SECURITY_PRODUCT_STATE_OFF ||
+        state == WSC_SECURITY_PRODUCT_STATE_SNOOZED ||
+        state == WSC_SECURITY_PRODUCT_STATE_EXPIRED) {
+      continue;
+    }
+
+    _bstr_t bName;
+    hr = product->get_ProductName(bName.GetAddress());
+    if (FAILED(hr)) {
+      return false;
+    }
+
+    auto buf = WideToUTF8(bName);
+    if (!buf) {
+      return false;
+    }
+
+    aJson.StringElement(buf.get());
+  }
+
+  return true;
+}
+
+static const ProviderKey gProvKeys[] = {
+    {WSC_SECURITY_PROVIDER_ANTIVIRUS, "av"},
+    {WSC_SECURITY_PROVIDER_ANTISPYWARE, "antispyware"},
+    {WSC_SECURITY_PROVIDER_FIREWALL, "firewall"}};
+
+static bool AddWscInfo(mozilla::JSONWriter& aJson) {
+  if (!mozilla::IsWin8OrLater()) {
+    // We haven't written anything yet, so we can return true here and continue
+    // capturing data.
+    return true;
+  }
+
+  // We need COM for this. Using ProcessRuntime so that process-global COM
+  // configuration is done correctly
+  mozilla::mscom::ProcessRuntime mscom(GeckoProcessType_Default);
+  if (!mscom) {
+    // We haven't written anything yet, so we can return true here and continue
+    // capturing data.
+    return true;
+  }
+
+  aJson.StartObjectProperty("security");
+
+  const CLSID clsid = __uuidof(WSCProductList);
+  const IID iid = __uuidof(IWSCProductList);
+
+  for (uint32_t index = 0; index < mozilla::ArrayLength(gProvKeys); ++index) {
+    // NB: A separate instance of IWSCProductList is needed for each distinct
+    // security provider type; MSDN says that we cannot reuse the same object
+    // and call Initialize() to pave over the previous data.
+    RefPtr<IWSCProductList> prodList;
+    HRESULT hr = ::CoCreateInstance(clsid, nullptr, CLSCTX_INPROC_SERVER, iid,
+                                    getter_AddRefs(prodList));
+    if (FAILED(hr)) {
+      return false;
+    }
+
+    hr = prodList->Initialize(gProvKeys[index].mProviderType);
+    if (FAILED(hr)) {
+      return false;
+    }
+
+    aJson.StartArrayProperty(gProvKeys[index].mKey);
+
+    if (!EnumWSCProductList(prodList, aJson)) {
+      return false;
+    }
+
+    aJson.EndArray();
+  }
+
+  aJson.EndObject();
+
+  return true;
+}
+#endif  // !defined(__MINGW32__)
+
+// Max array length for telemetry intake.
+static const size_t kMaxArrayLen = 1000;
+
+static bool AddModuleInfo(const nsAutoHandle& aSnapshot,
+                          mozilla::JSONWriter& aJson) {
+  if (aSnapshot.get() == INVALID_HANDLE_VALUE) {
+    // We haven't written anything yet, so we can return true here and continue
+    // capturing data.
+    return true;
+  }
+
+  SigMap signatures;
+  size_t moduleCount = 0;
+
+  MODULEENTRY32W module = {sizeof(module)};
+  if (!::Module32FirstW(aSnapshot, &module)) {
+    // We haven't written anything yet, so we can return true here and continue
+    // capturing data.
+    return true;
+  }
+
+  mozilla::glue::BasicDllServices dllServices;
+
+  aJson.StartObjectProperty("modules");
+
+  // For each module, add its version number (or empty string if not present),
+  // followed by an optional index into the signatures array
+  do {
+    ++moduleCount;
+
+    wchar_t leaf[_MAX_FNAME] = {};
+    if (::_wsplitpath_s(module.szExePath, nullptr, 0, nullptr, 0, leaf,
+                        mozilla::ArrayLength(leaf), nullptr, 0)) {
+      return false;
+    }
+
+    if (_wcslwr_s(leaf, mozilla::ArrayLength(leaf))) {
+      return false;
+    }
+
+    auto leafUtf8 = WideToUTF8(leaf);
+    if (!leafUtf8) {
+      return false;
+    }
+
+    aJson.StartArrayProperty(leafUtf8.get());
+
+    std::string version;
+    DWORD verInfoSize = ::GetFileVersionInfoSizeW(module.szExePath, nullptr);
+    if (verInfoSize) {
+      auto verInfoBuf = mozilla::MakeUnique<BYTE[]>(verInfoSize);
+
+      if (::GetFileVersionInfoW(module.szExePath, 0, verInfoSize,
+                                verInfoBuf.get())) {
+        VS_FIXEDFILEINFO* fixedInfo = nullptr;
+        UINT fixedInfoLen = 0;
+
+        if (::VerQueryValueW(verInfoBuf.get(), L"\\",
+                             reinterpret_cast<LPVOID*>(&fixedInfo),
+                             &fixedInfoLen)) {
+          std::ostringstream oss;
+          oss << HIWORD(fixedInfo->dwFileVersionMS) << '.'
+              << LOWORD(fixedInfo->dwFileVersionMS) << '.'
+              << HIWORD(fixedInfo->dwFileVersionLS) << '.'
+              << LOWORD(fixedInfo->dwFileVersionLS);
+          version = oss.str();
+        }
+      }
+    }
+
+    aJson.StringElement(version.c_str());
+
+    mozilla::Maybe<ptrdiff_t> sigIndex;
+    auto signedBy = dllServices.GetBinaryOrgName(module.szExePath);
+    if (signedBy) {
+      std::wstring strSignedBy(signedBy.get());
+      auto entry = std::find(signatures.begin(), signatures.end(), strSignedBy);
+      if (entry == signatures.end()) {
+        mozilla::Unused << signatures.append(std::move(strSignedBy));
+        entry = &signatures.back();
+      }
+
+      sigIndex = mozilla::Some(entry - signatures.begin());
+    }
+
+    if (sigIndex) {
+      aJson.IntElement(sigIndex.value());
+    }
+
+    aJson.EndArray();
+  } while (moduleCount < kMaxArrayLen && ::Module32NextW(aSnapshot, &module));
+
+  aJson.EndObject();
+
+  aJson.StartArrayProperty("signatures");
+
+  // Serialize each entry in the signatures array
+  for (auto&& itr : signatures) {
+    auto sigUtf8 = WideToUTF8(itr);
+    if (!sigUtf8) {
+      continue;
+    }
+
+    aJson.StringElement(sigUtf8.get());
+  }
+
+  aJson.EndArray();
+
+  return true;
+}
+
+namespace {
+
+struct PingThreadContext {
+  explicit PingThreadContext(const mozilla::LauncherError& aError)
+      : mLauncherError(aError),
+        mModulesSnapshot(::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0)) {}
+  mozilla::LauncherError mLauncherError;
+  nsAutoHandle mModulesSnapshot;
+};
+
+}  // anonymous namespace
+
+static bool PrepPing(const PingThreadContext& aContext,
+                     const std::wstring& aId,
+                     mozilla::JSONWriter& aJson) {
+#if defined(DEBUG)
+  const mozilla::JSONWriter::CollectionStyle style =
+      mozilla::JSONWriter::MultiLineStyle;
+#else
+  const mozilla::JSONWriter::CollectionStyle style =
+      mozilla::JSONWriter::SingleLineStyle;
+#endif  // defined(DEBUG)
+
+  aJson.Start(style);
+
+  aJson.StringProperty("type", "launcher-process-failure");
+  aJson.IntProperty("version", 1);
+
+  auto idUtf8 = WideToUTF8(aId);
+  if (idUtf8) {
+    aJson.StringProperty("id", idUtf8.get());
+  }
+
+  time_t now;
+  time(&now);
+  tm gmTm;
+  if (!gmtime_s(&gmTm, &now)) {
+    char isoTimeBuf[32] = {};
+    if (strftime(isoTimeBuf, mozilla::ArrayLength(isoTimeBuf), "%FT%T.000Z",
+                 &gmTm)) {
+      aJson.StringProperty("creationDate", isoTimeBuf);
+    }
+  }
+
+  aJson.StringProperty("update_channel", QUOTE_ME(MOZ_UPDATE_CHANNEL));
+
+  if (gAppData) {
+    aJson.StringProperty("build_id", gAppData->buildID);
+    aJson.StringProperty("build_version", gAppData->version);
+  }
+
+  OSVERSIONINFOEXW osv = {sizeof(osv)};
+  if (::GetVersionExW(reinterpret_cast<OSVERSIONINFOW*>(&osv))) {
+    std::ostringstream oss;
+    oss << osv.dwMajorVersion << "." << osv.dwMinorVersion << "."
+        << osv.dwBuildNumber;
+
+    if (osv.dwMajorVersion == 10 && osv.dwMinorVersion == 0) {
+      // Get the "Update Build Revision" (UBR) value
+      DWORD ubrValue;
+      DWORD ubrValueLen = sizeof(ubrValue);
+      LSTATUS ubrOk =
+          ::RegGetValueW(HKEY_LOCAL_MACHINE,
+                         L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
+                         L"UBR", RRF_RT_DWORD | RRF_SUBKEY_WOW6464KEY, nullptr,
+                         &ubrValue, &ubrValueLen);
+      if (ubrOk == ERROR_SUCCESS) {
+        oss << "." << ubrValue;
+      }
+    }
+
+    if (oss) {
+      aJson.StringProperty("os_version", oss.str().c_str());
+    }
+
+    bool isServer = osv.wProductType == VER_NT_DOMAIN_CONTROLLER ||
+                    osv.wProductType == VER_NT_SERVER;
+    aJson.BoolProperty("server_os", isServer);
+  }
+
+  WCHAR localeName[LOCALE_NAME_MAX_LENGTH] = {};
+  int localeNameLen =
+      ::GetUserDefaultLocaleName(localeName, mozilla::ArrayLength(localeName));
+  if (localeNameLen) {
+    auto localeNameUtf8 = WideToUTF8(localeName, localeNameLen - 1);
+    if (localeNameUtf8) {
+      aJson.StringProperty("os_locale", localeNameUtf8.get());
+    }
+  }
+
+  SYSTEM_INFO sysInfo;
+  ::GetNativeSystemInfo(&sysInfo);
+  aJson.IntProperty("cpu_arch", sysInfo.wProcessorArchitecture);
+  aJson.IntProperty("num_logical_cpus", sysInfo.dwNumberOfProcessors);
+
+  MEMORYSTATUSEX memStatus = {sizeof(memStatus)};
+  if (::GlobalMemoryStatusEx(&memStatus)) {
+    aJson.StartObjectProperty("memory");
+    aJson.IntProperty("total_phys", memStatus.ullTotalPhys);
+    aJson.IntProperty("avail_phys", memStatus.ullAvailPhys);
+    aJson.IntProperty("avail_page_file", memStatus.ullAvailPageFile);
+    aJson.IntProperty("avail_virt", memStatus.ullAvailVirtual);
+    aJson.EndObject();
+  }
+
+  aJson.StringProperty("xpcom_abi", TARGET_XPCOM_ABI);
+
+  aJson.StartObjectProperty("launcher_error", style);
+
+  std::string srcFileLeaf(aContext.mLauncherError.mFile);
+  // Obtain the leaf name of the file for privacy reasons
+  // (In case this is somebody's local build)
+  auto pos = srcFileLeaf.find_last_of("/\\");
+  if (pos != std::string::npos) {
+    srcFileLeaf = srcFileLeaf.substr(pos + 1);
+  }
+
+  aJson.StringProperty("source_file", srcFileLeaf.c_str());
+
+  aJson.IntProperty("source_line", aContext.mLauncherError.mLine);
+  aJson.IntProperty("hresult", aContext.mLauncherError.mError.AsHResult());
+  aJson.EndObject();
+
+#if !defined(__MINGW32__)
+  if (!AddWscInfo(aJson)) {
+    return false;
+  }
+#endif  // !defined(__MINGW32__)
+
+  if (!AddModuleInfo(aContext.mModulesSnapshot, aJson)) {
+    return false;
+  }
+
+  aJson.End();
+
+  return true;
+}
+
+static bool DoSendPing(const PingThreadContext& aContext) {
+  auto writeFunc = mozilla::MakeUnique<TempFileWriter>();
+  if (!(*writeFunc)) {
+    return false;
+  }
+
+  mozilla::JSONWriter json(std::move(writeFunc));
+
+  UUID uuid;
+  if (::UuidCreate(&uuid) != RPC_S_OK) {
+    return false;
+  }
+
+  wchar_t guidBuf[kGuidCharLenWithNul] = {};
+  if (::StringFromGUID2(uuid, guidBuf, kGuidCharLenWithNul) !=
+      kGuidCharLenWithNul) {
+    return false;
+  }
+
+  // Strip the curly braces off of the guid
+  std::wstring guidNoBraces(guidBuf + 1, kGuidCharLenNoBracesNoNul);
+
+  // Populate json with the ping information
+  if (!PrepPing(aContext, guidNoBraces, json)) {
+    return false;
+  }
+
+  // Obtain the name of the temp file that we have written
+  TempFileWriter& tempFile = *static_cast<TempFileWriter*>(json.WriteFunc());
+  if (!tempFile) {
+    return false;
+  }
+
+  const std::wstring& fileName = tempFile.GetFileName();
+
+  // Using the path to our executable binary, construct the path to
+  // pingsender.exe
+  mozilla::UniquePtr<wchar_t[]> exePath(mozilla::GetFullBinaryPath());
+
+  wchar_t drive[_MAX_DRIVE] = {};
+  wchar_t dir[_MAX_DIR] = {};
+  if (_wsplitpath_s(exePath.get(), drive, mozilla::ArrayLength(drive), dir,
+                    mozilla::ArrayLength(dir), nullptr, 0, nullptr, 0)) {
+    return false;
+  }
+
+  wchar_t pingSenderPath[MAX_PATH + 1] = {};
+  if (_wmakepath_s(pingSenderPath, mozilla::ArrayLength(pingSenderPath), drive,
+                   dir, L"pingsender", L"exe")) {
+    return false;
+  }
+
+  // Construct the telemetry URL
+  wchar_t urlBuf[mozilla::ArrayLength(kUrl) + kGuidCharLenNoBracesNoNul] = {};
+  if (wcscpy_s(urlBuf, kUrl)) {
+    return false;
+  }
+
+  if (wcscat_s(urlBuf, guidNoBraces.c_str())) {
+    return false;
+  }
+
+  // Now build the command line arguments to pingsender
+  wchar_t* pingSenderArgv[] = {pingSenderPath, urlBuf,
+                               const_cast<wchar_t*>(fileName.c_str())};
+
+  mozilla::UniquePtr<wchar_t[]> pingSenderCmdLine(mozilla::MakeCommandLine(
+      mozilla::ArrayLength(pingSenderArgv), pingSenderArgv));
+
+  // Now start pingsender to handle the rest
+  PROCESS_INFORMATION pi;
+
+  STARTUPINFOW si = {sizeof(si)};
+  si.dwFlags = STARTF_USESHOWWINDOW;
+  si.wShowWindow = SW_HIDE;
+
+  if (!::CreateProcessW(pingSenderPath, pingSenderCmdLine.get(), nullptr,
+                        nullptr, FALSE, 0, nullptr, nullptr, &si, &pi)) {
+    return false;
+  }
+
+  tempFile.SetSuccessfulHandoff();
+
+  nsAutoHandle proc(pi.hProcess);
+  nsAutoHandle thread(pi.hThread);
+
+  return true;
+}
+
+static unsigned __stdcall SendPingThread(void* aContext) {
+  mozilla::UniquePtr<PingThreadContext> context(
+      reinterpret_cast<PingThreadContext*>(aContext));
+
+  if (!DoSendPing(*context) || gForceEventLog) {
+    PostErrorToLog(context->mLauncherError);
+  }
+
+  return 0;
+}
+
+#endif  // defined(MOZ_TELEMETRY_REPORTING)
+
+static bool SendPing(const mozilla::LauncherError& aError) {
+#if defined(MOZ_TELEMETRY_REPORTING)
+  mozilla::LauncherRegistryInfo regInfo;
+  mozilla::LauncherResult<mozilla::LauncherRegistryInfo::EnabledState>
+      launcherEnabled = regInfo.IsEnabled();
+  if (launcherEnabled.isErr() ||
+      launcherEnabled.unwrap() ==
+          mozilla::LauncherRegistryInfo::EnabledState::ForceDisabled) {
+    // If the launcher is force disabled, we do not send any pings
+    // (since studies and thus telemetry have been opted out)
+    return false;
+  }
+
+  // Capture aError and our module list into context for processing on another
+  // thread.
+  auto thdParam = mozilla::MakeUnique<PingThreadContext>(aError);
+
+  // The ping does a lot of file I/O. Since we want this thread to continue
+  // executing browser startup, we should gather that information on a
+  // background thread.
+  uintptr_t thdHandle =
+      _beginthreadex(nullptr, 0, &SendPingThread, thdParam.get(),
+                     STACK_SIZE_PARAM_IS_A_RESERVATION, nullptr);
+  if (!thdHandle) {
+    return false;
+  }
+
+  // We have handed off thdParam to the background thread
+  mozilla::Unused << thdParam.release();
+
+  ::CloseHandle(reinterpret_cast<HANDLE>(thdHandle));
+  return true;
+#else
+  return false;
+#endif
+}
+
 namespace mozilla {
 
 void HandleLauncherError(const LauncherError& aError) {
-  // This is a placeholder error handler. We'll add telemetry and a fallback
-  // error log in future revisions.
-
 #if defined(MOZ_LAUNCHER_PROCESS)
   LauncherRegistryInfo regInfo;
   Unused << regInfo.DisableDueToFailure();
 #endif  // defined(MOZ_LAUNCHER_PROCESS)
 
-  WindowsError::UniqueString msg = aError.mError.AsString();
-  if (!msg) {
+  if (SendPing(aError) && !gForceEventLog) {
     return;
   }
 
-  ::MessageBoxW(nullptr, msg.get(), L"Firefox", MB_OK | MB_ICONERROR);
+  PostErrorToLog(aError);
+}
+
+void SetLauncherErrorAppData(const StaticXREAppData& aAppData) {
+  gAppData = &aAppData;
+}
+
+void SetLauncherErrorForceEventLog() {
+  gForceEventLog = true;
 }
 
 }  // namespace mozilla
--- a/browser/app/winlauncher/ErrorHandler.h
+++ b/browser/app/winlauncher/ErrorHandler.h
@@ -34,11 +34,18 @@ inline void HandleLauncherError(const La
 // This function is simply a convenience overload that unwraps the provided
 // GenericErrorResult<LauncherError> and forwards it to the main implementation.
 inline void HandleLauncherError(
     const GenericErrorResult<LauncherError>& aResult) {
   LauncherVoidResult r(aResult);
   HandleLauncherError(r);
 }
 
+// Forward declaration
+struct StaticXREAppData;
+
+void SetLauncherErrorAppData(const StaticXREAppData& aAppData);
+
+void SetLauncherErrorForceEventLog();
+
 }  // namespace mozilla
 
 #endif  //  mozilla_ErrorHandler_h
--- a/browser/app/winlauncher/LauncherProcessWin.cpp
+++ b/browser/app/winlauncher/LauncherProcessWin.cpp
@@ -167,58 +167,71 @@ static bool DoLauncherProcessChecks(int&
 
   result |= mozilla::CheckArg(
                 argc, argv, L"launcher", static_cast<const wchar_t**>(nullptr),
                 mozilla::CheckArgFlag::RemoveArg) == mozilla::ARG_FOUND;
 
   return result;
 }
 
-namespace mozilla {
-
-bool RunAsLauncherProcess(int& argc, wchar_t** argv) {
-  bool runAsLauncher = DoLauncherProcessChecks(argc, argv);
-
-  // If we're running as browser, return fast when we're a child process.
-  if (!runAsLauncher &&
-      mozilla::CheckArg(argc, argv, L"contentproc",
+static mozilla::Maybe<bool> RunAsLauncherProcess(int& argc, wchar_t** argv) {
+  // return fast when we're a child process.
+  // (The remainder of this function has some side effects that are
+  // undesirable for content processes)
+  if (mozilla::CheckArg(argc, argv, L"contentproc",
                         static_cast<const wchar_t**>(nullptr),
                         mozilla::CheckArgFlag::None) == mozilla::ARG_FOUND) {
-    return false;
+    return mozilla::Some(false);
   }
 
+  bool runAsLauncher = DoLauncherProcessChecks(argc, argv);
+
 #if defined(MOZ_LAUNCHER_PROCESS)
-  LauncherRegistryInfo::ProcessType desiredType =
-      runAsLauncher ? LauncherRegistryInfo::ProcessType::Launcher
-                    : LauncherRegistryInfo::ProcessType::Browser;
-  LauncherRegistryInfo regInfo;
-  LauncherResult<LauncherRegistryInfo::ProcessType> runAsType =
-      regInfo.Check(desiredType);
+  mozilla::LauncherRegistryInfo::ProcessType desiredType =
+      runAsLauncher ? mozilla::LauncherRegistryInfo::ProcessType::Launcher
+                    : mozilla::LauncherRegistryInfo::ProcessType::Browser;
+  mozilla::LauncherRegistryInfo regInfo;
+  mozilla::LauncherResult<mozilla::LauncherRegistryInfo::ProcessType>
+      runAsType = regInfo.Check(desiredType);
 
   if (runAsType.isErr()) {
-    HandleLauncherError(runAsType);
-    // If there is an error, we should always fall back to returning false
-    // for safety's sake.
-    return false;
+    mozilla::HandleLauncherError(runAsType);
+    return mozilla::Nothing();
   }
 
-  runAsLauncher =
-      runAsType.unwrap() == LauncherRegistryInfo::ProcessType::Launcher;
+  runAsLauncher = runAsType.unwrap() ==
+                  mozilla::LauncherRegistryInfo::ProcessType::Launcher;
 #endif  // defined(MOZ_LAUNCHER_PROCESS)
 
   if (!runAsLauncher) {
     // In this case, we will be proceeding to run as the browser.
     // We should check MOZ_DEBUG_BROWSER_* env vars.
     MaybeBreakForBrowserDebugging();
   }
 
-  return runAsLauncher;
+  return mozilla::Some(runAsLauncher);
 }
 
-int LauncherMain(int argc, wchar_t* argv[]) {
+namespace mozilla {
+
+Maybe<int> LauncherMain(int& argc, wchar_t* argv[],
+                        const StaticXREAppData& aAppData) {
+  SetLauncherErrorAppData(aAppData);
+
+  if (CheckArg(argc, argv, L"log-launcher-error",
+               static_cast<const wchar_t**>(nullptr),
+               mozilla::CheckArgFlag::RemoveArg) == ARG_FOUND) {
+    SetLauncherErrorForceEventLog();
+  }
+
+  Maybe<bool> runAsLauncher = RunAsLauncherProcess(argc, argv);
+  if (!runAsLauncher || !runAsLauncher.value()) {
+    return Nothing();
+  }
+
   // Make sure that the launcher process itself has image load policies set
   if (IsWin10AnniversaryUpdateOrLater()) {
     const DynamicallyLinkedFunctionPtr<decltype(&SetProcessMitigationPolicy)>
         pSetProcessMitigationPolicy(L"kernel32.dll",
                                     "SetProcessMitigationPolicy");
     if (pSetProcessMitigationPolicy) {
       PROCESS_MITIGATION_IMAGE_LOAD_POLICY imgLoadPol = {};
       imgLoadPol.PreferSystem32Images = 1;
@@ -226,57 +239,58 @@ int LauncherMain(int argc, wchar_t* argv
       DebugOnly<BOOL> setOk = pSetProcessMitigationPolicy(
           ProcessImageLoadPolicy, &imgLoadPol, sizeof(imgLoadPol));
       MOZ_ASSERT(setOk);
     }
   }
 
   if (!SetArgv0ToFullBinaryPath(argv)) {
     HandleLauncherError(LAUNCHER_ERROR_GENERIC());
-    return 1;
+    return Nothing();
   }
 
   LauncherFlags flags = ProcessCmdLine(argc, argv);
 
   nsAutoHandle mediumIlToken;
   LauncherResult<ElevationState> elevationState =
       GetElevationState(flags, mediumIlToken);
   if (elevationState.isErr()) {
     HandleLauncherError(elevationState);
-    return 1;
+    return Nothing();
   }
 
   // If we're elevated, we should relaunch ourselves as a normal user.
   // Note that we only call LaunchUnelevated when we don't need to wait for the
   // browser process.
   if (elevationState.unwrap() == ElevationState::eElevated &&
       !(flags &
         (LauncherFlags::eWaitForBrowser | LauncherFlags::eNoDeelevate)) &&
       !mediumIlToken.get()) {
     LauncherVoidResult launchedUnelevated = LaunchUnelevated(argc, argv);
     bool failed = launchedUnelevated.isErr();
     if (failed) {
       HandleLauncherError(launchedUnelevated);
+      return Nothing();
     }
 
-    return failed;
+    return Some(0);
   }
 
   // Now proceed with setting up the parameters for process creation
   UniquePtr<wchar_t[]> cmdLine(MakeCommandLine(argc, argv));
   if (!cmdLine) {
     HandleLauncherError(LAUNCHER_ERROR_GENERIC());
-    return 1;
+    return Nothing();
   }
 
   const Maybe<bool> isSafeMode =
       IsSafeModeRequested(argc, argv, SafeModeFlag::NoKeyPressCheck);
   if (!isSafeMode) {
     HandleLauncherError(LAUNCHER_ERROR_FROM_WIN32(ERROR_INVALID_PARAMETER));
-    return 1;
+    return Nothing();
   }
 
   ProcThreadAttributes attrs;
   SetMitigationPolicies(attrs, isSafeMode.value());
 
   HANDLE stdHandles[] = {::GetStdHandle(STD_INPUT_HANDLE),
                          ::GetStdHandle(STD_OUTPUT_HANDLE),
                          ::GetStdHandle(STD_ERROR_HANDLE)};
@@ -284,17 +298,17 @@ int LauncherMain(int argc, wchar_t* argv
   attrs.AddInheritableHandles(stdHandles);
 
   DWORD creationFlags = CREATE_SUSPENDED | CREATE_UNICODE_ENVIRONMENT;
 
   STARTUPINFOEXW siex;
   LauncherResult<bool> attrsOk = attrs.AssignTo(siex);
   if (attrsOk.isErr()) {
     HandleLauncherError(attrsOk);
-    return 1;
+    return Nothing();
   }
 
   BOOL inheritHandles = FALSE;
 
   if (attrsOk.unwrap()) {
     creationFlags |= EXTENDED_STARTUPINFO_PRESENT;
 
     if (attrs.HasInheritableHandles()) {
@@ -320,49 +334,49 @@ int LauncherMain(int argc, wchar_t* argv
   } else {
     createOk = ::CreateProcessW(argv[0], cmdLine.get(), nullptr, nullptr,
                                 inheritHandles, creationFlags, nullptr, nullptr,
                                 &siex.StartupInfo, &pi);
   }
 
   if (!createOk) {
     HandleLauncherError(LAUNCHER_ERROR_FROM_LAST());
-    return 1;
+    return Nothing();
   }
 
   nsAutoHandle process(pi.hProcess);
   nsAutoHandle mainThread(pi.hThread);
 
   LauncherVoidResult setupResult =
       PostCreationSetup(process.get(), mainThread.get(), isSafeMode.value());
   if (setupResult.isErr()) {
     HandleLauncherError(setupResult);
     ::TerminateProcess(process.get(), 1);
-    return 1;
+    return Nothing();
   }
 
   if (::ResumeThread(mainThread.get()) == static_cast<DWORD>(-1)) {
     HandleLauncherError(LAUNCHER_ERROR_FROM_LAST());
     ::TerminateProcess(process.get(), 1);
-    return 1;
+    return Nothing();
   }
 
   if (flags & LauncherFlags::eWaitForBrowser) {
     DWORD exitCode;
     if (::WaitForSingleObject(process.get(), INFINITE) == WAIT_OBJECT_0 &&
         ::GetExitCodeProcess(process.get(), &exitCode)) {
       // Propagate the browser process's exit code as our exit code.
-      return static_cast<int>(exitCode);
+      return Some(static_cast<int>(exitCode));
     }
   } else {
     const DWORD timeout =
         ::IsDebuggerPresent() ? INFINITE : kWaitForInputIdleTimeoutMS;
 
     // Keep the current process around until the callback process has created
     // its message queue, to avoid the launched process's windows being forced
     // into the background.
     mozilla::WaitForInputIdle(process.get(), timeout);
   }
 
-  return 0;
+  return Some(0);
 }
 
 }  // namespace mozilla
--- a/browser/app/winlauncher/LauncherProcessWin.h
+++ b/browser/app/winlauncher/LauncherProcessWin.h
@@ -2,24 +2,34 @@
 /* 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 https://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_LauncherProcessWin_h
 #define mozilla_LauncherProcessWin_h
 
+#include "mozilla/Maybe.h"
 #include "mozilla/TypedEnumBits.h"
 
 #include <stdint.h>
 
 namespace mozilla {
 
-bool RunAsLauncherProcess(int& argc, wchar_t* argv[]);
-int LauncherMain(int argc, wchar_t* argv[]);
+// Forward declaration
+struct StaticXREAppData;
+
+/**
+ * Determine whether or not the current process should be run as the launcher
+ * process, and run if so. If we are not supposed to run as the launcher
+ * process, or in the event of a launcher process failure, return Nothing, thus
+ * indicating that we should continue on the original startup code path.
+ */
+Maybe<int> LauncherMain(int& argc, wchar_t* argv[],
+                        const StaticXREAppData& aAppData);
 
 enum class LauncherFlags : uint32_t {
   eNone = 0,
   eWaitForBrowser = (1 << 0),  // Launcher should block until browser finishes
   eNoDeelevate = (1 << 1),     // If elevated, do not attempt to de-elevate
 };
 
 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(LauncherFlags)
--- a/browser/app/winlauncher/moz.build
+++ b/browser/app/winlauncher/moz.build
@@ -4,26 +4,29 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 Library('winlauncher')
 
 FORCE_STATIC_LIB = True
 
 UNIFIED_SOURCES += [
+    '/ipc/mscom/ProcessRuntime.cpp',
     'DllBlocklistWin.cpp',
     'ErrorHandler.cpp',
     'LauncherProcessWin.cpp',
     'LaunchUnelevated.cpp',
 ]
 
 OS_LIBS += [
     'ntdll',
     'oleaut32',
     'ole32',
+    'rpcrt4',
+    'version',
 ]
 
 TEST_DIRS += [
     'test',
 ]
 
 if CONFIG['MOZ_LAUNCHER_PROCESS']:
     UNIFIED_SOURCES += [
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -713,16 +713,29 @@ html|input.urlbar-input {
 
 #urlbar[actionoverride] > #urlbar-display-box,
 #urlbar:not([actiontype="switchtab"]):not([actiontype="extension"]) > #urlbar-display-box,
 #urlbar:not([actiontype="switchtab"]) > #urlbar-display-box > #switchtab,
 #urlbar:not([actiontype="extension"]) > #urlbar-display-box > #extension {
   display: none;
 }
 
+#PopupAutoComplete > richlistbox > richlistitem[originaltype="loginsFooter"] {
+  color: var(--btn-text-color);
+  padding: 0 20px;
+  min-height: 40px;
+  border-top: 1px solid rgba(38,38,38,.15);
+  background-color: #EDEDED;
+}
+
+#PopupAutoComplete > richlistbox > richlistitem[originaltype="loginsFooter"]:hover,
+#PopupAutoComplete > richlistbox > richlistitem[originaltype="loginsFooter"][selected] {
+  background-color: #DCDCDE;
+}
+
 #PopupAutoComplete[firstresultstyle="insecureWarning"] {
   min-width: 200px;
 }
 
 #PopupAutoComplete > richlistbox > richlistitem[originaltype="insecureWarning"] {
   -moz-binding: none;
   height: auto;
 }
--- a/browser/base/content/tabbrowser.css
+++ b/browser/base/content/tabbrowser.css
@@ -17,16 +17,21 @@
 .tab-throbber:not([busy]),
 .tab-icon-sound:not([soundplaying]):not([muted]):not([activemedia-blocked]),
 .tab-icon-sound[pinned],
 .tab-sharing-icon-overlay,
 .tab-icon-overlay {
   display: none;
 }
 
+.tab-icon-pending[preopened],
+.tab-icon-image[preopened] {
+  visibility: hidden;
+}
+
 .tab-sharing-icon-overlay[sharing]:not([selected]),
 .tab-icon-overlay[soundplaying][pinned],
 .tab-icon-overlay[muted][pinned],
 .tab-icon-overlay[activemedia-blocked][pinned],
 .tab-icon-overlay[crashed] {
   display: -moz-box;
 }
 
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -27,16 +27,17 @@ window._gBrowser = {
     Services.els.addSystemEventListener(document, "keydown", this, false);
     if (AppConstants.platform == "macosx") {
       Services.els.addSystemEventListener(document, "keypress", this, false);
     }
     window.addEventListener("sizemodechange", this);
     window.addEventListener("occlusionstatechange", this);
 
     this._setupInitialBrowserAndTab();
+    this._preopenPinnedTabs();
 
     if (Services.prefs.getBoolPref("browser.display.use_system_colors")) {
       this.tabpanels.style.backgroundColor = "-moz-default-background-color";
     } else if (Services.prefs.getIntPref("browser.display.document_color_use") == 2) {
       this.tabpanels.style.backgroundColor =
         Services.prefs.getCharPref("browser.display.background_color");
     }
 
@@ -378,16 +379,39 @@ window._gBrowser = {
     let filter = Cc["@mozilla.org/appshell/component/browser-status-filter;1"]
                   .createInstance(Ci.nsIWebProgress);
     filter.addProgressListener(tabListener, Ci.nsIWebProgress.NOTIFY_ALL);
     this._tabListeners.set(tab, tabListener);
     this._tabFilters.set(tab, filter);
     browser.webProgress.addProgressListener(filter, Ci.nsIWebProgress.NOTIFY_ALL);
   },
 
+  _preopenPinnedTabs() {
+    let numPinnedTabs = 0;
+    let windows = browserWindows();
+    windows.getNext();
+    let isOnlyWindow = !windows.hasMoreElements();
+    if (isOnlyWindow) {
+      numPinnedTabs = Services.prefs.getIntPref("browser.tabs.firstWindowRestore.numPinnedTabs", 0);
+    }
+
+    for (let i = 0; i < numPinnedTabs; i++) {
+      let tab = this.addTrustedTab("about:blank", {
+        skipAnimation: true,
+        noInitialLabel: true,
+        skipBackgroundNotify: true,
+        createLazyBrowser: true,
+        pinned: true,
+        isForFirstWindowRestore: true,
+      });
+
+      tab.setAttribute("preopened", "true");
+    }
+  },
+
   /**
    * BEGIN FORWARDED BROWSER PROPERTIES.  IF YOU ADD A PROPERTY TO THE BROWSER ELEMENT
    * MAKE SURE TO ADD IT HERE AS WELL.
    */
   get canGoBack() {
     return this.selectedBrowser.canGoBack;
   },
 
@@ -588,46 +612,66 @@ window._gBrowser = {
   },
 
   _updateTabBarForPinnedTabs() {
     this.tabContainer._unlockTabSizing();
     this.tabContainer._positionPinnedTabs();
     this.tabContainer._updateCloseButtons();
   },
 
-  _notifyPinnedStatus(aTab) {
+  _sendPinnedTabContentMessage(aTab) {
     this.getBrowserForTab(aTab).messageManager.sendAsyncMessage("Browser:AppTab", { isAppTab: aTab.pinned });
+  },
+
+  _notifyPinnedStatus(aTab, aDeferContentMessage = false) {
+    if (!aDeferContentMessage) {
+      this._sendPinnedTabContentMessage(aTab);
+    }
 
     let event = document.createEvent("Events");
     event.initEvent(aTab.pinned ? "TabPinned" : "TabUnpinned", true, false);
     aTab.dispatchEvent(event);
   },
 
+  _maybeUpdateNumPinnedTabsPref() {
+    if (BrowserWindowTracker.getTopWindow() == window) {
+      Services.prefs.setIntPref("browser.tabs.firstWindowRestore.numPinnedTabs",
+                                this._numPinnedTabs);
+    }
+  },
+
+  activatePreopenedPinnedTab(aTab) {
+    this._insertBrowser(aTab);
+    this._sendPinnedTabContentMessage(aTab);
+  },
+
   pinTab(aTab) {
     if (aTab.pinned)
       return;
 
     if (aTab.hidden)
       this.showTab(aTab);
 
     this.moveTabTo(aTab, this._numPinnedTabs);
     aTab.setAttribute("pinned", "true");
     this._updateTabBarForPinnedTabs();
     this._notifyPinnedStatus(aTab);
+    this._maybeUpdateNumPinnedTabsPref();
   },
 
   unpinTab(aTab) {
     if (!aTab.pinned)
       return;
 
     this.moveTabTo(aTab, this._numPinnedTabs - 1);
     aTab.removeAttribute("pinned");
     aTab.style.marginInlineStart = "";
     this._updateTabBarForPinnedTabs();
     this._notifyPinnedStatus(aTab);
+    this._maybeUpdateNumPinnedTabsPref();
   },
 
   previewTab(aTab, aCallback) {
     let currentTab = this.selectedTab;
     try {
       // Suppress focus, ownership and selected tab changes
       this._previewMode = true;
       this.selectedTab = aTab;
@@ -2291,16 +2335,17 @@ window._gBrowser = {
     bulkOrderedOpen,
     charset,
     createLazyBrowser,
     eventDetail,
     focusUrlBar,
     forceNotRemote,
     fromExternal,
     index,
+    isForFirstWindowRestore,
     lazyTabTitle,
     name,
     nextTabParentId,
     noInitialLabel,
     noReferrer,
     opener,
     openerBrowser,
     originPrincipal,
@@ -2474,16 +2519,19 @@ window._gBrowser = {
       if (tabAfter) {
         this._updateTabsAfterInsert();
       } else {
         t._tPos = index;
       }
 
       if (pinned) {
         this._updateTabBarForPinnedTabs();
+        if (!isForFirstWindowRestore) {
+          this._maybeUpdateNumPinnedTabsPref();
+        }
       }
       this.tabContainer._setPositionalAttributes();
 
       TabBarVisibility.update();
 
       // If we don't have a preferred remote type, and we have a remote
       // opener, use the opener's remote type.
       if (!preferredRemoteType && openerBrowser) {
@@ -2553,23 +2601,25 @@ window._gBrowser = {
 
         if (lazyBrowserURI) {
           // Lazy browser must be explicitly registered so tab will appear as
           // a switch-to-tab candidate in autocomplete.
           this.UrlbarProviderOpenTabs.registerOpenTab(lazyBrowserURI.spec,
                                                       userContextId);
           b.registeredOpenURI = lazyBrowserURI;
         }
-        SessionStore.setTabState(t, {
-          entries: [{
-            url: lazyBrowserURI ? lazyBrowserURI.spec : "about:blank",
-            title: lazyTabTitle,
-            triggeringPrincipal_base64: E10SUtils.serializePrincipal(triggeringPrincipal),
-          }],
-        });
+        if (!isForFirstWindowRestore) {
+          SessionStore.setTabState(t, {
+            entries: [{
+              url: lazyBrowserURI ? lazyBrowserURI.spec : "about:blank",
+              title: lazyTabTitle,
+              triggeringPrincipal_base64: E10SUtils.serializePrincipal(triggeringPrincipal),
+            }],
+          });
+        }
       } else {
         this._insertBrowser(t, true);
       }
     } catch (e) {
       Cu.reportError("Failed to create tab");
       Cu.reportError(e);
       t.remove();
       if (t.linkedBrowser) {
@@ -2597,17 +2647,19 @@ window._gBrowser = {
       if (!aURIObject ||
           (doGetProtocolFlags(aURIObject) & URI_INHERITS_SECURITY_CONTEXT)) {
         b.createAboutBlankContentViewer(originPrincipal);
       }
     }
 
     // If we didn't swap docShells with a preloaded browser
     // then let's just continue loading the page normally.
-    if (!usingPreloadedContent && (!uriIsAboutBlank || !allowInheritPrincipal)) {
+    if (!usingPreloadedContent &&
+        !createLazyBrowser &&
+        (!uriIsAboutBlank || !allowInheritPrincipal)) {
       // pretend the user typed this so it'll be available till
       // the document successfully loads
       if (aURI && !gInitialPages.includes(aURI)) {
         b.userTypedValue = aURI;
       }
 
       let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
       if (allowThirdPartyFixup) {
@@ -2650,17 +2702,17 @@ window._gBrowser = {
       requestAnimationFrame(function() {
         // kick the animation off
         t.setAttribute("fadein", "true");
       });
     }
 
     // Additionally send pinned tab events
     if (pinned) {
-      this._notifyPinnedStatus(t);
+      this._notifyPinnedStatus(t, isForFirstWindowRestore);
     }
 
     return t;
   },
 
   moveTabsToStart(contextTab) {
     let tabs = contextTab.multiselected ?
       this.selectedTabs :
@@ -3148,18 +3200,20 @@ window._gBrowser = {
     if (aTab.hidden)
       this.tabContainer._updateHiddenTabsStatus();
 
     // ... and fix up the _tPos properties immediately.
     for (let i = aTab._tPos; i < this.tabs.length; i++)
       this.tabs[i]._tPos = i;
 
     if (!this._windowIsClosing) {
-      if (wasPinned)
+      if (wasPinned) {
         this.tabContainer._positionPinnedTabs();
+        this._maybeUpdateNumPinnedTabsPref();
+      }
 
       // update tab close buttons state
       this.tabContainer._updateCloseButtons();
 
       setTimeout(function(tabs) {
         tabs._lastTabClosedByMouse = false;
       }, 0, this.tabContainer);
     }
@@ -3834,17 +3888,16 @@ window._gBrowser = {
     let createLazyBrowser = !aTab.linkedPanel;
     let params = {
       eventDetail: { adoptedTab: aTab },
       preferredRemoteType: linkedBrowser.remoteType,
       sameProcessAsFrameLoader: linkedBrowser.frameLoader,
       skipAnimation: true,
       index: aIndex,
       createLazyBrowser,
-      allowInheritPrincipal: createLazyBrowser,
     };
 
     let numPinned = this._numPinnedTabs;
     if (aIndex < numPinned || (aTab.pinned && aIndex == numPinned)) {
       params.pinned = true;
     }
 
     if (aTab.hasAttribute("usercontextid")) {
@@ -5011,16 +5064,17 @@ class TabProgressListener {
           // remoteness or is a new tab/window).
           this.mBrowser.urlbarChangeTracker.startedLoad();
         }
         delete this.mBrowser.initialPageLoadedFromUserAction;
         // If the browser is loading it must not be crashed anymore
         this.mTab.removeAttribute("crashed");
       }
 
+      this.mTab.removeAttribute("preopened");
       if (this._shouldShowProgress(aRequest)) {
         if (!(aStateFlags & Ci.nsIWebProgressListener.STATE_RESTORING) &&
             aWebProgress && aWebProgress.isTopLevel) {
           this.mTab.setAttribute("busy", "true");
           gBrowser._tabAttrModified(this.mTab, ["busy"]);
           this.mTab._notselectedsinceload = !this.mTab.selected;
           gBrowser.syncThrobberAnimations(this.mTab);
         }
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1932,20 +1932,20 @@
                   anonid="tab-loading-burst"
                   class="tab-loading-burst"/>
         <xul:hbox xbl:inherits="pinned,selected=visuallyselected,titlechanged,attention"
                   class="tab-content" align="center">
           <xul:hbox xbl:inherits="fadein,pinned,busy,progress,selected=visuallyselected"
                     anonid="tab-throbber"
                     class="tab-throbber"
                     layer="true"/>
-          <xul:hbox xbl:inherits="fadein,pinned,busy,progress,selected=visuallyselected,pendingicon"
+          <xul:hbox xbl:inherits="fadein,pinned,busy,progress,selected=visuallyselected,pendingicon,preopened"
                     anonid="tab-icon-pending"
                     class="tab-icon-pending"/>
-          <xul:image xbl:inherits="src=image,triggeringprincipal=iconloadingprincipal,requestcontextid,fadein,pinned,selected=visuallyselected,busy,crashed,sharing"
+          <xul:image xbl:inherits="src=image,triggeringprincipal=iconloadingprincipal,requestcontextid,fadein,pinned,selected=visuallyselected,busy,crashed,sharing,preopened"
                      anonid="tab-icon-image"
                      class="tab-icon-image"
                      validate="never"
                      role="presentation"/>
           <xul:image xbl:inherits="sharing,selected=visuallyselected,pinned"
                      anonid="sharing-icon"
                      class="tab-sharing-icon-overlay"
                      role="presentation"/>
--- a/browser/base/content/test/general/browser_minimize.js
+++ b/browser/base/content/test/general/browser_minimize.js
@@ -2,17 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 add_task(async function() {
     registerCleanupFunction(function() {
       window.restore();
     });
     function waitForActive() { return gBrowser.selectedTab.linkedBrowser.docShellIsActive; }
     function waitForInactive() { return !gBrowser.selectedTab.linkedBrowser.docShellIsActive; }
-    await promiseWaitForCondition(waitForActive);
+    await TestUtils.waitForCondition(waitForActive);
     is(gBrowser.selectedTab.linkedBrowser.docShellIsActive, true, "Docshell should be active");
     window.minimize();
-    await promiseWaitForCondition(waitForInactive);
+    await TestUtils.waitForCondition(waitForInactive);
     is(gBrowser.selectedTab.linkedBrowser.docShellIsActive, false, "Docshell should be Inactive");
     window.restore();
-    await promiseWaitForCondition(waitForActive);
+    await TestUtils.waitForCondition(waitForActive);
     is(gBrowser.selectedTab.linkedBrowser.docShellIsActive, true, "Docshell should be active again");
 });
--- a/browser/base/content/test/performance/browser_startup_content.js
+++ b/browser/base/content/test/performance/browser_startup_content.js
@@ -81,26 +81,30 @@ const whitelist = {
     "resource://gre/modules/addons/Content.js",
   ]),
   processScripts: new Set([
     "chrome://global/content/process-content.js",
     "resource:///modules/ContentObservers.js",
     "data:,ChromeUtils.import('resource://gre/modules/ExtensionProcessScript.jsm')",
     "resource://devtools/client/jsonview/converter-observer.js",
     "resource://gre/modules/WebRequestContent.js",
+    "resource://webcompat/aboutPageProcessScript.js",
   ]),
 };
 
 // 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 = {
   modules: new Set([
     "resource://gre/modules/nsAsyncShutdown.jsm",
     "resource://gre/modules/sessionstore/Utils.jsm",
+
+    // Webcompat about:config front-end
+    "resource://webcompat/AboutCompat.jsm",
   ]),
   frameScripts: new Set([]),
   processScripts: new Set([]),
 };
 
 const blacklist = {
   services: new Set([
     "@mozilla.org/base/telemetry-startup;1",
--- a/browser/base/content/test/static/browser_all_files_referenced.js
+++ b/browser/base/content/test/static/browser_all_files_referenced.js
@@ -176,16 +176,18 @@ var whitelist = [
   // kvstore.jsm wraps the API in nsIKeyValue.idl in a more ergonomic API
   // It landed in bug 1490496, and we expect to start using it shortly.
   {file: "resource://gre/modules/kvstore.jsm"},
   {file: "chrome://devtools/content/aboutdebugging-new/tmp-locale/en-US/aboutdebugging.ftl",
    isFromDevTools: true},
   // Bug 1526672
   {file: "resource://app/localization/en-US/browser/touchbar/touchbar.ftl",
    platforms: ["linux", "win"]},
+  // Referenced by the webcompat system addon for localization
+  {file: "resource://gre/localization/en-US/toolkit/about/aboutCompat.ftl"},
 ];
 
 whitelist = new Set(whitelist.filter(item =>
   ("isFromDevTools" in item) == isDevtools &&
   (!item.skipUnofficial || !AppConstants.MOZILLA_OFFICIAL) &&
   (!item.platforms || item.platforms.includes(AppConstants.platform))
 ).map(item => item.file));
 
--- a/browser/base/content/test/webrtc/head.js
+++ b/browser/base/content/test/webrtc/head.js
@@ -29,22 +29,16 @@ function waitForCondition(condition, nex
     if (conditionPassed) {
       moveOn();
     }
     tries++;
   }, 100);
   var moveOn = function() { clearInterval(interval); nextTest(); };
 }
 
-function promiseWaitForCondition(aConditionFn, retryTimes) {
-  return new Promise(resolve => {
-    waitForCondition(aConditionFn, resolve, "Condition didn't pass.", retryTimes);
-  });
-}
-
 /**
  * Waits for a window with the given URL to exist.
  *
  * @param url
  *        The url of the window.
  * @return {Promise} resolved when the window exists.
  * @resolves to the window
  */
@@ -88,17 +82,17 @@ function promiseIndicatorWindow() {
 async function assertWebRTCIndicatorStatus(expected) {
   let ui = ChromeUtils.import("resource:///modules/webrtcUI.jsm", {}).webrtcUI;
   let expectedState = expected ? "visible" : "hidden";
   let msg = "WebRTC indicator " + expectedState;
   if (!expected && ui.showGlobalIndicator) {
     // It seems the global indicator is not always removed synchronously
     // in some cases.
     info("waiting for the global indicator to be hidden");
-    await promiseWaitForCondition(() => !ui.showGlobalIndicator);
+    await TestUtils.waitForCondition(() => !ui.showGlobalIndicator);
   }
   is(ui.showGlobalIndicator, !!expected, msg);
 
   let expectVideo = false, expectAudio = false, expectScreen = false;
   if (expected) {
     if (expected.video)
       expectVideo = true;
     if (expected.audio)
--- a/browser/components/extensions/parent/ext-tabs.js
+++ b/browser/components/extensions/parent/ext-tabs.js
@@ -864,16 +864,23 @@ this.tabs = class extends ExtensionAPI {
             let window = destinationWindow || nativeTab.ownerGlobal;
             let gBrowser = window.gBrowser;
 
             // If we are not moving the tab to a different window, and the window
             // only has one tab, do nothing.
             if (nativeTab.ownerGlobal == window && gBrowser.tabs.length === 1) {
               continue;
             }
+            // If moving between windows, be sure privacy matches.  While gBrowser
+            // prevents this, we want to silently ignore it.
+            if (nativeTab.ownerGlobal != window &&
+                PrivateBrowsingUtils.isBrowserPrivate(window.gBrowser) !=
+                PrivateBrowsingUtils.isBrowserPrivate(nativeTab.ownerGlobal.gBrowser)) {
+              continue;
+            }
 
             let insertionPoint = indexMap.get(window) || moveProperties.index;
             // If the index is -1 it should go to the end of the tabs.
             if (insertionPoint == -1) {
               insertionPoint = gBrowser.tabs.length;
             }
 
             // We can only move pinned tabs to a point within, or just after,
--- a/browser/components/extensions/test/browser/browser_ext_getViews.js
+++ b/browser/components/extensions/test/browser/browser_ext_getViews.js
@@ -71,21 +71,30 @@ async function promiseBrowserContentUnlo
   let unloadPromise = new Promise(resolve => {
     Services.ppmm.addMessageListener(MSG_WINDOW_DESTROYED, function listener() {
       Services.ppmm.removeMessageListener(MSG_WINDOW_DESTROYED, listener);
       resolve();
     });
   });
 
   await ContentTask.spawn(browser, MSG_WINDOW_DESTROYED, (MSG_WINDOW_DESTROYED) => {
-    this.content.addEventListener("unload", () => {
-      // Use process message manager to ensure that the message is delivered
-      // even after the <browser>'s message manager is disconnected.
-      Services.cpmm.sendAsyncMessage(MSG_WINDOW_DESTROYED);
-    }, {once: true});
+    let innerWindowId = this.content.windowUtils.currentInnerWindowID;
+    let observer = (subject) => {
+      if (innerWindowId === subject.QueryInterface(Ci.nsISupportsPRUint64).data) {
+        Services.obs.removeObserver(observer, "inner-window-destroyed");
+
+        // Use process message manager to ensure that the message is delivered
+        // even after the <browser>'s message manager is disconnected.
+        Services.cpmm.sendAsyncMessage(MSG_WINDOW_DESTROYED);
+      }
+    };
+    // Observe inner-window-destroyed, like ExtensionPageChild, to ensure that
+    // the ExtensionPageContextChild instance has been unloaded when we resolve
+    // the unloadPromise.
+    Services.obs.addObserver(observer, "inner-window-destroyed");
   });
 
   // Return an object so that callers can use "await".
   return {unloadPromise};
 }
 
 add_task(async function() {
   let win1 = await BrowserTestUtils.openNewBrowserWindow();
@@ -98,26 +107,30 @@ add_task(async function() {
       "browser_action": {
         "default_popup": "popup.html",
       },
     },
 
     files: {
       "tab.html": `
       <!DOCTYPE html>
-      <html><body>
+      <html>
+      <head><meta charset="utf-8"></head>
+      <body>
       <script src="tab.js"></script>
       </body></html>
       `,
 
       "tab.js": genericChecker,
 
       "popup.html": `
       <!DOCTYPE html>
-      <html><body>
+      <html>
+      <head><meta charset="utf-8"></head>
+      <body>
       <script src="popup.js"></script>
       </body></html>
       `,
 
       "popup.js": genericChecker,
     },
 
     background: genericChecker,
--- a/browser/components/extensions/test/browser/browser_ext_tabs_move_window.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_move_window.js
@@ -1,37 +1,55 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
 add_task(async function() {
   await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.net/");
   let window1 = await BrowserTestUtils.openNewBrowserWindow();
   await BrowserTestUtils.openNewForegroundTab(window1.gBrowser, "http://example.com/");
+  let window2 = await BrowserTestUtils.openNewBrowserWindow({private: true});
+  await BrowserTestUtils.openNewForegroundTab(window2.gBrowser, "http://example.com/");
 
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "permissions": ["tabs"],
     },
-
+    incognitoOverride: "spanning",
     async background() {
       let tabs = await browser.tabs.query({url: "<all_urls>"});
       let destination = tabs[0];
       let source = tabs[1]; // skip over about:blank in window1
+      let privateTab = tabs[2];
+      browser.test.assertTrue(privateTab.incognito, "have a private tab.");
 
       browser.tabs.onUpdated.addListener(() => {
         // Bug 1398272: Adding onUpdated listener broke tab IDs across windows.
       });
 
       // Assuming that this windowId does not exist.
       await browser.test.assertRejects(
         browser.tabs.move(source.id, {windowId: 123144576, index: 0}),
         /Invalid window/,
         "Should receive invalid window error");
 
+      // Test that a tab cannot be moved to a private window.
+      let moved = await browser.tabs.move(source.id, {windowId: privateTab.windowId, index: 0});
+      browser.test.assertEq(moved.length, 0, "tab was not moved to private window");
+      // Test that a private tab cannot be moved to a non-private window.
+      moved = await browser.tabs.move(privateTab.id, {windowId: source.windowId, index: 0});
+      browser.test.assertEq(moved.length, 0, "tab was not moved from private window");
+
+      // Verify tabs did not move between windows via another query.
+      let tabs2 = await browser.tabs.query({url: "<all_urls>"});
+      for (let i = 0; i < 3; i++) {
+        browser.test.assertEq(tabs2[i].windowId, tabs[i].windowId, "tab was not moved to another window");
+        browser.test.assertEq(tabs2[i].incognito, tabs[i].incognito, "tab privateness matches.");
+      }
+
       browser.tabs.move(source.id, {windowId: destination.windowId, index: 0});
 
       tabs = await browser.tabs.query({url: "<all_urls>"});
       browser.test.assertEq(tabs[0].url, "http://example.com/");
       browser.test.assertEq(tabs[0].windowId, destination.windowId);
       browser.test.assertEq(tabs[0].id, source.id);
 
       browser.test.notifyPass("tabs.move.window");
@@ -41,16 +59,17 @@ add_task(async function() {
   await extension.startup();
   await extension.awaitFinish("tabs.move.window");
   await extension.unload();
 
   for (let tab of window.gBrowser.tabs) {
     BrowserTestUtils.removeTab(tab);
   }
   await BrowserTestUtils.closeWindow(window1);
+  await BrowserTestUtils.closeWindow(window2);
 });
 
 add_task(async function test_currentWindowAfterTabMoved() {
   const files = {
     "current.html": "<meta charset=utf-8><script src=current.js></script>",
     "current.js": function() {
       browser.test.onMessage.addListener(msg => {
         if (msg === "current") {
--- a/browser/components/places/PlacesUIUtils.jsm
+++ b/browser/components/places/PlacesUIUtils.jsm
@@ -707,17 +707,17 @@ var PlacesUIUtils = {
         else
           this.markPageAsTyped(aNode.uri);
       }
 
       const isJavaScriptURL = aNode.uri.startsWith("javascript:");
       aWindow.openTrustedLinkIn(aNode.uri, aWhere, {
         allowPopups: isJavaScriptURL,
         inBackground: this.loadBookmarksInBackground,
-        allowInheritPrincipal: isJavaScriptURL && aWhere == "current",
+        allowInheritPrincipal: isJavaScriptURL,
         private: aPrivate,
       });
     }
   },
 
   /**
    * Helper for guessing scheme from an url string.
    * Used to avoid nsIURI overhead in frequently called UI functions.
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -244,18 +244,18 @@ var SessionStore = {
   setBrowserState: function ss_setBrowserState(aState) {
     SessionStoreInternal.setBrowserState(aState);
   },
 
   getWindowState: function ss_getWindowState(aWindow) {
     return SessionStoreInternal.getWindowState(aWindow);
   },
 
-  setWindowState: function ss_setWindowState(aWindow, aState, aOverwrite) {
-    SessionStoreInternal.setWindowState(aWindow, aState, aOverwrite);
+  setWindowState: function ss_setWindowState(aWindow, aState, aOverwrite, aFirstWindow) {
+    SessionStoreInternal.setWindowState(aWindow, aState, aOverwrite, aFirstWindow);
   },
 
   getTabState: function ss_getTabState(aTab) {
     return SessionStoreInternal.getTabState(aTab);
   },
 
   setTabState: function ss_setTabState(aTab, aState) {
     SessionStoreInternal.setTabState(aTab, aState);
@@ -726,16 +726,17 @@ var SessionStoreInternal = {
 
     this._max_tabs_undo = this._prefBranch.getIntPref("sessionstore.max_tabs_undo");
     this._prefBranch.addObserver("sessionstore.max_tabs_undo", this, true);
 
     this._max_windows_undo = this._prefBranch.getIntPref("sessionstore.max_windows_undo");
     this._prefBranch.addObserver("sessionstore.max_windows_undo", this, true);
 
     this._restore_on_demand = this._prefBranch.getBoolPref("sessionstore.restore_on_demand");
+    this._restore_pinned_tabs_on_demand = this._prefBranch.getBoolPref("sessionstore.restore_pinned_tabs_on_demand");
     this._prefBranch.addObserver("sessionstore.restore_on_demand", this, true);
 
     gResistFingerprintingEnabled = Services.prefs.getBoolPref("privacy.resistFingerprinting");
     Services.prefs.addObserver("privacy.resistFingerprinting", this);
   },
 
   /**
    * Called on application shutdown, after notifications:
@@ -2494,22 +2495,25 @@ var SessionStoreInternal = {
     if (DyingWindowCache.has(aWindow)) {
       let data = DyingWindowCache.get(aWindow);
       return JSON.stringify({ windows: [data] });
     }
 
     throw Components.Exception("Window is not tracked", Cr.NS_ERROR_INVALID_ARG);
   },
 
-  setWindowState: function ssi_setWindowState(aWindow, aState, aOverwrite) {
+  setWindowState: function ssi_setWindowState(aWindow, aState, aOverwrite, aFirstWindow) {
     if (!aWindow.__SSi) {
       throw Components.Exception("Window is not tracked", Cr.NS_ERROR_INVALID_ARG);
     }
 
-    this.restoreWindows(aWindow, aState, {overwriteTabs: aOverwrite});
+    this.restoreWindows(aWindow, aState, {
+      overwriteTabs: aOverwrite,
+      firstWindow: aFirstWindow,
+    });
 
     // Notify of changes to closed objects.
     this._notifyOfClosedObjectsChange();
   },
 
   getTabState: function ssi_getTabState(aTab) {
     if (!aTab || !aTab.ownerGlobal) {
       throw Components.Exception("Need a valid tab", Cr.NS_ERROR_INVALID_ARG);
@@ -3198,16 +3202,17 @@ var SessionStoreInternal = {
     } catch (e) {}
 
     // Start the throbber to pretend we're doing something while actually
     // waiting for data from the frame script. This throbber is disabled
     // if the URI is a local about: URI.
     if (!uriObj || (uriObj && !window.gBrowser.isLocalAboutURI(uriObj))) {
       tab.setAttribute("busy", "true");
     }
+    tab.removeAttribute("preopened");
 
     // Hack to ensure that the about:home, about:newtab, and about:welcome
     // favicon is loaded instantaneously, to avoid flickering and improve
     // perceived performance.
     window.gBrowser.setDefaultIcon(tab, uriObj);
 
     TAB_STATE_FOR_BROWSER.set(tab.linkedBrowser, TAB_STATE_WILL_RESTORE);
 
@@ -3645,26 +3650,29 @@ var SessionStoreInternal = {
 
     // disable smooth scrolling while adding, moving, removing and selecting tabs
     let arrowScrollbox = tabbrowser.tabContainer.arrowScrollbox;
     let smoothScroll = arrowScrollbox.smoothScroll;
     arrowScrollbox.smoothScroll = false;
 
     // We need to keep track of the initially open tabs so that they
     // can be moved to the end of the restored tabs.
-    let initialTabs;
+    let initialTabs = [];
     if (!overwriteTabs && firstWindow) {
       initialTabs = Array.slice(tabbrowser.tabs);
     }
 
     // Get rid of tabs that aren't needed anymore.
     if (overwriteTabs) {
       for (let i = tabbrowser.browsers.length - 1; i >= 0; i--) {
-        if (!tabbrowser.tabs[i].selected) {
+        if (!tabbrowser.tabs[i].selected &&
+            !tabbrowser.tabs[i].hasAttribute("preopened")) {
           tabbrowser.removeTab(tabbrowser.tabs[i]);
+        } else if (tabbrowser.tabs[i].hasAttribute("preopened")) {
+          initialTabs.push(tabbrowser.tabs[i]);
         }
       }
     }
 
     let restoreTabsLazily = this._prefBranch.getBoolPref("sessionstore.restore_tabs_lazily") && this._restore_on_demand;
 
     for (var t = 0; t < newTabCount; t++) {
       let tabData = winData.tabs[t];
@@ -3673,23 +3681,34 @@ var SessionStoreInternal = {
       let select = t == selectTab - 1;
       let tab;
 
       // Re-use existing selected tab if possible to avoid the overhead of
       // selecting a new tab.
       if (select &&
           tabbrowser.selectedTab.userContextId == userContextId) {
         tab = tabbrowser.selectedTab;
-        if (!tabData.pinned) {
+        if (tab.pinned && !tabData.pinned) {
           tabbrowser.unpinTab(tab);
+        } else if (!tab.pinned && tabData.pinned) {
+          tabbrowser.removeTab(tabbrowser.tabs[t]);
+          tabbrowser.pinTab(tab);
+          tabbrowser.moveTabTo(tab, t);
         }
+
         tabbrowser.moveTabToEnd();
         if (aWindow.gMultiProcessBrowser && !tab.linkedBrowser.isRemoteBrowser) {
           tabbrowser.updateBrowserRemoteness(tab.linkedBrowser, true);
         }
+      } else if (tabData.pinned &&
+          tabbrowser.tabs[t] &&
+          tabbrowser.tabs[t].pinned &&
+          !tabbrowser.tabs[t].linkedPanel) {
+        tab = tabbrowser.tabs[t];
+        tabbrowser.activatePreopenedPinnedTab(tab);
       }
 
       // Add a new tab if needed.
       if (!tab) {
         let createLazyBrowser = restoreTabsLazily && !select && !tabData.pinned;
 
         let url = "about:blank";
         if (createLazyBrowser && tabData.entries && tabData.entries.length) {
@@ -3728,21 +3747,24 @@ var SessionStoreInternal = {
       }
 
       if (tabData.pinned) {
         tabbrowser.pinTab(tab);
       }
     }
 
     // Move the originally open tabs to the end.
-    if (initialTabs) {
-      let endPosition = tabbrowser.tabs.length - 1;
-      for (let i = 0; i < initialTabs.length; i++) {
-        tabbrowser.unpinTab(initialTabs[i]);
-        tabbrowser.moveTabTo(initialTabs[i], endPosition);
+    let endPosition = tabbrowser.tabs.length - 1;
+    for (let tab of initialTabs) {
+      if (tab.hasAttribute("preopened") &&
+          !tab.linkedPanel) {
+        tabbrowser.removeTab(tab);
+      } else if (!tab.hasAttribute("preopened")) {
+        tabbrowser.unpinTab(tab);
+        tabbrowser.moveTabTo(tab, endPosition);
       }
     }
 
     // We want to correlate the window with data from the last session, so
     // assign another id if we have one. Otherwise clear so we don't do
     // anything with it.
     delete aWindow.__SS_lastSessionWindowID;
     if (winData.__lastSessionWindowID)
@@ -3993,16 +4015,19 @@ var SessionStoreInternal = {
     if (selectedIndex > -1) {
       this.restoreTab(tabbrowser.selectedTab, aTabData[selectedIndex]);
     }
 
     // Restore all tabs.
     for (let t = 0; t < aTabs.length; t++) {
       if (t != selectedIndex) {
         this.restoreTab(aTabs[t], aTabData[t]);
+        if (this._restore_pinned_tabs_on_demand) {
+          aTabs[t].removeAttribute("preopened");
+        }
       }
     }
   },
 
   // Restores the given tab state for a given tab.
   restoreTab(tab, tabData, options = {}) {
     let browser = tab.linkedBrowser;
 
--- a/browser/components/sessionstore/test/browser.ini
+++ b/browser/components/sessionstore/test/browser.ini
@@ -108,16 +108,17 @@ support-files = file_formdata_password.h
 skip-if = (verify && (os == 'win' || os == 'mac'))
 [browser_global_store.js]
 [browser_history_persist.js]
 [browser_label_and_icon.js]
 [browser_merge_closed_tabs.js]
 [browser_old_favicon.js]
 [browser_page_title.js]
 [browser_pending_tabs.js]
+[browser_preopened_apptabs.js]
 [browser_privatetabs.js]
 [browser_purge_shistory.js]
 skip-if = e10s # Bug 1271024
 [browser_replace_load.js]
 [browser_restore_redirect.js]
 [browser_restore_cookies_noOriginAttributes.js]
 [browser_scrollPositions.js]
 [browser_scrollPositionsReaderMode.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/browser_preopened_apptabs.js
@@ -0,0 +1,138 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const PREF_RESTORE_ON_DEMAND = "browser.sessionstore.restore_on_demand";
+const PREF_NUM_PINNED_TABS = "browser.tabs.firstWindowRestore.numPinnedTabs";
+
+function muffleMainWindowType() {
+  let oldWinType = document.documentElement.getAttribute("windowtype");
+  // Check if we've already done this to allow calling multiple times:
+  if (oldWinType != "navigator:testrunner") {
+    // Make the main test window not count as a browser window any longer
+    document.documentElement.setAttribute("windowtype", "navigator:testrunner");
+
+    registerCleanupFunction(() => {
+      document.documentElement.setAttribute("windowtype", oldWinType);
+    });
+  }
+}
+
+registerCleanupFunction(function() {
+  Services.prefs.clearUserPref(PREF_NUM_PINNED_TABS);
+});
+
+add_task(async function testPrefSynced() {
+  Services.prefs.setIntPref(PREF_NUM_PINNED_TABS, 3);
+
+  let state = { windows: [{ tabs: [
+    { entries: [{ url: "http://example.org/#1", triggeringPrincipal_base64 }], pinned: true },
+    { entries: [{ url: "http://example.org/#2", triggeringPrincipal_base64 }], pinned: true },
+    { entries: [{ url: "http://example.org/#3", triggeringPrincipal_base64 }], pinned: true },
+  ], selected: 3 }] };
+
+  muffleMainWindowType();
+  let win = await BrowserTestUtils.openNewBrowserWindow();
+  Assert.equal([...win.gBrowser.tabs].filter(t => t.pinned).length, 3);
+  Assert.equal([...win.gBrowser.tabs].filter(t => !!t.linkedPanel).length, 1);
+  await setWindowState(win, state, false, true);
+  Assert.equal([...win.gBrowser.tabs].filter(t => t.pinned).length, 3);
+  Assert.equal([...win.gBrowser.tabs].filter(t => !!t.linkedPanel).length, 4);
+  await BrowserTestUtils.closeWindow(win);
+});
+
+add_task(async function testPrefSyncedSelected() {
+  Services.prefs.setIntPref(PREF_NUM_PINNED_TABS, 3);
+
+  let state = { windows: [{ tabs: [
+    { entries: [{ url: "http://example.org/#1", triggeringPrincipal_base64 }], pinned: true },
+    { entries: [{ url: "http://example.org/#2", triggeringPrincipal_base64 }], pinned: true },
+    { entries: [{ url: "http://example.org/#3", triggeringPrincipal_base64 }], pinned: true },
+  ], selected: 1 }] };
+
+  muffleMainWindowType();
+  let win = await BrowserTestUtils.openNewBrowserWindow();
+  Assert.equal([...win.gBrowser.tabs].filter(t => t.pinned).length, 3);
+  Assert.equal([...win.gBrowser.tabs].filter(t => !!t.linkedPanel).length, 1);
+  await setWindowState(win, state, false, true);
+  Assert.equal([...win.gBrowser.tabs].filter(t => t.pinned).length, 3);
+  Assert.equal([...win.gBrowser.tabs].filter(t => !!t.linkedPanel).length, 4);
+  await BrowserTestUtils.closeWindow(win);
+});
+
+add_task(async function testPrefTooHigh() {
+  Services.prefs.setIntPref(PREF_NUM_PINNED_TABS, 5);
+
+  let state = { windows: [{ tabs: [
+    { entries: [{ url: "http://example.org/#1", triggeringPrincipal_base64 }], pinned: true },
+    { entries: [{ url: "http://example.org/#2", triggeringPrincipal_base64 }], pinned: true },
+    { entries: [{ url: "http://example.org/#3", triggeringPrincipal_base64 }], pinned: true },
+  ], selected: 3 }] };
+
+  muffleMainWindowType();
+  let win = await BrowserTestUtils.openNewBrowserWindow();
+  Assert.equal([...win.gBrowser.tabs].filter(t => t.pinned).length, 5);
+  Assert.equal([...win.gBrowser.tabs].filter(t => !!t.linkedPanel).length, 1);
+  await setWindowState(win, state, false, true);
+  Assert.equal([...win.gBrowser.tabs].filter(t => t.pinned).length, 3);
+  Assert.equal([...win.gBrowser.tabs].filter(t => !!t.linkedPanel).length, 4);
+  await BrowserTestUtils.closeWindow(win);
+});
+
+add_task(async function testPrefTooLow() {
+  Services.prefs.setIntPref(PREF_NUM_PINNED_TABS, 1);
+
+  let state = { windows: [{ tabs: [
+    { entries: [{ url: "http://example.org/#1", triggeringPrincipal_base64 }], pinned: true },
+    { entries: [{ url: "http://example.org/#2", triggeringPrincipal_base64 }], pinned: true },
+    { entries: [{ url: "http://example.org/#3", triggeringPrincipal_base64 }], pinned: true },
+  ], selected: 3 }] };
+
+  muffleMainWindowType();
+  let win = await BrowserTestUtils.openNewBrowserWindow();
+  Assert.equal([...win.gBrowser.tabs].filter(t => t.pinned).length, 1);
+  Assert.equal([...win.gBrowser.tabs].filter(t => !!t.linkedPanel).length, 1);
+  await setWindowState(win, state, false, true);
+  Assert.equal([...win.gBrowser.tabs].filter(t => t.pinned).length, 3);
+  Assert.equal([...win.gBrowser.tabs].filter(t => !!t.linkedPanel).length, 4);
+  await BrowserTestUtils.closeWindow(win);
+});
+
+add_task(async function testPrefTooHighSelected() {
+  Services.prefs.setIntPref(PREF_NUM_PINNED_TABS, 5);
+
+  let state = { windows: [{ tabs: [
+    { entries: [{ url: "http://example.org/#1", triggeringPrincipal_base64 }], pinned: true },
+    { entries: [{ url: "http://example.org/#2", triggeringPrincipal_base64 }], pinned: true },
+    { entries: [{ url: "http://example.org/#3", triggeringPrincipal_base64 }], pinned: true },
+  ], selected: 1 }] };
+
+  muffleMainWindowType();
+  let win = await BrowserTestUtils.openNewBrowserWindow();
+  Assert.equal([...win.gBrowser.tabs].filter(t => t.pinned).length, 5);
+  Assert.equal([...win.gBrowser.tabs].filter(t => !!t.linkedPanel).length, 1);
+  await setWindowState(win, state, false, true);
+  Assert.equal([...win.gBrowser.tabs].filter(t => t.pinned).length, 3);
+  Assert.equal([...win.gBrowser.tabs].filter(t => !!t.linkedPanel).length, 4);
+  await BrowserTestUtils.closeWindow(win);
+});
+
+add_task(async function testPrefTooLowSelected() {
+  Services.prefs.setIntPref(PREF_NUM_PINNED_TABS, 1);
+
+  let state = { windows: [{ tabs: [
+    { entries: [{ url: "http://example.org/#1", triggeringPrincipal_base64 }], pinned: true },
+    { entries: [{ url: "http://example.org/#2", triggeringPrincipal_base64 }], pinned: true },
+    { entries: [{ url: "http://example.org/#3", triggeringPrincipal_base64 }], pinned: true },
+  ], selected: 1 }] };
+
+  muffleMainWindowType();
+  let win = await BrowserTestUtils.openNewBrowserWindow();
+  Assert.equal([...win.gBrowser.tabs].filter(t => t.pinned).length, 1);
+  Assert.equal([...win.gBrowser.tabs].filter(t => !!t.linkedPanel).length, 1);
+  await setWindowState(win, state, false, true);
+  Assert.equal([...win.gBrowser.tabs].filter(t => t.pinned).length, 3);
+  Assert.equal([...win.gBrowser.tabs].filter(t => !!t.linkedPanel).length, 4);
+  await BrowserTestUtils.closeWindow(win);
+});
--- a/browser/components/sessionstore/test/head.js
+++ b/browser/components/sessionstore/test/head.js
@@ -185,18 +185,19 @@ function promiseWindowRestored(win) {
   return new Promise(resolve => win.addEventListener("SSWindowRestored", resolve, {once: true}));
 }
 
 async function setBrowserState(state, win = window) {
   ss.setBrowserState(typeof state != "string" ? JSON.stringify(state) : state);
   await promiseWindowRestored(win);
 }
 
-async function setWindowState(win, state, overwrite = false) {
-  ss.setWindowState(win, typeof state != "string" ? JSON.stringify(state) : state, overwrite);
+async function setWindowState(win, state, overwrite = false, firstWindow = false) {
+  ss.setWindowState(win, typeof state != "string" ? JSON.stringify(state) : state,
+                    overwrite, firstWindow);
   await promiseWindowRestored(win);
 }
 
 /**
  * Wait for a content -> chrome message.
  */
 function promiseContentMessage(browser, name) {
   let mm = browser.messageManager;
--- a/browser/components/urlbar/UrlbarInput.jsm
+++ b/browser/components/urlbar/UrlbarInput.jsm
@@ -813,16 +813,24 @@ class UrlbarInput {
     try {
       UrlbarUtils.addToUrlbarHistory(url, this.window);
     } catch (ex) {
       // Things may go wrong when adding url to session history,
       // but don't let that interfere with the loading of the url.
       Cu.reportError(ex);
     }
 
+    // Reset DOS mitigations for the basic auth prompt.
+    // TODO: When bug 1498553 is resolved, we should be able to
+    // remove the !triggeringPrincipal condition here.
+    if (!params.triggeringPrincipal ||
+        params.triggeringPrincipal.isSystemPrincipal) {
+      delete browser.canceledAuthenticationPromptCounter;
+    }
+
     params.allowThirdPartyFixup = true;
 
     if (openUILinkWhere == "current") {
       params.targetBrowser = browser;
       params.indicateErrorPageLoad = true;
       params.allowPinnedTabHostChange = true;
       params.allowPopups = url.startsWith("javascript:");
     } else {
new file mode 100644
--- /dev/null
+++ b/browser/extensions/webcompat/AboutCompat.jsm
@@ -0,0 +1,35 @@
+/* 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 = ["AboutCompat"];
+
+const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+const addonID = "webcompat@mozilla.org";
+const addonPageRelativeURL = "/aboutCompat.html";
+
+function AboutCompat() {
+  this.chromeURL = WebExtensionPolicy.getByID(addonID).getURL(addonPageRelativeURL);
+}
+AboutCompat.prototype = {
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIAboutModule]),
+  getURIFlags() {
+    return Ci.nsIAboutModule.ALLOW_SCRIPT |
+           Ci.nsIAboutModule.URI_MUST_LOAD_IN_CHILD;
+  },
+
+  newChannel(aURI, aLoadInfo) {
+    const uri = Services.io.newURI(this.chromeURL);
+    aLoadInfo.resultPrincipalURI = uri;
+    const channel = Services.io.newChannelFromURIWithLoadInfo(uri, aLoadInfo);
+    channel.originalURI = aURI;
+
+    if (this.uriFlags & Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT) {
+      channel.owner = null;
+    }
+    return channel;
+  },
+};
new file mode 100644
--- /dev/null
+++ b/browser/extensions/webcompat/aboutCompat.css
@@ -0,0 +1,190 @@
+@media screen and (min-device-width:481px) {
+  :root {
+    font-family: sans-serif;
+    margin: 40px auto;
+    min-width: 30em;
+    max-width: 60em;
+  }
+
+  table {
+    width: 100%;
+    padding-bottom: 2em;
+  }
+
+  .float-right {
+    float: right;
+  }
+
+  .hidden {
+    display: none;
+  }
+
+  .table-title-container {
+    align-items: center;
+    display: flex;
+    justify-content: space-between;
+  }
+
+  .wide-button {
+    display: block;
+    min-height: 32px;
+    padding-left: 30px;
+    padding-right: 30px;
+  }
+
+  .submitting {
+    background-image: url(chrome://global/skin/icons/loading.png);
+    background-position: center;
+    background-repeat: no-repeat;
+    background-size: 16px;
+  }
+
+  .submitting .submit-crash-button-label {
+    display: none;
+  }
+
+  .failed-to-submit {
+    color: #ca8695;
+  }
+
+  a.button-as-link {
+    -moz-appearance: none;
+    min-height: 30px;
+    color: var(--in-content-text-color) !important;
+    border: 1px solid var(--in-content-box-border-color) !important;
+    border-radius: 2px;
+    background-color: var(--in-content-page-background);
+    line-height: 30px;
+    margin: 4px 8px;
+    /* Ensure font-size isn't overridden by widget styling (e.g. in forms.css) */
+    font-size: 1em;
+  }
+
+  a.button-as-link:hover {
+    background-color: var(--in-content-box-background-hover) !important;
+    text-decoration: none;
+  }
+
+  h2.lighter-font-weight {
+    font-weight: lighter;
+  }
+
+  html[dir=ltr] th {
+    text-align: left;
+  }
+
+  html[dir=rtl] th {
+    text-align: right;
+  }
+}
+
+@media screen and (min-device-width:320px) and (max-device-width:480px) {
+  * {
+    margin: 0;
+    padding: 0;
+  }
+
+  html {
+    font-family: sans-serif;
+    font-size: 14px;
+    -moz-text-size-adjust: none;
+    background-color: #f5f5f5;
+  }
+
+  table, tr, p {
+    display: block;
+    background: #fff;
+  }
+
+  table {
+    border-top: 2px solid #0a84ff;
+    margin-top: -2px;
+    position: absolute;
+    width: 100%;
+    z-index: 1;
+    display: none;
+  }
+
+  tr {
+    position: relative;
+    border-bottom: 1px solid #d7d9db;
+    padding: 1em;
+  }
+
+  a {
+    color: #000;
+    font-size: 94%;
+  }
+
+  .tab {
+    cursor: pointer;
+    position: relative;
+    z-index: 2;
+    display: inline-block;
+    text-align: left;
+    padding: 1em;
+    font-weight: bold;
+    border-top-left-radius: 3px;
+    border-top-right-radius: 3px;
+    border: 1px solid #d7d9db;
+    border-bottom: 0;
+    margin-bottom: 2px;
+    background: #f5f5f5;
+    color: #363b40;
+    font-size: 1em;
+    font-weight: bold;
+    padding: 1em;
+  }
+
+  .tab.active {
+    border-bottom-color: #fff;
+    background: #fff;
+    margin-bottom: 0;
+    padding-bottom: calc(1em + 2px);
+  }
+
+  .tab.active + table {
+    display: block;
+  }
+
+  td {
+    display: block;
+    position: relative;
+    padding-right: 6.5em;
+  }
+
+  td[colspan="4"] {
+    padding: 1em;
+    font-style: italic;
+    text-align: center;
+  }
+
+  td:not([colspan]):nth-child(1) {
+    font-weight: bold;
+  }
+
+  td:not([colspan]):nth-child(1) {
+    padding-bottom: 0.25em;
+  }
+
+  td:nth-child(3) {
+    display: contents;
+  }
+
+  button {
+    background: #e8e8e7;
+    position: absolute;
+    right: 0;
+    top: 0;
+    bottom: 0;
+    width: 6em;
+    border: 0;
+    border-left: 1px solid #d7d9db;
+    -moz-appearance: none;
+    color: #000;
+  }
+
+  button::-moz-focus-inner {
+    border: 0;
+  }
+}
new file mode 100644
--- /dev/null
+++ b/browser/extensions/webcompat/aboutCompat.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <link rel="stylesheet" href="aboutCompat.css" />
+  <link rel="stylesheet" media="screen and (min-device-width:481px), projection" type="text/css"
+          href="chrome://global/skin/in-content/common.css"/>
+  <link rel="localization" href="toolkit/about/aboutCompat.ftl"/>
+  <title data-l10n-id="text-title"></title>
+  <script src="aboutCompat.js"></script>
+  </head>
+<body>
+  <h2 class="tab active" data-l10n-id="label-overrides"></h2>
+  <table id="overrides">
+    <col/>
+    <col/>
+    <col/>
+  </table>
+  <h2 class="tab" data-l10n-id="label-interventions"></h2>
+  <table id="interventions">
+    <col/>
+    <col/>
+    <col/>
+  </table>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/extensions/webcompat/aboutCompat.js
@@ -0,0 +1,146 @@
+/* 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";
+
+/* globals browser */
+
+const portToAddon = (function() {
+  let port;
+
+  function connect() {
+    port = browser.runtime.connect({name: "AboutCompatTab"});
+    port.onMessage.addListener(onMessageFromAddon);
+    port.onDisconnect.addListener(e => {
+      port = undefined;
+    });
+  }
+
+  connect();
+
+  async function send(message) {
+    if (port) {
+      return port.postMessage(message);
+    }
+    return Promise.reject("background script port disconnected");
+  }
+
+  return {send};
+}());
+
+const $ = function(sel) { return document.querySelector(sel); };
+
+const DOMContentLoadedPromise = new Promise(resolve => {
+  document.addEventListener("DOMContentLoaded", () => {
+    resolve();
+  }, {once: true});
+});
+
+Promise.all([
+  browser.runtime.sendMessage("getOverridesAndInterventions"),
+  DOMContentLoadedPromise,
+]).then(([info]) => {
+  document.body.addEventListener("click", async evt => {
+    const ele = evt.target;
+    if (ele.nodeName === "BUTTON") {
+      const row = ele.closest("[data-id]");
+      if (row) {
+        evt.preventDefault();
+        ele.disabled = true;
+        const id = row.getAttribute("data-id");
+        try {
+          await browser.runtime.sendMessage({command: "toggle", id});
+        } catch (_) {
+          ele.disabled = false;
+        }
+      }
+    } else if (ele.classList.contains("tab")) {
+      document.querySelectorAll(".tab").forEach(tab => {
+        tab.classList.remove("active");
+      });
+      ele.classList.add("active");
+    }
+  });
+
+  redraw(info);
+});
+
+function onMessageFromAddon(msg) {
+  if ("interventionsChanged" in msg) {
+    redrawTable($("#interventions"), msg.interventionsChanged);
+  }
+
+  if ("overridesChanged" in msg) {
+    redrawTable($("#overrides"), msg.overridesChanged);
+  }
+
+  const id = msg.toggling || msg.toggled;
+  const button = $(`[data-id="${id}"] button`);
+  if (!button) {
+    return;
+  }
+  const active = msg.active;
+  document.l10n.setAttributes(button,
+                      active ? "label-disable" : "label-enable");
+  button.disabled = !!msg.toggling;
+}
+
+function redraw(info) {
+  const {overrides, interventions} = info;
+  redrawTable($("#overrides"), overrides);
+  redrawTable($("#interventions"), interventions);
+}
+
+function redrawTable(table, data) {
+  const df = document.createDocumentFragment();
+  table.querySelectorAll("tr").forEach(tr => { tr.remove(); });
+
+  let noEntriesMessage;
+  if (data === false) {
+    noEntriesMessage = "text-disabled-in-about-config";
+  } else if (data.length === 0) {
+    noEntriesMessage = table.id === "overrides" ? "text-no-overrides"
+                                                : "text-no-interventions";
+  }
+
+  if (noEntriesMessage) {
+    const tr = document.createElement("tr");
+    df.appendChild(tr);
+
+    const td = document.createElement("td");
+    td.setAttribute("colspan", "3");
+    document.l10n.setAttributes(td, noEntriesMessage);
+    tr.appendChild(td);
+
+    table.appendChild(df);
+    return;
+  }
+
+  for (const row of data) {
+    const tr = document.createElement("tr");
+    tr.setAttribute("data-id", row.id);
+    df.appendChild(tr);
+
+    let td = document.createElement("td");
+    td.innerText = row.domain;
+    tr.appendChild(td);
+
+    td = document.createElement("td");
+    const a = document.createElement("a");
+    const bug = row.bug;
+    a.href = `https://bugzilla.mozilla.org/show_bug.cgi?id=${bug}`;
+    document.l10n.setAttributes(a, "label-more-information", {bug});
+    a.target = "aboutCompatBug";
+    td.appendChild(a);
+    tr.appendChild(td);
+
+    td = document.createElement("td");
+    tr.appendChild(td);
+    const button = document.createElement("button");
+    document.l10n.setAttributes(button,
+                        row.active ? "label-disable" : "label-enable");
+    td.appendChild(button);
+  }
+  table.appendChild(df);
+}
--- a/browser/extensions/webcompat/aboutConfigPrefs.js
+++ b/browser/extensions/webcompat/aboutConfigPrefs.js
@@ -1,17 +1,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-/* global ExtensionAPI, ExtensionCommon */
+/* global ExtensionAPI, ExtensionCommon, Services, XPCOMUtils */
 
-const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+XPCOMUtils.defineLazyModuleGetters(this, {
+  Services: "resource://gre/modules/Services.jsm",
+});
 
 this.aboutConfigPrefs = class extends ExtensionAPI {
   getAPI(context) {
     const EventManager = ExtensionCommon.EventManager;
     const extensionIDBase = context.extension.id.split("@")[0];
     const extensionPrefNameBase = `extensions.${extensionIDBase}.`;
 
     return {
new file mode 100644
--- /dev/null
+++ b/browser/extensions/webcompat/aboutPage.js
@@ -0,0 +1,47 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+/* global ExtensionAPI, Services, XPCOMUtils */
+
+ChromeUtils.defineModuleGetter(this, "AppConstants",
+                               "resource://gre/modules/AppConstants.jsm");
+
+ChromeUtils.defineModuleGetter(this, "Services",
+                               "resource://gre/modules/Services.jsm");
+
+XPCOMUtils.defineLazyServiceGetter(this, "resProto",
+                                   "@mozilla.org/network/protocol;1?name=resource",
+                                   "nsISubstitutingProtocolHandler");
+
+const ResourceSubstitution = "webcompat";
+const ProcessScriptURL = "resource://webcompat/aboutPageProcessScript.js";
+
+const ShouldStart = ["default", "nightly", "nightly-try"].includes(AppConstants.MOZ_UPDATE_CHANNEL);
+
+this.aboutPage = class extends ExtensionAPI {
+  onStartup() {
+    if (!ShouldStart) {
+      return;
+    }
+
+    const {rootURI} = this.extension;
+
+    resProto.setSubstitution(ResourceSubstitution,
+                             Services.io.newURI("chrome/res/", null, rootURI));
+
+    Services.ppmm.loadProcessScript(ProcessScriptURL, true);
+  }
+
+  onShutdown() {
+    if (!ShouldStart) {
+      return;
+    }
+
+    resProto.setSubstitution(ResourceSubstitution, null);
+
+    Services.ppmm.removeDelayedProcessScript(ProcessScriptURL);
+  }
+};
new file mode 100644
--- /dev/null
+++ b/browser/extensions/webcompat/aboutPage.json
@@ -0,0 +1,4 @@
+[{
+    "namespace": "aboutCompat",
+    "description": "Enables the about:compat page"
+}]
new file mode 100644
--- /dev/null
+++ b/browser/extensions/webcompat/aboutPageProcessScript.js
@@ -0,0 +1,22 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const Cm = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+
+const classID = Components.ID("{97bf9550-2a7b-11e9-b56e-0800200c9a66}");
+
+if (!Cm.isCIDRegistered(classID)) {
+  const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+  const factory = XPCOMUtils.generateSingletonFactory(function() {
+    const {AboutCompat} = ChromeUtils.import("resource://webcompat/AboutCompat.jsm");
+    return new AboutCompat();
+  });
+
+  Cm.registerFactory(classID, "about:compat",
+                     "@mozilla.org/network/protocol/about;1?what=compat",
+                     factory);
+}
new file mode 100644
--- /dev/null
+++ b/browser/extensions/webcompat/background.js
@@ -0,0 +1,92 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+/* global browser, disableInjection, disableOverride, enableInjection,
+          enableOverride, InjectionsEnabled, UAOverridesEnabled */
+
+const UAOverrides = [];
+const Injections = [];
+
+function getOverrideOrInterventionById(id) {
+  for (const [type, things] of Object.entries({
+    overrides: UAOverrides,
+    interventions: Injections,
+  })) {
+    for (const what of things) {
+      if (what.id === id) {
+        return {type, what};
+      }
+    }
+  }
+  return {};
+}
+
+const portsToAboutCompatTabs = (function() {
+  const ports = new Set();
+
+  browser.runtime.onConnect.addListener(port => {
+    ports.add(port);
+    port.onDisconnect.addListener(function() {
+      ports.delete(port);
+    });
+  });
+
+  async function broadcast(message) {
+    for (const port of ports) {
+      port.postMessage(message);
+    }
+  }
+
+  return {broadcast};
+}());
+
+function filterOverrides(overrides) {
+  return overrides.filter(override => override.availableOnPlatform).map(override => {
+    const {id, active, bug, domain} = override;
+    return {id, active, bug, domain};
+  });
+}
+
+browser.runtime.onMessage.addListener(msg => {
+  switch (msg.command || msg) {
+    case "toggle": {
+      const id = msg.id;
+      const {type, what} = getOverrideOrInterventionById(id);
+      if (!what) {
+        return Promise.reject(`No such override or intervention to toggle: ${id}`);
+      }
+      portsToAboutCompatTabs.broadcast({toggling: id, active: what.active}).then(async () => {
+        switch (type) {
+          case "interventions": {
+            if (what.active) {
+              await disableInjection(what);
+            } else {
+              await enableInjection(what);
+            }
+            break;
+          }
+          case "overrides": {
+            if (what.active) {
+              await disableOverride(what);
+            } else {
+              await enableOverride(what);
+            }
+            break;
+          }
+        }
+        portsToAboutCompatTabs.broadcast({toggled: id, active: what.active});
+      });
+      break;
+    }
+    case "getOverridesAndInterventions": {
+      return Promise.resolve({
+        overrides: UAOverridesEnabled && filterOverrides(UAOverrides) || false,
+        interventions: InjectionsEnabled && filterOverrides(Injections) || false,
+      });
+    }
+  }
+  return undefined;
+});
--- a/browser/extensions/webcompat/injections.js
+++ b/browser/extensions/webcompat/injections.js
@@ -1,82 +1,140 @@
 /**
  * For detailed information on our policies, and a documention on this format
  * and its possibilites, please check the Mozilla-Wiki at
  *
  * https://wiki.mozilla.org/Compatibility/Go_Faster_Addon/Override_Policies_and_Workflows#User_Agent_overrides
  */
-const contentScripts = {
-  universal: [
-    {
+
+"use strict";
+
+/* globals browser, filterOverrides, Injections, portsToAboutCompatTabs */
+
+let InjectionsEnabled = true;
+
+for (const injection of [
+  {
+    id: "testinjection",
+    platform: "all",
+    domain: "webcompat-addon-testcases.schub.io",
+    bug: "1287966",
+    contentScripts: {
       matches: ["*://webcompat-addon-testcases.schub.io/*"],
       css: [{file: "injections/css/bug0000000-dummy-css-injection.css"}],
       js: [{file: "injections/js/bug0000000-dummy-js-injection.js"}],
       runAt: "document_start",
     },
-  ],
-  desktop: [
-    {
+  }, {
+    id: "bug1452707",
+    platform: "desktop",
+    domain: "ib.absa.co.za",
+    bug: "1452707",
+    contentScripts: {
       matches: ["https://ib.absa.co.za/*"],
       js: [{file: "injections/js/bug1452707-window.controllers-shim-ib.absa.co.za.js"}],
       runAt: "document_start",
     },
-    {
+  }, {
+    id: "bug1457335",
+    platform: "desktop",
+    domain: "histography.io",
+    bug: "1457335",
+    contentScripts: {
       matches: ["*://histography.io/*"],
       js: [{file: "injections/js/bug1457335-histography.io-ua-change.js"}],
       runAt: "document_start",
     },
-    {
+  }, {
+    id: "bug1472075",
+    platform: "desktop",
+    domain: "bankofamerica.com",
+    bug: "1472075",
+    contentScripts: {
       matches: ["*://*.bankofamerica.com/*"],
       js: [{file: "injections/js/bug1472075-bankofamerica.com-ua-change.js"}],
       runAt: "document_start",
     },
-    {
+  }, {
+    id: "bug1472081",
+    platform: "desktop",
+    domain: "election.gov.np",
+    bug: "1472081",
+    contentScripts: {
       matches: ["http://202.166.205.141/bbvrs/*"],
       js: [{file: "injections/js/bug1472081-election.gov.np-window.sidebar-shim.js"}],
       runAt: "document_start",
       allFrames: true,
     },
-    {
+  }, {
+    id: "bug1482066",
+    platform: "desktop",
+    domain: "portalminasnet.com",
+    bug: "1482066",
+    contentScripts: {
       matches: ["*://portalminasnet.com/*"],
       js: [{file: "injections/js/bug1482066-portalminasnet.com-window.sidebar-shim.js"}],
       runAt: "document_start",
       allFrames: true,
     },
-  ],
-  android: [],
-};
-
-/* globals browser */
+  },
+]) {
+  Injections.push(injection);
+}
 
 let port = browser.runtime.connect();
-let registeredContentScripts = [];
+const ActiveInjections = new Map();
 
 async function registerContentScripts() {
-  let platform = "desktop";
+  const platformMatches = ["all"];
   let platformInfo = await browser.runtime.getPlatformInfo();
-  if (platformInfo.os == "android") {
-    platform = "android";
+  platformMatches.push(platformInfo.os == "android" ? "android" : "desktop");
+
+  for (const injection of Injections) {
+    if (platformMatches.includes(injection.platform)) {
+      injection.availableOnPlatform = true;
+      await enableInjection(injection);
+    }
   }
 
-  let targetContentScripts = contentScripts.universal.concat(contentScripts[platform]);
-  targetContentScripts.forEach(async (contentScript) => {
-    try {
-      let handle = await browser.contentScripts.register(contentScript);
-      registeredContentScripts.push(handle);
-    } catch (ex) {
-      console.error("Registering WebCompat GoFaster content scripts failed: ", ex);
-    }
-  });
+  InjectionsEnabled = true;
+  portsToAboutCompatTabs.broadcast({interventionsChanged: filterOverrides(Injections)});
+}
+
+async function enableInjection(injection) {
+  if (injection.active) {
+    return;
+  }
+
+  try {
+    const handle = await browser.contentScripts.register(injection.contentScripts);
+    ActiveInjections.set(injection, handle);
+    injection.active = true;
+  } catch (ex) {
+    console.error("Registering WebCompat GoFaster content scripts failed: ", ex);
+  }
 }
 
 function unregisterContentScripts() {
-  registeredContentScripts.forEach((contentScript) => {
-    contentScript.unregister();
-  });
+  for (const injection of Injections) {
+    disableInjection(injection);
+  }
+  InjectionsEnabled = false;
+  portsToAboutCompatTabs.broadcast({interventionsChanged: false});
+}
+
+async function disableInjection(injection) {
+  if (!injection.active) {
+    return;
+  }
+
+  const contentScript = ActiveInjections.get(injection);
+  await contentScript.unregister();
+  ActiveInjections.delete(injection);
+  injection.active = false;
 }
 
 port.onMessage.addListener((message) => {
   switch (message.type) {
     case "injection-pref-changed":
       if (message.prefState) {
         registerContentScripts();
       } else {
new file mode 100644
--- /dev/null
+++ b/browser/extensions/webcompat/jar.mn
@@ -0,0 +1,7 @@
+# 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/.
+
+[features/webcompat@mozilla.org] chrome.jar:
+  res/AboutCompat.jsm (AboutCompat.jsm)
+  res/aboutPageProcessScript.js (aboutPageProcessScript.js)
--- a/browser/extensions/webcompat/manifest.json
+++ b/browser/extensions/webcompat/manifest.json
@@ -1,37 +1,46 @@
 {
   "manifest_version": 2,
   "name": "Web Compat",
   "description": "Urgent post-release fixes for web compatibility.",
-  "version": "3.0.0",
+  "version": "4.0.0",
 
   "applications": {
     "gecko": {
       "id": "webcompat@mozilla.org",
       "strict_min_version": "59.0b5"
     }
   },
 
   "experiment_apis": {
     "aboutConfigPrefs": {
       "schema": "aboutConfigPrefs.json",
       "parent": {
         "scopes": ["addon_parent"],
         "script": "aboutConfigPrefs.js",
         "paths": [["aboutConfigPrefs"]]
       }
+    },
+    "aboutPage": {
+      "schema": "aboutPage.json",
+      "parent": {
+        "scopes": ["addon_parent"],
+        "script": "aboutPage.js",
+        "events": ["startup"]
+      }
     }
   },
 
   "permissions": [
     "webRequest",
     "webRequestBlocking",
     "<all_urls>"
   ],
 
   "background": {
     "scripts": [
+      "background.js",
       "injections.js",
       "ua_overrides.js"
     ]
   }
 }
--- a/browser/extensions/webcompat/moz.build
+++ b/browser/extensions/webcompat/moz.build
@@ -3,18 +3,24 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DEFINES['MOZ_APP_VERSION'] = CONFIG['MOZ_APP_VERSION']
 DEFINES['MOZ_APP_MAXVERSION'] = CONFIG['MOZ_APP_MAXVERSION']
 
 FINAL_TARGET_FILES.features['webcompat@mozilla.org'] += [
+  'aboutCompat.css',
+  'aboutCompat.html',
+  'aboutCompat.js',
   'aboutConfigPrefs.js',
   'aboutConfigPrefs.json',
+  'aboutPage.js',
+  'aboutPage.json',
+  'background.js',
   'injections.js',
   'manifest.json',
   'ua_overrides.js'
 ]
 
 FINAL_TARGET_FILES.features['webcompat@mozilla.org']['injections']['css'] += [
   'injections/css/bug0000000-dummy-css-injection.css'
 ]
@@ -23,10 +29,12 @@ FINAL_TARGET_FILES.features['webcompat@m
   'injections/js/bug0000000-dummy-js-injection.js',
   'injections/js/bug1452707-window.controllers-shim-ib.absa.co.za.js',
   'injections/js/bug1457335-histography.io-ua-change.js',
   'injections/js/bug1472075-bankofamerica.com-ua-change.js',
   'injections/js/bug1472081-election.gov.np-window.sidebar-shim.js',
   'injections/js/bug1482066-portalminasnet.com-window.sidebar-shim.js'
 ]
 
+JAR_MANIFESTS += ['jar.mn']
+
 with Files('**'):
   BUG_COMPONENT = ('Web Compatibility Tools', 'Go Faster')
--- a/browser/extensions/webcompat/ua_overrides.js
+++ b/browser/extensions/webcompat/ua_overrides.js
@@ -1,326 +1,417 @@
 /**
  * For detailed information on our policies, and a documention on this format
  * and its possibilites, please check the Mozilla-Wiki at
  *
  * https://wiki.mozilla.org/Compatibility/Go_Faster_Addon/Override_Policies_and_Workflows#User_Agent_overrides
  */
-const UAOverrides = {
-  universal: [
-    /*
-     * This is a dummy override that applies a Chrome UA to a dummy site that
-     * blocks all browsers but Chrome.
-     *
-     * This was only put in place to allow QA to test this system addon on an
-     * actual site, since we were not able to find a proper override in time.
-     */
-    {
+
+"use strict";
+
+/* globals filterOverrides, portsToAboutCompatTabs, UAOverrides */
+
+let UAOverridesEnabled = true;
+
+for (const override of [
+  {
+    id: "testoverride",
+    platform: "all",
+    domain: "webcompat-addon-testcases.schub.io",
+    bug: "1287966",
+    config: {
       matches: ["*://webcompat-addon-testcases.schub.io/*"],
       uaTransformer: (originalUA) => {
         return UAHelpers.getPrefix(originalUA) + " AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36";
       },
     },
-  ],
-  desktop: [
+  }, {
     /*
      * Bug 1464106 - directvnow.com - Create a UA override for Directvnow.com for playback on desktop
      * WebCompat issue #3846 - https://webcompat.com/issues/3846
      *
      * directvnow.com is blocking Firefox via UA sniffing. Outreach is still going
      * on, and playback works fine if we spoof as Chrome.
      */
-    {
+    id: "bug1464106",
+    platform: "desktop",
+    domain: "directvnow.com",
+    bug: "1464106",
+    config: {
       matches: ["*://*.directvnow.com/*"],
       uaTransformer: (originalUA) => {
         return UAHelpers.getPrefix(originalUA) + " AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36";
       },
     },
-  ],
-  android: [
+  }, {
     /*
      * Bug 1480710 - m.imgur.com - Build UA override
      * WebCompat issue #13154 - https://webcompat.com/issues/13154
      *
      * imgur returns a 404 for requests to CSS and JS file if requested with a Fennec
      * User Agent. By removing the Fennec identifies and adding Chrome Mobile's, we
      * receive the correct CSS and JS files.
      */
-    {
+    id: "bug1480710",
+    platform: "android",
+    domain: "m.imgur.com",
+    bug: "1480710",
+    config: {
       matches: ["*://m.imgur.com/*"],
       uaTransformer: (originalUA) => {
         return UAHelpers.getPrefix(originalUA) + " AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.85 Mobile Safari/537.36";
       },
     },
-
+  }, {
     /*
      * Bug 755590 - sites.google.com - top bar doesn't show up in Firefox for Android
      *
      * Google Sites does show a different top bar template based on the User Agent.
      * For Fennec, this results in a broken top bar. Appending Chrome and Mobile Safari
      * identifiers to the UA results in a correct rendering.
      */
-    {
+    id: "bug755590",
+    platform: "android",
+    domain: "sites.google.com",
+    bug: "755590",
+    config: {
       matches: ["*://sites.google.com/*"],
       uaTransformer: (originalUA) => {
         return originalUA + " Chrome/68.0.3440.85 Mobile Safari/537.366";
       },
     },
-
+  }, {
     /*
      * Bug 945963 - tieba.baidu.com serves simplified mobile content to Firefox Android
      * WebCompat issue #18455 - https://webcompat.com/issues/18455
      *
      * tieba.baidu.com and tiebac.baidu.com serve a heavily simplified and less functional
      * mobile experience to Firefox for Android users. Adding the AppleWebKit indicator
      * to the User Agent gets us the same experience.
      */
-    {
+    id: "bug945963",
+    platform: "android",
+    domain: "tieba.baidu.com",
+    bug: "945963",
+    config: {
       matches: ["*://tieba.baidu.com/*", "*://tiebac.baidu.com/*"],
       uaTransformer: (originalUA) => {
         return originalUA + " AppleWebKit/537.36 (KHTML, like Gecko)";
       },
     },
-
+  }, {
     /*
      * Bug 1177298 - Write UA overrides for top Japanese Sites
      * (Imported from ua-update.json.in)
      *
      * To receive the proper mobile version instead of the desktop version or
      * a lower grade mobile experience, the UA is spoofed.
      */
-    {
+    id: "bug1177298",
+    platform: "android",
+    domain: "weather.yahoo.co.jp",
+    bug: "1177298",
+    config: {
       matches: ["*://weather.yahoo.co.jp/*"],
       uaTransformer: (_) => {
         return "Mozilla/5.0 (Linux; Android 5.0.2; Galaxy Nexus Build/IMM76B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.93 Mobile Safari/537.36";
       },
     },
-
+  }, {
     /*
      * Bug 1177298 - Write UA overrides for top Japanese Sites
      * (Imported from ua-update.json.in)
      *
      * To receive the proper mobile version instead of the desktop version or
      * a lower grade mobile experience, the UA is spoofed.
      */
-    {
+    id: "bug1177298",
+    platform: "android",
+    domain: "lohaco.jp",
+    bug: "1177298",
+    config: {
       matches: ["*://*.lohaco.jp/*"],
       uaTransformer: (_) => {
         return "Mozilla/5.0 (Linux; Android 5.0.2; Galaxy Nexus Build/IMM76B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.93 Mobile Safari/537.36";
       },
     },
-
+  }, {
     /*
      * Bug 1177298 - Write UA overrides for top Japanese Sites
      * (Imported from ua-update.json.in)
      *
      * To receive the proper mobile version instead of the desktop version or
      * a lower grade mobile experience, the UA is spoofed.
      */
-    {
+    id: "bug1177298",
+    platform: "android",
+    domain: "nhk.or.jp",
+    bug: "1177298",
+    config: {
       matches: ["*://*.nhk.or.jp/*"],
       uaTransformer: (originalUA) => {
         return originalUA + " AppleWebKit";
       },
     },
-
+  }, {
     /*
      * Bug 1177298 - Write UA overrides for top Japanese Sites
      * (Imported from ua-update.json.in)
      *
      * To receive the proper mobile version instead of the desktop version or
      * a lower grade mobile experience, the UA is spoofed.
      */
-    {
+    id: "bug1177298",
+    platform: "android",
+    domain: "uniqlo.com",
+    bug: "1177298",
+    config: {
       matches: ["*://*.uniqlo.com/*"],
       uaTransformer: (originalUA) => {
         return originalUA + " Mobile Safari";
       },
     },
-
+  }, {
     /*
      * Bug 1338260 - Add UA override for directTV
      * (Imported from ua-update.json.in)
      *
      * DirectTV has issues with scrolling and cut-off images. Pretending to be
      * Chrome for Android fixes those issues.
      */
-    {
+    id: "bug1338260",
+    platform: "android",
+    domain: "directv.com",
+    bug: "1338260",
+    config: {
       matches: ["*://*.directv.com/*"],
       uaTransformer: (_) => {
         return "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36";
       },
     },
-
+  }, {
     /*
      * Bug 1385206 - Create UA override for rakuten.co.jp on Firefox Android
      * (Imported from ua-update.json.in)
      *
      * rakuten.co.jp serves a Desktop version if Firefox is included in the UA.
      */
-    {
+    id: "bug1385206",
+    platform: "android",
+    domain: "rakuten.co.jp",
+    bug: "1385206",
+    config: {
       matches: ["*://*.rakuten.co.jp/*"],
       uaTransformer: (originalUA) => {
         return originalUA.replace(/Firefox.+$/, "");
       },
     },
-
+  }, {
     /*
      * Bug 969844 - mobile.de sends desktop site to Firefox on Android
      *
      * mobile.de sends the desktop site to Fennec. Spooing as Chrome works fine.
      */
-    {
+    id: "bug969844",
+    platform: "android",
+    domain: "mobile.de",
+    bug: "969844",
+    config: {
       matches: ["*://*.mobile.de/*"],
       uaTransformer: (_) => {
         return "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36";
       },
     },
-
+  }, {
     /*
      * Bug 1476436 - mobile.bet365.com - add UA override for fennec
      * WebCompat issue #17010 - https://webcompat.com/issues/17010
      *
      * mobile.bet365.com serves fennec an alternative version with less interactive
      * elements, although they work just fine. Spoofing as Chrome makes the
      * interactive elements appear.
      */
-    {
+    id: "bug1476436",
+    platform: "android",
+    domain: "mobile.bet365.com",
+    bug: "1476436",
+    config: {
       matches: ["*://mobile.bet365.com/*"],
       uaTransformer: (_) => {
         return "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36";
       },
     },
-
+  }, {
     /*
      * Bug 1509831 - cc.com - Add UA override for CC.com
      * WebCompat issue #329 - https://webcompat.com/issues/329
      *
      * ComedyCentral blocks Firefox for not being able to play HLS, which was
      * true in previous versions, but no longer is. With a spoofed Chrome UA,
      * the site works just fine.
      */
-    {
+    id: "bug1509831",
+    platform: "android",
+    domain: "cc.com",
+    bug: "1509831",
+    config: {
       matches: ["*://*.cc.com/*"],
       uaTransformer: (_) => {
         return "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36";
       },
     },
-
+  }, {
     /*
      * Bug 1508564 - cnbc.com - Add UA override for videos on www.cnbc.com
      * WebCompat issue #8410 - https://webcompat.com/issues/8410
      *
      * The video framework loaded in via pdk.theplatform.com fails to
      * acknowledge that Firefox does support HLS, so it fails to find a
      * supported video format and shows the loading bar forever. Spoofing as
      * Chrome works.
      */
-    {
+    id: "bug1508564",
+    platform: "android",
+    domain: "cnbc.com",
+    bug: "1508564",
+    config: {
       matches: ["*://*.cnbc.com/*"],
       uaTransformer: (_) => {
         return "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36";
       },
     },
-
+  }, {
     /*
      * Bug 1508516 - cineflix.com.br - Add UA override for cineflix.com.br/m/
      * WebCompat issue #21553 - https://webcompat.com/issues/21553
      *
      * The site renders a blank page with any Firefox snipped in the UA as it
      * is running into an exception. Spoofing as Chrome makes the site work
      * fine.
      */
-    {
+    id: "bug1508516",
+    platform: "android",
+    domain: "cineflix.com.br",
+    bug: "1508516",
+    config: {
       matches: ["*://*.cineflix.com.br/m/*"],
       uaTransformer: (originalUA) => {
         return UAHelpers.getPrefix(originalUA) + " AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36";
       },
     },
-
+  }, {
     /*
      * Bug 1509852 - redbull.com - Add UA override for redbull.com
      * WebCompat issue #21439 - https://webcompat.com/issues/21439
      *
      * Redbull.com blocks some features, for example the live video player, for
      * Fennec. Spoofing as Chrome results in us rendering the video just fine,
      * and everything else works as well.
      */
-    {
+    id: "bug1509852",
+    platform: "android",
+    domain: "redbull.com",
+    bug: "1509852",
+    config: {
       matches: ["*://*.redbull.com/*"],
       uaTransformer: (originalUA) => {
         return UAHelpers.getPrefix(originalUA) + " AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36";
       },
     },
-
+  }, {
     /*
      * Bug 1509873 - zmags.com - Add UA override for secure.viewer.zmags.com
      * WebCompat issue #21576 - https://webcompat.com/issues/21576
      *
      * The zmags viewer locks out Fennec with a "Browser unsupported" message,
      * but tests showed that it works just fine with a Chrome UA. Outreach
      * attempts were unsuccessful, and as the site has a relatively high rank,
      * we alter the UA.
      */
-    {
+    id: "bug1509873",
+    platform: "android",
+    domain: "zmags.com",
+    bug: "1509873",
+    config: {
       matches: ["*://*.viewer.zmags.com/*"],
       uaTransformer: (originalUA) => {
         return UAHelpers.getPrefix(originalUA) + " AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36";
       },
     },
-  ],
-};
+  },
+]) {
+  UAOverrides.push(override);
+}
 
 /* globals browser */
 
 const UAHelpers = {
   getPrefix(originalUA) {
     return originalUA.substr(0, originalUA.indexOf(")") + 1);
   },
 };
 
-let activeListeners = [];
-function buildAndRegisterListener(matches, transformer) {
-  let listener = (details) => {
+const ActiveListeners = new Map();
+
+function enableOverride(override) {
+  if (override.active) {
+    return;
+  }
+
+  const {matches, uaTransformer} = override.config;
+  const listener = (details) => {
     for (var header of details.requestHeaders) {
       if (header.name.toLowerCase() === "user-agent") {
-        header.value = transformer(header.value);
+        header.value = uaTransformer(header.value);
       }
     }
     return {requestHeaders: details.requestHeaders};
   };
 
   browser.webRequest.onBeforeSendHeaders.addListener(
     listener,
     {urls: matches},
     ["blocking", "requestHeaders"]
   );
 
-  activeListeners.push(listener);
+  ActiveListeners.set(override, listener);
+  override.active = true;
 }
 
 async function registerUAOverrides() {
-  let platform = "desktop";
+  const platformMatches = ["all"];
   let platformInfo = await browser.runtime.getPlatformInfo();
-  if (platformInfo.os == "android") {
-    platform = "android";
+  platformMatches.push(platformInfo.os == "android" ? "android" : "desktop");
+
+  for (const override of UAOverrides) {
+    if (platformMatches.includes(override.platform)) {
+      override.availableOnPlatform = true;
+      enableOverride(override);
+    }
   }
-
-  let targetOverrides = UAOverrides.universal.concat(UAOverrides[platform]);
-  targetOverrides.forEach((override) => {
-    buildAndRegisterListener(override.matches, override.uaTransformer);
-  });
+  UAOverridesEnabled = true;
+  portsToAboutCompatTabs.broadcast({overridesChanged: filterOverrides(UAOverrides)});
 }
 
 function unregisterUAOverrides() {
-  activeListeners.forEach((listener) => {
-    browser.webRequest.onBeforeSendHeaders.removeListener(listener);
-  });
+  for (const override of UAOverrides) {
+    disableOverride(override);
+  }
+  UAOverridesEnabled = false;
+  portsToAboutCompatTabs.broadcast({overridesChanged: false});
+}
 
-  activeListeners = [];
+function disableOverride(override) {
+  if (!override.active) {
+    return;
+  }
+
+  browser.webRequest.onBeforeSendHeaders.removeListener(ActiveListeners.get(override));
+  override.active = false;
+  ActiveListeners.delete(override);
 }
 
 const OVERRIDE_PREF = "perform_ua_overrides";
 function checkOverridePref() {
   browser.aboutConfigPrefs.getPref(OVERRIDE_PREF).then(value => {
     if (value === undefined) {
       browser.aboutConfigPrefs.setPref(OVERRIDE_PREF, true);
     } else if (value === false) {
--- a/browser/modules/BrowserWindowTracker.jsm
+++ b/browser/modules/BrowserWindowTracker.jsm
@@ -91,16 +91,21 @@ function _trackWindowOrder(window) {
 }
 
 function _untrackWindowOrder(window) {
   let idx = _trackedWindows.indexOf(window);
   if (idx >= 0)
     _trackedWindows.splice(idx, 1);
 }
 
+function _trackPinnedTabs(window) {
+  Services.prefs.setIntPref("browser.tabs.firstWindowRestore.numPinnedTabs",
+                            window.gBrowser._numPinnedTabs);
+}
+
 // Methods that impact a window. Put into single object for organization.
 var WindowHelper = {
   addWindow(window) {
     // Add event listeners
     TAB_EVENTS.forEach(function(event) {
       window.gBrowser.tabContainer.addEventListener(event, _handleEvent);
     });
     WINDOW_EVENTS.forEach(function(event) {
@@ -133,16 +138,17 @@ var WindowHelper = {
 
   onActivate(window) {
     // If this window was the last focused window, we don't need to do anything
     if (window == _trackedWindows[0])
       return;
 
     _untrackWindowOrder(window);
     _trackWindowOrder(window);
+    _trackPinnedTabs(window);
 
     _updateCurrentContentOuterWindowID(window.gBrowser.selectedBrowser);
   },
 };
 
 this.BrowserWindowTracker = {
   /**
    * Get the most recent browser window.
--- a/build/docs/index.rst
+++ b/build/docs/index.rst
@@ -27,16 +27,17 @@ Important Concepts
    defining-binaries
    defining-xpcom-components
    toolchains
    locales
    rust
    sparse
    Support for projects building with GN <gn>
    telemetry
+   test_certificates
 
 integrated development environment (IDE)
 ========================================
 .. toctree::
    :maxdepth: 1
 
    androideclipse
    cppeclipse
new file mode 100644
--- /dev/null
+++ b/build/docs/test_certificates.rst
@@ -0,0 +1,40 @@
+.. _test_certificates:
+
+==============
+Adding Certificates for Testing
+==============
+
+Sometimes we need to write tests for scenarios that require custom client, server or certificate authority (CA) certificates. For that purpose, you can generate such certificates using ``build/pgo/genpgocert.py``.
+
+The certificate specifications (and key specifications) are located in ``build/pgo/certs/``.
+
+To add a new **server certificate**, add a ``${cert_name}.certspec`` file to that folder.
+If it needs a non-default private key, add a corresponding ``${cert_name}.server.keyspec``.
+
+For a new **client certificate**, add a ``${cert_name}.client.keyspec`` and corresponding ``${cert_name}.certspec``.
+
+To add a new **CA**, add a ``${cert_name}.ca.keyspec`` as well as a corresponding ``${cert_name}.certspec`` to that folder.
+
+.. hint::
+
+   * The full syntax for .certspec files is documented at https://searchfox.org/mozilla-central/source/security/manager/ssl/tests/unit/pycert.py
+
+   * The full syntax for .keyspec files is documented at https://searchfox.org/mozilla-central/source/security/manager/ssl/tests/unit/pykey.py
+
+Then regenerate the certificates by running:::
+
+   ./mach python build/pgo/genpgocert.py
+
+These commands will modify cert9.db and key4.db, and if you have added a .keyspec file will generate a ``{$cert_name}.client`` or ``{$cert_name}.ca`` file.
+
+**These files need to be committed.**
+
+If you've created a new server certificate, you probably want to modify ``build/pgo/server-locations.txt`` to add a location with your specified certificate:::
+
+   https://my-test.example.com:443           cert=${cert_name}
+
+You will need to run ``./mach build`` again afterwards.
+
+.. important::
+
+   Make sure to exactly follow the naming conventions and use the same ``cert_name`` in all places
--- a/build/moz.configure/toolchain.configure
+++ b/build/moz.configure/toolchain.configure
@@ -317,16 +317,22 @@ def ccache(value):
         return value
     # If --with-ccache was given without an explicit value, we default to
     # 'ccache'.
     return 'ccache'
 
 
 ccache = check_prog('CCACHE', progs=(), input=ccache)
 
+js_option(env='CCACHE_PREFIX',
+          nargs=1,
+          help='Compiler prefix to use when using ccache')
+
+set_config('CCACHE_PREFIX', depends_if('CCACHE_PREFIX')(lambda prefix: prefix[0]))
+
 # Distinguish ccache from sccache.
 
 
 @depends_if(ccache)
 def ccache_is_sccache(ccache):
     return check_cmd_output(ccache, '--version').startswith('sccache')
 
 
@@ -1216,16 +1222,25 @@ host_c_compiler = compiler('C', host, ot
 host_cxx_compiler = compiler('C++', host, c_compiler=host_c_compiler,
                              other_compiler=cxx_compiler,
                              other_c_compiler=c_compiler)
 
 # Generic compiler-based conditions.
 building_with_gcc = depends(c_compiler)(lambda info: info.type == 'gcc')
 
 
+@depends(cxx_compiler)
+@imports('os')
+def cxx_is_icecream(info):
+    if (os.path.islink(info.compiler) and os.path.basename(
+            os.readlink(info.compiler)) == 'icecc'):
+        return True
+set_config('CXX_IS_ICECREAM', cxx_is_icecream)
+
+
 @depends(c_compiler)
 def msvs_version(info):
     # clang-cl emulates the same version scheme as cl. And MSVS_VERSION needs to
     # be set for GYP on Windows.
     if info.type == 'clang-cl':
         return '2017'
 
     return ''
--- a/build/mozconfig.nasm
+++ b/build/mozconfig.nasm
@@ -2,10 +2,11 @@
 # 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/.
 
 case "$(uname -s)" in
 MINGW*)
     export NASM=$topsrcdir/nasm/nasm.exe
     ;;
 *)
+    export NASM=$topsrcdir/nasm/nasm
     ;;
 esac
--- a/build/pgo/certs/README
+++ b/build/pgo/certs/README
@@ -1,29 +1,13 @@
-The certificate authority and server certificates here are generated by
-$topsrcdir/build/pgo/genpgocert.py.
-
-You can regenerate the certificates by running: ./mach python
-build/pgo/genpgocert.py
-
-To add a new CA, add a ${cert_name}.ca.keyspec as well as a corresponding
-${cert_name}.certspec to this folder.
+This directory contains CA and server certificates for testing.
 
-To add new server certificates, add a ${cert_name}.certspec file to this folder.
-If it needs a non-default private key, add a corresponding
-${cert_name}.server.keyspec.
+You can find instructions on how to add or modify certificates at:
 
-For new client certificates, add a ${cert_name}.client.keyspec and corresponding
-${cert_name}.certspec.
-
-The naming convention here is because the generated ".client" and ".ca" PEM
-files need to be copied into this folder for Mochitests' runtests.py to import.
-
-These commands will modify cert9.db and key4.db. The changes to these should be
-committed.
+https://firefox-source-docs.mozilla.org/build/buildsystem/test_certificates.html
 
 Specific notes for certs:
 
   dynamicPinningGood: Changing this keyspec will require changing
   browser/base/content/test/general/pinning_headers.sjs . You can obtain a new
   valid pin via:
 
   certutil -L -d . -n dynamicPinningGood -r | openssl x509 -inform der -pubkey \
--- a/build/pgo/server-locations.txt
+++ b/build/pgo/server-locations.txt
@@ -38,16 +38,18 @@
 #
 # "cert=nickname" tells the pgo server to use a particular certificate
 # for this host. The certificate is referenced by its nickname that must
 # not contain any spaces. The certificate  key files (PKCS12 modules)
 # for custom certification are loaded from build/pgo/certs
 # directory. When new certificate is added to this dir pgo/ssltunnel
 # must be built then. This is only necessary for cases where we really do
 # want specific certs.
+# You can find instructions on how to add or modify certificates at:
+# https://firefox-source-docs.mozilla.org/build/buildsystem/test_certificates.html
 #
 # "redir=hostname" tells the pgo server is only used for https://
 # hosts while processing the CONNECT tunnel request. It responds
 # to the CONNECT with a 302 and redirection to the hostname instead
 # of connecting to the real back end and replying with a 200. This
 # mode exists primarily to ensure we don't allow a proxy to do that.
 #
 
--- a/config/config.mk
+++ b/config/config.mk
@@ -266,16 +266,20 @@ export LIB
 endif
 
 ifdef MOZ_USING_CCACHE
 ifdef CLANG_CXX
 export CCACHE_CPP2=1
 endif
 endif
 
+ifdef CCACHE_PREFIX
+export CCACHE_PREFIX
+endif
+
 # Set link flags according to whether we want a console.
 ifeq ($(OS_ARCH),WINNT)
 ifdef MOZ_WINCONSOLE
 ifeq ($(MOZ_WINCONSOLE),1)
 WIN32_EXE_LDFLAGS	+= $(WIN32_CONSOLE_EXE_LDFLAGS)
 else # MOZ_WINCONSOLE
 WIN32_EXE_LDFLAGS	+= $(WIN32_GUI_EXE_LDFLAGS)
 endif
--- a/devtools/client/debugger/new/src/components/PrimaryPanes/tests/__snapshots__/SourcesTree.spec.js.snap
+++ b/devtools/client/debugger/new/src/components/PrimaryPanes/tests/__snapshots__/SourcesTree.spec.js.snap
@@ -1,16 +1,28 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`SourcesTree After changing expanded nodes Shows the tree with four.js, five.js and six.js expanded 1`] = `
 <div
   className="sources-pane thread"
   key="pane"
 >
   <div
+    className="node thread-header"
+  >
+    <AccessibleImage
+      className="file"
+    />
+    <span
+      className="label"
+    >
+      Main Thread
+    </span>
+  </div>
+  <div
     className="sources-list"
     key="tree"
   >
     <ManagedTree
       autoExpandAll={false}
       autoExpandDepth={0}
       expanded={
         Array [
@@ -37,16 +49,28 @@ exports[`SourcesTree After changing expa
 `;
 
 exports[`SourcesTree Should show the tree with nothing expanded 1`] = `
 <div
   className="sources-pane thread"
   key="pane"
 >
   <div
+    className="node thread-header"
+  >
+    <AccessibleImage
+      className="file"
+    />
+    <span
+      className="label"
+    >
+      Main Thread
+    </span>
+  </div>
+  <div
     className="sources-list"
     key="tree"
   >
     <ManagedTree
       autoExpandAll={false}
       autoExpandDepth={1}
       getChildren={[Function]}
       getParent={[Function]}
@@ -66,16 +90,28 @@ exports[`SourcesTree Should show the tre
 `;
 
 exports[`SourcesTree When loading initial source Shows the tree with one.js, two.js and three.js expanded 1`] = `
 <div
   className="sources-pane thread"
   key="pane"
 >
   <div
+    className="node thread-header"
+  >
+    <AccessibleImage
+      className="file"
+    />
+    <span
+      className="label"
+    >
+      Main Thread
+    </span>
+  </div>
+  <div
     className="sources-list"
     key="tree"
   >
     <ManagedTree
       autoExpandAll={false}
       autoExpandDepth={0}
       expanded={
         Array [
@@ -102,16 +138,28 @@ exports[`SourcesTree When loading initia
 `;
 
 exports[`SourcesTree on receiving new props updates highlighted items updates highlightItems if selectedSource changes 1`] = `
 <div
   className="sources-pane thread"
   key="pane"
 >
   <div
+    className="node thread-header"
+  >
+    <AccessibleImage
+      className="file"
+    />
+    <span
+      className="label"
+    >
+      Main Thread
+    </span>
+  </div>
+  <div
     className="sources-list"
     key="tree"
   >
     <ManagedTree
       autoExpandAll={false}
       autoExpandDepth={1}
       getChildren={[Function]}
       getParent={[Function]}
--- a/devtools/client/inspector/boxmodel/box-model.js
+++ b/devtools/client/inspector/boxmodel/box-model.js
@@ -7,16 +7,17 @@
 const {
   updateGeometryEditorEnabled,
   updateLayout,
   updateOffsetParent,
 } = require("./actions/box-model");
 
 loader.lazyRequireGetter(this, "EditingSession", "devtools/client/inspector/boxmodel/utils/editing-session");
 loader.lazyRequireGetter(this, "InplaceEditor", "devtools/client/shared/inplace-editor", true);
+loader.lazyRequireGetter(this, "RulePreviewTooltip", "devtools/client/shared/widgets/tooltip/RulePreviewTooltip");
 
 const NUMERIC = /^-?[\d\.]+$/;
 
 /**
  * A singleton instance of the box model controllers.
  *
  * @param  {Inspector} inspector
  *         An instance of the Inspector currently loaded in the toolbox.
@@ -32,16 +33,17 @@ function BoxModel(inspector, window) {
 
   this.onHideBoxModelHighlighter = this.onHideBoxModelHighlighter.bind(this);
   this.onHideGeometryEditor = this.onHideGeometryEditor.bind(this);
   this.onMarkupViewLeave = this.onMarkupViewLeave.bind(this);
   this.onMarkupViewNodeHover = this.onMarkupViewNodeHover.bind(this);
   this.onNewSelection = this.onNewSelection.bind(this);
   this.onShowBoxModelEditor = this.onShowBoxModelEditor.bind(this);
   this.onShowBoxModelHighlighter = this.onShowBoxModelHighlighter.bind(this);
+  this.onShowRulePreviewTooltip = this.onShowRulePreviewTooltip.bind(this);
   this.onSidebarSelect = this.onSidebarSelect.bind(this);
   this.onToggleGeometryEditor = this.onToggleGeometryEditor.bind(this);
 
   this.inspector.selection.on("new-node-front", this.onNewSelection);
   this.inspector.sidebar.on("select", this.onSidebarSelect);
 }
 
 BoxModel.prototype = {
@@ -49,42 +51,56 @@ BoxModel.prototype = {
   /**
    * Destruction function called when the inspector is destroyed. Removes event listeners
    * and cleans up references.
    */
   destroy() {
     this.inspector.selection.off("new-node-front", this.onNewSelection);
     this.inspector.sidebar.off("select", this.onSidebarSelect);
 
+    if (this._tooltip) {
+      this._tooltip.destroy();
+    }
+
     this.untrackReflows();
 
     this._highlighters = null;
+    this._tooltip = null;
     this.document = null;
     this.inspector = null;
     this.walker = null;
   },
 
   get highlighters() {
     if (!this._highlighters) {
       // highlighters is a lazy getter in the inspector.
       this._highlighters = this.inspector.highlighters;
     }
 
     return this._highlighters;
   },
 
+  get rulePreviewTooltip() {
+    if (!this._tooltip) {
+      this._tooltip = new RulePreviewTooltip(this.inspector.toolbox.doc);
+    }
+
+    return this._tooltip;
+  },
+
   /**
    * Returns an object containing the box model's handler functions used in the box
    * model's React component props.
    */
   getComponentProps() {
     return {
       onHideBoxModelHighlighter: this.onHideBoxModelHighlighter,
       onShowBoxModelEditor: this.onShowBoxModelEditor,
       onShowBoxModelHighlighter: this.onShowBoxModelHighlighter,
+      onShowRulePreviewTooltip: this.onShowRulePreviewTooltip,
       onToggleGeometryEditor: this.onToggleGeometryEditor,
     };
   },
 
   /**
    * Returns true if the layout panel is visible, and false otherwise.
    */
   isPanelVisible() {
@@ -255,16 +271,37 @@ BoxModel.prototype = {
         this.inspector.selection.isElementNode()) {
       this.trackReflows();
     }
 
     this.updateBoxModel("new-selection");
   },
 
   /**
+   * Shows the RulePreviewTooltip when a box model editable value is hovered on the
+   * box model panel.
+   *
+   * @param  {Element} target
+   *         The target element.
+   * @param  {String} property
+   *         The name of the property.
+   */
+  onShowRulePreviewTooltip(target, property) {
+    const { highlightProperty } = this.inspector.getPanel("ruleview").view;
+    const isHighlighted = highlightProperty(property);
+
+    // Only show the tooltip if the property is not highlighted.
+    // TODO: In the future, use an associated ruleId for toggling the tooltip instead of
+    // the Boolean returned from highlightProperty.
+    if (!isHighlighted) {
+      this.rulePreviewTooltip.show(target);
+    }
+  },
+
+  /**
    * Shows the inplace editor when a box model editable value is clicked on the
    * box model panel.
    *
    * @param  {DOMNode} element
    *         The element that was clicked.
    * @param  {Event} event
    *         The event object.
    * @param  {String} property
--- a/devtools/client/inspector/boxmodel/components/BoxModel.js
+++ b/devtools/client/inspector/boxmodel/components/BoxModel.js
@@ -17,16 +17,17 @@ const Types = require("../types");
 class BoxModel extends PureComponent {
   static get propTypes() {
     return {
       boxModel: PropTypes.shape(Types.boxModel).isRequired,
       onHideBoxModelHighlighter: PropTypes.func.isRequired,
       onShowBoxModelEditor: PropTypes.func.isRequired,
       onShowBoxModelHighlighter: PropTypes.func.isRequired,
       onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
+      onShowRulePreviewTooltip: PropTypes.func.isRequired,
       onToggleGeometryEditor: PropTypes.func.isRequired,
       showBoxModelProperties: PropTypes.bool.isRequired,
       setSelectedNode: PropTypes.func.isRequired,
     };
   }
 
   constructor(props) {
     super(props);
@@ -43,16 +44,17 @@ class BoxModel extends PureComponent {
 
   render() {
     const {
       boxModel,
       onHideBoxModelHighlighter,
       onShowBoxModelEditor,
       onShowBoxModelHighlighter,
       onShowBoxModelHighlighterForNode,
+      onShowRulePreviewTooltip,
       onToggleGeometryEditor,
       setSelectedNode,
       showBoxModelProperties,
     } = this.props;
 
     return (
       dom.div(
         {
@@ -67,16 +69,17 @@ class BoxModel extends PureComponent {
           boxModel,
           boxModelContainer: this.boxModelContainer,
           ref: boxModelMain => {
             this.boxModelMain = boxModelMain;
           },
           onHideBoxModelHighlighter,
           onShowBoxModelEditor,
           onShowBoxModelHighlighter,
+          onShowRulePreviewTooltip,
         }),
         BoxModelInfo({
           boxModel,
           onToggleGeometryEditor,
         }),
         showBoxModelProperties ?
           BoxModelProperties({
             boxModel,
--- a/devtools/client/inspector/boxmodel/components/BoxModelEditable.js
+++ b/devtools/client/inspector/boxmodel/components/BoxModelEditable.js
@@ -1,44 +1,61 @@
 /* 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 Services = require("Services");
 const { PureComponent } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const { editableItem } = require("devtools/client/shared/inplace-editor");
 
 const LONG_TEXT_ROTATE_LIMIT = 3;
+const HIGHLIGHT_RULE_PREF =
+  Services.prefs.getBoolPref("devtools.layout.boxmodel.highlightProperty");
 
 class BoxModelEditable extends PureComponent {
   static get propTypes() {
     return {
       box: PropTypes.string.isRequired,
       direction: PropTypes.string,
       focusable: PropTypes.bool.isRequired,
       level: PropTypes.string,
       onShowBoxModelEditor: PropTypes.func.isRequired,
+      onShowRulePreviewTooltip: PropTypes.func.isRequired,
       property: PropTypes.string.isRequired,
       textContent: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
     };
   }
 
+  constructor(props) {
+    super(props);
+    this.onMouseOver = this.onMouseOver.bind(this);
+  }
+
   componentDidMount() {
     const { property, onShowBoxModelEditor } = this.props;
 
     editableItem({
       element: this.boxModelEditable,
     }, (element, event) => {
       onShowBoxModelEditor(element, event, property);
     });
   }
 
+  onMouseOver(event) {
+    const { onShowRulePreviewTooltip, property } = this.props;
+
+    if (event.shiftKey && HIGHLIGHT_RULE_PREF) {
+      onShowRulePreviewTooltip(event.target, property);
+    }
+  }
+
   render() {
     const {
       box,
       direction,
       focusable,
       level,
       property,
       textContent,
@@ -57,16 +74,17 @@ class BoxModelEditable extends PureCompo
                       ${rotate ? " boxmodel-rotate" : ""}`,
         },
         dom.span(
           {
             className: "boxmodel-editable",
             "data-box": box,
             tabIndex: box === level && focusable ? 0 : -1,
             title: property,
+            onMouseOver: this.onMouseOver,
             ref: span => {
               this.boxModelEditable = span;
             },
           },
           textContent
         )
       )
     );
--- a/devtools/client/inspector/boxmodel/components/BoxModelMain.js
+++ b/devtools/client/inspector/boxmodel/components/BoxModelMain.js
@@ -20,16 +20,17 @@ const SHARED_L10N = new LocalizationHelp
 class BoxModelMain extends PureComponent {
   static get propTypes() {
     return {
       boxModel: PropTypes.shape(Types.boxModel).isRequired,
       boxModelContainer: PropTypes.object,
       onHideBoxModelHighlighter: PropTypes.func.isRequired,
       onShowBoxModelEditor: PropTypes.func.isRequired,
       onShowBoxModelHighlighter: PropTypes.func.isRequired,
+      onShowRulePreviewTooltip: PropTypes.func.isRequired,
     };
   }
 
   constructor(props) {
     super(props);
 
     this.state = {
       activeDescendant: null,
@@ -380,16 +381,17 @@ class BoxModelMain extends PureComponent
       target.blur();
     }
   }
 
   render() {
     const {
       boxModel,
       onShowBoxModelEditor,
+      onShowRulePreviewTooltip,
     } = this.props;
     const { layout } = boxModel;
     let { height, width } = layout;
     const { activeDescendant: level, focusable } = this.state;
 
     const borderTop = this.getBorderOrPaddingValue("border-top-width");
     const borderRight = this.getBorderOrPaddingValue("border-right-width");
     const borderBottom = this.getBorderOrPaddingValue("border-bottom-width");
@@ -421,25 +423,27 @@ class BoxModelMain extends PureComponent
           focusable,
           level,
           property: "width",
           ref: editable => {
             this.contentEditable = editable;
           },
           textContent: width,
           onShowBoxModelEditor,
+          onShowRulePreviewTooltip,
         }),
         dom.span({}, "\u00D7"),
         BoxModelEditable({
           box: "content",
           focusable,
           level,
           property: "height",
           textContent: height,
           onShowBoxModelEditor,
+          onShowRulePreviewTooltip,
         })
       )
       :
       dom.p({ className: "boxmodel-size" },
         dom.span({ title: "content" },
           SHARED_L10N.getFormatStr("dimensions", width, height)
         )
       );
@@ -539,171 +543,187 @@ class BoxModelMain extends PureComponent
             focusable,
             level,
             property: "position-top",
             ref: editable => {
               this.positionEditable = editable;
             },
             textContent: positionTop,
             onShowBoxModelEditor,
+            onShowRulePreviewTooltip,
           })
           :
           null,
         displayPosition ?
           BoxModelEditable({
             box: "position",
             direction: "right",
             focusable,
             level,
             property: "position-right",
             textContent: positionRight,
             onShowBoxModelEditor,
+            onShowRulePreviewTooltip,
           })
           :
           null,
         displayPosition ?
           BoxModelEditable({
             box: "position",
             direction: "bottom",
             focusable,
             level,
             property: "position-bottom",
             textContent: positionBottom,
             onShowBoxModelEditor,
+            onShowRulePreviewTooltip,
           })
           :
           null,
         displayPosition ?
           BoxModelEditable({
             box: "position",
             direction: "left",
             focusable,
             level,
             property: "position-left",
             textContent: positionLeft,
             onShowBoxModelEditor,
+            onShowRulePreviewTooltip,
           })
           :
           null,
         BoxModelEditable({
           box: "margin",
           direction: "top",
           focusable,
           level,
           property: "margin-top",
           ref: editable => {
             this.marginEditable = editable;
           },
           textContent: marginTop,
           onShowBoxModelEditor,
+          onShowRulePreviewTooltip,
         }),
         BoxModelEditable({
           box: "margin",
           direction: "right",
           focusable,
           level,
           property: "margin-right",
           textContent: marginRight,
           onShowBoxModelEditor,
+          onShowRulePreviewTooltip,
         }),
         BoxModelEditable({
           box: "margin",
           direction: "bottom",
           focusable,
           level,
           property: "margin-bottom",
           textContent: marginBottom,
           onShowBoxModelEditor,
+          onShowRulePreviewTooltip,
         }),
         BoxModelEditable({
           box: "margin",
           direction: "left",
           focusable,
           level,
           property: "margin-left",
           textContent: marginLeft,
           onShowBoxModelEditor,
+          onShowRulePreviewTooltip,
         }),
         BoxModelEditable({
           box: "border",
           direction: "top",
           focusable,
           level,
           property: "border-top-width",
           ref: editable => {
             this.borderEditable = editable;
           },
           textContent: borderTop,
           onShowBoxModelEditor,
+          onShowRulePreviewTooltip,
         }),
         BoxModelEditable({
           box: "border",
           direction: "right",
           focusable,
           level,
           property: "border-right-width",
           textContent: borderRight,
           onShowBoxModelEditor,
+          onShowRulePreviewTooltip,
         }),
         BoxModelEditable({
           box: "border",
           direction: "bottom",
           focusable,
           level,
           property: "border-bottom-width",
           textContent: borderBottom,
           onShowBoxModelEditor,
+          onShowRulePreviewTooltip,
         }),
         BoxModelEditable({
           box: "border",
           direction: "left",
           focusable,
           level,
           property: "border-left-width",
           textContent: borderLeft,
           onShowBoxModelEditor,
+          onShowRulePreviewTooltip,
         }),
         BoxModelEditable({
           box: "padding",
           direction: "top",
           focusable,
           level,
           property: "padding-top",
           ref: editable => {
             this.paddingEditable = editable;
           },
           textContent: paddingTop,
           onShowBoxModelEditor,
+          onShowRulePreviewTooltip,
         }),
         BoxModelEditable({
           box: "padding",
           direction: "right",
           focusable,
           level,
           property: "padding-right",
           textContent: paddingRight,
           onShowBoxModelEditor,
+          onShowRulePreviewTooltip,
         }),
         BoxModelEditable({
           box: "padding",
           direction: "bottom",
           focusable,
           level,
           property: "padding-bottom",
           textContent: paddingBottom,
           onShowBoxModelEditor,
+          onShowRulePreviewTooltip,
         }),
         BoxModelEditable({
           box: "padding",
           direction: "left",
           focusable,
           level,
           property: "padding-left",
           textContent: paddingLeft,
           onShowBoxModelEditor,
+          onShowRulePreviewTooltip,
         }),
         contentBox
       )
     );
   }
 }
 
 module.exports = BoxModelMain;
--- a/devtools/client/inspector/boxmodel/test/browser.ini
+++ b/devtools/client/inspector/boxmodel/test/browser.ini
@@ -17,22 +17,24 @@ support-files =
 [browser_boxmodel_editablemodel.js]
 [browser_boxmodel_editablemodel_allproperties.js]
 disabled=too many intermittent failures (bug 1009322)
 [browser_boxmodel_editablemodel_bluronclick.js]
 [browser_boxmodel_editablemodel_border.js]
 [browser_boxmodel_editablemodel_pseudo.js]
 [browser_boxmodel_editablemodel_stylerules.js]
 [browser_boxmodel_guides.js]
+[browser_boxmodel_jump-to-rule-on-hover.js]
 [browser_boxmodel_layout-accordion-state.js]
 [browser_boxmodel_navigation.js]
 [browser_boxmodel_offsetparent.js]
 [browser_boxmodel_positions.js]
 [browser_boxmodel_properties.js]
 [browser_boxmodel_pseudo-element.js]
 [browser_boxmodel_rotate-labels-on-sides.js]
+[browser_boxmodel_show-tooltip-for-unassociated-rule.js]
 [browser_boxmodel_sync.js]
 [browser_boxmodel_tooltips.js]
 skip-if = true # Bug 1336198
 [browser_boxmodel_update-after-navigation.js]
 [browser_boxmodel_update-after-reload.js]
 [browser_boxmodel_update-in-iframes.js]
 disabled=Bug 1020038 boxmodel-view updates for iframe elements changes
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_jump-to-rule-on-hover.js
@@ -0,0 +1,43 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that hovering over a box model value will jump to its source CSS rule in the
+// rules view when the shift key is pressed.
+
+const TEST_URI =
+  `<style>
+    #box {
+      margin: 5px;
+    }
+  </style>
+  <div id="box"></div>`;
+
+add_task(async function() {
+  await addTab("data:text/html," + encodeURIComponent(TEST_URI));
+  const { inspector, boxmodel } = await openLayoutView();
+  await selectNode("#box", inspector);
+
+  info("Test that hovering over margin-top value highlights the property in rules view.");
+  const ruleView = await inspector.getPanel("ruleview").view;
+  const el = boxmodel.document.querySelector(".boxmodel-margin.boxmodel-top > span");
+
+  info("Wait for mouse to hover over margin-top element.");
+  const onHighlightProperty = ruleView.once("scrolled-to-element");
+  EventUtils.synthesizeMouseAtCenter(el, { type: "mousemove", shiftKey: true },
+    boxmodel.document.defaultView);
+  await onHighlightProperty;
+
+  info("Check that margin-top is visible in the rule view.");
+  const { rules, styleWindow } = ruleView;
+  const marginTop = rules[1].textProps[0].computed[0];
+  ok(isInViewport(marginTop.element, styleWindow),
+    "margin-top is visible in the rule view.");
+});
+
+function isInViewport(element, win) {
+  const { top, left, bottom, right } = element.getBoundingClientRect();
+  return top >= 0 && bottom <= win.innerHeight && left >= 0 && right <= win.innerWidth;
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_show-tooltip-for-unassociated-rule.js
@@ -0,0 +1,37 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const L10N = new LocalizationHelper("devtools/client/locales/inspector.properties");
+
+// Test that hovering over a box model value with no associated rule will show a tooltip
+// saying: "No associated rule".
+
+const TEST_URI =
+  `<style>
+    #box {}
+  </style>
+  <div id="box"></div>`;
+
+add_task(async function() {
+  await addTab("data:text/html," + encodeURIComponent(TEST_URI));
+  const { inspector, boxmodel } = await openLayoutView();
+  const { rulePreviewTooltip } = boxmodel;
+  await selectNode("#box", inspector);
+
+  info("Test that hovering over margin-top shows tooltip showing 'No associated rule'.");
+  const el = boxmodel.document.querySelector(".boxmodel-margin.boxmodel-top > span");
+
+  info("Wait for mouse to hover over margin-top element.");
+  const onRulePreviewTooltipShown = rulePreviewTooltip._tooltip.once("shown", () => {
+    ok(true, "Tooltip shown.");
+    is(rulePreviewTooltip.message.textContent,
+      L10N.getStr("rulePreviewTooltip.noAssociatedRule"),
+      `Tooltip shows ${L10N.getStr("rulePreviewTooltip.noAssociatedRule")}`);
+  });
+  EventUtils.synthesizeMouseAtCenter(el, { type: "mousemove", shiftKey: true },
+    boxmodel.document.defaultView);
+  await onRulePreviewTooltipShown;
+});
--- a/devtools/client/inspector/changes/ChangesContextMenu.js
+++ b/devtools/client/inspector/changes/ChangesContextMenu.js
@@ -23,17 +23,16 @@ class ChangesContextMenu {
     this.inspector = this.view.inspector;
     // Document object to which the Changes panel belongs to.
     this.document = this.view.document;
     // DOM element container for the Changes panel content.
     this.panel = this.document.getElementById("sidebar-panel-changes");
     // Window object to which the Changes panel belongs to.
     this.window = this.document.defaultView;
 
-    this._onCopyChanges = this.view.copyChanges.bind(this.view);
     this._onCopyRule = this.view.copyRule.bind(this.view);
     this._onCopySelection = this.view.copySelection.bind(this.view);
     this._onSelectAll = this._onSelectAll.bind(this);
   }
 
   show(event) {
     this._openMenu({
       target: event.target,
@@ -53,51 +52,29 @@ class ChangesContextMenu {
       accesskey: getStr("changes.contextmenu.copy.accessKey"),
       click: this._onCopySelection,
       disabled: !this._hasTextSelected(),
     });
     menu.append(menuitemCopy);
 
     const ruleEl = target.closest("[data-rule-id]");
     const ruleId = ruleEl ? ruleEl.dataset.ruleId : null;
-    const sourceEl = target.closest("[data-source-id]");
-    const sourceId = sourceEl ? sourceEl.dataset.sourceId : null;
 
-    // When both rule id and source id are found, deal with just for that rule.
-    if (ruleId && sourceId) {
-      // Copy Changes option
-      menu.append(new MenuItem({
-        label: getStr("changes.contextmenu.copyChanges"),
-        click: () => this._onCopyChanges(ruleId, sourceId),
-      }));
-
+    if (ruleId) {
       // Copy Rule option
       menu.append(new MenuItem({
         label: getStr("changes.contextmenu.copyRule"),
         click: () => this._onCopyRule(ruleId),
       }));
 
       menu.append(new MenuItem({
         type: "separator",
       }));
     }
 
-    // When only the source id is found, deal with all changed rules in that source.
-    if (!ruleId && sourceId) {
-      // Copy All Changes option
-      menu.append(new MenuItem({
-        label: getStr("changes.contextmenu.copyAllChanges"),
-        click: () => this._onCopyChanges(null, sourceId),
-      }));
-
-      menu.append(new MenuItem({
-        type: "separator",
-      }));
-    }
-
     // Select All option
     const menuitemSelectAll = new MenuItem({
       label: getStr("changes.contextmenu.selectAll"),
       accesskey: getStr("changes.contextmenu.selectAll.accessKey"),
       click: this._onSelectAll,
     });
     menu.append(menuitemSelectAll);
 
--- a/devtools/client/inspector/changes/ChangesView.js
+++ b/devtools/client/inspector/changes/ChangesView.js
@@ -35,16 +35,18 @@ class ChangesView {
     this.telemetry = this.inspector.telemetry;
     this.window = window;
 
     this.onAddChange = this.onAddChange.bind(this);
     this.onClearChanges = this.onClearChanges.bind(this);
     this.onChangesFront = this.onChangesFront.bind(this);
     this.onContextMenu = this.onContextMenu.bind(this);
     this.onCopy = this.onCopy.bind(this);
+    this.onCopyAllChanges = this.copyAllChanges.bind(this);
+    this.onCopyRule = this.copyRule.bind(this);
     this.destroy = this.destroy.bind(this);
 
     this.init();
   }
 
   get contextMenu() {
     if (!this._contextMenu) {
       this._contextMenu = new ChangesContextMenu(this);
@@ -52,16 +54,18 @@ class ChangesView {
 
     return this._contextMenu;
   }
 
   init() {
     const changesApp = ChangesApp({
       onContextMenu: this.onContextMenu,
       onCopy: this.onCopy,
+      onCopyAllChanges: this.onCopyAllChanges,
+      onCopyRule: this.onCopyRule,
     });
 
     // listen to the front for initialization, add listeners
     // when it is ready
     this._getChangesFront();
 
     // Expose the provider to let inspector.js use it in setupSidebar.
     this.provider = createElement(Provider, {
@@ -100,16 +104,24 @@ class ChangesView {
       // The connection to the server may have been cut, for
       // example during test
       // teardown. Here we just catch the error and silently
       // ignore it.
     }
   }
 
   /**
+   * Handler for the "Copy All Changes" button. Simple wrapper that just calls
+   * |this.copyChanges()| with no filters in order to trigger default operation.
+   */
+  copyAllChanges() {
+    this.copyChanges();
+  }
+
+  /**
    * Handler for the "Copy Changes" option from the context menu.
    * Builds a CSS text with the aggregated changes and copies it to the clipboard.
    *
    * Optional rule and source ids can be used to filter the scope of the operation:
    * - if both a rule id and source id are provided, copy only the changes to the
    * matching rule within the matching source.
    * - if only a source id is provided, copy the changes to all rules within the
    * matching source.
--- a/devtools/client/inspector/changes/components/ChangesApp.js
+++ b/devtools/client/inspector/changes/components/ChangesApp.js
@@ -18,23 +18,51 @@ class ChangesApp extends PureComponent {
   static get propTypes() {
     return {
       // Nested CSS rule tree structure of CSS changes grouped by source (stylesheet)
       changesTree: PropTypes.object.isRequired,
       // Event handler for "contextmenu" event
       onContextMenu: PropTypes.func.isRequired,
       // Event handler for "copy" event
       onCopy: PropTypes.func.isRequired,
+      // Event handler for click on "Copy All Changes" button
+      onCopyAllChanges: PropTypes.func.isRequired,
+      // Event handler for click on "Copy Rule" button
+      onCopyRule: PropTypes.func.isRequired,
     };
   }
 
   constructor(props) {
     super(props);
   }
 
+  renderCopyAllChangesButton() {
+    return dom.button(
+      {
+        className: "changes__copy-all-changes-button",
+        onClick: this.props.onCopyAllChanges,
+        title: getStr("changes.contextmenu.copyAllChangesDescription"),
+      },
+      getStr("changes.contextmenu.copyAllChanges")
+    );
+  }
+
+  renderCopyButton(ruleId) {
+    return dom.button(
+      {
+        className: "changes__copy-rule-button",
+        onClick: e => {
+          this.props.onCopyRule(ruleId);
+        },
+        title: getStr("changes.contextmenu.copyRuleDescription"),
+      },
+      getStr("changes.contextmenu.copyRule")
+    );
+  }
+
   renderDeclarations(remove = [], add = []) {
     const removals = remove
       // Sorting changed declarations in the order they appear in the Rules view.
       .sort((a, b) => a.index > b.index)
       .map(({property, value, index}) => {
         return CSSDeclaration({
           key: "remove-" + property + index,
           className: "level diff-remove",
@@ -59,23 +87,24 @@ class ChangesApp extends PureComponent {
 
     return [removals, additions];
   }
 
   renderRule(ruleId, rule, level = 0) {
     return dom.div(
       {
         key: ruleId,
-        className: "rule devtools-monospace",
+        className: "changes__rule devtools-monospace",
         "data-rule-id": ruleId,
         style: {
           "--diff-level": level,
         },
       },
       this.renderSelectors(rule.selectors),
+      this.renderCopyButton(ruleId),
       // Render any nested child rules if they exist.
       rule.children.map(childRule => {
         return this.renderRule(childRule.ruleId, childRule, level + 1);
       }),
       // Render any changed CSS declarations.
       this.renderDeclarations(rule.remove, rule.add),
       dom.div({ className: `level` }, "}")
     );
@@ -102,17 +131,17 @@ class ChangesApp extends PureComponent {
     }
 
     const elements = [];
 
     for (const [selector, diffClass] of selectorDiffClassMap) {
       elements.push(dom.div(
         {
           key: selector,
-          className: `level selector ${diffClass}`,
+          className: `level changes__selector ${diffClass}`,
           title: selector,
         },
         getDiffMarker(diffClass),
         selector,
         dom.span({}, " {")
       ));
     }
 
@@ -169,16 +198,17 @@ class ChangesApp extends PureComponent {
     return dom.div(
       {
         className: "theme-sidebar inspector-tabpanel",
         id: "sidebar-panel-changes",
         onContextMenu: this.props.onContextMenu,
         onCopy: this.props.onCopy,
       },
       !hasChanges && this.renderEmptyState(),
+      hasChanges && this.renderCopyAllChangesButton(),
       hasChanges && this.renderDiff(this.props.changesTree)
     );
   }
 }
 
 /**
  * Get a React element with text content of either a plus or minus sign according to
  * the given CSS class name used to mark added or removed lines in the changes diff view.
--- a/devtools/client/inspector/changes/selectors/changes.js
+++ b/devtools/client/inspector/changes/selectors/changes.js
@@ -220,20 +220,21 @@ function getChangesStylesheet(state, fil
 
     return removals + additions;
   }
 
   // Iterate through all sources in the change tree and build a CSS stylesheet string.
   return Object.entries(changeTree).reduce((stylesheetText, [sourceId, source]) => {
     const { href, rules } = source;
     // Write code comment with source origin
-    stylesheetText += `/* ${getSourceForDisplay(source)} | ${href} */\n`;
+    stylesheetText += `\n/* ${getSourceForDisplay(source)} | ${href} */\n`;
     // Write CSS rules
     stylesheetText += Object.entries(rules).reduce((str, [ruleId, rule]) => {
-      str += writeRule(ruleId, rule, 0);
+      // Add a new like only after top-level rules (level == 0)
+      str += writeRule(ruleId, rule, 0) + "\n";
       return str;
     }, "");
 
     return stylesheetText;
   }, "");
 }
 
 module.exports = {
--- a/devtools/client/inspector/changes/test/browser_changes_rule_selector.js
+++ b/devtools/client/inspector/changes/test/browser_changes_rule_selector.js
@@ -35,20 +35,20 @@ add_task(async function() {
   const onRuleViewChanged = once(ruleView, "ruleview-changed");
   info("Pressing Enter key to commit the change");
   EventUtils.synthesizeKey("KEY_Enter");
   info("Waiting for rule view to update");
   await onRuleViewChanged;
   info("Wait for the change to be tracked");
   await onTrackChange;
 
-  const rules = panel.querySelectorAll(".rule");
+  const rules = panel.querySelectorAll(".changes__rule");
   is(rules.length, 1, "One rule was tracked as changed");
 
-  const selectors = rules.item(0).querySelectorAll(".selector");
+  const selectors = rules.item(0).querySelectorAll(".changes__selector");
   is(selectors.length, 2, "Two selectors were tracked as changed");
 
   const firstSelector = selectors.item(0);
   is(firstSelector.title, "div", "Old selector name was tracked.");
   ok(firstSelector.classList.contains("diff-remove"), "Old selector was removed.");
 
   const secondSelector = selectors.item(1);
   is(secondSelector.title, ".test", "New selector name was tracked.");
--- a/devtools/client/inspector/changes/test/unit/test_changes_stylesheet.js
+++ b/devtools/client/inspector/changes/test/unit/test_changes_stylesheet.js
@@ -5,39 +5,39 @@
 
 // Check that getChangesStylesheet() serializes tracked changes from nested CSS rules
 // into the expected stylesheet format.
 
 const { getChangesStylesheet } = require("devtools/client/inspector/changes/selectors/changes");
 
 const { CHANGES_STATE } = require("resource://test/mocks");
 
-// Wrap multi-line string in backticks and trim to ensure exact string check in test.
+// Wrap multi-line string in backticks to ensure exact check in test, including new lines.
 const STYLESHEET_FOR_ANCESTOR = `
 /* Inline #0 | http://localhost:5000/at-rules-nested.html */
 
 @media (min-width: 50em) {
   @supports (display: grid) {
     body {
       /* background-color: royalblue; */
       background-color: red;
     }
   }
 }
-`.trim();
+`;
 
-// Wrap multi-line string in backticks and trim to ensure exact string check in test.
+// Wrap multi-line string in backticks to ensure exact check in test, including new lines.
 const STYLESHEET_FOR_DESCENDANT = `
 /* Inline #0 | http://localhost:5000/at-rules-nested.html */
 
 body {
   /* background-color: royalblue; */
   background-color: red;
 }
-`.trim();
+`;
 
 add_test(() => {
   info("Check stylesheet generated for the first ancestor in the CSS rule tree.");
   equal(getChangesStylesheet(CHANGES_STATE), STYLESHEET_FOR_ANCESTOR,
     "Stylesheet includes all ancestors.");
 
   info("Check stylesheet generated for the last descendant in the CSS rule tree.");
   const filter = { sourceIds: ["source1"], ruleIds: ["rule3"] };
--- a/devtools/client/inspector/layout/layout.js
+++ b/devtools/client/inspector/layout/layout.js
@@ -35,16 +35,17 @@ class LayoutView {
       onShowBoxModelHighlighterForNode,
       setSelectedNode,
     } = this.inspector.getCommonComponentProps();
 
     const {
       onHideBoxModelHighlighter,
       onShowBoxModelEditor,
       onShowBoxModelHighlighter,
+      onShowRulePreviewTooltip,
       onToggleGeometryEditor,
     } = this.inspector.getPanel("boxmodel").getComponentProps();
 
     this.flexboxInspector = new FlexboxInspector(this.inspector, this.inspector.panelWin);
     const {
       onSetFlexboxOverlayColor,
       onToggleFlexboxHighlighter,
     } = this.flexboxInspector.getComponentProps();
@@ -62,16 +63,17 @@ class LayoutView {
     const layoutApp = LayoutApp({
       getSwatchColorPickerTooltip: () => this.swatchColorPickerTooltip,
       onHideBoxModelHighlighter,
       onSetFlexboxOverlayColor,
       onSetGridOverlayColor,
       onShowBoxModelEditor,
       onShowBoxModelHighlighter,
       onShowBoxModelHighlighterForNode,
+      onShowRulePreviewTooltip,
       onShowGridOutlineHighlight,
       onToggleFlexboxHighlighter,
       onToggleGeometryEditor,
       onToggleGridHighlighter,
       onToggleShowGridAreas,
       onToggleShowGridLineNumbers,
       onToggleShowInfiniteLines,
       setSelectedNode,
--- a/devtools/client/locales/en-US/changes.properties
+++ b/devtools/client/locales/en-US/changes.properties
@@ -28,28 +28,33 @@ changes.iframeLabel=iframe
 # LOCALIZATION NOTE (changes.contextmenu.copy): Label for "Copy" option in Changes panel
 # context menu
 changes.contextmenu.copy=Copy
 
 # LOCALIZATION NOTE (changes.contextmenu.copy.accessKey): Access key for "Copy"
 # option in the Changes panel.
 changes.contextmenu.copy.accessKey=C
 
-# LOCALIZATION NOTE (changes.contextmenu.copyChanges): Label for "Copy Changes" option in
-# Changes panel context menu which copies only the subset of changed CSS declarations.
-changes.contextmenu.copyChanges=Copy Changes
-
 # LOCALIZATION NOTE (changes.contextmenu.copyAllChanges): Label for "Copy All Changes"
 # option in Changes panel context menu which copies all changed CSS declarations from a
 # stylesheet
 changes.contextmenu.copyAllChanges=Copy All Changes
 
+# LOCALIZATION NOTE (changes.contextmenu.copyAllChangesDescription): Detailed explanation
+# for "Copy All Changes" option in Changes panel. Used as title attribute on "Copy All
+# Changes" button
+changes.contextmenu.copyAllChangesDescription=Copy a list of all CSS changes to clipboard.
+
 # LOCALIZATION NOTE (changes.contextmenu.copyRule): Label for "Copy Rule" option in
 # Changes panel context menu which copies the complete contents of a CSS rule.
 changes.contextmenu.copyRule=Copy Rule
 
+# LOCALIZATION NOTE (changes.contextmenu.copyRuleDescription): Detailed explanation for
+# "Copy Rule" option in Changes panel. Used as title attribute on "Copy Rule" button.
+changes.contextmenu.copyRuleDescription=Copy contents of this CSS rule to clipboard.
+
 # LOCALIZATION NOTE (changes.contextmenu.selectAll): Label for "Select All" option in the
 # Changes panel context menu to select all text content.
 changes.contextmenu.selectAll=Select All
 
 # LOCALIZATION NOTE (changes.contextmenu.selectAll.accessKey): Access key for "Select All"
 # option in the Changes panel.
 changes.contextmenu.selectAll.accessKey=A
--- a/devtools/client/locales/en-US/inspector.properties
+++ b/devtools/client/locales/en-US/inspector.properties
@@ -474,8 +474,12 @@ inspector.noProperties=No CSS properties
 
 # LOCALIZATION NOTE (markupView.scrollableBadge.label): This is the text displayed inside a
 # badge, in the inspector, next to nodes that are scrollable in the page.
 markupView.scrollableBadge.label=scroll
 
 # LOCALIZATION NOTE (markupView.scrollableBadge.tooltip): This is the tooltip that is displayed
 # when hovering over badges next to scrollable elements in the inspector.
 markupView.scrollableBadge.tooltip=This element has scrollable overflow.
+
+# LOCALIZATION NOTE (rulePreviewTooltip.noAssociatedRule): This is the text displayed inside
+# the RulePreviewTooltip when a rule cannot be found for a CSS property declaration.
+rulePreviewTooltip.noAssociatedRule=No associated rule
--- a/devtools/client/preferences/devtools-client.js
+++ b/devtools/client/preferences/devtools-client.js
@@ -72,16 +72,23 @@ pref("devtools.gridinspector.maxHighligh
 
 // Whether or not the box model panel is opened in the layout view
 pref("devtools.layout.boxmodel.opened", true);
 // Whether or not the flexbox panel is opened in the layout view
 pref("devtools.layout.flexbox.opened", true);
 // Whether or not the grid inspector panel is opened in the layout view
 pref("devtools.layout.grid.opened", true);
 
+// Enable hovering Box Model values and jumping to their source CSS rule in the rule-view
+#if defined(NIGHTLY_BUILD)
+pref("devtools.layout.boxmodel.highlightProperty", true);
+#else
+pref("devtools.layout.boxmodel.highlightProperty", false);
+#endif
+
 // By how many times eyedropper will magnify pixels
 pref("devtools.eyedropper.zoom", 6);
 
 // Enable to collapse attributes that are too long.
 pref("devtools.markup.collapseAttributes", true);
 
 // Length to collapse attributes
 pref("devtools.markup.collapseAttributeLength", 120);
new file mode 100644
--- /dev/null
+++ b/devtools/client/shared/widgets/tooltip/RulePreviewTooltip.js
@@ -0,0 +1,65 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=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/. */
+
+"use strict";
+
+const { LocalizationHelper } = require("devtools/shared/l10n");
+const { HTMLTooltip } = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
+
+const XHTML_NS = "http://www.w3.org/1999/xhtml";
+const L10N = new LocalizationHelper("devtools/client/locales/inspector.properties");
+
+/**
+* Tooltip displayed for when a CSS property is selected/highlighted.
+* TODO: For now, the tooltip content only shows "No Associated Rule". In Bug 1528288,
+* we will be implementing content for showing the source CSS rule.
+*/
+class RulePreviewTooltip {
+  constructor(doc) {
+    this.show = this.show.bind(this);
+    this.destroy = this.destroy.bind(this);
+
+    // Initialize tooltip structure.
+    this._tooltip = new HTMLTooltip(doc, {
+      type: "arrow",
+      consumeOutsideClicks: true,
+      useXulWrapper: true,
+    });
+
+    this.container = doc.createElementNS(XHTML_NS, "div");
+    this.container.className = "rule-preview-tooltip-container";
+
+    this.message = doc.createElementNS(XHTML_NS, "span");
+    this.message.className = "rule-preview-tooltip-message";
+    this.message.textContent = L10N.getStr("rulePreviewTooltip.noAssociatedRule");
+    this.container.appendChild(this.message);
+
+    // TODO: Implement structure for showing the source CSS rule.
+
+    this._tooltip.panel.innerHTML = "";
+    this._tooltip.panel.appendChild(this.container);
+    this._tooltip.setContentSize({ width: "auto", height: "auto" });
+  }
+
+  /**
+   * Shows the tooltip on a given element.
+   *
+   * @param  {Element} element
+   *         The target element to show the tooltip with.
+   */
+  show(element) {
+    element.addEventListener("mouseout", () => this._tooltip.hide());
+    this._tooltip.show(element);
+  }
+
+  destroy() {
+    this._tooltip.destroy();
+    this.container = null;
+    this.message = null;
+  }
+}
+
+module.exports = RulePreviewTooltip;
--- a/devtools/client/shared/widgets/tooltip/moz.build
+++ b/devtools/client/shared/widgets/tooltip/moz.build
@@ -4,15 +4,16 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DevToolsModules(
     'EventTooltipHelper.js',
     'HTMLTooltip.js',
     'ImageTooltipHelper.js',
     'InlineTooltip.js',
+    'RulePreviewTooltip.js',
     'SwatchBasedEditorTooltip.js',
     'SwatchColorPickerTooltip.js',
     'SwatchCubicBezierTooltip.js',
     'SwatchFilterTooltip.js',
     'TooltipToggle.js',
     'VariableTooltipHelper.js'
 )
--- a/devtools/client/themes/boxmodel.css
+++ b/devtools/client/themes/boxmodel.css
@@ -344,16 +344,39 @@
   border-radius: 3px;
   padding: 0 2px;
 }
 
 .boxmodel-editable:hover {
   border-bottom-color: hsl(0, 0%, 50%);
 }
 
+.boxmodel-editable[data-box="margin"]:hover {
+  background-color: var(--marginbox-border-color);
+}
+
+.boxmodel-editable[data-box="padding"]:hover {
+  background-color: #c78fc7b3;
+}
+
+.boxmodel-editable[data-box="border"]:hover {
+  background-color: #545454;
+}
+
+.boxmodel-editable[data-box="content"]:hover {
+  background-color: var(--contentbox-border-color);
+}
+
+.boxmodel-editable[data-box="margin"]:hover,
+.boxmodel-editable[data-box="padding"]:hover,
+.boxmodel-editable[data-box="border"]:hover,
+.boxmodel-editable[data-box="content"]:hover {
+  border-radius: 3px;
+}
+
 .boxmodel-size > span {
   cursor: default;
 }
 
 /* Box Model Info: contains the position and size of the element */
 
 .boxmodel-element-size {
   flex: 1;
--- a/devtools/client/themes/changes.css
+++ b/devtools/client/themes/changes.css
@@ -1,29 +1,33 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
  /* CSS Variables specific to the Changes panel that aren't defined by the themes */
  :root {
+   --changes-button-background-color: var(--grey-90-a10);
+   --changes-button-background-color-hover: var(--grey-90-a15);
+   --changes-button-background-color-active: var(--grey-90-a20);
    --diff-add-background-color: #f1feec;
    --diff-add-text-color: #54983f;
    --diff-remove-background-color: #fbf2f5;
    --diff-remove-text-color: #bf7173;
    --diff-source-background: var(--theme-toolbar-background);
    --diff-level: 0;
    --diff-level-offset: 10px;
    /*
     Minimum padding so content on the first level (zero) isn't touching the edge. Added
     and removed lines will re-declare this to add extra padding to clear the +/- icons.
    */
    --diff-level-min-offset: 5px;
  }
 
 :root.theme-dark {
+  --changes-button-background-color: #252526;
   --diff-add-background-color: rgba(18, 188, 0, 0.15);
   --diff-add-text-color: #12BC00;
   --diff-remove-background-color: rgba(255, 0, 57, 0.15);
   --diff-remove-text-color: #FF0039;
   --diff-source-background: #222225;
 }
 
 :root[dir="rtl"] {
@@ -72,20 +76,64 @@
 #sidebar-panel-changes .level {
   padding-top: 3px;
   padding-right: 5px;
   padding-bottom: 3px;
   padding-left: calc(var(--diff-level-min-offset) +
                      var(--diff-level-offset) * var(--diff-level));
 }
 
-#sidebar-panel-changes .selector {
+.changes__copy-all-changes-button {
+  -moz-context-properties: fill;
+  background: url(chrome://devtools/skin/images/copy.svg) no-repeat;
+  background-size: 16px;
+  border: none;
+  color: var(--theme-body-color);
+  fill: currentColor;
+  height: 16px;
+  margin: 7px 10px;
+  padding-left: 21px;
+}
+
+/* Hide the Copy Rule button by default. */
+.changes__copy-rule-button {
+  background-color: var(--changes-button-background-color);
+  border-radius: 5px;
+  border: none;
+  color: var(--theme-body-color);
+  display: none;
+  padding: 1px 5px;
+  position: absolute;
+  right: 5px;
+  top: 2px;
+}
+
+.changes__copy-rule-button:hover {
+  background-color: var(--changes-button-background-color-hover);
+}
+
+.changes__copy-rule-button:active {
+  background-color: var(--changes-button-background-color-active);
+}
+
+.changes__rule {
+  position: relative;
+}
+
+.changes__selector {
   word-wrap: break-word;
 }
 
+/* Show the Copy Rule button when hovering over the rule's selector elements */
+.changes__selector:hover + .changes__copy-rule-button,
+.changes__selector:hover + .changes__selector + .changes__copy-rule-button,
+.changes__copy-rule-button:hover {
+  display: block;
+}
+
 #sidebar-panel-changes .declaration-name {
   margin-left: 10px;
 }
 
 .diff-add,
 .diff-remove {
   --diff-level-min-offset: 15px;
   position: relative;
--- a/devtools/client/themes/tooltips.css
+++ b/devtools/client/themes/tooltips.css
@@ -708,9 +708,32 @@
   width: 14px;
   height: 14px;
   fill: currentColor;
   -moz-context-properties: fill;
   background-image: url(chrome://devtools/skin/images/help.svg);
   background-size: contain;
   background-repeat: no-repeat;
   margin-inline-start: 4px;
-}
\ No newline at end of file
+}
+
+/* Tooltip: Rule Preview */
+
+.rule-preview-tooltip-container {
+  display: flex;
+  flex-direction: column;
+  max-width: 200px;
+  color: var(--theme-body-color);
+  padding: 5px;
+}
+
+.rule-preview-tooltip-message {
+  white-space: pre-wrap;
+}
+
+.rule-preview-tooltip-source-rule-footer {
+  align-self: center;
+  border-top: 1px solid var(--theme-body-color);
+  margin-top: 5px;
+  margin-right: 5px;
+  margin-left: 5px;
+  padding: 3px;
+}
--- a/devtools/client/themes/variables.css
+++ b/devtools/client/themes/variables.css
@@ -250,9 +250,12 @@
   --grey-45: #939395;
   --grey-50: #737373;
   --grey-55: #5c5c5f;
   --grey-60: #4a4a4f;
   --grey-70: #38383d;
   --grey-80: #2a2a2e;
   --grey-85: #1b1b1d;
   --grey-90: #0c0c0d;
+  --grey-90-a10: rgba(12, 12, 13, 0.1);
+  --grey-90-a15: rgba(12, 12, 13, 0.15);
+  --grey-90-a20: rgba(12, 12, 13, 0.2);
 }
--- a/dom/base/DOMParser.cpp
+++ b/dom/base/DOMParser.cpp
@@ -82,18 +82,18 @@ already_AddRefed<Document> DOMParser::Pa
   // Convert from UTF16 to UTF8 using fallible allocations
   if (!AppendUTF16toUTF8(aStr, utf8str, mozilla::fallible)) {
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return nullptr;
   }
 
   // The new stream holds a reference to the buffer
   nsCOMPtr<nsIInputStream> stream;
-  nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream), utf8str.get(),
-                                      utf8str.Length(), NS_ASSIGNMENT_DEPEND);
+  nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream), utf8str,
+                                      NS_ASSIGNMENT_DEPEND);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     aRv.Throw(rv);
     return nullptr;
   }
 
   return ParseFromStream(stream, NS_LITERAL_STRING("UTF-8"), utf8str.Length(),
                          aType, aRv);
 }
@@ -106,18 +106,19 @@ already_AddRefed<Document> DOMParser::Pa
 }
 
 already_AddRefed<Document> DOMParser::ParseFromBuffer(Span<const uint8_t> aBuf,
                                                       SupportedType aType,
                                                       ErrorResult& aRv) {
   // The new stream holds a reference to the buffer
   nsCOMPtr<nsIInputStream> stream;
   nsresult rv = NS_NewByteInputStream(
-      getter_AddRefs(stream), reinterpret_cast<const char*>(aBuf.Elements()),
-      aBuf.Length(), NS_ASSIGNMENT_DEPEND);
+      getter_AddRefs(stream),
+      MakeSpan(reinterpret_cast<const char*>(aBuf.Elements()), aBuf.Length()),
+      NS_ASSIGNMENT_DEPEND);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
   return ParseFromStream(stream, VoidString(), aBuf.Length(), aType, aRv);
 }
 
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -154,16 +154,17 @@
 #include "nsIDOMXULMenuListElement.h"
 #include "nsIDOMXULMultSelectCntrlEl.h"
 #include "nsIDOMXULRadioGroupElement.h"
 #include "nsIDOMXULRelatedElement.h"
 #include "nsIDOMXULMultSelectCntrlEl.h"
 #include "nsIDOMXULSelectCntrlEl.h"
 #include "nsIDOMXULSelectCntrlItemEl.h"
 #include "nsIBrowser.h"
+#include "nsIAutoCompletePopup.h"
 
 #include "nsISpeculativeConnect.h"
 #include "nsIIOService.h"
 
 #include "DOMMatrix.h"
 
 using mozilla::gfx::Matrix4x4;
 
@@ -4079,16 +4080,22 @@ Element::AsXULSelectControlItem() {
 }
 
 already_AddRefed<nsIBrowser> Element::AsBrowser() {
   nsCOMPtr<nsIBrowser> value;
   GetCustomInterface(getter_AddRefs(value));
   return value.forget();
 }
 
+already_AddRefed<nsIAutoCompletePopup> Element::AsAutoCompletePopup() {
+  nsCOMPtr<nsIAutoCompletePopup> value;
+  GetCustomInterface(getter_AddRefs(value));
+  return value.forget();
+}
+
 MOZ_DEFINE_MALLOC_SIZE_OF(ServoElementMallocSizeOf)
 MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ServoElementMallocEnclosingSizeOf)
 
 void Element::AddSizeOfExcludingThis(nsWindowSizes& aSizes,
                                      size_t* aNodeSize) const {
   FragmentOrElement::AddSizeOfExcludingThis(aSizes, aNodeSize);
   *aNodeSize += mAttrs.SizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
 
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -67,16 +67,17 @@ class nsIDOMXULContainerItemElement;
 class nsIDOMXULControlElement;
 class nsIDOMXULMenuListElement;
 class nsIDOMXULMultiSelectControlElement;
 class nsIDOMXULRadioGroupElement;
 class nsIDOMXULRelatedElement;
 class nsIDOMXULSelectControlElement;
 class nsIDOMXULSelectControlItemElement;
 class nsIBrowser;
+class nsIAutoCompletePopup;
 
 namespace mozilla {
 class DeclarationBlock;
 struct MutationClosureData;
 class TextEditor;
 namespace css {
 struct URLValue;
 }  // namespace css
@@ -1586,16 +1587,17 @@ class Element : public FragmentOrElement
   already_AddRefed<nsIDOMXULMenuListElement> AsXULMenuList();
   already_AddRefed<nsIDOMXULMultiSelectControlElement>
   AsXULMultiSelectControl();
   already_AddRefed<nsIDOMXULRadioGroupElement> AsXULRadioGroup();
   already_AddRefed<nsIDOMXULRelatedElement> AsXULRelated();
   already_AddRefed<nsIDOMXULSelectControlElement> AsXULSelectControl();
   already_AddRefed<nsIDOMXULSelectControlItemElement> AsXULSelectControlItem();
   already_AddRefed<nsIBrowser> AsBrowser();
+  already_AddRefed<nsIAutoCompletePopup> AsAutoCompletePopup();
 
  protected:
   /*
    * Named-bools for use with SetAttrAndNotify to make call sites easier to
    * read.
    */
   static const bool kFireMutationEvent = true;
   static const bool kDontFireMutationEvent = false;
--- a/dom/base/TabGroup.cpp
+++ b/dom/base/TabGroup.cpp
@@ -1,17 +1,17 @@
 /* -*- 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 "mozilla/dom/TabGroup.h"
 
-#include "mozilla/dom/nsIContentChild.h"
+#include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/TimeoutManager.h"
 #include "mozilla/AbstractThread.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/ThrottledEventQueue.h"
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -39,16 +39,17 @@
 #include "mozilla/BackgroundHangMonitor.h"
 #include "mozilla/Base64.h"
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/Components.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/LoadInfo.h"
 #include "mozilla/dom/BlobURLProtocolHandler.h"
+#include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/CustomElementRegistry.h"
 #include "mozilla/dom/Document.h"
 #include "mozilla/dom/MessageBroadcaster.h"
 #include "mozilla/dom/DocumentFragment.h"
 #include "mozilla/dom/DOMException.h"
 #include "mozilla/dom/DOMExceptionBinding.h"
 #include "mozilla/dom/DOMTypes.h"
@@ -7246,17 +7247,17 @@ void nsContentUtils::SetKeyboardIndicato
                           (void*)&stateInfo);
 }
 
 nsresult nsContentUtils::IPCTransferableToTransferable(
     const IPCDataTransfer& aDataTransfer, const bool& aIsPrivateData,
     nsIPrincipal* aRequestingPrincipal,
     const nsContentPolicyType& aContentPolicyType,
     nsITransferable* aTransferable,
-    mozilla::dom::nsIContentParent* aContentParent,
+    mozilla::dom::ContentParent* aContentParent,
     mozilla::dom::TabChild* aTabChild) {
   nsresult rv;
 
   const nsTArray<IPCDataTransferItem>& items = aDataTransfer.items();
   for (const auto& item : items) {
     aTransferable->AddDataFlavor(item.flavor().get());
 
     if (item.data().type() == IPCDataTransferData::TnsString) {
@@ -7307,18 +7308,18 @@ nsresult nsContentUtils::IPCTransferable
   aTransferable->SetIsPrivateData(aIsPrivateData);
   aTransferable->SetRequestingPrincipal(aRequestingPrincipal);
   aTransferable->SetContentPolicyType(aContentPolicyType);
   return NS_OK;
 }
 
 void nsContentUtils::TransferablesToIPCTransferables(
     nsIArray* aTransferables, nsTArray<IPCDataTransfer>& aIPC,
-    bool aInSyncMessage, mozilla::dom::nsIContentChild* aChild,
-    mozilla::dom::nsIContentParent* aParent) {
+    bool aInSyncMessage, mozilla::dom::ContentChild* aChild,
+    mozilla::dom::ContentParent* aParent) {
   aIPC.Clear();
   if (aTransferables) {
     uint32_t transferableCount = 0;
     aTransferables->GetLength(&transferableCount);
     for (uint32_t i = 0; i < transferableCount; ++i) {
       IPCDataTransfer* dt = aIPC.AppendElement();
       nsCOMPtr<nsITransferable> transferable =
           do_QueryElementAt(aTransferables, i);
@@ -7440,18 +7441,18 @@ nsresult nsContentUtils::DataTransferIte
 bool nsContentUtils::IsFlavorImage(const nsACString& aFlavor) {
   return aFlavor.EqualsLiteral(kNativeImageMime) ||
          aFlavor.EqualsLiteral(kJPEGImageMime) ||
          aFlavor.EqualsLiteral(kJPGImageMime) ||
          aFlavor.EqualsLiteral(kPNGImageMime) ||
          aFlavor.EqualsLiteral(kGIFImageMime);
 }
 
-static Shmem ConvertToShmem(mozilla::dom::nsIContentChild* aChild,
-                            mozilla::dom::nsIContentParent* aParent,
+static Shmem ConvertToShmem(mozilla::dom::ContentChild* aChild,
+                            mozilla::dom::ContentParent* aParent,
                             const nsACString& aInput) {
   MOZ_ASSERT((aChild && !aParent) || (!aChild && aParent));
 
   IShmemAllocator* allocator = aChild ? static_cast<IShmemAllocator*>(aChild)
                                       : static_cast<IShmemAllocator*>(aParent);
 
   Shmem result;
   if (!allocator->AllocShmem(aInput.Length(), SharedMemory::TYPE_BASIC,
@@ -7461,18 +7462,18 @@ static Shmem ConvertToShmem(mozilla::dom
 
   memcpy(result.get<char>(), aInput.BeginReading(), aInput.Length());
 
   return result;
 }
 
 void nsContentUtils::TransferableToIPCTransferable(
     nsITransferable* aTransferable, IPCDataTransfer* aIPCDataTransfer,
-    bool aInSyncMessage, mozilla::dom::nsIContentChild* aChild,
-    mozilla::dom::nsIContentParent* aParent) {
+    bool aInSyncMessage, mozilla::dom::ContentChild* aChild,
+    mozilla::dom::ContentParent* aParent) {
   MOZ_ASSERT((aChild && !aParent) || (!aChild && aParent));
 
   if (aTransferable) {
     nsTArray<nsCString> flavorList;
     aTransferable->FlavorsTransferableCanExport(flavorList);
 
     for (uint32_t j = 0; j < flavorList.Length(); ++j) {
       nsCString& flavorStr = flavorList[j];
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -139,18 +139,18 @@ class Event;
 class EventTarget;
 class HTMLInputElement;
 class IPCDataTransfer;
 class IPCDataTransferItem;
 struct LifecycleCallbackArgs;
 struct LifecycleAdoptedCallbackArgs;
 class MessageBroadcaster;
 class NodeInfo;
-class nsIContentChild;
-class nsIContentParent;
+class ContentChild;
+class ContentParent;
 class TabChild;
 class Selection;
 class TabParent;
 }  // namespace dom
 
 namespace ipc {
 class Shmem;
 class IShmemAllocator;
@@ -2828,29 +2828,29 @@ class nsContentUtils {
    */
   static bool IsFlavorImage(const nsACString& aFlavor);
 
   static nsresult IPCTransferableToTransferable(
       const mozilla::dom::IPCDataTransfer& aDataTransfer,
       const bool& aIsPrivateData, nsIPrincipal* aRequestingPrincipal,
       const nsContentPolicyType& aContentPolicyType,
       nsITransferable* aTransferable,
-      mozilla::dom::nsIContentParent* aContentParent,
+      mozilla::dom::ContentParent* aContentParent,
       mozilla::dom::TabChild* aTabChild);
 
   static void TransferablesToIPCTransferables(
       nsIArray* aTransferables, nsTArray<mozilla::dom::IPCDataTransfer>& aIPC,
-      bool aInSyncMessage, mozilla::dom::nsIContentChild* aChild,
-      mozilla::dom::nsIContentParent* aParent);
+      bool aInSyncMessage, mozilla::dom::ContentChild* aChild,
+      mozilla::dom::ContentParent* aParent);
 
   static void TransferableToIPCTransferable(
       nsITransferable* aTransferable,
       mozilla::dom::IPCDataTransfer* aIPCDataTransfer, bool aInSyncMessage,
-      mozilla::dom::nsIContentChild* aChild,
-      mozilla::dom::nsIContentParent* aParent);
+      mozilla::dom::ContentChild* aChild,
+      mozilla::dom::ContentParent* aParent);
 
   /*
    * Get the pixel data from the given source surface and return it as a buffer.
    * The length and stride will be assigned from the surface.
    */
   static mozilla::UniquePtr<char[]> GetSurfaceData(
       mozilla::NotNull<mozilla::gfx::DataSourceSurface*> aSurface,
       size_t* aLength, int32_t* aStride);
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -8,17 +8,17 @@
 
 #include "nsFocusManager.h"
 
 #include "ChildIterator.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsGkAtoms.h"
 #include "nsGlobalWindow.h"
 #include "nsContentUtils.h"
-#include "nsIContentParent.h"
+#include "ContentParent.h"
 #include "nsPIDOMWindow.h"
 #include "nsIDOMChromeWindow.h"
 #include "nsIHTMLDocument.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsIFormControl.h"
 #include "nsLayoutUtils.h"
 #include "nsIPresShell.h"
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -2391,19 +2391,18 @@ static Tuple<ContentParent*, TabParent*>
 
   RefPtr<nsFrameLoader> otherLoader;
   browser->GetSameProcessAsFrameLoader(getter_AddRefs(otherLoader));
   if (!otherLoader) {
     return ReturnTuple(nullptr, nullptr);
   }
 
   TabParent* tabParent = TabParent::GetFrom(otherLoader);
-  if (tabParent && tabParent->Manager() &&
-      tabParent->Manager()->IsContentParent()) {
-    return MakeTuple(tabParent->Manager()->AsContentParent(), tabParent);
+  if (tabParent && tabParent->Manager()) {
+    return MakeTuple(tabParent->Manager(), tabParent);
   }
 
   return ReturnTuple(nullptr, nullptr);
 }
 
 bool nsFrameLoader::TryRemoteBrowser() {
   NS_ASSERTION(!mRemoteBrowser && !mRemoteFrameChild,
                "TryRemoteBrowser called with a remote browser already?");
@@ -2437,19 +2436,18 @@ bool nsFrameLoader::TryRemoteBrowser() {
   if (!parentDocShell) {
     return false;
   }
 
   TabParent* openingTab = TabParent::GetFrom(parentDocShell->GetOpener());
   RefPtr<ContentParent> openerContentParent;
   RefPtr<TabParent> sameTabGroupAs;
 
-  if (openingTab && openingTab->Manager() &&
-      openingTab->Manager()->IsContentParent()) {
-    openerContentParent = openingTab->Manager()->AsContentParent();
+  if (openingTab && openingTab->Manager()) {
+    openerContentParent = openingTab->Manager();
   }
 
   // <iframe mozbrowser> gets to skip these checks.
   // iframes for JS plugins also get to skip these checks. We control the URL
   // that gets loaded, but the load is triggered from the document containing
   // the plugin.
   // out of process iframes also get to skip this check.
   if (!OwnerIsMozBrowserFrame() && !XRE_IsContentProcess()) {
@@ -2725,17 +2723,17 @@ class nsAsyncMessageToChild : public nsS
 nsresult nsFrameLoader::DoSendAsyncMessage(JSContext* aCx,
                                            const nsAString& aMessage,
                                            StructuredCloneData& aData,
                                            JS::Handle<JSObject*> aCpows,
                                            nsIPrincipal* aPrincipal) {
   TabParent* tabParent = mRemoteBrowser;
   if (tabParent) {
     ClonedMessageData data;
-    nsIContentParent* cp = tabParent->Manager();
+    ContentParent* cp = tabParent->Manager();
     if (!BuildClonedMessageDataForParent(cp, aData, data)) {
       MOZ_CRASH();
       return NS_ERROR_DOM_DATA_CLONE_ERR;
     }
     InfallibleTArray<mozilla::jsipc::CpowEntry> cpows;
     jsipc::CPOWManager* mgr = cp->GetCPOWManager();
     if (aCpows && (!mgr || !mgr->Wrap(aCx, aCpows, &cpows))) {
       return NS_ERROR_UNEXPECTED;
@@ -2964,17 +2962,17 @@ void nsFrameLoader::RequestUpdatePositio
 
 void nsFrameLoader::Print(uint64_t aOuterWindowID,
                           nsIPrintSettings* aPrintSettings,
                           nsIWebProgressListener* aProgressListener,
                           ErrorResult& aRv) {
 #if defined(NS_PRINTING)
   if (mRemoteBrowser) {
     RefPtr<embedding::PrintingParent> printingParent =
-        mRemoteBrowser->Manager()->AsContentParent()->GetPrintingParent();
+        mRemoteBrowser->Manager()->GetPrintingParent();
 
     embedding::PrintData printData;
     nsresult rv = printingParent->SerializeAndEnsureRemotePrintJob(
         aPrintSettings, aProgressListener, nullptr, &printData);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       aRv.Throw(rv);
       return;
     }
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -163,23 +163,23 @@ void MessageManagerCallback::DoGetRemote
   if (!parent) {
     return;
   }
 
   parent->GetRemoteType(aRemoteType, aError);
 }
 
 bool MessageManagerCallback::BuildClonedMessageDataForParent(
-    nsIContentParent* aParent, StructuredCloneData& aData,
+    ContentParent* aParent, StructuredCloneData& aData,
     ClonedMessageData& aClonedData) {
   return aData.BuildClonedMessageDataForParent(aParent, aClonedData);
 }
 
 bool MessageManagerCallback::BuildClonedMessageDataForChild(
-    nsIContentChild* aChild, StructuredCloneData& aData,
+    ContentChild* aChild, StructuredCloneData& aData,
     ClonedMessageData& aClonedData) {
   return aData.BuildClonedMessageDataForChild(aChild, aClonedData);
 }
 
 void mozilla::dom::ipc::UnpackClonedMessageDataForParent(
     const ClonedMessageData& aClonedData, StructuredCloneData& aData) {
   aData.BorrowFromClonedMessageDataForParent(aClonedData);
 }
--- a/dom/base/nsFrameMessageManager.h
+++ b/dom/base/nsFrameMessageManager.h
@@ -39,18 +39,18 @@ class nsFrameLoader;
 namespace mozilla {
 
 namespace ipc {
 class FileDescriptor;
 }
 
 namespace dom {
 
-class nsIContentParent;
-class nsIContentChild;
+class ContentParent;
+class ContentChild;
 class ChildProcessMessageManager;
 class ChromeMessageBroadcaster;
 class ClonedMessageData;
 class MessageBroadcaster;
 class MessageListener;
 class MessageListenerManager;
 class MessageManagerReporter;
 template <typename T>
@@ -105,20 +105,20 @@ class MessageManagerCallback {
       const {
     return nullptr;
   }
 
   virtual void DoGetRemoteType(nsAString& aRemoteType,
                                ErrorResult& aError) const;
 
  protected:
-  bool BuildClonedMessageDataForParent(nsIContentParent* aParent,
+  bool BuildClonedMessageDataForParent(ContentParent* aParent,
                                        StructuredCloneData& aData,
                                        ClonedMessageData& aClonedData);
-  bool BuildClonedMessageDataForChild(nsIContentChild* aChild,
+  bool BuildClonedMessageDataForChild(ContentChild* aChild,
                                       StructuredCloneData& aData,
                                       ClonedMessageData& aClonedData);
 };
 
 void UnpackClonedMessageDataForParent(const ClonedMessageData& aClonedData,
                                       StructuredCloneData& aData);
 
 void UnpackClonedMessageDataForChild(const ClonedMessageData& aClonedData,
--- a/dom/events/DataTransfer.cpp
+++ b/dom/events/DataTransfer.cpp
@@ -1503,17 +1503,18 @@ void DataTransfer::FillInExternalCustomT
   }
 
   CheckedInt<int32_t> checkedLen(len);
   if (!checkedLen.isValid()) {
     return;
   }
 
   nsCOMPtr<nsIInputStream> stringStream;
-  NS_NewByteInputStream(getter_AddRefs(stringStream), chrs, checkedLen.value(),
+  NS_NewByteInputStream(getter_AddRefs(stringStream),
+                        MakeSpan(chrs, checkedLen.value()),
                         NS_ASSIGNMENT_ADOPT);
 
   nsCOMPtr<nsIObjectInputStream> stream = NS_NewObjectInputStream(stringStream);
 
   uint32_t type;
   do {
     rv = stream->Read32(&type);
     NS_ENSURE_SUCCESS_VOID(rv);
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -1240,20 +1240,17 @@ void EventStateManager::DispatchCrossPro
       // Let the child process synthesize a mouse event if needed, and
       // ensure we don't synthesize one in this process.
       *aStatus = nsEventStatus_eConsumeNoDefault;
       remote->SendRealTouchEvent(*aEvent->AsTouchEvent());
       return;
     }
     case eDragEventClass: {
       RefPtr<TabParent> tabParent = remote;
-      if (tabParent->Manager()->IsContentParent()) {
-        tabParent->Manager()->AsContentParent()->MaybeInvokeDragSession(
-            tabParent);
-      }
+      tabParent->Manager()->MaybeInvokeDragSession(tabParent);
 
       nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
       uint32_t dropEffect = nsIDragService::DRAGDROP_ACTION_NONE;
       uint32_t action = nsIDragService::DRAGDROP_ACTION_NONE;
       nsCOMPtr<nsIPrincipal> principal;
       if (dragSession) {
         dragSession->DragEventDispatchedToChildProcess();
         dragSession->GetDragAction(&action);
@@ -5505,24 +5502,20 @@ nsresult EventStateManager::DoContentCom
     aEvent->mIsEnabled = canDoIt;
     if (canDoIt && !aEvent->mOnlyEnabledCheck) {
       switch (aEvent->mMessage) {
         case eContentCommandPasteTransferable: {
           nsFocusManager* fm = nsFocusManager::GetFocusManager();
           nsIContent* focusedContent = fm ? fm->GetFocusedElement() : nullptr;
           RefPtr<TabParent> remote = TabParent::GetFrom(focusedContent);
           if (remote) {
-            NS_ENSURE_TRUE(remote->Manager()->IsContentParent(),
-                           NS_ERROR_FAILURE);
-
             nsCOMPtr<nsITransferable> transferable = aEvent->mTransferable;
             IPCDataTransfer ipcDataTransfer;
-            ContentParent* cp = remote->Manager()->AsContentParent();
             nsContentUtils::TransferableToIPCTransferable(
-                transferable, &ipcDataTransfer, false, nullptr, cp);
+                transferable, &ipcDataTransfer, false, nullptr, remote->Manager());
             bool isPrivateData = transferable->GetIsPrivateData();
             nsCOMPtr<nsIPrincipal> requestingPrincipal =
                 transferable->GetRequestingPrincipal();
             nsContentPolicyType contentPolicyType =
                 transferable->GetContentPolicyType();
             remote->SendPasteTransferable(ipcDataTransfer, isPrivateData,
                                           IPC::Principal(requestingPrincipal),
                                           contentPolicyType);
--- a/dom/fetch/BodyExtractor.cpp
+++ b/dom/fetch/BodyExtractor.cpp
@@ -27,18 +27,18 @@ static nsresult GetBufferDataAsStream(
     uint64_t* aContentLength, nsACString& aContentType, nsACString& aCharset) {
   aContentType.SetIsVoid(true);
   aCharset.Truncate();
 
   *aContentLength = aDataLength;
   const char* data = reinterpret_cast<const char*>(aData);
 
   nsCOMPtr<nsIInputStream> stream;
-  nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream), data, aDataLength,
-                                      NS_ASSIGNMENT_COPY);
+  nsresult rv = NS_NewByteInputStream(
+      getter_AddRefs(stream), MakeSpan(data, aDataLength), NS_ASSIGNMENT_COPY);
   NS_ENSURE_SUCCESS(rv, rv);
 
   stream.forget(aResult);
 
   return NS_OK;
 }
 
 template <>
--- a/dom/file/MemoryBlobImpl.cpp
+++ b/dom/file/MemoryBlobImpl.cpp
@@ -31,17 +31,17 @@ nsresult MemoryBlobImpl::DataOwnerAdapte
                                                   nsIInputStream** _retval) {
   nsresult rv;
   MOZ_ASSERT(aDataOwner, "Uh ...");
 
   nsCOMPtr<nsIInputStream> stream;
 
   rv = NS_NewByteInputStream(
       getter_AddRefs(stream),
-      static_cast<const char*>(aDataOwner->mData) + aStart, (int32_t)aLength,
+      MakeSpan(static_cast<const char*>(aDataOwner->mData) + aStart, aLength),
       NS_ASSIGNMENT_DEPEND);
   NS_ENSURE_SUCCESS(rv, rv);
 
   NS_ADDREF(*_retval =
                 new MemoryBlobImpl::DataOwnerAdapter(aDataOwner, stream));
 
   return NS_OK;
 }
--- a/dom/file/TemporaryFileBlobImpl.cpp
+++ b/dom/file/TemporaryFileBlobImpl.cpp
@@ -48,31 +48,31 @@ class TemporaryFileInputStream final : p
 
     stream.forget(aInputStream);
     return NS_OK;
   }
 
   void Serialize(InputStreamParams& aParams,
                  FileDescriptorArray& aFileDescriptors, bool aDelayedStart,
                  uint32_t aMaxSize, uint32_t* aSizeUsed,
-                 nsIContentChild* aManager) override {
+                 ContentChild* aManager) override {
     MOZ_CRASH("This inputStream cannot be serialized.");
   }
 
   void Serialize(InputStreamParams& aParams,
                  FileDescriptorArray& aFileDescriptors, bool aDelayedStart,
                  uint32_t aMaxSize, uint32_t* aSizeUsed,
                  PBackgroundChild* aManager) override {
     MOZ_CRASH("This inputStream cannot be serialized.");
   }
 
   void Serialize(InputStreamParams& aParams,
                  FileDescriptorArray& aFileDescriptors, bool aDelayedStart,
                  uint32_t aMaxSize, uint32_t* aSizeUsed,
-                 nsIContentParent* aManager) override {
+                 ContentParent* aManager) override {
     MOZ_CRASH("This inputStream cannot be serialized.");
   }
 
   void Serialize(InputStreamParams& aParams,
                  FileDescriptorArray& aFileDescriptors, bool aDelayedStart,
                  uint32_t aMaxSize, uint32_t* aSizeUsed,
                  PBackgroundParent* aManager) override {
     MOZ_CRASH("This inputStream cannot be serialized.");
--- a/dom/file/ipc/IPCBlobInputStream.cpp
+++ b/dom/file/ipc/IPCBlobInputStream.cpp
@@ -562,17 +562,17 @@ IPCBlobInputStream::OnInputStreamReady(n
 }
 
 // nsIIPCSerializableInputStream
 
 void IPCBlobInputStream::Serialize(mozilla::ipc::InputStreamParams& aParams,
                                    FileDescriptorArray& aFileDescriptors,
                                    bool aDelayedStart, uint32_t aMaxSize,
                                    uint32_t* aSizeUsed,
-                                   nsIContentChild* aManager) {
+                                   ContentChild* aManager) {
   MOZ_ASSERT(aSizeUsed);
   *aSizeUsed = 0;
 
   SerializeInternal(aParams);
 }
 
 void IPCBlobInputStream::Serialize(mozilla::ipc::InputStreamParams& aParams,
                                    FileDescriptorArray& aFileDescriptors,
@@ -584,17 +584,17 @@ void IPCBlobInputStream::Serialize(mozil
 
   SerializeInternal(aParams);
 }
 
 void IPCBlobInputStream::Serialize(mozilla::ipc::InputStreamParams& aParams,
                                    FileDescriptorArray& aFileDescriptors,
                                    bool aDelayedStart, uint32_t aMaxSize,
                                    uint32_t* aSizeUsed,
-                                   nsIContentParent* aManager) {
+                                   ContentParent* aManager) {
   MOZ_ASSERT(aSizeUsed);
   *aSizeUsed = 0;
 
   SerializeInternal(aParams);
 }
 
 void IPCBlobInputStream::Serialize(mozilla::ipc::InputStreamParams& aParams,
                                    FileDescriptorArray& aFileDescriptors,
--- a/dom/file/ipc/IPCBlobInputStreamParent.cpp
+++ b/dom/file/ipc/IPCBlobInputStreamParent.cpp
@@ -43,17 +43,17 @@ IPCBlobInputStreamParent::Create(const n
 
   actor->mCallback = IPCBlobInputStreamStorage::Get()->TakeCallback(aID);
 
   return actor.forget();
 }
 
 IPCBlobInputStreamParent::IPCBlobInputStreamParent(const nsID& aID,
                                                    uint64_t aSize,
-                                                   nsIContentParent* aManager)
+                                                   ContentParent* aManager)
     : mID(aID),
       mSize(aSize),
       mContentManager(aManager),
       mPBackgroundManager(nullptr),
       mMigrating(false) {}
 
 IPCBlobInputStreamParent::IPCBlobInputStreamParent(const nsID& aID,
                                                    uint64_t aSize,
--- a/dom/file/ipc/IPCBlobInputStreamParent.h
+++ b/dom/file/ipc/IPCBlobInputStreamParent.h
@@ -56,29 +56,29 @@ class IPCBlobInputStreamParent final
   mozilla::ipc::IPCResult RecvClose();
 
   mozilla::ipc::IPCResult Recv__delete__() override;
 
   bool HasValidStream() const;
 
  private:
   IPCBlobInputStreamParent(const nsID& aID, uint64_t aSize,
-                           nsIContentParent* aManager);
+                           ContentParent* aManager);
 
   IPCBlobInputStreamParent(const nsID& aID, uint64_t aSize,
                            mozilla::ipc::PBackgroundParent* aManager);
 
   ~IPCBlobInputStreamParent() = default;
 
   const nsID mID;
   const uint64_t mSize;
 
   // Only 1 of these 2 is set. Raw pointer because these 2 managers are keeping
   // the parent actor alive. The pointers will be nullified in ActorDestroyed.
-  nsIContentParent* mContentManager;
+  ContentParent* mContentManager;
   mozilla::ipc::PBackgroundParent* mPBackgroundManager;
 
   RefPtr<IPCBlobInputStreamParentCallback> mCallback;
 
   bool mMigrating;
 };
 
 }  // namespace dom
--- a/dom/file/ipc/IPCBlobUtils.cpp
+++ b/dom/file/ipc/IPCBlobUtils.cpp
@@ -129,49 +129,49 @@ nsresult SerializeInputStreamChild(nsIIn
   }
 
   aIPCBlob.inputStream() = ipcStream.TakeValue();
   return NS_OK;
 }
 
 nsresult SerializeInputStream(nsIInputStream* aInputStream, uint64_t aSize,
                               uint64_t aChildID, IPCBlob& aIPCBlob,
-                              nsIContentParent* aManager) {
+                              ContentParent* aManager) {
   return SerializeInputStreamParent(aInputStream, aSize, aChildID, aIPCBlob,
                                     aManager);
 }
 
 nsresult SerializeInputStream(nsIInputStream* aInputStream, uint64_t aSize,
                               uint64_t aChildID, IPCBlob& aIPCBlob,
                               PBackgroundParent* aManager) {
   return SerializeInputStreamParent(aInputStream, aSize, aChildID, aIPCBlob,
                                     aManager);
 }
 
 nsresult SerializeInputStream(nsIInputStream* aInputStream, uint64_t aSize,
                               uint64_t aChildID, IPCBlob& aIPCBlob,
-                              nsIContentChild* aManager) {
+                              ContentChild* aManager) {
   return SerializeInputStreamChild(aInputStream, aIPCBlob, aManager);
 }
 
 nsresult SerializeInputStream(nsIInputStream* aInputStream, uint64_t aSize,
                               uint64_t aChildID, IPCBlob& aIPCBlob,
                               PBackgroundChild* aManager) {
   return SerializeInputStreamChild(aInputStream, aIPCBlob, aManager);
 }
 
-uint64_t ChildIDFromManager(nsIContentParent* aManager) {
+uint64_t ChildIDFromManager(ContentParent* aManager) {
   return aManager->ChildID();
 }
 
 uint64_t ChildIDFromManager(PBackgroundParent* aManager) {
   return BackgroundParent::GetChildID(aManager);
 }
 
-uint64_t ChildIDFromManager(nsIContentChild* aManager) { return 0; }
+uint64_t ChildIDFromManager(ContentChild* aManager) { return 0; }
 
 uint64_t ChildIDFromManager(PBackgroundChild* aManager) { return 0; }
 
 template <typename M>
 nsresult SerializeInternal(BlobImpl* aBlobImpl, M* aManager,
                            IPCBlob& aIPCBlob) {
   MOZ_ASSERT(aBlobImpl);
 
@@ -227,27 +227,27 @@ nsresult SerializeInternal(BlobImpl* aBl
                             ChildIDFromManager(aManager), aIPCBlob, aManager);
   if (NS_WARN_IF(rv.Failed())) {
     return rv.StealNSResult();
   }
 
   return NS_OK;
 }
 
-nsresult Serialize(BlobImpl* aBlobImpl, nsIContentChild* aManager,
+nsresult Serialize(BlobImpl* aBlobImpl, ContentChild* aManager,
                    IPCBlob& aIPCBlob) {
   return SerializeInternal(aBlobImpl, aManager, aIPCBlob);
 }
 
 nsresult Serialize(BlobImpl* aBlobImpl, PBackgroundChild* aManager,
                    IPCBlob& aIPCBlob) {
   return SerializeInternal(aBlobImpl, aManager, aIPCBlob);
 }
 
-nsresult Serialize(BlobImpl* aBlobImpl, nsIContentParent* aManager,
+nsresult Serialize(BlobImpl* aBlobImpl, ContentParent* aManager,
                    IPCBlob& aIPCBlob) {
   return SerializeInternal(aBlobImpl, aManager, aIPCBlob);
 }
 
 nsresult Serialize(BlobImpl* aBlobImpl, PBackgroundParent* aManager,
                    IPCBlob& aIPCBlob) {
   return SerializeInternal(aBlobImpl, aManager, aIPCBlob);
 }
--- a/dom/file/ipc/IPCBlobUtils.h
+++ b/dom/file/ipc/IPCBlobUtils.h
@@ -217,32 +217,32 @@ namespace ipc {
 class IProtocol;
 class PBackgroundChild;
 class PBackgroundParent;
 }  // namespace ipc
 
 namespace dom {
 
 class IPCBlob;
-class nsIContentChild;
-class nsIContentParent;
+class ContentChild;
+class ContentParent;
 
 namespace IPCBlobUtils {
 
 already_AddRefed<BlobImpl> Deserialize(const IPCBlob& aIPCBlob);
 
 // These 4 methods serialize aBlobImpl into aIPCBlob using the right manager.
 
-nsresult Serialize(BlobImpl* aBlobImpl, nsIContentChild* aManager,
+nsresult Serialize(BlobImpl* aBlobImpl, ContentChild* aManager,
                    IPCBlob& aIPCBlob);
 
 nsresult Serialize(BlobImpl* aBlobImpl,
                    mozilla::ipc::PBackgroundChild* aManager, IPCBlob& aIPCBlob);
 
-nsresult Serialize(BlobImpl* aBlobImpl, nsIContentParent* aManager,
+nsresult Serialize(BlobImpl* aBlobImpl, ContentParent* aManager,
                    IPCBlob& aIPCBlob);
 
 nsresult Serialize(BlobImpl* aBlobImpl,
                    mozilla::ipc::PBackgroundParent* aManager,
                    IPCBlob& aIPCBlob);
 
 // WARNING: If you pass any actor which does not have P{Content,Background} as
 // its toplevel protocol, this method will MOZ_CRASH.
--- a/dom/file/ipc/PIPCBlobInputStream.ipdl
+++ b/dom/file/ipc/PIPCBlobInputStream.ipdl
@@ -1,27 +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/. */
 
 include protocol PBackground;
 include protocol PChildToParentStream;
 include protocol PContent;
-include protocol PContentBridge;
 include protocol PFileDescriptorSet;
 include protocol PParentToChildStream;
 
 include IPCStream;
 
 namespace mozilla {
 namespace ipc {
 
 protocol PIPCBlobInputStream
 {
-  manager PBackground or PContent or PContentBridge;
+  manager PBackground or PContent;
 
 parent:
   async StreamNeeded();
 
   async LengthNeeded();
 
   // When this is called, the parent releases the inputStream and sends a
   // __delete__.
--- a/dom/indexedDB/FileSnapshot.cpp
+++ b/dom/indexedDB/FileSnapshot.cpp
@@ -268,33 +268,33 @@ StreamWrapper::ReadSegments(nsWriteSegme
 NS_IMETHODIMP
 StreamWrapper::IsNonBlocking(bool* _retval) {
   return mInputStream->IsNonBlocking(_retval);
 }
 
 void StreamWrapper::Serialize(InputStreamParams& aParams,
                               FileDescriptorArray& aFileDescriptors,
                               bool aDelayedStart, uint32_t aMaxSize,
-                              uint32_t* aSizeUsed, nsIContentChild* aManager) {
+                              uint32_t* aSizeUsed, ContentChild* aManager) {
   SerializeInternal(aParams, aFileDescriptors, aDelayedStart, aMaxSize,
                     aSizeUsed, aManager);
 }
 
 void StreamWrapper::Serialize(InputStreamParams& aParams,
                               FileDescriptorArray& aFileDescriptors,
                               bool aDelayedStart, uint32_t aMaxSize,
                               uint32_t* aSizeUsed, PBackgroundChild* aManager) {
   SerializeInternal(aParams, aFileDescriptors, aDelayedStart, aMaxSize,
                     aSizeUsed, aManager);
 }
 
 void StreamWrapper::Serialize(InputStreamParams& aParams,
                               FileDescriptorArray& aFileDescriptors,
                               bool aDelayedStart, uint32_t aMaxSize,
-                              uint32_t* aSizeUsed, nsIContentParent* aManager) {
+                              uint32_t* aSizeUsed, ContentParent* aManager) {
   SerializeInternal(aParams, aFileDescriptors, aDelayedStart, aMaxSize,
                     aSizeUsed, aManager);
 }
 
 void StreamWrapper::Serialize(InputStreamParams& aParams,
                               FileDescriptorArray& aFileDescriptors,
                               bool aDelayedStart, uint32_t aMaxSize,
                               uint32_t* aSizeUsed,
deleted file mode 100644
--- a/dom/ipc/ContentBridgeChild.cpp
+++ /dev/null
@@ -1,182 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "mozilla/dom/ContentBridgeChild.h"
-#include "mozilla/dom/ContentChild.h"
-#include "mozilla/dom/File.h"
-#include "mozilla/dom/TabChild.h"
-#include "mozilla/dom/TabGroup.h"
-#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
-#include "mozilla/ipc/InputStreamUtils.h"
-#include "base/task.h"
-
-using namespace mozilla::ipc;
-using namespace mozilla::jsipc;
-
-namespace mozilla {
-namespace dom {
-
-NS_IMPL_ISUPPORTS(ContentBridgeChild, nsIContentChild)
-
-ContentBridgeChild::ContentBridgeChild() {}
-
-ContentBridgeChild::~ContentBridgeChild() {}
-
-void ContentBridgeChild::ActorDestroy(ActorDestroyReason aWhy) {
-  MessageLoop::current()->PostTask(
-      NewRunnableMethod("dom::ContentBridgeChild::DeferredDestroy", this,
-                        &ContentBridgeChild::DeferredDestroy));
-}
-
-/*static*/ void ContentBridgeChild::Create(
-    Endpoint<PContentBridgeChild>&& aEndpoint) {
-  RefPtr<ContentBridgeChild> bridge = new ContentBridgeChild();
-  bridge->mSelfRef = bridge;
-
-  DebugOnly<bool> ok = aEndpoint.Bind(bridge);
-  MOZ_ASSERT(ok);
-}
-
-void ContentBridgeChild::DeferredDestroy() {
-  mSelfRef = nullptr;
-  // |this| was just destroyed, hands off
-}
-
-mozilla::ipc::IPCResult ContentBridgeChild::RecvAsyncMessage(
-    const nsString& aMsg, InfallibleTArray<jsipc::CpowEntry>&& aCpows,
-    const IPC::Principal& aPrincipal, const ClonedMessageData& aData) {
-  return nsIContentChild::RecvAsyncMessage(aMsg, std::move(aCpows), aPrincipal,
-                                           aData);
-}
-
-bool ContentBridgeChild::SendPBrowserConstructor(
-    PBrowserChild* aActor, const TabId& aTabId, const TabId& aSameTabGroupAs,
-    const IPCTabContext& aContext, const uint32_t& aChromeFlags,
-    const ContentParentId& aCpID, const bool& aIsForBrowser) {
-  return PContentBridgeChild::SendPBrowserConstructor(
-      aActor, aTabId, aSameTabGroupAs, aContext, aChromeFlags, aCpID,
-      aIsForBrowser);
-}
-
-PFileDescriptorSetChild* ContentBridgeChild::SendPFileDescriptorSetConstructor(
-    const FileDescriptor& aFD) {
-  return PContentBridgeChild::SendPFileDescriptorSetConstructor(aFD);
-}
-
-PChildToParentStreamChild*
-ContentBridgeChild::SendPChildToParentStreamConstructor(
-    PChildToParentStreamChild* aActor) {
-  return PContentBridgeChild::SendPChildToParentStreamConstructor(aActor);
-}
-
-// This implementation is identical to ContentChild::GetCPOWManager but we can't
-// move it to nsIContentChild because it calls ManagedPJavaScriptChild() which
-// only exists in PContentChild and PContentBridgeChild.
-jsipc::CPOWManager* ContentBridgeChild::GetCPOWManager() {
-  if (PJavaScriptChild* c =
-          LoneManagedOrNullAsserts(ManagedPJavaScriptChild())) {
-    return CPOWManagerFor(c);
-  }
-  return CPOWManagerFor(SendPJavaScriptConstructor());
-}
-
-mozilla::jsipc::PJavaScriptChild* ContentBridgeChild::AllocPJavaScriptChild() {
-  return nsIContentChild::AllocPJavaScriptChild();
-}
-
-bool ContentBridgeChild::DeallocPJavaScriptChild(PJavaScriptChild* child) {
-  return nsIContentChild::DeallocPJavaScriptChild(child);
-}
-
-PBrowserChild* ContentBridgeChild::AllocPBrowserChild(
-    const TabId& aTabId, const TabId& aSameTabGroupAs,
-    const IPCTabContext& aContext, const uint32_t& aChromeFlags,
-    const ContentParentId& aCpID, const bool& aIsForBrowser) {
-  return nsIContentChild::AllocPBrowserChild(
-      aTabId, aSameTabGroupAs, aContext, aChromeFlags, aCpID, aIsForBrowser);
-}
-
-bool ContentBridgeChild::DeallocPBrowserChild(PBrowserChild* aChild) {
-  return nsIContentChild::DeallocPBrowserChild(aChild);
-}
-
-mozilla::ipc::IPCResult ContentBridgeChild::RecvPBrowserConstructor(
-    PBrowserChild* aActor, const TabId& aTabId, const TabId& aSameTabGroupAs,
-    const IPCTabContext& aContext, const uint32_t& aChromeFlags,
-    const ContentParentId& aCpID, const bool& aIsForBrowser) {
-  return nsIContentChild::RecvPBrowserConstructor(
-      aActor, aTabId, aSameTabGroupAs, aContext, aChromeFlags, aCpID,
-      aIsForBrowser);
-}
-
-PIPCBlobInputStreamChild* ContentBridgeChild::AllocPIPCBlobInputStreamChild(
-    const nsID& aID, const uint64_t& aSize) {
-  return nsIContentChild::AllocPIPCBlobInputStreamChild(aID, aSize);
-}
-
-bool ContentBridgeChild::DeallocPIPCBlobInputStreamChild(
-    PIPCBlobInputStreamChild* aActor) {
-  return nsIContentChild::DeallocPIPCBlobInputStreamChild(aActor);
-}
-
-PChildToParentStreamChild*
-ContentBridgeChild::AllocPChildToParentStreamChild() {
-  return nsIContentChild::AllocPChildToParentStreamChild();
-}
-
-bool ContentBridgeChild::DeallocPChildToParentStreamChild(
-    PChildToParentStreamChild* aActor) {
-  return nsIContentChild::DeallocPChildToParentStreamChild(aActor);
-}
-
-PParentToChildStreamChild*
-ContentBridgeChild::AllocPParentToChildStreamChild() {
-  return nsIContentChild::AllocPParentToChildStreamChild();
-}
-
-bool ContentBridgeChild::DeallocPParentToChildStreamChild(
-    PParentToChildStreamChild* aActor) {
-  return nsIContentChild::DeallocPParentToChildStreamChild(aActor);
-}
-
-PFileDescriptorSetChild* ContentBridgeChild::AllocPFileDescriptorSetChild(
-    const FileDescriptor& aFD) {
-  return nsIContentChild::AllocPFileDescriptorSetChild(aFD);
-}
-
-bool ContentBridgeChild::DeallocPFileDescriptorSetChild(
-    PFileDescriptorSetChild* aActor) {
-  return nsIContentChild::DeallocPFileDescriptorSetChild(aActor);
-}
-
-mozilla::ipc::IPCResult ContentBridgeChild::RecvActivate(PBrowserChild* aTab) {
-  TabChild* tab = static_cast<TabChild*>(aTab);
-  return tab->RecvActivate();
-}
-
-mozilla::ipc::IPCResult ContentBridgeChild::RecvDeactivate(
-    PBrowserChild* aTab) {
-  TabChild* tab = static_cast<TabChild*>(aTab);
-  return tab->RecvDeactivate();
-}
-
-already_AddRefed<nsIEventTarget> ContentBridgeChild::GetConstructedEventTarget(
-    const Message& aMsg) {
-  // Currently we only set targets for PBrowser.
-  if (aMsg.type() != PContentBridge::Msg_PBrowserConstructor__ID) {
-    return nullptr;
-  }
-
-  return nsIContentChild::GetConstructedEventTarget(aMsg);
-}
-
-already_AddRefed<nsIEventTarget> ContentBridgeChild::GetEventTargetFor(
-    TabChild* aTabChild) {
-  return IToplevelProtocol::GetActorEventTarget(aTabChild);
-}
-
-}  // namespace dom
-}  // namespace mozilla
deleted file mode 100644
--- a/dom/ipc/ContentBridgeChild.h
+++ /dev/null
@@ -1,114 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef mozilla_dom_ContentBridgeChild_h
-#define mozilla_dom_ContentBridgeChild_h
-
-#include "mozilla/dom/PContentBridgeChild.h"
-#include "mozilla/dom/nsIContentChild.h"
-
-namespace mozilla {
-namespace dom {
-
-class ContentBridgeChild final : public PContentBridgeChild,
-                                 public nsIContentChild {
-  friend class PContentBridgeChild;
-
- public:
-  explicit ContentBridgeChild();
-
-  NS_DECL_ISUPPORTS
-
-  static void Create(Endpoint<PContentBridgeChild>&& aEndpoint);
-
-  virtual void ActorDestroy(ActorDestroyReason aWhy) override;
-  void DeferredDestroy();
-
-  virtual mozilla::ipc::IPCResult RecvAsyncMessage(
-      const nsString& aMsg, InfallibleTArray<jsipc::CpowEntry>&& aCpows,
-      const IPC::Principal& aPrincipal,
-      const ClonedMessageData& aData) override;
-
-  jsipc::CPOWManager* GetCPOWManager() override;
-
-  virtual bool SendPBrowserConstructor(
-      PBrowserChild* aActor, const TabId& aTabId, const TabId& aSameTabGroupAs,
-      const IPCTabContext& aContext, const uint32_t& aChromeFlags,
-      const ContentParentId& aCpID, const bool& aIsForBrowser) override;
-
-  virtual mozilla::ipc::PFileDescriptorSetChild*
-  SendPFileDescriptorSetConstructor(
-      const mozilla::ipc::FileDescriptor&) override;
-
-  virtual mozilla::ipc::PChildToParentStreamChild*
-  SendPChildToParentStreamConstructor(
-      mozilla::ipc::PChildToParentStreamChild*) override;
-
-  mozilla::ipc::IPCResult RecvActivate(PBrowserChild* aTab);
-
-  mozilla::ipc::IPCResult RecvDeactivate(PBrowserChild* aTab);
-
-  virtual already_AddRefed<nsIEventTarget> GetEventTargetFor(
-      TabChild* aTabChild) override;
-
-  FORWARD_SHMEM_ALLOCATOR_TO(PContentBridgeChild)
-
- protected:
-  virtual ~ContentBridgeChild();
-
-  virtual PBrowserChild* AllocPBrowserChild(const TabId& aTabId,
-                                            const TabId& aSameTabGroupAs,
-                                            const IPCTabContext& aContext,
-                                            const uint32_t& aChromeFlags,
-                                            const ContentParentId& aCpID,
-                                            const bool& aIsForBrowser) override;
-  virtual bool DeallocPBrowserChild(PBrowserChild*) override;
-  virtual mozilla::ipc::IPCResult RecvPBrowserConstructor(
-      PBrowserChild* aCctor, const TabId& aTabId, const TabId& aSameTabGroupAs,
-      const IPCTabContext& aContext, const uint32_t& aChromeFlags,
-      const ContentParentId& aCpID, const bool& aIsForBrowser) override;
-
-  virtual mozilla::jsipc::PJavaScriptChild* AllocPJavaScriptChild() override;
-  virtual bool DeallocPJavaScriptChild(
-      mozilla::jsipc::PJavaScriptChild*) override;
-
-  virtual PIPCBlobInputStreamChild* AllocPIPCBlobInputStreamChild(
-      const nsID& aID, const uint64_t& aSize) override;
-
-  virtual bool DeallocPIPCBlobInputStreamChild(
-      PIPCBlobInputStreamChild*) override;
-
-  virtual mozilla::ipc::PChildToParentStreamChild*
-  AllocPChildToParentStreamChild() override;
-
-  virtual bool DeallocPChildToParentStreamChild(
-      mozilla::ipc::PChildToParentStreamChild* aActor) override;
-
-  virtual PParentToChildStreamChild* AllocPParentToChildStreamChild() override;
-
-  virtual bool DeallocPParentToChildStreamChild(
-      PParentToChildStreamChild* aActor) override;
-
-  virtual PFileDescriptorSetChild* AllocPFileDescriptorSetChild(
-      const mozilla::ipc::FileDescriptor& aFD) override;
-
-  virtual bool DeallocPFileDescriptorSetChild(
-      mozilla::ipc::PFileDescriptorSetChild* aActor) override;
-
-  DISALLOW_EVIL_CONSTRUCTORS(ContentBridgeChild);
-
- private:
-  virtual already_AddRefed<nsIEventTarget> GetConstructedEventTarget(
-      const Message& aMsg) override;
-
- protected:  // members
-  RefPtr<ContentBridgeChild> mSelfRef;
-};
-
-}  // namespace dom
-}  // namespace mozilla
-
-#endif  // mozilla_dom_ContentBridgeChild_h
deleted file mode 100644
--- a/dom/ipc/ContentBridgeParent.cpp
+++ /dev/null
@@ -1,204 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "mozilla/dom/ContentBridgeParent.h"
-#include "mozilla/dom/ProcessMessageManager.h"
-#include "mozilla/dom/TabParent.h"
-#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
-#include "nsXULAppAPI.h"
-#include "nsIObserverService.h"
-#include "base/task.h"
-
-using namespace mozilla::ipc;
-using namespace mozilla::jsipc;
-
-namespace mozilla {
-namespace dom {
-
-NS_IMPL_ISUPPORTS(ContentBridgeParent, nsIContentParent, nsIObserver)
-
-ContentBridgeParent::ContentBridgeParent()
-    : mIsForBrowser(false), mIsForJSPlugin(false) {}
-
-ContentBridgeParent::~ContentBridgeParent() {}
-
-void ContentBridgeParent::ActorDestroy(ActorDestroyReason aWhy) {
-  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
-  if (os) {
-    os->RemoveObserver(this, "content-child-shutdown");
-  }
-  MessageLoop::current()->PostTask(
-      NewRunnableMethod("dom::ContentBridgeParent::DeferredDestroy", this,
-                        &ContentBridgeParent::DeferredDestroy));
-}
-
-/*static*/ ContentBridgeParent* ContentBridgeParent::Create(
-    Endpoint<PContentBridgeParent>&& aEndpoint) {
-  RefPtr<ContentBridgeParent> bridge = new ContentBridgeParent();
-  bridge->mSelfRef = bridge;
-
-  DebugOnly<bool> ok = aEndpoint.Bind(bridge);
-  MOZ_ASSERT(ok);
-
-  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
-  if (os) {
-    os->AddObserver(bridge, "content-child-shutdown", false);
-  }
-
-  // Initialize the message manager (and load delayed scripts) now that we
-  // have established communications with the child.
-  bridge->mMessageManager->InitWithCallback(bridge);
-
-  return bridge.get();
-}
-
-void ContentBridgeParent::DeferredDestroy() {
-  mSelfRef = nullptr;
-  // |this| was just destroyed, hands off
-}
-
-mozilla::ipc::IPCResult ContentBridgeParent::RecvSyncMessage(
-    const nsString& aMsg, const ClonedMessageData& aData,
-    InfallibleTArray<jsipc::CpowEntry>&& aCpows,
-    const IPC::Principal& aPrincipal, nsTArray<StructuredCloneData>* aRetvals) {
-  return nsIContentParent::RecvSyncMessage(aMsg, aData, std::move(aCpows),
-                                           aPrincipal, aRetvals);
-}
-
-mozilla::ipc::IPCResult ContentBridgeParent::RecvAsyncMessage(
-    const nsString& aMsg, InfallibleTArray<jsipc::CpowEntry>&& aCpows,
-    const IPC::Principal& aPrincipal, const ClonedMessageData& aData) {
-  return nsIContentParent::RecvAsyncMessage(aMsg, std::move(aCpows), aPrincipal,
-                                            aData);
-}
-
-PBrowserParent* ContentBridgeParent::SendPBrowserConstructor(
-    PBrowserParent* aActor, const TabId& aTabId, const TabId& aSameTabGroupAs,
-    const IPCTabContext& aContext, const uint32_t& aChromeFlags,
-    const ContentParentId& aCpID, const bool& aIsForBrowser) {
-  return PContentBridgeParent::SendPBrowserConstructor(
-      aActor, aTabId, aSameTabGroupAs, aContext, aChromeFlags, aCpID,
-      aIsForBrowser);
-}
-
-PParentToChildStreamParent*
-ContentBridgeParent::SendPParentToChildStreamConstructor(
-    PParentToChildStreamParent* aActor) {
-  return PContentBridgeParent::SendPParentToChildStreamConstructor(aActor);
-}
-
-PIPCBlobInputStreamParent*
-ContentBridgeParent::SendPIPCBlobInputStreamConstructor(
-    PIPCBlobInputStreamParent* aActor, const nsID& aID, const uint64_t& aSize) {
-  return PContentBridgeParent::SendPIPCBlobInputStreamConstructor(aActor, aID,
-                                                                  aSize);
-}
-
-PIPCBlobInputStreamParent* ContentBridgeParent::AllocPIPCBlobInputStreamParent(
-    const nsID& aID, const uint64_t& aSize) {
-  return nsIContentParent::AllocPIPCBlobInputStreamParent(aID, aSize);
-}
-
-bool ContentBridgeParent::DeallocPIPCBlobInputStreamParent(
-    PIPCBlobInputStreamParent* aActor) {
-  return nsIContentParent::DeallocPIPCBlobInputStreamParent(aActor);
-}
-
-mozilla::jsipc::PJavaScriptParent*
-ContentBridgeParent::AllocPJavaScriptParent() {
-  return nsIContentParent::AllocPJavaScriptParent();
-}
-
-bool ContentBridgeParent::DeallocPJavaScriptParent(PJavaScriptParent* parent) {
-  return nsIContentParent::DeallocPJavaScriptParent(parent);
-}
-
-PBrowserParent* ContentBridgeParent::AllocPBrowserParent(
-    const TabId& aTabId, const TabId& aSameTabGroupAs,
-    const IPCTabContext& aContext, const uint32_t& aChromeFlags,
-    const ContentParentId& aCpID, const bool& aIsForBrowser) {
-  return nsIContentParent::AllocPBrowserParent(
-      aTabId, aSameTabGroupAs, aContext, aChromeFlags, aCpID, aIsForBrowser);
-}
-
-bool ContentBridgeParent::DeallocPBrowserParent(PBrowserParent* aParent) {
-  return nsIContentParent::DeallocPBrowserParent(aParent);
-}
-
-mozilla::ipc::IPCResult ContentBridgeParent::RecvPBrowserConstructor(
-    PBrowserParent* actor, const TabId& tabId, const TabId& sameTabGroupAs,
-    const IPCTabContext& context, const uint32_t& chromeFlags,
-    const ContentParentId& cpId, const bool& isForBrowser) {
-  return nsIContentParent::RecvPBrowserConstructor(
-      actor, tabId, sameTabGroupAs, context, chromeFlags, cpId, isForBrowser);
-}
-
-void ContentBridgeParent::NotifyTabDestroyed() {
-  int32_t numLiveTabs = ManagedPBrowserParent().Count();
-  if (numLiveTabs == 1) {
-    MessageLoop::current()->PostTask(NewRunnableMethod(
-        "dom::ContentBridgeParent::Close", this, &ContentBridgeParent::Close));
-  }
-}
-
-// This implementation is identical to ContentParent::GetCPOWManager but we
-// can't move it to nsIContentParent because it calls ManagedPJavaScriptParent()
-// which only exists in PContentParent and PContentBridgeParent.
-jsipc::CPOWManager* ContentBridgeParent::GetCPOWManager() {
-  if (PJavaScriptParent* p =
-          LoneManagedOrNullAsserts(ManagedPJavaScriptParent())) {
-    return CPOWManagerFor(p);
-  }
-  return nullptr;
-}
-
-NS_IMETHODIMP
-ContentBridgeParent::Observe(nsISupports* aSubject, const char* aTopic,
-                             const char16_t* aData) {
-  if (!strcmp(aTopic, "content-child-shutdown")) {
-    Close();
-  }
-  return NS_OK;
-}
-
-PFileDescriptorSetParent*
-ContentBridgeParent::SendPFileDescriptorSetConstructor(
-    const FileDescriptor& aFD) {
-  return PContentBridgeParent::SendPFileDescriptorSetConstructor(aFD);
-}
-
-PFileDescriptorSetParent* ContentBridgeParent::AllocPFileDescriptorSetParent(
-    const FileDescriptor& aFD) {
-  return nsIContentParent::AllocPFileDescriptorSetParent(aFD);
-}
-
-bool ContentBridgeParent::DeallocPFileDescriptorSetParent(
-    PFileDescriptorSetParent* aActor) {
-  return nsIContentParent::DeallocPFileDescriptorSetParent(aActor);
-}
-
-PChildToParentStreamParent*
-ContentBridgeParent::AllocPChildToParentStreamParent() {
-  return nsIContentParent::AllocPChildToParentStreamParent();
-}
-
-bool ContentBridgeParent::DeallocPChildToParentStreamParent(
-    PChildToParentStreamParent* aActor) {
-  return nsIContentParent::DeallocPChildToParentStreamParent(aActor);
-}
-
-PParentToChildStreamParent*
-ContentBridgeParent::AllocPParentToChildStreamParent() {
-  return nsIContentParent::AllocPParentToChildStreamParent();
-}
-
-bool ContentBridgeParent::DeallocPParentToChildStreamParent(
-    PParentToChildStreamParent* aActor) {
-  return nsIContentParent::DeallocPParentToChildStreamParent(aActor);
-}
-
-}  // namespace dom
-}  // namespace mozilla
deleted file mode 100644
--- a/dom/ipc/ContentBridgeParent.h
+++ /dev/null
@@ -1,155 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef mozilla_dom_ContentBridgeParent_h
-#define mozilla_dom_ContentBridgeParent_h
-
-#include "mozilla/dom/PContentBridgeParent.h"
-#include "mozilla/dom/nsIContentParent.h"
-#include "mozilla/dom/ipc/IdType.h"
-#include "nsIObserver.h"
-
-namespace mozilla {
-namespace dom {
-
-class ContentBridgeParent : public PContentBridgeParent,
-                            public nsIContentParent,
-                            public nsIObserver {
-  friend class PContentBridgeParent;
-
- public:
-  explicit ContentBridgeParent();
-
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIOBSERVER
-
-  virtual void ActorDestroy(ActorDestroyReason aWhy) override;
-  void DeferredDestroy();
-  virtual bool IsContentBridgeParent() const override { return true; }
-  void NotifyTabDestroyed();
-
-  static ContentBridgeParent* Create(
-      Endpoint<PContentBridgeParent>&& aEndpoint);
-
-  virtual PBrowserParent* SendPBrowserConstructor(
-      PBrowserParent* aActor, const TabId& aTabId, const TabId& aSameTabGroupAs,
-      const IPCTabContext& aContext, const uint32_t& aChromeFlags,
-      const ContentParentId& aCpID, const bool& aIsForBrowser) override;
-
-  virtual PFileDescriptorSetParent* SendPFileDescriptorSetConstructor(
-      const FileDescriptor&) override;
-
-  FORWARD_SHMEM_ALLOCATOR_TO(PContentBridgeParent)
-
-  jsipc::CPOWManager* GetCPOWManager() override;
-
-  virtual ContentParentId ChildID() const override { return mChildID; }
-  virtual bool IsForBrowser() const override { return mIsForBrowser; }
-  virtual int32_t Pid() const override {
-    // XXX: do we need this for ContentBridgeParent?
-    return -1;
-  }
-  virtual bool IsForJSPlugin() const override { return mIsForJSPlugin; }
-
-  virtual mozilla::ipc::PParentToChildStreamParent*
-  SendPParentToChildStreamConstructor(
-      mozilla::ipc::PParentToChildStreamParent*) override;
-
-  virtual bool SendActivate(PBrowserParent* aTab) override {
-    return PContentBridgeParent::SendActivate(aTab);
-  }
-
-  virtual bool SendDeactivate(PBrowserParent* aTab) override {
-    return PContentBridgeParent::SendDeactivate(aTab);
-  }
-
- protected:
-  virtual ~ContentBridgeParent();
-
-  void SetChildID(ContentParentId aId) { mChildID = aId; }
-
-  void SetIsForBrowser(bool aIsForBrowser) { mIsForBrowser = aIsForBrowser; }
-  void SetIsForJSPlugin(bool aIsForJSPlugin) {
-    mIsForJSPlugin = aIsForJSPlugin;
-  }
-
-  void Close() {
-    // Trick NewRunnableMethod
-    PContentBridgeParent::Close();
-  }
-
- protected:
-  virtual mozilla::ipc::IPCResult RecvSyncMessage(
-      const nsString& aMsg, const ClonedMessageData& aData,
-      InfallibleTArray<jsipc::CpowEntry>&& aCpows,
-      const IPC::Principal& aPrincipal,
-      nsTArray<StructuredCloneData>* aRetvals) override;
-
-  virtual mozilla::ipc::IPCResult RecvAsyncMessage(
-      const nsString& aMsg, InfallibleTArray<jsipc::CpowEntry>&& aCpows,
-      const IPC::Principal& aPrincipal,
-      const ClonedMessageData& aData) override;
-
-  virtual jsipc::PJavaScriptParent* AllocPJavaScriptParent() override;
-
-  virtual bool DeallocPJavaScriptParent(jsipc::PJavaScriptParent*) override;
-
-  virtual PBrowserParent* AllocPBrowserParent(
-      const TabId& aTabId, const TabId& aSameTabGroupAs,
-      const IPCTabContext& aContext, const uint32_t& aChromeFlags,
-      const ContentParentId& aCpID, const bool& aIsForBrowser) override;
-
-  virtual bool DeallocPBrowserParent(PBrowserParent*) override;
-
-  virtual mozilla::ipc::IPCResult RecvPBrowserConstructor(
-      PBrowserParent* actor, const TabId& tabId, const TabId& sameTabGroupAs,
-      const IPCTabContext& context, const uint32_t& chromeFlags,
-      const ContentParentId& cpId, const bool& isForBrowser) override;
-
-  virtual PIPCBlobInputStreamParent* SendPIPCBlobInputStreamConstructor(
-      PIPCBlobInputStreamParent* aActor, const nsID& aID,
-      const uint64_t& aSize) override;
-
-  virtual PIPCBlobInputStreamParent* AllocPIPCBlobInputStreamParent(
-      const nsID& aID, const uint64_t& aSize) override;
-
-  virtual bool DeallocPIPCBlobInputStreamParent(
-      PIPCBlobInputStreamParent*) override;
-
-  virtual PChildToParentStreamParent* AllocPChildToParentStreamParent()
-      override;
-
-  virtual bool DeallocPChildToParentStreamParent(
-      PChildToParentStreamParent* aActor) override;
-
-  virtual mozilla::ipc::PParentToChildStreamParent*
-  AllocPParentToChildStreamParent() override;
-
-  virtual bool DeallocPParentToChildStreamParent(
-      mozilla::ipc::PParentToChildStreamParent* aActor) override;
-
-  virtual PFileDescriptorSetParent* AllocPFileDescriptorSetParent(
-      const mozilla::ipc::FileDescriptor&) override;
-
-  virtual bool DeallocPFileDescriptorSetParent(
-      PFileDescriptorSetParent*) override;
-
-  DISALLOW_EVIL_CONSTRUCTORS(ContentBridgeParent);
-
- protected:  // members
-  RefPtr<ContentBridgeParent> mSelfRef;
-  ContentParentId mChildID;
-  bool mIsForBrowser;
-  bool mIsForJSPlugin;
-
- private:
-  friend class ContentParent;
-};
-
-}  // namespace dom
-}  // namespace mozilla
-
-#endif  // mozilla_dom_ContentBridgeParent_h
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -26,18 +26,16 @@
 #include "mozilla/StaticPrefs.h"
 #include "mozilla/TelemetryIPC.h"
 #include "mozilla/VideoDecoderManagerChild.h"
 #include "mozilla/devtools/HeapSnapshotTempFileHelperChild.h"
 #include "mozilla/docshell/OfflineCacheUpdateChild.h"
 #include "mozilla/dom/ClientManager.h"
 #include "mozilla/dom/ClientOpenWindowOpActors.h"
 #include "mozilla/dom/ChildProcessMessageManager.h"
-#include "mozilla/dom/ContentBridgeChild.h"
-#include "mozilla/dom/ContentBridgeParent.h"
 #include "mozilla/dom/ContentProcessMessageManager.h"
 #include "mozilla/dom/DOMPrefs.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/DataTransfer.h"
 #include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/ExternalHelperAppChild.h"
 #include "mozilla/dom/FileCreatorHelper.h"
 #include "mozilla/dom/GetFilesHelper.h"
@@ -47,31 +45,32 @@
 #include "mozilla/dom/MemoryReportRequest.h"
 #include "mozilla/dom/PLoginReputationChild.h"
 #include "mozilla/dom/PSessionStorageObserverChild.h"
 #include "mozilla/dom/PostMessageEvent.h"
 #include "mozilla/dom/PushNotifier.h"
 #include "mozilla/dom/RemoteWorkerService.h"
 #include "mozilla/dom/ServiceWorkerManager.h"
 #include "mozilla/dom/TabGroup.h"
-#include "mozilla/dom/nsIContentChild.h"
 #include "mozilla/dom/URLClassifierChild.h"
 #include "mozilla/dom/WorkerDebugger.h"
 #include "mozilla/dom/WorkerDebuggerManager.h"
+#include "mozilla/dom/ipc/IPCBlobInputStreamChild.h"
 #include "mozilla/dom/ipc/SharedMap.h"
 #include "mozilla/gfx/gfxVars.h"
 #include "mozilla/gfx/Logging.h"
 #include "mozilla/psm/PSMContentListener.h"
 #include "mozilla/hal_sandbox/PHalChild.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/FileDescriptorSetChild.h"
 #include "mozilla/ipc/FileDescriptorUtils.h"
 #include "mozilla/ipc/GeckoChildProcessHost.h"
 #include "mozilla/ipc/ProcessChild.h"
 #include "mozilla/ipc/PChildToParentStreamChild.h"
+#include "mozilla/ipc/PParentToChildStreamChild.h"
 #include "mozilla/intl/LocaleService.h"
 #include "mozilla/ipc/TestShellChild.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 #include "mozilla/jsipc/PJavaScript.h"
 #include "mozilla/layers/APZChild.h"
 #include "mozilla/layers/CompositorManagerChild.h"
 #include "mozilla/layers/ContentProcessController.h"
 #include "mozilla/layers/ImageBridgeChild.h"
@@ -223,16 +222,19 @@
 #    include "mozilla/a11y/AccessibleWrap.h"
 #  endif
 #endif
 
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/PPresentationChild.h"
 #include "mozilla/dom/PresentationIPCService.h"
 #include "mozilla/ipc/InputStreamUtils.h"
+#include "mozilla/ipc/IPCStreamAlloc.h"
+#include "mozilla/ipc/IPCStreamDestination.h"
+#include "mozilla/ipc/IPCStreamSource.h"
 
 #ifdef MOZ_WEBSPEECH
 #  include "mozilla/dom/PSpeechSynthesisChild.h"
 #endif
 
 #include "ClearOnShutdown.h"
 #include "ProcessUtils.h"
 #include "URIUtils.h"
@@ -575,19 +577,18 @@ ContentChild::~ContentChild() {
 #endif
 }
 
 #ifdef _MSC_VER
 #  pragma warning(pop)
 #endif
 
 NS_INTERFACE_MAP_BEGIN(ContentChild)
-  NS_INTERFACE_MAP_ENTRY(nsIContentChild)
   NS_INTERFACE_MAP_ENTRY(nsIWindowProvider)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentChild)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 mozilla::ipc::IPCResult ContentChild::RecvSetXPCOMProcessAttributes(
     const XPCOMInitData& aXPCOMInit, const StructuredCloneData& aInitialData,
     nsTArray<LookAndFeelInt>&& aLookAndFeelIntCache,
     nsTArray<SystemFontListEntry>&& aFontList) {
   if (!sShutdownCanary) {
     return IPC_OK();
@@ -1311,22 +1312,16 @@ mozilla::ipc::IPCResult ContentChild::Re
 
 bool ContentChild::DeallocPCycleCollectWithLogsChild(
     PCycleCollectWithLogsChild* aActor) {
   RefPtr<CycleCollectWithLogsChild> actor =
       dont_AddRef(static_cast<CycleCollectWithLogsChild*>(aActor));
   return true;
 }
 
-mozilla::ipc::IPCResult ContentChild::RecvInitContentBridgeChild(
-    Endpoint<PContentBridgeChild>&& aEndpoint) {
-  ContentBridgeChild::Create(std::move(aEndpoint));
-  return IPC_OK();
-}
-
 mozilla::ipc::IPCResult ContentChild::RecvInitGMPService(
     Endpoint<PGMPServiceChild>&& aGMPService) {
   if (!GMPServiceChild::Create(std::move(aGMPService))) {
     return IPC_FAIL_NO_REASON(this);
   }
   return IPC_OK();
 }
 
@@ -1687,31 +1682,49 @@ static void FirstIdle(void) {
   if (!recordreplay::IsRecordingOrReplaying()) {
     ContentChild::GetSingleton()->SendFirstIdle();
   }
 }
 
 mozilla::jsipc::PJavaScriptChild* ContentChild::AllocPJavaScriptChild() {
   MOZ_ASSERT(ManagedPJavaScriptChild().IsEmpty());
 
-  return nsIContentChild::AllocPJavaScriptChild();
+  return NewJavaScriptChild();
 }
 
 bool ContentChild::DeallocPJavaScriptChild(PJavaScriptChild* aChild) {
-  return nsIContentChild::DeallocPJavaScriptChild(aChild);
+  ReleaseJavaScriptChild(aChild);
+  return true;
 }
 
 PBrowserChild* ContentChild::AllocPBrowserChild(const TabId& aTabId,
                                                 const TabId& aSameTabGroupAs,
                                                 const IPCTabContext& aContext,
                                                 const uint32_t& aChromeFlags,
                                                 const ContentParentId& aCpID,
                                                 const bool& aIsForBrowser) {
-  return nsIContentChild::AllocPBrowserChild(
-      aTabId, aSameTabGroupAs, aContext, aChromeFlags, aCpID, aIsForBrowser);
+  // We'll happily accept any kind of IPCTabContext here; we don't need to
+  // check that it's of a certain type for security purposes, because we
+  // believe whatever the parent process tells us.
+
+  MaybeInvalidTabContext tc(aContext);
+  if (!tc.IsValid()) {
+    NS_ERROR(nsPrintfCString("Received an invalid TabContext from "
+                             "the parent process. (%s)  Crashing...",
+                             tc.GetInvalidReason())
+                 .get());
+    MOZ_CRASH("Invalid TabContext received from the parent process.");
+  }
+
+  RefPtr<TabChild> child =
+      TabChild::Create(static_cast<ContentChild*>(this), aTabId,
+                       aSameTabGroupAs, tc.GetTabContext(), aChromeFlags);
+
+  // The ref here is released in DeallocPBrowserChild.
+  return child.forget().take();
 }
 
 bool ContentChild::SendPBrowserConstructor(
     PBrowserChild* aActor, const TabId& aTabId, const TabId& aSameTabGroupAs,
     const IPCTabContext& aContext, const uint32_t& aChromeFlags,
     const ContentParentId& aCpID, const bool& aIsForBrowser) {
   if (IsShuttingDown()) {
     return false;
@@ -1737,19 +1750,30 @@ mozilla::ipc::IPCResult ContentChild::Re
     gFirstIdleTask = firstIdleTask;
     if (NS_FAILED(NS_DispatchToCurrentThreadQueue(firstIdleTask.forget(),
                                                   EventQueuePriority::Idle))) {
       gFirstIdleTask = nullptr;
       hasRunOnce = false;
     }
   }
 
-  return nsIContentChild::RecvPBrowserConstructor(
-      aActor, aTabId, aSameTabGroupAs, aContext, aChromeFlags, aCpID,
-      aIsForBrowser);
+  auto tabChild = static_cast<TabChild*>(aActor);
+
+  if (NS_WARN_IF(NS_FAILED(tabChild->Init(/* aOpener */ nullptr)))) {
+    return IPC_FAIL(tabChild, "TabChild::Init failed");
+  }
+
+  nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+  if (os) {
+    os->NotifyObservers(static_cast<nsITabChild*>(tabChild),
+                        "tab-child-created", nullptr);
+  }
+  // Notify parent that we are ready to handle input events.
+  tabChild->SendRemoteIsReadyToHandleInputEvents();
+  return IPC_OK();
 }
 
 void ContentChild::GetAvailableDictionaries(
     InfallibleTArray<nsString>& aDictionaries) {
   aDictionaries = mAvailableDictionaries;
 }
 
 PFileDescriptorSetChild* ContentChild::SendPFileDescriptorSetConstructor(
@@ -1758,36 +1782,46 @@ PFileDescriptorSetChild* ContentChild::S
     return nullptr;
   }
 
   return PContentChild::SendPFileDescriptorSetConstructor(aFD);
 }
 
 PFileDescriptorSetChild* ContentChild::AllocPFileDescriptorSetChild(
     const FileDescriptor& aFD) {
-  return nsIContentChild::AllocPFileDescriptorSetChild(aFD);
+  return new FileDescriptorSetChild(aFD);
 }
 
 bool ContentChild::DeallocPFileDescriptorSetChild(
     PFileDescriptorSetChild* aActor) {
-  return nsIContentChild::DeallocPFileDescriptorSetChild(aActor);
+  delete static_cast<FileDescriptorSetChild*>(aActor);
+  return true;
 }
 
 bool ContentChild::DeallocPBrowserChild(PBrowserChild* aIframe) {
-  return nsIContentChild::DeallocPBrowserChild(aIframe);
+  TabChild* child = static_cast<TabChild*>(aIframe);
+  NS_RELEASE(child);
+  return true;
 }
 
 PIPCBlobInputStreamChild* ContentChild::AllocPIPCBlobInputStreamChild(
     const nsID& aID, const uint64_t& aSize) {
-  return nsIContentChild::AllocPIPCBlobInputStreamChild(aID, aSize);
+  // IPCBlobInputStreamChild is refcounted. Here it's created and in
+  // DeallocPIPCBlobInputStreamChild is released.
+
+  RefPtr<IPCBlobInputStreamChild> actor =
+      new IPCBlobInputStreamChild(aID, aSize);
+  return actor.forget().take();
 }
 
 bool ContentChild::DeallocPIPCBlobInputStreamChild(
     PIPCBlobInputStreamChild* aActor) {
-  return nsIContentChild::DeallocPIPCBlobInputStreamChild(aActor);
+  RefPtr<IPCBlobInputStreamChild> actor =
+      dont_AddRef(static_cast<IPCBlobInputStreamChild*>(aActor));
+  return true;
 }
 
 mozilla::PRemoteSpellcheckEngineChild*
 ContentChild::AllocPRemoteSpellcheckEngineChild() {
   MOZ_CRASH(
       "Default Constructor for PRemoteSpellcheckEngineChild should never be "
       "called");
   return nullptr;
@@ -1942,31 +1976,33 @@ PChildToParentStreamChild* ContentChild:
   if (IsShuttingDown()) {
     return nullptr;
   }
 
   return PContentChild::SendPChildToParentStreamConstructor(aActor);
 }
 
 PChildToParentStreamChild* ContentChild::AllocPChildToParentStreamChild() {
-  return nsIContentChild::AllocPChildToParentStreamChild();
+  MOZ_CRASH("PChildToParentStreamChild actors should be manually constructed!");
 }
 
 bool ContentChild::DeallocPChildToParentStreamChild(
     PChildToParentStreamChild* aActor) {
-  return nsIContentChild::DeallocPChildToParentStreamChild(aActor);
+  delete aActor;
+  return true;
 }
 
 PParentToChildStreamChild* ContentChild::AllocPParentToChildStreamChild() {
-  return nsIContentChild::AllocPParentToChildStreamChild();
+  return mozilla::ipc::AllocPParentToChildStreamChild();
 }
 
 bool ContentChild::DeallocPParentToChildStreamChild(
     PParentToChildStreamChild* aActor) {
-  return nsIContentChild::DeallocPParentToChildStreamChild(aActor);
+  delete aActor;
+  return true;
 }
 
 PPSMContentDownloaderChild* ContentChild::AllocPPSMContentDownloaderChild(
     const uint32_t& aCertType) {
   // NB: We don't need aCertType in the child actor.
   RefPtr<PSMContentDownloaderChild> child = new PSMContentDownloaderChild();
   return child.forget().take();
 }
@@ -2793,18 +2829,17 @@ void ContentChild::StartForceKillTimer()
 /* static */ void ContentChild::ForceKillTimerCallback(nsITimer* aTimer,
                                                        void* aClosure) {
   ProcessChild::QuickExit();
 }
 
 mozilla::ipc::IPCResult ContentChild::RecvShutdown() {
   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
   if (os) {
-    os->NotifyObservers(static_cast<nsIContentChild*>(this),
-                        "content-child-will-shutdown", nullptr);
+    os->NotifyObservers(this, "content-child-will-shutdown", nullptr);
   }
 
   ShutdownInternal();
   return IPC_OK();
 }
 
 void ContentChild::ShutdownInternal() {
   // If we receive the shutdown message from within a nested event loop, we want
@@ -2841,18 +2876,17 @@ void ContentChild::ShutdownInternal() {
 
   if (mPolicy) {
     mPolicy->Deactivate();
     mPolicy = nullptr;
   }
 
   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
   if (os) {
-    os->NotifyObservers(static_cast<nsIContentChild*>(this),
-                        "content-child-shutdown", nullptr);
+    os->NotifyObservers(this, "content-child-shutdown", nullptr);
   }
 
 #if defined(XP_WIN)
   mozilla::widget::StopAudioSession();
 #endif
 
   GetIPCChannel()->SetAbortOnError(false);
 
@@ -3285,17 +3319,48 @@ bool ContentChild::DeallocPSessionStorag
 // actors created by the ContentParent.
 already_AddRefed<nsIEventTarget> ContentChild::GetConstructedEventTarget(
     const Message& aMsg) {
   // Currently we only set targets for PBrowser.
   if (aMsg.type() != PContent::Msg_PBrowserConstructor__ID) {
     return nullptr;
   }
 
-  return nsIContentChild::GetConstructedEventTarget(aMsg);
+  ActorHandle handle;
+  TabId tabId, sameTabGroupAs;
+  PickleIterator iter(aMsg);
+  if (!IPC::ReadParam(&aMsg, &iter, &handle)) {
+    return nullptr;
+  }
+  aMsg.IgnoreSentinel(&iter);
+  if (!IPC::ReadParam(&aMsg, &iter, &tabId)) {
+    return nullptr;
+  }
+  aMsg.IgnoreSentinel(&iter);
+  if (!IPC::ReadParam(&aMsg, &iter, &sameTabGroupAs)) {
+    return nullptr;
+  }
+
+  // If sameTabGroupAs is non-zero, then the new tab will be in the same
+  // TabGroup as a previously created tab. Rather than try to find the
+  // previously created tab (whose constructor message may not even have been
+  // processed yet, in theory) and look up its event target, we just use the
+  // default event target. This means that runnables for this tab will not be
+  // labeled. However, this path is only taken for print preview and view
+  // source, which are not performance-sensitive.
+  if (sameTabGroupAs) {
+    return nullptr;
+  }
+
+  // If the request for a new TabChild is coming from the parent process, then
+  // there is no opener. Therefore, we create a fresh TabGroup.
+  RefPtr<TabGroup> tabGroup = new TabGroup();
+  nsCOMPtr<nsIEventTarget> target =
+      tabGroup->EventTargetFor(TaskCategory::Other);
+  return target.forget();
 }
 
 void ContentChild::FileCreationRequest(nsID& aUUID, FileCreatorHelper* aHelper,
                                        const nsAString& aFullPath,
                                        const nsAString& aType,
                                        const nsAString& aName,
                                        const Optional<int64_t>& aLastModified,
                                        bool aExistenceCheck,
@@ -3846,9 +3911,9 @@ nsresult GetRepoDir(nsIFile** aRepoDir) 
 }
 
 nsresult GetObjDir(nsIFile** aObjDir) {
   MOZ_ASSERT(IsDevelopmentBuild());
   return GetDirFromBundlePlist(NS_LITERAL_STRING(MAC_DEV_OBJ_KEY), aObjDir);
 }
 #endif /* XP_MACOSX */
 
-}  // namespace mozilla
+}  // namespace mozilla
\ No newline at end of file
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -4,25 +4,28 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_ContentChild_h
 #define mozilla_dom_ContentChild_h
 
 #include "mozilla/Atomics.h"
 #include "mozilla/Attributes.h"
-#include "mozilla/dom/ContentBridgeParent.h"
-#include "mozilla/dom/nsIContentChild.h"
 #include "mozilla/dom/PBrowserOrId.h"
 #include "mozilla/dom/PContentChild.h"
+#include "mozilla/dom/CPOWManagerGetter.h"
 #include "mozilla/StaticPtr.h"
+#include "mozilla/ipc/Shmem.h"
+#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 #include "nsAutoPtr.h"
 #include "nsHashKeys.h"
 #include "nsIObserver.h"
 #include "nsTHashtable.h"
+#include "nsStringFwd.h"
+#include "nsTArrayForwardDeclare.h"
 #include "nsRefPtrHashtable.h"
 
 #include "nsIWindowProvider.h"
 
 #if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
 #  include "nsIFile.h"
 #endif
 
@@ -73,17 +76,18 @@ class AlertObserver;
 class ConsoleListener;
 class ClonedMessageData;
 class TabChild;
 class GetFilesHelperChild;
 class FileCreatorHelper;
 
 class ContentChild final : public PContentChild,
                            public nsIWindowProvider,
-                           public nsIContentChild {
+                           public CPOWManagerGetter,
+                           public mozilla::ipc::IShmemAllocator {
   typedef mozilla::dom::ClonedMessageData ClonedMessageData;
   typedef mozilla::ipc::FileDescriptor FileDescriptor;
   typedef mozilla::ipc::OptionalURIParams OptionalURIParams;
   typedef mozilla::ipc::PFileDescriptorSetChild PFileDescriptorSetChild;
   typedef mozilla::ipc::URIParams URIParams;
 
   friend class PContentChild;
 
@@ -151,19 +155,16 @@ class ContentChild final : public PConte
   bool IsShuttingDown() const;
 
   ipc::SharedMap* SharedData() { return mSharedData; };
 
   static void AppendProcessId(nsACString& aName);
 
   static void UpdateCookieStatus(nsIChannel* aChannel);
 
-  mozilla::ipc::IPCResult RecvInitContentBridgeChild(
-      Endpoint<PContentBridgeChild>&& aEndpoint);
-
   mozilla::ipc::IPCResult RecvInitGMPService(
       Endpoint<PGMPServiceChild>&& aGMPService);
 
   mozilla::ipc::IPCResult RecvInitProfiler(
       Endpoint<PProfilerChild>&& aEndpoint);
 
   mozilla::ipc::IPCResult RecvGMPsChanged(
       nsTArray<GMPCapabilityData>&& capabilities);
@@ -189,30 +190,29 @@ class ContentChild final : public PConte
 
   virtual mozilla::ipc::IPCResult RecvAudioDefaultDeviceChange();
 
   mozilla::ipc::IPCResult RecvReinitRenderingForDeviceReset();
 
   virtual mozilla::ipc::IPCResult RecvSetProcessSandbox(
       const MaybeFileDesc& aBroker);
 
-  virtual PBrowserChild* AllocPBrowserChild(const TabId& aTabId,
-                                            const TabId& aSameTabGroupAs,
-                                            const IPCTabContext& aContext,
-                                            const uint32_t& aChromeFlags,
-                                            const ContentParentId& aCpID,
-                                            const bool& aIsForBrowser) override;
+  PBrowserChild* AllocPBrowserChild(const TabId& aTabId,
+                                    const TabId& aSameTabGroupAs,
+                                    const IPCTabContext& aContext,
+                                    const uint32_t& aChromeFlags,
+                                    const ContentParentId& aCpID,
+                                    const bool& aIsForBrowser);
 
-  virtual bool DeallocPBrowserChild(PBrowserChild*) override;
+  bool DeallocPBrowserChild(PBrowserChild*);
 
-  virtual PIPCBlobInputStreamChild* AllocPIPCBlobInputStreamChild(
-      const nsID& aID, const uint64_t& aSize) override;
+  PIPCBlobInputStreamChild* AllocPIPCBlobInputStreamChild(
+      const nsID& aID, const uint64_t& aSize);
 
-  virtual bool DeallocPIPCBlobInputStreamChild(
-      PIPCBlobInputStreamChild* aActor) override;
+  bool DeallocPIPCBlobInputStreamChild(PIPCBlobInputStreamChild* aActor);
 
   PHalChild* AllocPHalChild();
   bool DeallocPHalChild(PHalChild*);
 
   PHeapSnapshotTempFileHelperChild* AllocPHeapSnapshotTempFileHelperChild();
 
   bool DeallocPHeapSnapshotTempFileHelperChild(
       PHeapSnapshotTempFileHelperChild*);
@@ -258,26 +258,24 @@ class ContentChild final : public PConte
   PNeckoChild* AllocPNeckoChild();
 
   bool DeallocPNeckoChild(PNeckoChild*);
 
   PPrintingChild* AllocPPrintingChild();
 
   bool DeallocPPrintingChild(PPrintingChild*);
 
-  virtual PChildToParentStreamChild* SendPChildToParentStreamConstructor(
-      PChildToParentStreamChild*) override;
+  PChildToParentStreamChild* SendPChildToParentStreamConstructor(
+      PChildToParentStreamChild*);
 
-  virtual PChildToParentStreamChild* AllocPChildToParentStreamChild() override;
-  virtual bool DeallocPChildToParentStreamChild(
-      PChildToParentStreamChild*) override;
+  PChildToParentStreamChild* AllocPChildToParentStreamChild();
+  bool DeallocPChildToParentStreamChild(PChildToParentStreamChild*);
 
-  virtual PParentToChildStreamChild* AllocPParentToChildStreamChild() override;
-  virtual bool DeallocPParentToChildStreamChild(
-      PParentToChildStreamChild*) override;
+  PParentToChildStreamChild* AllocPParentToChildStreamChild();
+  bool DeallocPParentToChildStreamChild(PParentToChildStreamChild*);
 
   PPSMContentDownloaderChild* AllocPPSMContentDownloaderChild(
       const uint32_t& aCertType);
 
   bool DeallocPPSMContentDownloaderChild(
       PPSMContentDownloaderChild* aDownloader);
 
   PExternalHelperAppChild* AllocPExternalHelperAppChild(
@@ -321,20 +319,19 @@ class ContentChild final : public PConte
       InfallibleTArray<OverrideMapping>&& overrides, const nsCString& locale,
       const bool& reset);
   mozilla::ipc::IPCResult RecvRegisterChromeItem(
       const ChromeRegistryItem& item);
 
   mozilla::ipc::IPCResult RecvClearImageCache(const bool& privateLoader,
                                               const bool& chrome);
 
-  virtual mozilla::jsipc::PJavaScriptChild* AllocPJavaScriptChild() override;
+  mozilla::jsipc::PJavaScriptChild* AllocPJavaScriptChild();
 
-  virtual bool DeallocPJavaScriptChild(
-      mozilla::jsipc::PJavaScriptChild*) override;
+  bool DeallocPJavaScriptChild(mozilla::jsipc::PJavaScriptChild*);
 
   PRemoteSpellcheckEngineChild* AllocPRemoteSpellcheckEngineChild();
 
   bool DeallocPRemoteSpellcheckEngineChild(PRemoteSpellcheckEngineChild*);
 
   mozilla::ipc::IPCResult RecvSetOffline(const bool& offline);
 
   mozilla::ipc::IPCResult RecvSetConnectivity(const bool& connectivity);
@@ -361,20 +358,20 @@ class ContentChild final : public PConte
 
   mozilla::ipc::IPCResult RecvDataStorageClear(const nsString& aFilename);
 
   mozilla::ipc::IPCResult RecvNotifyAlertsObserver(const nsCString& aType,
                                                    const nsString& aData);
 
   virtual mozilla::ipc::IPCResult RecvLoadProcessScript(const nsString& aURL);
 
-  virtual mozilla::ipc::IPCResult RecvAsyncMessage(
-      const nsString& aMsg, InfallibleTArray<CpowEntry>&& aCpows,
-      const IPC::Principal& aPrincipal,
-      const ClonedMessageData& aData) override;
+  mozilla::ipc::IPCResult RecvAsyncMessage(const nsString& aMsg,
+                                           InfallibleTArray<CpowEntry>&& aCpows,
+                                           const IPC::Principal& aPrincipal,
+                                           const ClonedMessageData& aData);
 
   mozilla::ipc::IPCResult RecvRegisterStringBundles(
       nsTArray<StringBundleDescriptor>&& stringBundles);
 
   mozilla::ipc::IPCResult RecvUpdateSharedData(
       const FileDescriptor& aMapFile, const uint32_t& aMapSize,
       nsTArray<IPCBlob>&& aBlobs, nsTArray<nsCString>&& aChangedKeys);
 
@@ -503,29 +500,29 @@ class ContentChild final : public PConte
 #if defined(XP_WIN) && defined(ACCESSIBILITY)
   uint32_t GetChromeMainThreadId() const { return mMainChromeTid; }
 
   uint32_t GetMsaaID() const { return mMsaaID; }
 #endif
 
   bool IsForBrowser() const { return mIsForBrowser; }
 
-  virtual PFileDescriptorSetChild* SendPFileDescriptorSetConstructor(
-      const FileDescriptor&) override;
+  PFileDescriptorSetChild* SendPFileDescriptorSetConstructor(
+      const FileDescriptor&);
 
-  virtual PFileDescriptorSetChild* AllocPFileDescriptorSetChild(
-      const FileDescriptor&) override;
+  PFileDescriptorSetChild* AllocPFileDescriptorSetChild(const FileDescriptor&);
+
+  bool DeallocPFileDescriptorSetChild(PFileDescriptorSetChild*);
 
-  virtual bool DeallocPFileDescriptorSetChild(
-      PFileDescriptorSetChild*) override;
-
-  virtual bool SendPBrowserConstructor(
-      PBrowserChild* actor, const TabId& aTabId, const TabId& aSameTabGroupAs,
-      const IPCTabContext& context, const uint32_t& chromeFlags,
-      const ContentParentId& aCpID, const bool& aIsForBrowser) override;
+  bool SendPBrowserConstructor(PBrowserChild* actor, const TabId& aTabId,
+                               const TabId& aSameTabGroupAs,
+                               const IPCTabContext& context,
+                               const uint32_t& chromeFlags,
+                               const ContentParentId& aCpID,
+                               const bool& aIsForBrowser);
 
   virtual mozilla::ipc::IPCResult RecvPBrowserConstructor(
       PBrowserChild* aCctor, const TabId& aTabId, const TabId& aSameTabGroupAs,
       const IPCTabContext& aContext, const uint32_t& aChromeFlags,
       const ContentParentId& aCpID, const bool& aIsForBrowser) override;
 
   FORWARD_SHMEM_ALLOCATOR_TO(PContentChild)
 
@@ -659,18 +656,17 @@ class ContentChild final : public PConte
                            const nsAString& aName,
                            const Optional<int64_t>& aLastModified,
                            bool aExistenceCheck, bool aIsFromNsIFile);
 
   typedef std::function<void(PRFileDesc*)> AnonymousTemporaryFileCallback;
   nsresult AsyncOpenAnonymousTemporaryFile(
       const AnonymousTemporaryFileCallback& aCallback);
 
-  virtual already_AddRefed<nsIEventTarget> GetEventTargetFor(
-      TabChild* aTabChild) override;
+  already_AddRefed<nsIEventTarget> GetEventTargetFor(TabChild* aTabChild);
 
   mozilla::ipc::IPCResult RecvSetPluginList(
       const uint32_t& aPluginEpoch, nsTArray<PluginTag>&& aPluginTags,
       nsTArray<FakePluginTag>&& aFakePluginTags);
 
   PClientOpenWindowOpChild* AllocPClientOpenWindowOpChild(
       const ClientOpenWindowArgs& aArgs);
 
@@ -821,9 +817,9 @@ class ContentChild final : public PConte
   DISALLOW_EVIL_CONSTRUCTORS(ContentChild);
 };
 
 uint64_t NextWindowID();
 
 }  // namespace dom
 }  // namespace mozilla
 
-#endif  // mozilla_dom_ContentChild_h
+#endif  // mozilla_dom_ContentChild_h
\ No newline at end of file
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -38,55 +38,60 @@
 #include "mozilla/DataStorage.h"
 #include "mozilla/devtools/HeapSnapshotTempFileHelperParent.h"
 #include "mozilla/docshell/OfflineCacheUpdateParent.h"
 #include "mozilla/dom/BrowsingContext.h"
 #include "mozilla/dom/BrowsingContextGroup.h"
 #include "mozilla/dom/CanonicalBrowsingContext.h"
 #include "mozilla/dom/ClientManager.h"
 #include "mozilla/dom/ClientOpenWindowOpActors.h"
+#include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/DataTransfer.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/FileCreatorHelper.h"
 #include "mozilla/dom/FileSystemSecurity.h"
 #include "mozilla/dom/IPCBlobUtils.h"
 #include "mozilla/dom/ExternalHelperAppParent.h"
 #include "mozilla/dom/GetFilesHelper.h"
 #include "mozilla/dom/GeolocationBinding.h"
 #include "mozilla/dom/JSWindowActorService.h"
 #include "mozilla/dom/LocalStorageCommon.h"
 #include "mozilla/dom/MemoryReportRequest.h"
 #include "mozilla/dom/Notification.h"
-#include "mozilla/dom/PContentBridgeParent.h"
 #include "mozilla/dom/PContentPermissionRequestParent.h"
 #include "mozilla/dom/PCycleCollectWithLogsParent.h"
 #include "mozilla/dom/PositionError.h"
 #include "mozilla/dom/ServiceWorkerRegistrar.h"
 #include "mozilla/dom/power/PowerManagerService.h"
 #include "mozilla/dom/Permissions.h"
 #include "mozilla/dom/PresentationParent.h"
 #include "mozilla/dom/PPresentationParent.h"
 #include "mozilla/dom/PushNotifier.h"
 #include "mozilla/dom/quota/QuotaManagerService.h"
 #include "mozilla/dom/ServiceWorkerUtils.h"
 #include "mozilla/dom/URLClassifierParent.h"
+#include "mozilla/dom/ipc/IPCBlobInputStreamParent.h"
 #include "mozilla/dom/ipc/SharedMap.h"
 #include "mozilla/embedding/printingui/PrintingParent.h"
 #include "mozilla/extensions/StreamFilterParent.h"
 #include "mozilla/gfx/gfxVars.h"
 #include "mozilla/gfx/GPUProcessManager.h"
 #include "mozilla/hal_sandbox/PHalParent.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/ipc/CrashReporterHost.h"
+#include "mozilla/ipc/FileDescriptorSetParent.h"
 #include "mozilla/ipc/FileDescriptorUtils.h"
 #include "mozilla/ipc/PChildToParentStreamParent.h"
 #include "mozilla/ipc/TestShellParent.h"
 #include "mozilla/ipc/IPCStreamUtils.h"
+#include "mozilla/ipc/IPCStreamAlloc.h"
+#include "mozilla/ipc/IPCStreamDestination.h"
+#include "mozilla/ipc/IPCStreamSource.h"
 #include "mozilla/intl/LocaleService.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 #include "mozilla/layers/PAPZParent.h"
 #include "mozilla/layers/CompositorThread.h"
 #include "mozilla/layers/ImageBridgeParent.h"
 #include "mozilla/layers/LayerTreeOwnerTracker.h"
 #include "mozilla/loader/ScriptCacheActors.h"
 #include "mozilla/LoginReputationIPC.h"
@@ -209,16 +214,17 @@
 #include "nsPluginTags.h"
 #include "nsIBlocklistService.h"
 #include "mozilla/StyleSheet.h"
 #include "mozilla/StyleSheetInlines.h"
 #include "nsICaptivePortalService.h"
 #include "nsIObjectLoadingContent.h"
 #include "nsIBidiKeyboard.h"
 #include "nsLayoutStylesheetCache.h"
+#include "MMPrinter.h"
 
 #include "mozilla/Sprintf.h"
 
 #ifdef MOZ_WEBRTC
 #  include "signaling/src/peerconnection/WebrtcGlobalParent.h"
 #endif
 
 #if defined(XP_MACOSX)
@@ -284,16 +290,25 @@
 
 #ifdef MOZ_CODE_COVERAGE
 #  include "mozilla/CodeCoverageHandler.h"
 #endif
 
 // For VP9Benchmark::sBenchmarkFpsPref
 #include "Benchmark.h"
 
+// XXX need another bug to move this to a common header.
+#ifdef DISABLE_ASSERTS_FOR_FUZZING
+#  define ASSERT_UNLESS_FUZZING(...) \
+    do {                             \
+    } while (0)
+#else
+#  define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false, __VA_ARGS__)
+#endif
+
 static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
 
 using base::KillProcess;
 
 using namespace CrashReporter;
 using namespace mozilla::dom::power;
 using namespace mozilla::media;
 using namespace mozilla::embedding;
@@ -431,17 +446,17 @@ ContentParentsMemoryReporter::CollectRep
       }
       numQueuedMessages = channel->Unsound_NumQueuedMessages();
     }
 
     nsPrintfCString path(
         "queued-ipc-messages/content-parent"
         "(%s, pid=%d, %s, 0x%p, refcnt=%" PRIuPTR ")",
         NS_ConvertUTF16toUTF8(friendlyName).get(), cp->Pid(), channelStr,
-        static_cast<nsIContentParent*>(cp), refcnt);
+        static_cast<nsIObserver*>(cp), refcnt);
 
     NS_NAMED_LITERAL_CSTRING(
         desc,
         "The number of unset IPC messages held in this ContentParent's "
         "channel.  A large value here might indicate that we're leaking "
         "messages.  Similarly, a ContentParent object for a process that's no "
         "longer running could indicate that we're leaking ContentParents.");
 
@@ -945,105 +960,16 @@ extern const wchar_t* kPluginWidgetConte
   ContentParent* cp = reinterpret_cast<ContentParent*>(
       ::GetPropW(hwnd, kPluginWidgetContentParentProperty));
   if (cp && !cp->IsDestroyed()) {
     Unused << cp->SendUpdateWindow((uintptr_t)hwnd);
   }
 }
 #endif  // defined(XP_WIN)
 
-mozilla::ipc::IPCResult ContentParent::RecvCreateChildProcess(
-    const IPCTabContext& aContext, const hal::ProcessPriority& aPriority,
-    const TabId& aOpenerTabId, const TabId& aTabId, ContentParentId* aCpId,
-    bool* aIsForBrowser) {
-#if 0
-  if (!CanOpenBrowser(aContext)) {
-      return false;
-  }
-#endif
-  RefPtr<ContentParent> cp;
-  MaybeInvalidTabContext tc(aContext);
-  if (!tc.IsValid()) {
-    NS_ERROR(nsPrintfCString("Received an invalid TabContext from "
-                             "the child process. (%s)",
-                             tc.GetInvalidReason())
-                 .get());
-    return IPC_FAIL_NO_REASON(this);
-  }
-
-  if (tc.GetTabContext().IsJSPlugin()) {
-    cp =
-        GetNewOrUsedJSPluginProcess(tc.GetTabContext().JSPluginId(), aPriority);
-  } else {
-    cp = GetNewOrUsedBrowserProcess(
-        nullptr, NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE), aPriority, this);
-  }
-
-  if (!cp) {
-    *aCpId = 0;
-    *aIsForBrowser = false;
-    return IPC_OK();
-  }
-
-  *aCpId = cp->ChildID();
-  *aIsForBrowser = cp->IsForBrowser();
-
-  ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
-  if (cp->IsForJSPlugin()) {
-    // We group all the iframes for a specific JS plugin into one process,
-    // regardless of origin. As a consequence that process can't be a child of
-    // the content process that contains the document with the element loading
-    // the plugin. All content processes need to be able to communicate with the
-    // process for the JS plugin.
-    cpm->RegisterRemoteFrame(aTabId, ChildID(), aOpenerTabId, aContext,
-                             cp->ChildID());
-    return IPC_OK();
-  }
-
-  // cp was already added to the ContentProcessManager, this just sets the
-  // parent ID.
-  cpm->AddContentProcess(cp, this->ChildID());
-
-  if (cpm->AddGrandchildProcess(this->ChildID(), cp->ChildID()) &&
-      cpm->RegisterRemoteFrame(aTabId, ChildID(), aOpenerTabId, aContext,
-                               cp->ChildID())) {
-    return IPC_OK();
-  }
-
-  return IPC_FAIL_NO_REASON(this);
-}
-
-mozilla::ipc::IPCResult ContentParent::RecvBridgeToChildProcess(
-    const ContentParentId& aCpId, Endpoint<PContentBridgeParent>* aEndpoint) {
-  ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
-  ContentParent* cp = cpm->GetContentProcessById(aCpId);
-
-  if (cp && cp->CanCommunicateWith(ChildID())) {
-    Endpoint<PContentBridgeParent> parent;
-    Endpoint<PContentBridgeChild> child;
-
-    if (NS_FAILED(PContentBridge::CreateEndpoints(OtherPid(), cp->OtherPid(),
-                                                  &parent, &child))) {
-      return IPC_FAIL(this, "CreateEndpoints failed");
-    }
-
-    *aEndpoint = std::move(parent);
-
-    if (!cp->SendInitContentBridgeChild(std::move(child))) {
-      return IPC_FAIL(this, "SendInitContentBridgeChild failed");
-    }
-
-    return IPC_OK();
-  }
-
-  // You can't bridge to a process you didn't open!
-  KillHard("BridgeToChildProcess");
-  return IPC_FAIL_NO_REASON(this);
-}
-
 static nsIDocShell* GetOpenerDocShellHelper(Element* aFrameElement) {
   // Propagate the private-browsing status of the element's parent
   // docshell to the remote docshell, via the chrome flags.
   MOZ_ASSERT(aFrameElement);
   nsPIDOMWindowOuter* win = aFrameElement->OwnerDoc()->GetWindow();
   if (!win) {
     NS_WARNING("Remote frame has no window");
     return nullptr;
@@ -1170,58 +1096,53 @@ mozilla::ipc::IPCResult ContentParent::R
       MOZ_ASSERT(!parent->GetOwnerElement(),
                  "Shouldn't have an owner elemnt before");
       parent->SetOwnerElement(aFrameElement);
       return parent;
     }
   }
 
   ProcessPriority initialPriority = GetInitialProcessPriority(aFrameElement);
-  bool isInContentProcess = !XRE_IsParentProcess();
   TabId tabId(nsContentUtils::GenerateTabId());
 
   nsIDocShell* docShell = GetOpenerDocShellHelper(aFrameElement);
   TabId openerTabId;
   if (docShell) {
     openerTabId = TabParent::GetTabIdFrom(docShell);
   }
 
   bool isPreloadBrowser = false;
   nsAutoString isPreloadBrowserStr;
   if (aFrameElement->GetAttr(kNameSpaceID_None, nsGkAtoms::preloadedState,
                              isPreloadBrowserStr)) {
     isPreloadBrowser = isPreloadBrowserStr.EqualsLiteral("preloaded");
   }
 
-  RefPtr<nsIContentParent> constructorSender;
-  if (isInContentProcess) {
-    MOZ_ASSERT(aContext.IsMozBrowserElement() || aContext.IsJSPlugin());
-    constructorSender = CreateContentBridgeParent(aContext, initialPriority,
-                                                  openerTabId, tabId);
+  RefPtr<ContentParent> constructorSender;
+  MOZ_RELEASE_ASSERT(XRE_IsParentProcess(), "Cannot allocate TabParent in content process");
+  if (aOpenerContentParent) {
+    constructorSender = aOpenerContentParent;
   } else {
-    if (aOpenerContentParent) {
-      constructorSender = aOpenerContentParent;
+    if (aContext.IsJSPlugin()) {
+      constructorSender =
+          GetNewOrUsedJSPluginProcess(aContext.JSPluginId(), initialPriority);
     } else {
-      if (aContext.IsJSPlugin()) {
-        constructorSender =
-            GetNewOrUsedJSPluginProcess(aContext.JSPluginId(), initialPriority);
-      } else {
-        constructorSender = GetNewOrUsedBrowserProcess(
-            aFrameElement, remoteType, initialPriority, nullptr,
-            isPreloadBrowser);
-      }
-      if (!constructorSender) {
-        return nullptr;
-      }
+      constructorSender = GetNewOrUsedBrowserProcess(
+          aFrameElement, remoteType, initialPriority, nullptr,
+          isPreloadBrowser);
+    }
+    if (!constructorSender) {
+      return nullptr;
     }
-    ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
-    cpm->RegisterRemoteFrame(tabId, ContentParentId(0), openerTabId,
-                             aContext.AsIPCTabContext(),
-                             constructorSender->ChildID());
-  }
+  }
+  ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+  cpm->RegisterRemoteFrame(tabId, ContentParentId(0), openerTabId,
+                            aContext.AsIPCTabContext(),
+                            constructorSender->ChildID());
+
   if (constructorSender) {
     nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
     docShell->GetTreeOwner(getter_AddRefs(treeOwner));
     if (!treeOwner) {
       return nullptr;
     }
 
     nsCOMPtr<nsIWebBrowserChrome> wbc = do_GetInterface(treeOwner);
@@ -1262,44 +1183,16 @@ mozilla::ipc::IPCResult ContentParent::R
       RefPtr<TabParent> constructedTabParent = TabParent::GetFrom(browser);
       constructedTabParent->SetOwnerElement(aFrameElement);
       return constructedTabParent;
     }
   }
   return nullptr;
 }
 
-/*static*/ ContentBridgeParent* ContentParent::CreateContentBridgeParent(
-    const TabContext& aContext, const hal::ProcessPriority& aPriority,
-    const TabId& aOpenerTabId, const TabId& aTabId) {
-  MOZ_ASSERT(aTabId);
-
-  ContentChild* child = ContentChild::GetSingleton();
-  ContentParentId cpId;
-  bool isForBrowser;
-  if (!child->SendCreateChildProcess(aContext.AsIPCTabContext(), aPriority,
-                                     aOpenerTabId, aTabId, &cpId,
-                                     &isForBrowser)) {
-    return nullptr;
-  }
-  if (cpId == 0) {
-    return nullptr;
-  }
-  Endpoint<PContentBridgeParent> endpoint;
-  if (!child->SendBridgeToChildProcess(cpId, &endpoint)) {
-    return nullptr;
-  }
-  ContentBridgeParent* parent =
-      ContentBridgeParent::Create(std::move(endpoint));
-  parent->SetChildID(cpId);
-  parent->SetIsForBrowser(isForBrowser);
-  parent->SetIsForJSPlugin(aContext.IsJSPlugin());
-  return parent;
-}
-
 void ContentParent::GetAll(nsTArray<ContentParent*>& aArray) {
   aArray.Clear();
 
   for (auto* cp : AllProcesses(eLive)) {
     aArray.AppendElement(cp);
   }
 }
 
@@ -2326,18 +2219,17 @@ ContentParent::LaunchSubprocessAsync(hal
   return retval;
 }
 
 ContentParent::ContentParent(ContentParent* aOpener,
                              const nsAString& aRemoteType,
                              RecordReplayState aRecordReplayState,
                              const nsAString& aRecordingFile,
                              int32_t aJSPluginID)
-    : nsIContentParent(),
-      mSelfRef(nullptr),
+    : mSelfRef(nullptr),
       mSubprocess(nullptr),
       mLaunchTS(TimeStamp::Now()),
       mLaunchYieldTS(mLaunchTS),
       mActivateTS(mLaunchTS),
       mOpener(aOpener),
       mRemoteType(aRemoteType),
       mChildID(gContentChildID++),
       mGeolocationWatchID(-1),
@@ -2357,16 +2249,18 @@ ContentParent::ContentParent(ContentPare
       mIsInputPriorityEventEnabled(false),
       mHangMonitorActor(nullptr) {
   // Insert ourselves into the global linked list of ContentParent objects.
   if (!sContentParents) {
     sContentParents = new LinkedList<ContentParent>();
   }
   sContentParents->insertBack(this);
 
+  mMessageManager = nsFrameMessageManager::NewProcessMessageManager(true);
+
   // From this point on, NS_WARNING, NS_ASSERTION, etc. should print out the
   // PID along with the warning.
   nsDebugImpl::SetMultiprocessMode("Parent");
 
 #if defined(XP_WIN)
   if (XRE_IsParentProcess()) {
     audio::AudioNotificationSender::Init();
   }
@@ -3012,17 +2906,17 @@ mozilla::ipc::IPCResult ContentParent::R
 // We want ContentParent to show up in CC logs for debugging purposes, but we
 // don't actually cycle collect it.
 NS_IMPL_CYCLE_COLLECTION_0(ContentParent)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(ContentParent)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(ContentParent)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ContentParent)
-  NS_INTERFACE_MAP_ENTRY(nsIContentParent)
+  NS_INTERFACE_MAP_ENTRY_CONCRETE(ContentParent)
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
   NS_INTERFACE_MAP_ENTRY(nsIDOMGeoPositionCallback)
   NS_INTERFACE_MAP_ENTRY(nsIDOMGeoPositionErrorCallback)
   NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
 NS_INTERFACE_MAP_END
 
 NS_IMETHODIMP
@@ -3243,51 +3137,178 @@ mozilla::ipc::IPCResult ContentParent::R
     return IPC_FAIL(this, "BackgroundParent::Alloc failed");
   }
 
   return IPC_OK();
 }
 
 mozilla::jsipc::PJavaScriptParent* ContentParent::AllocPJavaScriptParent() {
   MOZ_ASSERT(ManagedPJavaScriptParent().IsEmpty());
-  return nsIContentParent::AllocPJavaScriptParent();
+  return NewJavaScriptParent();
 }
 
 bool ContentParent::DeallocPJavaScriptParent(PJavaScriptParent* parent) {
-  return nsIContentParent::DeallocPJavaScriptParent(parent);
+  ReleaseJavaScriptParent(parent);
+  return true;
+}
+
+bool ContentParent::CanOpenBrowser(const IPCTabContext& aContext) {
+  // (PopupIPCTabContext lets the child process prove that it has access to
+  // the app it's trying to open.)
+  // On e10s we also allow UnsafeTabContext to allow service workers to open
+  // windows. This is enforced in MaybeInvalidTabContext.
+  if (aContext.type() != IPCTabContext::TPopupIPCTabContext &&
+      aContext.type() != IPCTabContext::TUnsafeIPCTabContext) {
+    ASSERT_UNLESS_FUZZING(
+        "Unexpected IPCTabContext type.  Aborting AllocPBrowserParent.");
+    return false;
+  }
+
+  if (aContext.type() == IPCTabContext::TPopupIPCTabContext) {
+    const PopupIPCTabContext& popupContext = aContext.get_PopupIPCTabContext();
+    if (popupContext.opener().type() != PBrowserOrId::TPBrowserParent) {
+      ASSERT_UNLESS_FUZZING(
+          "Unexpected PopupIPCTabContext type.  Aborting AllocPBrowserParent.");
+      return false;
+    }
+
+    auto opener =
+        TabParent::GetFrom(popupContext.opener().get_PBrowserParent());
+    if (!opener) {
+      ASSERT_UNLESS_FUZZING(
+          "Got null opener from child; aborting AllocPBrowserParent.");
+      return false;
+    }
+
+    // Popup windows of isMozBrowserElement frames must be isMozBrowserElement
+    // if the parent isMozBrowserElement.  Allocating a !isMozBrowserElement
+    // frame with same app ID would allow the content to access data it's not
+    // supposed to.
+    if (!popupContext.isMozBrowserElement() && opener->IsMozBrowserElement()) {
+      ASSERT_UNLESS_FUZZING(
+          "Child trying to escalate privileges!  Aborting "
+          "AllocPBrowserParent.");
+      return false;
+    }
+  }
+
+  MaybeInvalidTabContext tc(aContext);
+  if (!tc.IsValid()) {
+    NS_ERROR(nsPrintfCString("Child passed us an invalid TabContext.  (%s)  "
+                             "Aborting AllocPBrowserParent.",
+                             tc.GetInvalidReason())
+                 .get());
+    return false;
+  }
+
+  return true;
 }
 
 PBrowserParent* ContentParent::AllocPBrowserParent(
     const TabId& aTabId, const TabId& aSameTabGroupAs,
     const IPCTabContext& aContext, const uint32_t& aChromeFlags,
     const ContentParentId& aCpId, const bool& aIsForBrowser) {
-  return nsIContentParent::AllocPBrowserParent(
-      aTabId, aSameTabGroupAs, aContext, aChromeFlags, aCpId, aIsForBrowser);
+  MOZ_ASSERT(!aSameTabGroupAs);
+
+  Unused << aCpId;
+  Unused << aIsForBrowser;
+
+  if (!CanOpenBrowser(aContext)) {
+    return nullptr;
+  }
+
+  uint32_t chromeFlags = aChromeFlags;
+  TabId openerTabId(0);
+  ContentParentId openerCpId(0);
+  if (aContext.type() == IPCTabContext::TPopupIPCTabContext) {
+    // CanOpenBrowser has ensured that the IPCTabContext is of
+    // type PopupIPCTabContext, and that the opener TabParent is
+    // reachable.
+    const PopupIPCTabContext& popupContext = aContext.get_PopupIPCTabContext();
+    auto opener =
+        TabParent::GetFrom(popupContext.opener().get_PBrowserParent());
+    openerTabId = opener->GetTabId();
+    openerCpId = opener->Manager()->ChildID();
+
+    // We must ensure that the private browsing and remoteness flags
+    // match those of the opener.
+    nsCOMPtr<nsILoadContext> loadContext = opener->GetLoadContext();
+    if (!loadContext) {
+      return nullptr;
+    }
+
+    bool isPrivate;
+    loadContext->GetUsePrivateBrowsing(&isPrivate);
+    if (isPrivate) {
+      chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW;
+    }
+  }
+
+  if (openerTabId > 0 ||
+      aContext.type() == IPCTabContext::TUnsafeIPCTabContext) {
+    MOZ_ASSERT(XRE_IsParentProcess());
+    if (!XRE_IsParentProcess()) {
+      return nullptr;
+    }
+
+    // The creation of PBrowser was triggered from content process through
+    // either window.open() or service worker's openWindow().
+    // We need to register remote frame with the child generated tab id.
+    ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+    if (!cpm->RegisterRemoteFrame(aTabId, openerCpId, openerTabId, aContext,
+                                  aCpId)) {
+      return nullptr;
+    }
+  }
+
+  // And because we're allocating a remote browser, of course the
+  // window is remote.
+  chromeFlags |= nsIWebBrowserChrome::CHROME_REMOTE_WINDOW;
+
+  MaybeInvalidTabContext tc(aContext);
+  MOZ_ASSERT(tc.IsValid());
+  TabParent* parent = new TabParent(static_cast<ContentParent*>(this), aTabId,
+                                    tc.GetTabContext(), chromeFlags);
+
+  // We release this ref in DeallocPBrowserParent()
+  NS_ADDREF(parent);
+  return parent;
 }
 
 bool ContentParent::DeallocPBrowserParent(PBrowserParent* frame) {
-  return nsIContentParent::DeallocPBrowserParent(frame);
+  TabParent* parent = TabParent::GetFrom(frame);
+  NS_RELEASE(parent);
+  return true;
 }
 
 mozilla::ipc::IPCResult ContentParent::RecvPBrowserConstructor(
     PBrowserParent* actor, const TabId& tabId, const TabId& sameTabGroupAs,
     const IPCTabContext& context, const uint32_t& chromeFlags,
     const ContentParentId& cpId, const bool& isForBrowser) {
-  return nsIContentParent::RecvPBrowserConstructor(
-      actor, tabId, sameTabGroupAs, context, chromeFlags, cpId, isForBrowser);
+  TabParent* parent = TabParent::GetFrom(actor);
+  // When enabling input event prioritization, input events may preempt other
+  // normal priority IPC messages. To prevent the input events preempt
+  // PBrowserConstructor, we use an IPC 'RemoteIsReadyToHandleInputEvents' to
+  // notify parent that TabChild is created. In this case, PBrowser is initiated
+  // from content so that we can set TabParent as ready to handle input events.
+  parent->SetReadyToHandleInputEvents();
+  return IPC_OK();
 }
 
 PIPCBlobInputStreamParent* ContentParent::AllocPIPCBlobInputStreamParent(
     const nsID& aID, const uint64_t& aSize) {
-  return nsIContentParent::AllocPIPCBlobInputStreamParent(aID, aSize);
+  MOZ_CRASH("PIPCBlobInputStreamParent actors should be manually constructed!");
+  return nullptr;
 }
 
 bool ContentParent::DeallocPIPCBlobInputStreamParent(
     PIPCBlobInputStreamParent* aActor) {
-  return nsIContentParent::DeallocPIPCBlobInputStreamParent(aActor);
+  RefPtr<IPCBlobInputStreamParent> actor =
+      dont_AddRef(static_cast<IPCBlobInputStreamParent*>(aActor));
+  return true;
 }
 
 mozilla::PRemoteSpellcheckEngineParent*
 ContentParent::AllocPRemoteSpellcheckEngineParent() {
   mozilla::RemoteSpellcheckEngineParent* parent =
       new mozilla::RemoteSpellcheckEngineParent();
   return parent;
 }
@@ -3555,36 +3576,33 @@ mozilla::ipc::IPCResult ContentParent::R
                                                    &endpoint);
 
   aResolver(std::move(endpoint));
 
   return IPC_OK();
 }
 
 PChildToParentStreamParent* ContentParent::AllocPChildToParentStreamParent() {
-  return nsIContentParent::AllocPChildToParentStreamParent();
+  return mozilla::ipc::AllocPChildToParentStreamParent();
 }
 
 bool ContentParent::DeallocPChildToParentStreamParent(
     PChildToParentStreamParent* aActor) {
-  return nsIContentParent::DeallocPChildToParentStreamParent(aActor);
-}
-
-PParentToChildStreamParent* ContentParent::SendPParentToChildStreamConstructor(
-    PParentToChildStreamParent* aActor) {
-  return PContentParent::SendPParentToChildStreamConstructor(aActor);
+  delete aActor;
+  return true;
 }
 
 PParentToChildStreamParent* ContentParent::AllocPParentToChildStreamParent() {
-  return nsIContentParent::AllocPParentToChildStreamParent();
+  MOZ_CRASH("PParentToChildStreamParent actors should be manually constructed!");
 }
 
 bool ContentParent::DeallocPParentToChildStreamParent(
     PParentToChildStreamParent* aActor) {
-  return nsIContentParent::DeallocPParentToChildStreamParent(aActor);
+  delete aActor;
+  return true;
 }
 
 PPSMContentDownloaderParent* ContentParent::AllocPPSMContentDownloaderParent(
     const uint32_t& aCertType) {
   RefPtr<PSMContentDownloaderParent> downloader =
       new PSMContentDownloaderParent(aCertType);
   return downloader.forget().take();
 }
@@ -3880,33 +3898,69 @@ mozilla::ipc::IPCResult ContentParent::R
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult ContentParent::RecvSyncMessage(
     const nsString& aMsg, const ClonedMessageData& aData,
     InfallibleTArray<CpowEntry>&& aCpows, const IPC::Principal& aPrincipal,
     nsTArray<StructuredCloneData>* aRetvals) {
-  return nsIContentParent::RecvSyncMessage(aMsg, aData, std::move(aCpows),
-                                           aPrincipal, aRetvals);
+  AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING("ContentParent::RecvSyncMessage",
+                                             OTHER, aMsg);
+  MMPrinter::Print("ContentParent::RecvSyncMessage", aMsg, aData);
+
+  CrossProcessCpowHolder cpows(this, aCpows);
+  RefPtr<nsFrameMessageManager> ppm = mMessageManager;
+  if (ppm) {
+    ipc::StructuredCloneData data;
+    ipc::UnpackClonedMessageDataForParent(aData, data);
+
+    ppm->ReceiveMessage(ppm, nullptr, aMsg, true, &data, &cpows, aPrincipal,
+                        aRetvals, IgnoreErrors());
+  }
+  return IPC_OK();
 }
 
 mozilla::ipc::IPCResult ContentParent::RecvRpcMessage(
     const nsString& aMsg, const ClonedMessageData& aData,
     InfallibleTArray<CpowEntry>&& aCpows, const IPC::Principal& aPrincipal,
     nsTArray<StructuredCloneData>* aRetvals) {
-  return nsIContentParent::RecvRpcMessage(aMsg, aData, std::move(aCpows),
-                                          aPrincipal, aRetvals);
+  AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING("ContentParent::RecvRpcMessage",
+                                             OTHER, aMsg);
+  MMPrinter::Print("ContentParent::RecvRpcMessage", aMsg, aData);
+
+  CrossProcessCpowHolder cpows(this, aCpows);
+  RefPtr<nsFrameMessageManager> ppm = mMessageManager;
+  if (ppm) {
+    ipc::StructuredCloneData data;
+    ipc::UnpackClonedMessageDataForParent(aData, data);
+
+    ppm->ReceiveMessage(ppm, nullptr, aMsg, true, &data, &cpows, aPrincipal,
+                        aRetvals, IgnoreErrors());
+  }
+  return IPC_OK();
 }
 
 mozilla::ipc::IPCResult ContentParent::RecvAsyncMessage(
     const nsString& aMsg, InfallibleTArray<CpowEntry>&& aCpows,
     const IPC::Principal& aPrincipal, const ClonedMessageData& aData) {
-  return nsIContentParent::RecvAsyncMessage(aMsg, std::move(aCpows), aPrincipal,
-                                            aData);
+  AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING("ContentParent::RecvAsyncMessage",
+                                             OTHER, aMsg);
+  MMPrinter::Print("ContentParent::RecvAsyncMessage", aMsg, aData);
+
+  CrossProcessCpowHolder cpows(this, aCpows);
+  RefPtr<nsFrameMessageManager> ppm = mMessageManager;
+  if (ppm) {
+    ipc::StructuredCloneData data;
+    ipc::UnpackClonedMessageDataForParent(aData, data);
+
+    ppm->ReceiveMessage(ppm, nullptr, aMsg, false, &data, &cpows, aPrincipal,
+                        nullptr, IgnoreErrors());
+  }
+  return IPC_OK();
 }
 
 static int32_t AddGeolocationListener(
     nsIDOMGeoPositionCallback* watcher,
     nsIDOMGeoPositionErrorCallback* errorCallBack, bool highAccuracy) {
   RefPtr<Geolocation> geo = Geolocation::NonWindowSingleton();
 
   UniquePtr<PositionOptions> options = MakeUnique<PositionOptions>();
@@ -4107,30 +4161,16 @@ nsresult ContentParent::DoSendAsyncMessa
   }
   if (!SendAsyncMessage(nsString(aMessage), cpows, Principal(aPrincipal),
                         data)) {
     return NS_ERROR_UNEXPECTED;
   }
   return NS_OK;
 }
 
-PIPCBlobInputStreamParent* ContentParent::SendPIPCBlobInputStreamConstructor(
-    PIPCBlobInputStreamParent* aActor, const nsID& aID, const uint64_t& aSize) {
-  return PContentParent::SendPIPCBlobInputStreamConstructor(aActor, aID, aSize);
-}
-
-PBrowserParent* ContentParent::SendPBrowserConstructor(
-    PBrowserParent* aActor, const TabId& aTabId, const TabId& aSameTabGroupAs,
-    const IPCTabContext& aContext, const uint32_t& aChromeFlags,
-    const ContentParentId& aCpId, const bool& aIsForBrowser) {
-  return PContentParent::SendPBrowserConstructor(
-      aActor, aTabId, aSameTabGroupAs, aContext, aChromeFlags, aCpId,
-      aIsForBrowser);
-}
-
 mozilla::ipc::IPCResult ContentParent::RecvKeywordToURI(
     const nsCString& aKeyword, nsString* aProviderName,
     RefPtr<nsIInputStream>* aPostData, OptionalURIParams* aURI) {
   *aPostData = nullptr;
   *aURI = void_t();
 
   nsCOMPtr<nsIURIFixup> fixup = components::URIFixup::Service();
   if (!fixup) {
@@ -4370,29 +4410,25 @@ mozilla::ipc::IPCResult ContentParent::R
     return IPC_OK();
   }
 
   formProcessor->ProvideContent(NS_LITERAL_STRING("SELECT"), *aContent,
                                 *aAttribute);
   return IPC_OK();
 }
 
-PFileDescriptorSetParent* ContentParent::SendPFileDescriptorSetConstructor(
-    const FileDescriptor& aFD) {
-  return PContentParent::SendPFileDescriptorSetConstructor(aFD);
-}
-
 PFileDescriptorSetParent* ContentParent::AllocPFileDescriptorSetParent(
     const FileDescriptor& aFD) {
-  return nsIContentParent::AllocPFileDescriptorSetParent(aFD);
+  return new FileDescriptorSetParent(aFD);
 }
 
 bool ContentParent::DeallocPFileDescriptorSetParent(
     PFileDescriptorSetParent* aActor) {
-  return nsIContentParent::DeallocPFileDescriptorSetParent(aActor);
+  delete static_cast<FileDescriptorSetParent*>(aActor);
+  return true;
 }
 
 bool ContentParent::IgnoreIPCPrincipal() {
   static bool sDidAddVarCache = false;
   static bool sIgnoreIPCPrincipal = false;
   if (!sDidAddVarCache) {
     sDidAddVarCache = true;
     Preferences::AddBoolVarCache(&sIgnoreIPCPrincipal,
@@ -5522,30 +5558,16 @@ mozilla::ipc::IPCResult ContentParent::R
 
   if (!SendFileCreationResponse(aID, FileCreationSuccessResult(ipcBlob))) {
     return IPC_FAIL_NO_REASON(this);
   }
 
   return IPC_OK();
 }
 
-bool ContentParent::CanCommunicateWith(ContentParentId aOtherProcess) {
-  // Normally a process can only communicate with its parent, but a JS plugin
-  // process can communicate with any process.
-  ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
-  ContentParentId parentId;
-  if (!cpm->GetParentProcessId(ChildID(), &parentId)) {
-    return false;
-  }
-  if (IsForJSPlugin()) {
-    return parentId == ContentParentId(0);
-  }
-  return parentId == aOtherProcess;
-}
-
 nsresult ContentParent::SaveRecording(nsIFile* aFile, bool* aRetval) {
   if (mRecordReplayState != eRecording) {
     *aRetval = false;
     return NS_OK;
   }
 
   PRFileDesc* prfd;
   nsresult rv = aFile->OpenNSPRFileDesc(
@@ -5874,9 +5896,9 @@ mozilla::ipc::IPCResult ContentParent::R
 NS_IMPL_ISUPPORTS(ParentIdleListener, nsIObserver)
 
 NS_IMETHODIMP
 ParentIdleListener::Observe(nsISupports*, const char* aTopic,
                             const char16_t* aData) {
   mozilla::Unused << mParent->SendNotifyIdleObserver(
       mObserver, nsDependentCString(aTopic), nsDependentString(aData));
   return NS_OK;
-}
+}
\ No newline at end of file
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -3,21 +3,24 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_ContentParent_h
 #define mozilla_dom_ContentParent_h
 
 #include "mozilla/dom/PContentParent.h"
-#include "mozilla/dom/nsIContentParent.h"
+#include "mozilla/dom/CPOWManagerGetter.h"
+#include "mozilla/dom/ipc/IdType.h"
 #include "mozilla/gfx/gfxVarReceiver.h"
 #include "mozilla/gfx/GPUProcessListener.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/ipc/GeckoChildProcessHost.h"
+#include "mozilla/ipc/PParentToChildStreamParent.h"
+#include "mozilla/ipc/PChildToParentStreamParent.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/FileUtils.h"
 #include "mozilla/HalTypes.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/MemoryReportingProcess.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Variant.h"
@@ -84,66 +87,75 @@ class URIParams;
 class TestShellParent;
 #ifdef FUZZING
 class ProtocolFuzzerHelper;
 #endif
 }  // namespace ipc
 
 namespace jsipc {
 class PJavaScriptParent;
+class CpowEntry;
 }  // namespace jsipc
 
 namespace layers {
 struct TextureFactoryIdentifier;
 }  // namespace layers
 
 namespace dom {
 
 class BrowsingContextGroup;
 class Element;
 class TabParent;
 class ClonedMessageData;
 class MemoryReport;
 class TabContext;
-class ContentBridgeParent;
 class GetFilesHelper;
 class MemoryReportRequestHost;
 
+#define NS_CONTENTPARENT_IID                         \
+  {                                                  \
+    0xeeec9ebf, 0x8ecf, 0x4e38, {                    \
+      0x81, 0xda, 0xb7, 0x34, 0x13, 0x7e, 0xac, 0xf3 \
+    }                                                \
+  }
+
 class ContentParent final : public PContentParent,
-                            public nsIContentParent,
                             public nsIObserver,
                             public nsIDOMGeoPositionCallback,
                             public nsIDOMGeoPositionErrorCallback,
                             public nsIInterfaceRequestor,
                             public gfx::gfxVarReceiver,
                             public mozilla::LinkedListElement<ContentParent>,
                             public gfx::GPUProcessListener,
-                            public mozilla::MemoryReportingProcess {
+                            public mozilla::MemoryReportingProcess,
+                            public mozilla::dom::ipc::MessageManagerCallback,
+                            public CPOWManagerGetter,
+                            public mozilla::ipc::IShmemAllocator {
   typedef mozilla::ipc::GeckoChildProcessHost GeckoChildProcessHost;
   typedef mozilla::ipc::OptionalURIParams OptionalURIParams;
   typedef mozilla::ipc::PFileDescriptorSetParent PFileDescriptorSetParent;
   typedef mozilla::ipc::TestShellParent TestShellParent;
   typedef mozilla::ipc::URIParams URIParams;
   typedef mozilla::ipc::PrincipalInfo PrincipalInfo;
   typedef mozilla::dom::ClonedMessageData ClonedMessageData;
   typedef mozilla::dom::BrowsingContextGroup BrowsingContextGroup;
 
   friend class mozilla::PreallocatedProcessManagerImpl;
   friend class PContentParent;
 #ifdef FUZZING
   friend class mozilla::ipc::ProtocolFuzzerHelper;
 #endif
 
  public:
-  virtual bool IsContentParent() const override { return true; }
-
   using LaunchError = GeckoChildProcessHost::LaunchError;
   using LaunchPromise =
       GeckoChildProcessHost::LaunchPromise<RefPtr<ContentParent>>;
 
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_CONTENTPARENT_IID)
+
   /**
    * Create a subprocess suitable for use later as a content process.
    */
   static RefPtr<LaunchPromise> PreallocateProcess();
 
   /**
    * Start up the content-process machinery.  This might include
    * scheduling pre-launch tasks.
@@ -275,24 +287,16 @@ class ContentParent final : public PCont
    *           this plugin window.
    */
   static void SendAsyncUpdate(nsIWidget* aWidget);
 #endif
 
   // Let managees query if it is safe to send messages.
   bool IsDestroyed() const { return !mIPCOpen; }
 
-  mozilla::ipc::IPCResult RecvCreateChildProcess(
-      const IPCTabContext& aContext, const hal::ProcessPriority& aPriority,
-      const TabId& aOpenerTabId, const TabId& aTabId, ContentParentId* aCpId,
-      bool* aIsForBrowser);
-
-  mozilla::ipc::IPCResult RecvBridgeToChildProcess(
-      const ContentParentId& aCpId, Endpoint<PContentBridgeParent>* aEndpoint);
-
   mozilla::ipc::IPCResult RecvOpenRecordReplayChannel(
       const uint32_t& channelId, FileDescriptor* connection);
   mozilla::ipc::IPCResult RecvCreateReplayingProcess(
       const uint32_t& aChannelId);
 
   mozilla::ipc::IPCResult RecvCreateGMPService();
 
   mozilla::ipc::IPCResult RecvLoadPlugin(
@@ -366,39 +370,43 @@ class ContentParent final : public PCont
   void UpdateCookieStatus(nsIChannel* aChannel);
 
   bool IsLaunching() const {
     return mLifecycleState == LifecycleState::LAUNCHING;
   }
   bool IsAlive() const override;
   bool IsDead() const { return mLifecycleState == LifecycleState::DEAD; }
 
-  virtual bool IsForBrowser() const override { return mIsForBrowser; }
-  virtual bool IsForJSPlugin() const override {
+  bool IsForBrowser() const { return mIsForBrowser; }
+  bool IsForJSPlugin() const {
     return mJSPluginID != nsFakePluginTag::NOT_JSPLUGIN;
   }
 
   GeckoChildProcessHost* Process() const { return mSubprocess; }
 
   ContentParent* Opener() const { return mOpener; }
   nsIContentProcessInfo* ScriptableHelper() const { return mScriptableHelper; }
 
+  mozilla::dom::ProcessMessageManager* GetMessageManager() const {
+    return mMessageManager;
+  }
+
   bool NeedsPermissionsUpdate(const nsACString& aPermissionKey) const;
 
   /**
    * Kill our subprocess and make sure it dies.  Should only be used
    * in emergency situations since it bypasses the normal shutdown
    * process.
    *
    * WARNING: aReason appears in telemetry, so any new value passed in requires
    * data review.
    */
   void KillHard(const char* aWhy);
 
-  ContentParentId ChildID() const override { return mChildID; }
+  ContentParentId ChildID() const { return mChildID; }
 
   /**
    * Get a user-friendly name for this ContentParent.  We make no guarantees
    * about this name: It might not be unique, apps can spoof special names,
    * etc.  So please don't use this name to make any decisions about the
    * ContentParent based on the value returned here.
    */
   void FriendlyName(nsAString& aName, bool aAnonymize = false);
@@ -425,42 +433,32 @@ class ContentParent final : public PCont
    */
   already_AddRefed<embedding::PrintingParent> GetPrintingParent();
 #endif
 
   mozilla::ipc::IPCResult RecvInitStreamFilter(
       const uint64_t& aChannelId, const nsString& aAddonId,
       InitStreamFilterResolver&& aResolver);
 
-  virtual PChildToParentStreamParent* AllocPChildToParentStreamParent()
-      override;
-  virtual bool DeallocPChildToParentStreamParent(
-      PChildToParentStreamParent* aActor) override;
-
-  virtual PParentToChildStreamParent* SendPParentToChildStreamConstructor(
-      PParentToChildStreamParent*) override;
+  PChildToParentStreamParent* AllocPChildToParentStreamParent();
+  bool DeallocPChildToParentStreamParent(PChildToParentStreamParent* aActor);
 
-  virtual PFileDescriptorSetParent* SendPFileDescriptorSetConstructor(
-      const FileDescriptor&) override;
-
-  virtual PParentToChildStreamParent* AllocPParentToChildStreamParent()
-      override;
-  virtual bool DeallocPParentToChildStreamParent(
-      PParentToChildStreamParent* aActor) override;
+  PParentToChildStreamParent* AllocPParentToChildStreamParent();
+  bool DeallocPParentToChildStreamParent(PParentToChildStreamParent* aActor);
 
   PHalParent* AllocPHalParent();
 
   virtual mozilla::ipc::IPCResult RecvPHalConstructor(
       PHalParent* aActor) override {
     return PContentParent::RecvPHalConstructor(aActor);
   }
 
   PHeapSnapshotTempFileHelperParent* AllocPHeapSnapshotTempFileHelperParent();
 
-  virtual PJavaScriptParent* AllocPJavaScriptParent() override;
+  PJavaScriptParent* AllocPJavaScriptParent();
 
   virtual mozilla::ipc::IPCResult RecvPJavaScriptConstructor(
       PJavaScriptParent* aActor) override {
     return PContentParent::RecvPJavaScriptConstructor(aActor);
   }
 
   PRemoteSpellcheckEngineParent* AllocPRemoteSpellcheckEngineParent();
 
@@ -577,24 +575,16 @@ class ContentParent final : public PCont
   PSessionStorageObserverParent* AllocPSessionStorageObserverParent();
 
   virtual mozilla::ipc::IPCResult RecvPSessionStorageObserverConstructor(
       PSessionStorageObserverParent* aActor) override;
 
   bool DeallocPSessionStorageObserverParent(
       PSessionStorageObserverParent* aActor);
 
-  virtual bool SendActivate(PBrowserParent* aTab) override {
-    return PContentParent::SendActivate(aTab);
-  }
-
-  virtual bool SendDeactivate(PBrowserParent* aTab) override {
-    return PContentParent::SendDeactivate(aTab);
-  }
-
   bool DeallocPURLClassifierLocalParent(PURLClassifierLocalParent* aActor);
 
   bool DeallocPURLClassifierParent(PURLClassifierParent* aActor);
 
   // Use the PHangMonitor channel to ask the child to repaint a tab.
   void PaintTabWhileInterruptingJS(TabParent* aTabParent, bool aForceRepaint,
                                    const layers::LayersObserverEpoch& aEpoch);
 
@@ -637,16 +627,18 @@ class ContentParent final : public PCont
   mozilla::ipc::IPCResult RecvWindowBlur(BrowsingContext* aContext);
   mozilla::ipc::IPCResult RecvWindowPostMessage(
       BrowsingContext* aContext, const ClonedMessageData& aMessage,
       const PostMessageData& aData);
 
   mozilla::ipc::IPCResult RecvSetUserGestureActivation(
       BrowsingContext* aContext, bool aNewValue);
 
+  FORWARD_SHMEM_ALLOCATOR_TO(PContentParent)
+
  protected:
   void OnChannelConnected(int32_t pid) override;
 
   virtual void ActorDestroy(ActorDestroyReason why) override;
 
   bool ShouldContinueFromReplyTimeout() override;
 
   void OnVarChanged(const GfxVarUpdate& aVar) override;
@@ -663,49 +655,35 @@ class ContentParent final : public PCont
    */
   static nsClassHashtable<nsStringHashKey, nsTArray<ContentParent*>>*
       sBrowserContentParents;
   static nsTArray<ContentParent*>* sPrivateContent;
   static nsDataHashtable<nsUint32HashKey, ContentParent*>*
       sJSPluginContentParents;
   static StaticAutoPtr<LinkedList<ContentParent>> sContentParents;
 
-  static ContentBridgeParent* CreateContentBridgeParent(
-      const TabContext& aContext, const hal::ProcessPriority& aPriority,
-      const TabId& aOpenerTabId, const TabId& aTabId);
-
 #if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
   // Cached Mac sandbox params used when launching content processes.
   static StaticAutoPtr<std::vector<std::string>> sMacSandboxParams;
 #endif
 
-  // Hide the raw constructor methods since we don't want client code
-  // using them.
-  virtual PBrowserParent* SendPBrowserConstructor(
-      PBrowserParent* actor, const TabId& aTabId, const TabId& aSameTabGroupsAs,
-      const IPCTabContext& context, const uint32_t& chromeFlags,
-      const ContentParentId& aCpId, const bool& aIsForBrowser) override;
-  using PContentParent::SendPTestShellConstructor;
-
   // Set aLoadUri to true to load aURIToLoad and to false to only create the
   // window. aURIToLoad should always be provided, if available, to ensure
   // compatibility with GeckoView.
   mozilla::ipc::IPCResult CommonCreateWindow(
       PBrowserParent* aThisTab, bool aSetOpener, const uint32_t& aChromeFlags,
       const bool& aCalledFromJS, const bool& aPositionSpecified,
       const bool& aSizeSpecified, nsIURI* aURIToLoad,
       const nsCString& aFeatures, const nsCString& aBaseURI,
       const float& aFullZoom, uint64_t aNextTabParentId, const nsString& aName,
       nsresult& aResult, nsCOMPtr<nsITabParent>& aNewTabParent,
       bool* aWindowIsNew, int32_t& aOpenLocation,
       nsIPrincipal* aTriggeringPrincipal, uint32_t aReferrerPolicy,
       bool aLoadUri, nsIContentSecurityPolicy* aCsp);
 
-  FORWARD_SHMEM_ALLOCATOR_TO(PContentParent)
-
   enum RecordReplayState { eNotRecordingOrReplaying, eRecording, eReplaying };
 
   explicit ContentParent(int32_t aPluginID)
       : ContentParent(nullptr, EmptyString(), eNotRecordingOrReplaying,
                       EmptyString(), aPluginID) {}
   ContentParent(ContentParent* aOpener, const nsAString& aRemoteType,
                 RecordReplayState aRecordReplayState = eNotRecordingOrReplaying,
                 const nsAString& aRecordingFile = EmptyString())
@@ -815,57 +793,55 @@ class ContentParent final : public PCont
   // content process.
   //
   // See nsIPermissionManager::GetPermissionsForKey for more information on
   // these keys.
   void EnsurePermissionsByKey(const nsCString& aKey);
 
   static void ForceKillTimerCallback(nsITimer* aTimer, void* aClosure);
 
+  bool CanOpenBrowser(const IPCTabContext& aContext);
+
   /**
    * Get or create the corresponding content parent array to
    * |aContentProcessType|.
    */
   static nsTArray<ContentParent*>& GetOrCreatePool(
       const nsAString& aContentProcessType);
 
   mozilla::ipc::IPCResult RecvInitBackground(
       Endpoint<mozilla::ipc::PBackgroundParent>&& aEndpoint);
 
   mozilla::ipc::IPCResult RecvAddMemoryReport(const MemoryReport& aReport);
   mozilla::ipc::IPCResult RecvFinishMemoryReport(const uint32_t& aGeneration);
   mozilla::ipc::IPCResult RecvAddPerformanceMetrics(
       const nsID& aID, nsTArray<PerformanceInfo>&& aMetrics);
 
-  virtual bool DeallocPJavaScriptParent(
-      mozilla::jsipc::PJavaScriptParent*) override;
+  bool DeallocPJavaScriptParent(mozilla::jsipc::PJavaScriptParent*);
 
   bool DeallocPRemoteSpellcheckEngineParent(PRemoteSpellcheckEngineParent*);
 
-  virtual PBrowserParent* AllocPBrowserParent(
-      const TabId& aTabId, const TabId& aSameTabGroupAs,
-      const IPCTabContext& aContext, const uint32_t& aChromeFlags,
-      const ContentParentId& aCpId, const bool& aIsForBrowser) override;
+  PBrowserParent* AllocPBrowserParent(const TabId& aTabId,
+                                      const TabId& aSameTabGroupAs,
+                                      const IPCTabContext& aContext,
+                                      const uint32_t& aChromeFlags,
+                                      const ContentParentId& aCpId,
+                                      const bool& aIsForBrowser);
 
-  virtual bool DeallocPBrowserParent(PBrowserParent* frame) override;
+  bool DeallocPBrowserParent(PBrowserParent* frame);
 
   virtual mozilla::ipc::IPCResult RecvPBrowserConstructor(
       PBrowserParent* actor, const TabId& tabId, const TabId& sameTabGroupAs,
       const IPCTabContext& context, const uint32_t& chromeFlags,
       const ContentParentId& cpId, const bool& isForBrowser) override;
 
-  virtual PIPCBlobInputStreamParent* SendPIPCBlobInputStreamConstructor(
-      PIPCBlobInputStreamParent* aActor, const nsID& aID,
-      const uint64_t& aSize) override;
+  PIPCBlobInputStreamParent* AllocPIPCBlobInputStreamParent(
+      const nsID& aID, const uint64_t& aSize);
 
-  virtual PIPCBlobInputStreamParent* AllocPIPCBlobInputStreamParent(
-      const nsID& aID, const uint64_t& aSize) override;
-
-  virtual bool DeallocPIPCBlobInputStreamParent(
-      PIPCBlobInputStreamParent* aActor) override;
+  bool DeallocPIPCBlobInputStreamParent(PIPCBlobInputStreamParent* aActor);
 
   mozilla::ipc::IPCResult RecvIsSecureURI(
       const uint32_t& aType, const URIParams& aURI, const uint32_t& aFlags,
       const OriginAttributes& aOriginAttributes, bool* aIsSecureURI);
 
   mozilla::ipc::IPCResult RecvAccumulateMixedContentHSTS(
       const URIParams& aURI, const bool& aActive,
       const OriginAttributes& aOriginAttributes);
@@ -1003,27 +979,27 @@ class ContentParent final : public PCont
   mozilla::ipc::IPCResult RecvLoadURIExternal(const URIParams& uri,
                                               PBrowserParent* windowContext);
   mozilla::ipc::IPCResult RecvExtProtocolChannelConnectParent(
       const uint32_t& registrarId);
 
   mozilla::ipc::IPCResult RecvSyncMessage(
       const nsString& aMsg, const ClonedMessageData& aData,
       InfallibleTArray<CpowEntry>&& aCpows, const IPC::Principal& aPrincipal,
-      nsTArray<StructuredCloneData>* aRetvals) override;
+      nsTArray<StructuredCloneData>* aRetvals);
 
   mozilla::ipc::IPCResult RecvRpcMessage(
       const nsString& aMsg, const ClonedMessageData& aData,
       InfallibleTArray<CpowEntry>&& aCpows, const IPC::Principal& aPrincipal,
-      nsTArray<StructuredCloneData>* aRetvals) override;
+      nsTArray<StructuredCloneData>* aRetvals);
 
-  mozilla::ipc::IPCResult RecvAsyncMessage(
-      const nsString& aMsg, InfallibleTArray<CpowEntry>&& aCpows,
-      const IPC::Principal& aPrincipal,
-      const ClonedMessageData& aData) override;
+  mozilla::ipc::IPCResult RecvAsyncMessage(const nsString& aMsg,
+                                           InfallibleTArray<CpowEntry>&& aCpows,
+                                           const IPC::Principal& aPrincipal,
+                                           const ClonedMessageData& aData);
 
   mozilla::ipc::IPCResult RecvAddGeolocationListener(
       const IPC::Principal& aPrincipal, const bool& aHighAccuracy);
   mozilla::ipc::IPCResult RecvRemoveGeolocationListener();
 
   mozilla::ipc::IPCResult RecvSetGeolocationHigherAccuracy(const bool& aEnable);
 
   mozilla::ipc::IPCResult RecvConsoleMessage(const nsString& aMessage);
@@ -1099,21 +1075,20 @@ class ContentParent final : public PCont
                                                  const nsString& challenge,
                                                  const nsString& keytype,
                                                  const nsString& keyparams,
                                                  nsString* newValue);
 
   mozilla::ipc::IPCResult RecvKeygenProvideContent(
       nsString* aAttribute, nsTArray<nsString>* aContent);
 
-  virtual PFileDescriptorSetParent* AllocPFileDescriptorSetParent(
-      const mozilla::ipc::FileDescriptor&) override;
+  PFileDescriptorSetParent* AllocPFileDescriptorSetParent(
+      const mozilla::ipc::FileDescriptor&);
 
-  virtual bool DeallocPFileDescriptorSetParent(
-      PFileDescriptorSetParent*) override;
+  bool DeallocPFileDescriptorSetParent(PFileDescriptorSetParent*);
 
   PWebrtcGlobalParent* AllocPWebrtcGlobalParent();
   bool DeallocPWebrtcGlobalParent(PWebrtcGlobalParent* aActor);
 
   mozilla::ipc::IPCResult RecvUpdateDropEffect(const uint32_t& aDragAction,
                                                const uint32_t& aDropEffect);
 
   mozilla::ipc::IPCResult RecvShutdownProfile(const nsCString& aProfile);
@@ -1192,18 +1167,16 @@ class ContentParent final : public PCont
   void SendGetFilesResponseAndForget(const nsID& aID,
                                      const GetFilesResponseResult& aResult);
 
   bool SendRequestMemoryReport(const uint32_t& aGeneration,
                                const bool& aAnonymize,
                                const bool& aMinimizeMemoryUsage,
                                const MaybeFileDesc& aDMDFile) override;
 
-  bool CanCommunicateWith(ContentParentId aOtherProcess);
-
   nsresult SaveRecording(nsIFile* aFile, bool* aRetval);
 
   bool IsRecordingOrReplaying() const {
     return mRecordReplayState != eNotRecordingOrReplaying;
   }
 
   void OnBrowsingContextGroupSubscribe(BrowsingContextGroup* aGroup);
   void OnBrowsingContextGroupUnsubscribe(BrowsingContextGroup* aGroup);
@@ -1329,29 +1302,33 @@ class ContentParent final : public PCont
 
   UniquePtr<mozilla::ipc::CrashReporterHost> mCrashReporter;
 
   // Collects any pref changes that occur during process launch (after
   // the initial map is passed in command-line arguments) to be sent
   // when the process can receive IPC messages.
   nsTArray<Pref> mQueuedPrefs;
 
+  RefPtr<mozilla::dom::ProcessMessageManager> mMessageManager;
+
   static uint64_t sNextTabParentId;
   static nsDataHashtable<nsUint64HashKey, TabParent*> sNextTabParents;
 
 #if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
   // When set to true, indicates that content processes should
   // initialize their sandbox during startup instead of waiting
   // for the SetProcessSandbox IPDL message.
   static bool sEarlySandboxInit;
 #endif
 
   nsTHashtable<nsRefPtrHashKey<BrowsingContextGroup>> mGroups;
 };
 
+NS_DEFINE_STATIC_IID_ACCESSOR(ContentParent, NS_CONTENTPARENT_IID)
+
 }  // namespace dom
 }  // namespace mozilla
 
 class ParentIdleListener : public nsIObserver {
   friend class mozilla::dom::ContentParent;
 
  public:
   NS_DECL_ISUPPORTS
--- a/dom/ipc/ContentProcessManager.cpp
+++ b/dom/ipc/ContentProcessManager.cpp
@@ -63,29 +63,16 @@ void ContentProcessManager::RemoveConten
   for (auto iter = mContentParentMap.begin(); iter != mContentParentMap.end();
        ++iter) {
     if (!iter->second.mChildrenCpId.empty()) {
       iter->second.mChildrenCpId.erase(aChildCpId);
     }
   }
 }
 
-bool ContentProcessManager::AddGrandchildProcess(
-    const ContentParentId& aParentCpId, const ContentParentId& aChildCpId) {
-  MOZ_ASSERT(NS_IsMainThread());
-
-  auto iter = mContentParentMap.find(aParentCpId);
-  if (NS_WARN_IF(iter == mContentParentMap.end())) {
-    ASSERT_UNLESS_FUZZING("Parent process should be already in map!");
-    return false;
-  }
-  iter->second.mChildrenCpId.insert(aChildCpId);
-  return true;
-}
-
 bool ContentProcessManager::GetParentProcessId(
     const ContentParentId& aChildCpId,
     /*out*/ ContentParentId* aParentCpId) {
   MOZ_ASSERT(NS_IsMainThread());
 
   auto iter = mContentParentMap.find(aChildCpId);
   if (NS_WARN_IF(iter == mContentParentMap.end())) {
     ASSERT_UNLESS_FUZZING();
--- a/dom/ipc/ContentProcessManager.h
+++ b/dom/ipc/ContentProcessManager.h
@@ -43,22 +43,16 @@ class ContentProcessManager final {
   void AddContentProcess(
       ContentParent* aChildCp,
       const ContentParentId& aParentCpId = ContentParentId(0));
   /**
    * Remove the content process by id.
    */
   void RemoveContentProcess(const ContentParentId& aChildCpId);
   /**
-   * Add a grandchild content process into the map.
-   * aParentCpId must be already added in the map by AddContentProcess().
-   */
-  bool AddGrandchildProcess(const ContentParentId& aParentCpId,
-                            const ContentParentId& aChildCpId);
-  /**
    * Get the parent process's id by child process's id.
    * Used to check if a child really belongs to the parent.
    */
   bool GetParentProcessId(const ContentParentId& aChildCpId,
                           /*out*/ ContentParentId* aParentCpId);
   /**
    * Return the ContentParent pointer by id.
    */
--- a/dom/ipc/FilePickerParent.cpp
+++ b/dom/ipc/FilePickerParent.cpp
@@ -123,17 +123,17 @@ FilePickerParent::IORunnable::Run() {
 
   return NS_OK;
 }
 
 void FilePickerParent::IORunnable::Destroy() { mFilePickerParent = nullptr; }
 
 void FilePickerParent::SendFilesOrDirectories(
     const nsTArray<BlobImplOrString>& aData) {
-  nsIContentParent* parent = TabParent::GetFrom(Manager())->Manager();
+  ContentParent* parent = TabParent::GetFrom(Manager())->Manager();
 
   if (mMode == nsIFilePicker::modeGetFolder) {
     MOZ_ASSERT(aData.Length() <= 1);
     if (aData.IsEmpty()) {
       Unused << Send__delete__(this, void_t(), mResult);
       return;
     }
 
--- a/dom/ipc/JSWindowActorChild.cpp
+++ b/dom/ipc/JSWindowActorChild.cpp
@@ -90,17 +90,17 @@ void JSWindowActorChild::SendAsyncMessag
         NS_SUCCEEDED(rv),
         "JS Window Actor AsyncMessageToParent dispatch failed");
     return;
   }
 
   // If we're a cross-process, send the async message over the corresponding
   // actor.
   ClonedMessageData msgData;
-  nsIContentChild* cc = ContentChild::GetSingleton();
+  ContentChild* cc = ContentChild::GetSingleton();
   if (!data.BuildClonedMessageDataForChild(cc, msgData)) {
     aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
     return;
   }
 
   if (!mManager->SendAsyncMessage(mName, PromiseFlatString(aMessageName),
                                   msgData)) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
--- a/dom/ipc/JSWindowActorParent.cpp
+++ b/dom/ipc/JSWindowActorParent.cpp
@@ -89,17 +89,17 @@ void JSWindowActorParent::SendAsyncMessa
                          "JS Window Actor AsyncMessageToChild dispatch failed");
     return;
   }
 
   // If we're a cross-process, send the async message over the corresponding
   // actor.
   ClonedMessageData msgData;
   RefPtr<TabParent> tabParent = mManager->GetTabParent();
-  nsIContentParent* cp = tabParent->Manager();
+  ContentParent* cp = tabParent->Manager();
   if (!data.BuildClonedMessageDataForParent(cp, msgData)) {
     aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
     return;
   }
 
   if (!mManager->SendAsyncMessage(mName, PromiseFlatString(aMessageName),
                                   msgData)) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -2,17 +2,16 @@
 /* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
 
 /* 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 protocol PColorPicker;
 include protocol PContent;
-include protocol PContentBridge;
 include protocol PDocAccessible;
 include protocol PFilePicker;
 include protocol PIndexedDBPermissionRequest;
 include protocol PPluginWidget;
 include protocol PRemotePrintJob;
 include protocol PChildToParentStream;
 include protocol PParentToChildStream;
 include protocol PFileDescriptorSet;
@@ -129,17 +128,17 @@ struct RequestData
 union OptionalShmem
 {
   void_t;
   Shmem;
 };
 
 nested(upto inside_cpow) sync protocol PBrowser
 {
-    manager PContent or PContentBridge;
+    manager PContent;
 
     manages PColorPicker;
     manages PDocAccessible;
     manages PFilePicker;
     manages PIndexedDBPermissionRequest;
     manages PPluginWidget;
     manages PPaymentRequest;
     manages PWindowGlobal;
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -3,17 +3,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PBackground;
 include protocol PBrowser;
 include protocol PClientOpenWindowOp;
 include protocol PCompositorManager;
-include protocol PContentBridge;
 include protocol PContentPermissionRequest;
 include protocol PCycleCollectWithLogs;
 include protocol PPSMContentDownloader;
 include protocol PExternalHelperApp;
 include protocol PHandlerService;
 include protocol PFileDescriptorSet;
 include protocol PHal;
 include protocol PHeapSnapshotTempFileHelper;
@@ -59,21 +58,16 @@ include PContentPermission;
 include ServiceWorkerConfiguration;
 include GraphicsMessages;
 include MemoryReportTypes;
 include ClientIPCTypes;
 include HangTypes;
 include PrefsTypes;
 include NeckoChannelParams;
 
-// Workaround to prevent error if PContentChild.cpp & PContentBridgeParent.cpp
-// are put into different UnifiedProtocolsXX.cpp files.
-// XXX Remove this once bug 1069073 is fixed
-include "mozilla/dom/PContentBridgeParent.h";
-
 using refcounted class nsIDOMGeoPosition from "nsGeoPositionIPCSerialiser.h";
 using refcounted class nsIAlertNotification from "mozilla/AlertNotificationIPCSerializer.h";
 
 using struct ChromePackage from "mozilla/chrome/RegistryMessageUtils.h";
 using struct SubstitutionMapping from "mozilla/chrome/RegistryMessageUtils.h";
 using struct OverrideMapping from "mozilla/chrome/RegistryMessageUtils.h";
 using base::ProcessId from "base/process.h";
 using struct IPC::Permission from "mozilla/net/NeckoMessageUtils.h";
@@ -406,17 +400,16 @@ both:
     // to persist a subdocument.  For child->parent, arguments are
     // ignored and should be null/zero.
     async PWebBrowserPersistDocument(nullable PBrowser aBrowser,
                                      uint64_t aOuterWindowID);
 
 child:
     async InitGMPService(Endpoint<PGMPServiceChild> service);
     async InitProcessHangMonitor(Endpoint<PProcessHangMonitorChild> hangMonitor);
-    async InitContentBridgeChild(Endpoint<PContentBridgeChild> endpoint);
     async InitProfiler(Endpoint<PProfilerChild> aEndpoint);
 
     // Give the content process its endpoints to the compositor.
     async InitRendering(
       Endpoint<PCompositorManagerChild> compositor,
       Endpoint<PImageBridgeChild> imageBridge,
       Endpoint<PVRManagerChild> vr,
       Endpoint<PVideoDecoderManagerChild> video,
@@ -782,24 +775,16 @@ child:
                                OptionalLoadInfoArgs aLoadInfo,
                                uint64_t aChannelId,
                                nsIURI aOriginalURI,
                                uint64_t aIdentifier);
 
 parent:
     async InitBackground(Endpoint<PBackgroundParent> aEndpoint);
 
-    sync CreateChildProcess(IPCTabContext context,
-                            ProcessPriority priority,
-                            TabId openerTabId,
-                            TabId tabId)
-        returns (ContentParentId cpId, bool isForBrowser);
-    sync BridgeToChildProcess(ContentParentId cpId)
-        returns (Endpoint<PContentBridgeParent> endpoint);
-
     sync OpenRecordReplayChannel(uint32_t channelId)
         returns (FileDescriptor connection);
     async CreateReplayingProcess(uint32_t channelId);
 
     async CreateGMPService();
 
     async InitStreamFilter(uint64_t channelId, nsString addonId)
         returns (Endpoint<PStreamFilterChild> aEndpoint);
@@ -1304,9 +1289,9 @@ both:
     async WindowClose(BrowsingContext aContext, bool aTrustedCaller);
     async WindowFocus(BrowsingContext aContext);
     async WindowBlur(BrowsingContext aContext);
     async WindowPostMessage(BrowsingContext aContext, ClonedMessageData aMessage,
                             PostMessageData aData);
 };
 
 }
-}
+}
\ No newline at end of file
deleted file mode 100644
--- a/dom/ipc/PContentBridge.ipdl
+++ /dev/null
@@ -1,82 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=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 protocol PBrowser;
-include protocol PContent;
-include protocol PJavaScript;
-include protocol PFileDescriptorSet;
-include protocol PChildToParentStream;
-include protocol PParentToChildStream;
-include protocol PIPCBlobInputStream;
-
-include DOMTypes;
-include JavaScriptTypes;
-include ProtocolTypes;
-include PTabContext;
-
-using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h";
-using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h";
-using mozilla::dom::ContentParentId from "mozilla/dom/ipc/IdType.h";
-using class mozilla::dom::MessagePort from "mozilla/dom/MessagePort.h";
-using class mozilla::dom::ipc::StructuredCloneData from "mozilla/dom/ipc/StructuredCloneData.h";
-
-namespace mozilla {
-namespace dom {
-
-/*
- * PContentBridge allows us to represent a parent/child relationship between two
- * child processes.  When a child process wants to open its own child, it asks
- * the root process to create a new process and then bridge them.  The first
- * child will allocate the PContentBridgeParent, and the newly opened child will
- * allocate the PContentBridgeChild.  This protocol allows these processes to
- * share PBrowsers and send messages to each other.
- */
-nested(upto inside_cpow) sync protocol PContentBridge
-{
-    manages PBrowser;
-    manages PFileDescriptorSet;
-    manages PJavaScript;
-    manages PChildToParentStream;
-    manages PParentToChildStream;
-    manages PIPCBlobInputStream;
-
-child:
-    async PParentToChildStream();
-
-child:
-   /**
-     * Sending an activate message moves focus to the child.
-     */
-    async Activate(PBrowser aTab);
-
-    async Deactivate(PBrowser aTab);
-
-    async PIPCBlobInputStream(nsID aID, uint64_t aSize);
-
-parent:
-    sync SyncMessage(nsString aMessage, ClonedMessageData aData,
-                     CpowEntry[] aCpows, Principal aPrincipal)
-      returns (StructuredCloneData[] retval);
-
-    async PJavaScript();
-
-    async PChildToParentStream();
-
-both:
-    // Both the parent and the child can construct the PBrowser.
-    // See the comment in PContent::PBrowser().
-    async PBrowser(TabId tabId, TabId sameTabGroupAs,
-                   IPCTabContext context, uint32_t chromeFlags,
-                   ContentParentId cpId, bool isForBrowser);
-
-    async PFileDescriptorSet(FileDescriptor fd);
-
-    async AsyncMessage(nsString aMessage, CpowEntry[] aCpows,
-                       Principal aPrincipal, ClonedMessageData aData);
-};
-
-}
-}
--- a/dom/ipc/ProcessPriorityManager.cpp
+++ b/dom/ipc/ProcessPriorityManager.cpp
@@ -435,19 +435,19 @@ void ProcessPriorityManagerImpl::SetProc
     pppm->SetPriorityNow(aPriority);
   }
 }
 
 void ProcessPriorityManagerImpl::ObserveContentParentCreated(
     nsISupports* aContentParent) {
   // Do nothing; it's sufficient to get the PPPM.  But assign to nsRefPtr so we
   // don't leak the already_AddRefed object.
-  nsCOMPtr<nsIContentParent> cp = do_QueryInterface(aContentParent);
+  RefPtr<ContentParent> cp = do_QueryObject(aContentParent);
   RefPtr<ParticularProcessPriorityManager> pppm =
-      GetParticularProcessPriorityManager(cp->AsContentParent());
+      GetParticularProcessPriorityManager(cp);
 }
 
 void ProcessPriorityManagerImpl::ObserveContentParentDestroyed(
     nsISupports* aSubject) {
   nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
   NS_ENSURE_TRUE_VOID(props);
 
   uint64_t childID = CONTENT_PROCESS_ID_UNKNOWN;
@@ -472,19 +472,18 @@ void ProcessPriorityManagerImpl::NotifyP
   } else if (newPriority < PROCESS_PRIORITY_FOREGROUND_HIGH &&
              aOldPriority >= PROCESS_PRIORITY_FOREGROUND_HIGH) {
     mHighPriorityChildIDs.RemoveEntry(aParticularManager->ChildID());
   }
 }
 
 void ProcessPriorityManagerImpl::TabActivityChanged(TabParent* aTabParent,
                                                     bool aIsActive) {
-  ContentParent* cp = aTabParent->Manager()->AsContentParent();
   RefPtr<ParticularProcessPriorityManager> pppm =
-      GetParticularProcessPriorityManager(cp);
+      GetParticularProcessPriorityManager(aTabParent->Manager());
   if (!pppm) {
     return;
   }
 
   pppm->TabActivityChanged(aTabParent, aIsActive);
 }
 
 NS_IMPL_ISUPPORTS(ParticularProcessPriorityManager, nsIObserver,
--- a/dom/ipc/RemoteFrameParent.cpp
+++ b/dom/ipc/RemoteFrameParent.cpp
@@ -30,17 +30,17 @@ nsresult RemoteFrameParent::Init(const n
   attrs.SyncAttributesWithPrivateBrowsing(false);
   MutableTabContext tabContext;
   tabContext.SetTabContext(false, 0, UIStateChangeType_Set,
                            UIStateChangeType_Set, attrs, aPresentationURL);
 
   ProcessPriority initialPriority = PROCESS_PRIORITY_FOREGROUND;
 
   // Get our ConstructorSender object.
-  RefPtr<nsIContentParent> constructorSender =
+  RefPtr<ContentParent> constructorSender =
       ContentParent::GetNewOrUsedBrowserProcess(
           nullptr, aRemoteType, initialPriority, nullptr, false);
   if (NS_WARN_IF(!constructorSender)) {
     MOZ_ASSERT(false, "Unable to allocate content process!");
     return NS_ERROR_FAILURE;
   }
 
   ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
--- a/dom/ipc/StructuredCloneData.cpp
+++ b/dom/ipc/StructuredCloneData.cpp
@@ -190,22 +190,22 @@ bool BuildClonedMessageData(M* aManager,
       streams.AppendElement(stream->TakeValue());
     }
   }
 
   return true;
 }
 
 bool StructuredCloneData::BuildClonedMessageDataForParent(
-    nsIContentParent* aParent, ClonedMessageData& aClonedData) {
+    ContentParent* aParent, ClonedMessageData& aClonedData) {
   return BuildClonedMessageData(aParent, *this, aClonedData);
 }
 
 bool StructuredCloneData::BuildClonedMessageDataForChild(
-    nsIContentChild* aChild, ClonedMessageData& aClonedData) {
+    ContentChild* aChild, ClonedMessageData& aClonedData) {
   return BuildClonedMessageData(aChild, *this, aClonedData);
 }
 
 bool StructuredCloneData::BuildClonedMessageDataForBackgroundParent(
     PBackgroundParent* aParent, ClonedMessageData& aClonedData) {
   return BuildClonedMessageData(aParent, *this, aClonedData);
 }
 
--- a/dom/ipc/StructuredCloneData.h
+++ b/dom/ipc/StructuredCloneData.h
@@ -24,18 +24,18 @@ namespace ipc {
 class AutoIPCStream;
 class PBackgroundChild;
 class PBackgroundParent;
 
 }  // namespace ipc
 
 namespace dom {
 
-class nsIContentChild;
-class nsIContentParent;
+class ContentChild;
+class ContentParent;
 
 namespace ipc {
 
 /**
  * Wraps the non-reference-counted JSStructuredCloneData class to have a
  * reference count so that multiple StructuredCloneData instances can reference
  * a single underlying serialized representation.
  *
@@ -176,19 +176,19 @@ class StructuredCloneData : public Struc
 
   void Write(JSContext* aCx, JS::Handle<JS::Value> aValue,
              JS::Handle<JS::Value> aTransfers, ErrorResult& aRv);
 
   // Actor-varying methods to convert the structured clone stored in this holder
   // by a previous call to Write() into ClonedMessageData IPC representation.
   // (Blobs are represented in IPC by IPCBlob actors, so we need the parent to
   // be able to create them.)
-  bool BuildClonedMessageDataForParent(nsIContentParent* aParent,
+  bool BuildClonedMessageDataForParent(ContentParent* aParent,
                                        ClonedMessageData& aClonedData);
-  bool BuildClonedMessageDataForChild(nsIContentChild* aChild,
+  bool BuildClonedMessageDataForChild(ContentChild* aChild,
                                       ClonedMessageData& aClonedData);
   bool BuildClonedMessageDataForBackgroundParent(
       mozilla::ipc::PBackgroundParent* aParent, ClonedMessageData& aClonedData);
   bool BuildClonedMessageDataForBackgroundChild(
       mozilla::ipc::PBackgroundChild* aChild, ClonedMessageData& aClonedData);
 
   // Actor-varying and memory-management-strategy-varying methods to initialize
   // this holder from a ClonedMessageData representation.
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -345,27 +345,27 @@ already_AddRefed<TabChild> TabChild::Fin
   if (iter == NestedTabChildMap().end()) {
     return nullptr;
   }
   RefPtr<TabChild> tabChild = iter->second;
   return tabChild.forget();
 }
 
 /*static*/ already_AddRefed<TabChild> TabChild::Create(
-    nsIContentChild* aManager, const TabId& aTabId,
+    ContentChild* aManager, const TabId& aTabId,
     const TabId& aSameTabGroupAs, const TabContext& aContext,
     uint32_t aChromeFlags) {
   RefPtr<TabChild> groupChild = FindTabChild(aSameTabGroupAs);
   dom::TabGroup* group = groupChild ? groupChild->TabGroup() : nullptr;
   RefPtr<TabChild> iframe =
       new TabChild(aManager, aTabId, group, aContext, aChromeFlags);
   return iframe.forget();
 }
 
-TabChild::TabChild(nsIContentChild* aManager, const TabId& aTabId,
+TabChild::TabChild(ContentChild* aManager, const TabId& aTabId,
                    dom::TabGroup* aTabGroup, const TabContext& aContext,
                    uint32_t aChromeFlags)
     : TabContext(aContext),
       mTabGroup(aTabGroup),
       mManager(aManager),
       mChromeFlags(aChromeFlags),
       mMaxTouchPoints(0),
       mLayersId{0},
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -226,23 +226,23 @@ class TabChild final : public TabChildBa
 
   // Return a list of all active TabChildren.
   static nsTArray<RefPtr<TabChild>> GetAll();
 
  public:
   /**
    * Create a new TabChild object.
    */
-  TabChild(nsIContentChild* aManager, const TabId& aTabId, TabGroup* aTabGroup,
+  TabChild(ContentChild* aManager, const TabId& aTabId, TabGroup* aTabGroup,
            const TabContext& aContext, uint32_t aChromeFlags);
 
   nsresult Init(mozIDOMWindowProxy* aParent);
 
   /** Return a TabChild with the given attributes. */
-  static already_AddRefed<TabChild> Create(nsIContentChild* aManager,
+  static already_AddRefed<TabChild> Create(ContentChild* aManager,
                                            const TabId& aTabId,
                                            const TabId& aSameTabGroupAs,
                                            const TabContext& aContext,
                                            uint32_t aChromeFlags);
 
   // Let managees query if it is safe to send messages.
   bool IsDestroyed() const { return mDestroyed; }
 
@@ -463,17 +463,17 @@ class TabChild final : public TabChildBa
    * Signal to this TabChild that it should be made visible:
    * activated widget, retained layer tree, etc.  (Respectively,
    * made not visible.)
    */
   void MakeVisible();
   void MakeHidden();
   bool IsVisible();
 
-  nsIContentChild* Manager() const { return mManager; }
+  ContentChild* Manager() const { return mManager; }
 
   static inline TabChild* GetFrom(nsIDocShell* aDocShell) {
     if (!aDocShell) {
       return nullptr;
     }
 
     nsCOMPtr<nsITabChild> tc = aDocShell->GetTabChild();
     return static_cast<TabChild*>(tc.get());
@@ -798,17 +798,17 @@ class TabChild final : public TabChildBa
   class DelayedDeleteRunnable;
 
   TextureFactoryIdentifier mTextureFactoryIdentifier;
   RefPtr<nsWebBrowser> mWebBrowser;
   nsCOMPtr<nsIWebNavigation> mWebNav;
   RefPtr<mozilla::dom::TabGroup> mTabGroup;
   RefPtr<PuppetWidget> mPuppetWidget;
   nsCOMPtr<nsIURI> mLastURI;
-  RefPtr<nsIContentChild> mManager;
+  RefPtr<ContentChild> mManager;
   uint32_t mChromeFlags;
   uint32_t mMaxTouchPoints;
   layers::LayersId mLayersId;
   int64_t mBeforeUnloadListeners;
   CSSRect mUnscaledOuterRect;
   Maybe<bool> mLayersConnected;
   bool mDidFakeShow;
   bool mNotified;
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -9,17 +9,16 @@
 #include "TabParent.h"
 
 #ifdef ACCESSIBILITY
 #  include "mozilla/a11y/DocAccessibleParent.h"
 #  include "nsAccessibilityService.h"
 #endif
 #include "mozilla/BrowserElementParent.h"
 #include "mozilla/dom/ChromeMessageSender.h"
-#include "mozilla/dom/ContentBridgeParent.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/DataTransfer.h"
 #include "mozilla/dom/DataTransferItemList.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/indexedDB/ActorsParent.h"
 #include "mozilla/dom/IPCBlobUtils.h"
 #include "mozilla/dom/PaymentRequestParent.h"
 #include "mozilla/dom/RemoteFrameParent.h"
@@ -139,17 +138,17 @@ using mozilla::Unused;
 namespace mozilla {
 namespace dom {
 
 TabParent::LayerToTabParentTable* TabParent::sLayerToTabParentTable = nullptr;
 
 NS_IMPL_ISUPPORTS(TabParent, nsITabParent, nsIAuthPromptProvider,
                   nsISupportsWeakReference)
 
-TabParent::TabParent(nsIContentParent* aManager, const TabId& aTabId,
+TabParent::TabParent(ContentParent* aManager, const TabId& aTabId,
                      const TabContext& aContext, uint32_t aChromeFlags)
     : TabContext(aContext),
       mFrameElement(nullptr),
       mContentCache(*this),
       mRect(0, 0, 0, 0),
       mDimensions(0, 0),
       mOrientation(0),
       mDPI(0),
@@ -381,43 +380,33 @@ void TabParent::Destroy() {
   if (mIsDestroyed) {
     return;
   }
 
   DestroyInternal();
 
   mIsDestroyed = true;
 
-  if (XRE_IsParentProcess()) {
-    ContentParent::NotifyTabDestroying(this->GetTabId(),
-                                       Manager()->AsContentParent()->ChildID());
-  } else {
-    ContentParent::NotifyTabDestroying(this->GetTabId(), Manager()->ChildID());
-  }
+  ContentParent::NotifyTabDestroying(this->GetTabId(), Manager()->ChildID());
 
   mMarkedDestroying = true;
 }
 
 mozilla::ipc::IPCResult TabParent::RecvEnsureLayersConnected(
     CompositorOptions* aCompositorOptions) {
   if (mRenderFrame.IsInitialized()) {
     mRenderFrame.EnsureLayersConnected(aCompositorOptions);
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult TabParent::Recv__delete__() {
-  if (XRE_IsParentProcess()) {
-    ContentParent::UnregisterRemoteFrame(
-        mTabId, Manager()->AsContentParent()->ChildID(), mMarkedDestroying);
-  } else {
-    Manager()->AsContentBridgeParent()->NotifyTabDestroyed();
-    ContentParent::UnregisterRemoteFrame(mTabId, Manager()->ChildID(),
-                                         mMarkedDestroying);
-  }
+  MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
+  ContentParent::UnregisterRemoteFrame(mTabId, Manager()->ChildID(),
+                                       mMarkedDestroying);
 
   return IPC_OK();
 }
 
 void TabParent::ActorDestroy(ActorDestroyReason why) {
   if (mRenderFrame.IsInitialized()) {
     // It's important to unmap layers after the remote browser has been
     // destroyed, otherwise it may still send messages to the compositor which
@@ -957,21 +946,16 @@ auto TabParent::AllocPIndexedDBPermissio
     const Principal& aPrincipal) -> PIndexedDBPermissionRequestParent* {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsCOMPtr<nsIPrincipal> principal(aPrincipal);
   if (!principal) {
     return nullptr;
   }
 
-  nsCOMPtr<nsIContentParent> manager = Manager();
-  if (!manager->IsContentParent()) {
-    MOZ_CRASH("Figure out security checks for bridged content!");
-  }
-
   if (NS_WARN_IF(!mFrameElement)) {
     return nullptr;
   }
 
   return mozilla::dom::indexedDB::AllocPIndexedDBPermissionRequestParent(
       mFrameElement, principal);
 }
 
@@ -1072,18 +1056,17 @@ void TabParent::SendRealMouseEvent(Widge
     }
     return;
   }
 
   ScrollableLayerGuid guid;
   uint64_t blockId;
   ApzAwareEventRoutingToChild(&guid, &blockId, nullptr);
 
-  bool isInputPriorityEventEnabled =
-      Manager()->AsContentParent()->IsInputPriorityEventEnabled();
+  bool isInputPriorityEventEnabled = Manager()->IsInputPriorityEventEnabled();
 
   if (mIsMouseEnterIntoWidgetEventSuppressed) {
     // In the case that the TabParent suppressed the eMouseEnterWidget event due
     // to its corresponding TabChild wasn't ready to handle it, we have to
     // resend it when the TabChild is ready.
     mIsMouseEnterIntoWidgetEventSuppressed = false;
     WidgetMouseEvent localEvent(aEvent);
     localEvent.mMessage = eMouseEnterIntoWidget;
@@ -1200,17 +1183,17 @@ bool TabParent::QueryDropLinksForVerific
 }
 
 void TabParent::SendRealDragEvent(WidgetDragEvent& aEvent, uint32_t aDragAction,
                                   uint32_t aDropEffect,
                                   const IPC::Principal& aPrincipal) {
   if (mIsDestroyed || !mIsReadyToHandleInputEvents) {
     return;
   }
-  MOZ_ASSERT(!Manager()->AsContentParent()->IsInputPriorityEventEnabled());
+  MOZ_ASSERT(!Manager()->IsInputPriorityEventEnabled());
   aEvent.mRefPoint += GetChildProcessOffset();
   if (aEvent.mMessage == eDrop) {
     if (!QueryDropLinksForVerification()) {
       return;
     }
   }
   DebugOnly<bool> ret = PBrowserParent::SendRealDragEvent(
       aEvent, aDragAction, aDropEffect, aPrincipal);
@@ -1223,17 +1206,17 @@ void TabParent::SendMouseWheelEvent(Widg
     return;
   }
 
   ScrollableLayerGuid guid;
   uint64_t blockId;
   ApzAwareEventRoutingToChild(&guid, &blockId, nullptr);
   aEvent.mRefPoint += GetChildProcessOffset();
   DebugOnly<bool> ret =
-      Manager()->AsContentParent()->IsInputPriorityEventEnabled()
+      Manager()->IsInputPriorityEventEnabled()
           ? PBrowserParent::SendMouseWheelEvent(aEvent, guid, blockId)
           : PBrowserParent::SendNormalPriorityMouseWheelEvent(aEvent, guid,
                                                               blockId);
 
   NS_WARNING_ASSERTION(ret, "PBrowserParent::SendMouseWheelEvent() failed");
   MOZ_ASSERT(!ret || aEvent.HasBeenPostedToRemoteProcess());
 }
 
@@ -1485,17 +1468,17 @@ void TabParent::SendRealKeyEvent(WidgetK
   if (aEvent.mMessage == eKeyPress) {
     // XXX Should we do this only when input context indicates an editor having
     //     focus and the key event won't cause inputting text?
     aEvent.InitAllEditCommands();
   } else {
     aEvent.PreventNativeKeyBindings();
   }
   DebugOnly<bool> ret =
-      Manager()->AsContentParent()->IsInputPriorityEventEnabled()
+      Manager()->IsInputPriorityEventEnabled()
           ? PBrowserParent::SendRealKeyEvent(aEvent)
           : PBrowserParent::SendNormalPriorityRealKeyEvent(aEvent);
 
   NS_WARNING_ASSERTION(ret, "PBrowserParent::SendRealKeyEvent() failed");
   MOZ_ASSERT(!ret || aEvent.HasBeenPostedToRemoteProcess());
 }
 
 void TabParent::SendRealTouchEvent(WidgetTouchEvent& aEvent) {
@@ -1524,18 +1507,17 @@ void TabParent::SendRealTouchEvent(Widge
     return;
   }
 
   LayoutDeviceIntPoint offset = GetChildProcessOffset();
   for (uint32_t i = 0; i < aEvent.mTouches.Length(); i++) {
     aEvent.mTouches[i]->mRefPoint += offset;
   }
 
-  bool inputPriorityEventEnabled =
-      Manager()->AsContentParent()->IsInputPriorityEventEnabled();
+  bool inputPriorityEventEnabled = Manager()->IsInputPriorityEventEnabled();
 
   if (aEvent.mMessage == eTouchMove) {
     DebugOnly<bool> ret =
         inputPriorityEventEnabled
             ? PBrowserParent::SendRealTouchMoveEvent(aEvent, guid, blockId,
                                                      apzResponse)
             : PBrowserParent::SendNormalPriorityRealTouchMoveEvent(
                   aEvent, guid, blockId, apzResponse);
@@ -1578,17 +1560,17 @@ bool TabParent::SendHandleTap(TapType aT
           fm->SetFocus(element, nsIFocusManager::FLAG_BYMOUSE |
                                     nsIFocusManager::FLAG_BYTOUCH |
                                     nsIFocusManager::FLAG_NOSCROLL);
         }
       }
     }
   }
   LayoutDeviceIntPoint offset = GetChildProcessOffset();
-  return Manager()->AsContentParent()->IsInputPriorityEventEnabled()
+  return Manager()->IsInputPriorityEventEnabled()
              ? PBrowserParent::SendHandleTap(aType, aPoint + offset, aModifiers,
                                              aGuid, aInputBlockId)
              : PBrowserParent::SendNormalPriorityHandleTap(
                    aType, aPoint + offset, aModifiers, aGuid, aInputBlockId);
 }
 
 mozilla::ipc::IPCResult TabParent::RecvSyncMessage(
     const nsString& aMessage, const ClonedMessageData& aData,
@@ -2188,17 +2170,17 @@ bool TabParent::SendCompositionEvent(Wid
   if (mIsDestroyed) {
     return false;
   }
 
   if (!mContentCache.OnCompositionEvent(aEvent)) {
     return true;
   }
 
-  bool ret = Manager()->AsContentParent()->IsInputPriorityEventEnabled()
+  bool ret = Manager()->IsInputPriorityEventEnabled()
                  ? PBrowserParent::SendCompositionEvent(aEvent)
                  : PBrowserParent::SendNormalPriorityCompositionEvent(aEvent);
   if (NS_WARN_IF(!ret)) {
     return false;
   }
   MOZ_ASSERT(aEvent.HasBeenPostedToRemoteProcess());
   return true;
 }
@@ -2207,17 +2189,17 @@ bool TabParent::SendSelectionEvent(Widge
   if (mIsDestroyed) {
     return false;
   }
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget) {
     return true;
   }
   mContentCache.OnSelectionEvent(aEvent);
-  bool ret = Manager()->AsContentParent()->IsInputPriorityEventEnabled()
+  bool ret = Manager()->IsInputPriorityEventEnabled()
                  ? PBrowserParent::SendSelectionEvent(aEvent)
                  : PBrowserParent::SendNormalPrioritySelectionEvent(aEvent);
   if (NS_WARN_IF(!ret)) {
     return false;
   }
   MOZ_ASSERT(aEvent.HasBeenPostedToRemoteProcess());
   aEvent.mSucceeded = true;
   return true;
@@ -2614,17 +2596,17 @@ TabParent::SetDocShellIsActive(bool isAc
           a11y::nsWinUtils::HideNativeWindow(window);
         }
       }
     }
   }
 #endif
 
   // Keep track of how many active recording/replaying tabs there are.
-  if (Manager()->AsContentParent()->IsRecordingOrReplaying()) {
+  if (Manager()->IsRecordingOrReplaying()) {
     SetIsActiveRecordReplayTab(isActive);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TabParent::GetDocShellIsActive(bool* aIsActive) {
@@ -2711,35 +2693,35 @@ void TabParent::SetRenderLayersInternal(
   // RenderLayers requests are ignored.
   mLayerTreeEpoch = mLayerTreeEpoch.Next();
 
   Unused << SendRenderLayers(aEnabled, aForceRepaint, mLayerTreeEpoch);
 
   // Ask the child to repaint using the PHangMonitor channel/thread (which may
   // be less congested).
   if (aEnabled) {
-    ContentParent* cp = Manager()->AsContentParent();
-    cp->PaintTabWhileInterruptingJS(this, aForceRepaint, mLayerTreeEpoch);
+    Manager()->PaintTabWhileInterruptingJS(this, aForceRepaint,
+                                           mLayerTreeEpoch);
   }
 }
 
 NS_IMETHODIMP
 TabParent::PreserveLayers(bool aPreserveLayers) {
   mPreserveLayers = aPreserveLayers;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TabParent::SaveRecording(const nsAString& aFilename, bool* aRetval) {
   nsCOMPtr<nsIFile> file;
   nsresult rv = NS_NewLocalFile(aFilename, false, getter_AddRefs(file));
   if (NS_FAILED(rv)) {
     return rv;
   }
-  return Manager()->AsContentParent()->SaveRecording(file, aRetval);
+  return Manager()->SaveRecording(file, aRetval);
 }
 
 NS_IMETHODIMP
 TabParent::GetContentBlockingLog(Promise** aPromise) {
   NS_ENSURE_ARG_POINTER(aPromise);
 
   *aPromise = nullptr;
   if (!mFrameElement) {
@@ -2819,23 +2801,17 @@ TabParent::GetHasPresented(bool* aResult
 }
 
 void TabParent::NavigateByKey(bool aForward, bool aForDocumentNavigation) {
   Unused << SendNavigateByKey(aForward, aForDocumentNavigation);
 }
 
 NS_IMETHODIMP
 TabParent::TransmitPermissionsForPrincipal(nsIPrincipal* aPrincipal) {
-  nsCOMPtr<nsIContentParent> manager = Manager();
-  if (!manager->IsContentParent()) {
-    return NS_ERROR_UNEXPECTED;
-  }
-
-  return manager->AsContentParent()->TransmitPermissionsForPrincipal(
-      aPrincipal);
+  return Manager()->TransmitPermissionsForPrincipal(aPrincipal);
 }
 
 NS_IMETHODIMP
 TabParent::GetHasBeforeUnload(bool* aResult) {
   *aResult = mHasBeforeUnload;
   return NS_OK;
 }
 
@@ -3120,36 +3096,33 @@ mozilla::ipc::IPCResult TabParent::RecvA
 mozilla::ipc::IPCResult TabParent::RecvInvokeDragSession(
     nsTArray<IPCDataTransfer>&& aTransfers, const uint32_t& aAction,
     const OptionalShmem& aVisualDnDData, const uint32_t& aStride,
     const gfx::SurfaceFormat& aFormat, const LayoutDeviceIntRect& aDragRect,
     const IPC::Principal& aPrincipal) {
   mInitialDataTransferItems.Clear();
   nsIPresShell* shell = mFrameElement->OwnerDoc()->GetShell();
   if (!shell) {
-    if (Manager()->IsContentParent()) {
-      Unused << Manager()->AsContentParent()->SendEndDragSession(
-          true, true, LayoutDeviceIntPoint(), 0);
-      // Continue sending input events with input priority when stopping the dnd
-      // session.
-      Manager()->AsContentParent()->SetInputPriorityEventEnabled(true);
-    }
+    Unused << Manager()->SendEndDragSession(true, true, LayoutDeviceIntPoint(),
+                                            0);
+    // Continue sending input events with input priority when stopping the dnd
+    // session.
+    Manager()->SetInputPriorityEventEnabled(true);
     return IPC_OK();
   }
 
   EventStateManager* esm = shell->GetPresContext()->EventStateManager();
   for (uint32_t i = 0; i < aTransfers.Length(); ++i) {
     mInitialDataTransferItems.AppendElement(std::move(aTransfers[i].items()));
   }
-  if (Manager()->IsContentParent()) {
-    nsCOMPtr<nsIDragService> dragService =
-        do_GetService("@mozilla.org/widget/dragservice;1");
-    if (dragService) {
-      dragService->MaybeAddChildProcess(Manager()->AsContentParent());
-    }
+
+  nsCOMPtr<nsIDragService> dragService =
+      do_GetService("@mozilla.org/widget/dragservice;1");
+  if (dragService) {
+    dragService->MaybeAddChildProcess(Manager());
   }
 
   if (aVisualDnDData.type() == OptionalShmem::Tvoid_t ||
       !aVisualDnDData.get_Shmem().IsReadable() ||
       aVisualDnDData.get_Shmem().Size<char>() < aDragRect.height * aStride) {
     mDnDVisualization = nullptr;
   } else {
     mDnDVisualization = gfx::CreateDataSourceSurfaceFromData(
@@ -3235,26 +3208,20 @@ bool TabParent::TakeDragVisualization(
 bool TabParent::AsyncPanZoomEnabled() const {
   nsCOMPtr<nsIWidget> widget = GetWidget();
   return widget && widget->AsyncPanZoomEnabled();
 }
 
 void TabParent::StartPersistence(uint64_t aOuterWindowID,
                                  nsIWebBrowserPersistDocumentReceiver* aRecv,
                                  ErrorResult& aRv) {
-  nsCOMPtr<nsIContentParent> manager = Manager();
-  if (!manager->IsContentParent()) {
-    aRv.Throw(NS_ERROR_UNEXPECTED);
-    return;
-  }
   auto* actor = new WebBrowserPersistDocumentParent();
   actor->SetOnReady(aRecv);
-  bool ok =
-      manager->AsContentParent()->SendPWebBrowserPersistDocumentConstructor(
-          actor, this, aOuterWindowID);
+  bool ok = Manager()->SendPWebBrowserPersistDocumentConstructor(
+      actor, this, aOuterWindowID);
   if (!ok) {
     aRv.Throw(NS_ERROR_FAILURE);
   }
   // (The actor will be destroyed on constructor failure.)
 }
 
 NS_IMETHODIMP
 TabParent::StartApzAutoscroll(float aAnchorX, float aAnchorY,
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -64,17 +64,17 @@ namespace gfx {
 class SourceSurface;
 class DataSourceSurface;
 }  // namespace gfx
 
 namespace dom {
 
 class CanonicalBrowsingContext;
 class ClonedMessageData;
-class nsIContentParent;
+class ContentParent;
 class Element;
 class DataTransfer;
 
 namespace ipc {
 class StructuredCloneData;
 }  // namespace ipc
 
 class TabParent final : public PBrowserParent,
@@ -95,17 +95,17 @@ class TabParent final : public PBrowserP
   // Helper class for ContentParent::RecvCreateWindow.
   struct AutoUseNewTab;
 
   // nsITabParent
   NS_DECL_NSITABPARENT
   // nsIDOMEventListener interfaces
   NS_DECL_NSIDOMEVENTLISTENER
 
-  TabParent(nsIContentParent* aManager, const TabId& aTabId,
+  TabParent(ContentParent* aManager, const TabId& aTabId,
             const TabContext& aContext, uint32_t aChromeFlags);
 
   Element* GetOwnerElement() const { return mFrameElement; }
   already_AddRefed<nsPIDOMWindowOuter> GetParentWindowOuter();
 
   void SetOwnerElement(Element* aElement);
 
   void CacheFrameLoader(nsFrameLoader* aFrameLoader);
@@ -472,17 +472,17 @@ class TabParent final : public PBrowserP
   static TabParent* GetFrom(nsITabParent* aTabParent);
 
   static TabParent* GetFrom(PBrowserParent* aTabParent);
 
   static TabParent* GetFrom(nsIContent* aContent);
 
   static TabId GetTabIdFrom(nsIDocShell* docshell);
 
-  nsIContentParent* Manager() const { return mManager; }
+  ContentParent* Manager() const { return mManager; }
 
   /**
    * Let managees query if Destroy() is already called so they don't send out
    * messages when the PBrowser actor is being destroyed.
    */
   bool IsDestroyed() const { return mIsDestroyed; }
 
   // Returns the closest widget for our frameloader's content.
@@ -622,17 +622,17 @@ class TabParent final : public PBrowserP
 
   void DestroyInternal();
 
   void SetRenderLayersInternal(bool aEnabled, bool aForceRepaint);
 
   already_AddRefed<nsFrameLoader> GetFrameLoader(
       bool aUseCachedFrameLoaderAfterDestroy = false) const;
 
-  RefPtr<nsIContentParent> mManager;
+  RefPtr<ContentParent> mManager;
   void TryCacheDPIAndScale();
 
   bool AsyncPanZoomEnabled() const;
 
   // Cached value indicating the docshell active state of the remote browser.
   bool mDocShellIsActive;
 
   // Update state prior to routing an APZ-aware event to the child process.
--- a/dom/ipc/moz.build
+++ b/dom/ipc/moz.build
@@ -26,32 +26,28 @@ EXPORTS.mozilla.dom.ipc += [
     'StringTable.h',
     'StructuredCloneData.h',
 ]
 
 EXPORTS.mozilla.dom += [
     'CoalescedInputData.h',
     'CoalescedMouseData.h',
     'CoalescedWheelData.h',
-    'ContentBridgeChild.h',
-    'ContentBridgeParent.h',
     'ContentChild.h',
     'ContentParent.h',
     'ContentProcess.h',
     'ContentProcessManager.h',
     'CPOWManagerGetter.h',
     'CSPMessageUtils.h',
     'DocShellMessageUtils.h',
     'FilePickerParent.h',
     'JSWindowActorChild.h',
     'JSWindowActorParent.h',
     'JSWindowActorService.h',
     'MemoryReportRequest.h',
-    'nsIContentChild.h',
-    'nsIContentParent.h',
     'PermissionMessageUtils.h',
     'RemoteFrameChild.h',
     'RemoteFrameParent.h',
     'TabChild.h',
     'TabContext.h',
     'TabMessageUtils.h',
     'TabParent.h',
     'URLClassifierChild.h',
@@ -66,32 +62,28 @@ EXPORTS.mozilla += [
     'ProcessHangMonitorIPC.h',
     'ProcessPriorityManager.h',
 ]
 
 UNIFIED_SOURCES += [
     'CoalescedMouseData.cpp',
     'CoalescedWheelData.cpp',
     'ColorPickerParent.cpp',
-    'ContentBridgeChild.cpp',
-    'ContentBridgeParent.cpp',
     'ContentParent.cpp',
     'ContentProcess.cpp',
     'ContentProcessManager.cpp',
     'CSPMessageUtils.cpp',
     'DocShellMessageUtils.cpp',
     'FilePickerParent.cpp',
     'JSWindowActorChild.cpp',
     'JSWindowActorParent.cpp',
     'JSWindowActorService.cpp',
     'MemMapSnapshot.cpp',
     'MemoryReportRequest.cpp',
     'MMPrinter.cpp',
-    'nsIContentChild.cpp',
-    'nsIContentParent.cpp',
     'PermissionMessageUtils.cpp',
     'PreallocatedProcessManager.cpp',
     'ProcessPriorityManager.cpp',
     'RemoteFrameChild.cpp',
     'RemoteFrameParent.cpp',
     'SharedMap.cpp',
     'SharedStringMap.cpp',
     'StructuredCloneData.cpp',
@@ -112,17 +104,16 @@ SOURCES += [
 
 IPDL_SOURCES += [
     'DOMTypes.ipdlh',
     'MemoryReportTypes.ipdlh',
     'PBrowser.ipdl',
     'PBrowserOrId.ipdlh',
     'PColorPicker.ipdl',
     'PContent.ipdl',
-    'PContentBridge.ipdl',
     'PContentPermission.ipdlh',
     'PContentPermissionRequest.ipdl',
     'PCycleCollectWithLogs.ipdl',
     'PFilePicker.ipdl',
     'PLoginReputation.ipdl',
     'PPluginWidget.ipdl',
     'PProcessHangMonitor.ipdl',
     'PrefsTypes.ipdlh',
deleted file mode 100644
--- a/dom/ipc/nsIContentChild.cpp
+++ /dev/null
@@ -1,204 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsIContentChild.h"
-
-#include "mozilla/dom/ContentChild.h"
-#include "mozilla/dom/ChildProcessMessageManager.h"
-#include "mozilla/dom/DOMTypes.h"
-#include "mozilla/dom/File.h"
-#include "mozilla/dom/PermissionMessageUtils.h"
-#include "mozilla/dom/TabChild.h"
-#include "mozilla/dom/TabGroup.h"
-#include "mozilla/dom/ipc/StructuredCloneData.h"
-#include "mozilla/ipc/FileDescriptorSetChild.h"
-#include "mozilla/ipc/InputStreamUtils.h"
-#include "mozilla/ipc/IPCStreamAlloc.h"
-#include "mozilla/ipc/IPCStreamDestination.h"
-#include "mozilla/ipc/IPCStreamSource.h"
-#include "mozilla/ipc/PChildToParentStreamChild.h"
-#include "mozilla/ipc/PParentToChildStreamChild.h"
-#include "mozilla/dom/ipc/IPCBlobInputStreamChild.h"
-
-#include "MMPrinter.h"
-#include "nsPrintfCString.h"
-#include "xpcpublic.h"
-
-using namespace mozilla::ipc;
-using namespace mozilla::jsipc;
-
-namespace mozilla {
-namespace dom {
-
-PJavaScriptChild* nsIContentChild::AllocPJavaScriptChild() {
-  return NewJavaScriptChild();
-}
-
-bool nsIContentChild::DeallocPJavaScriptChild(PJavaScriptChild* aChild) {
-  ReleaseJavaScriptChild(aChild);
-  return true;
-}
-
-PBrowserChild* nsIContentChild::AllocPBrowserChild(
-    const TabId& aTabId, const TabId& aSameTabGroupAs,
-    const IPCTabContext& aContext, const uint32_t& aChromeFlags,
-    const ContentParentId& aCpID, const bool& aIsForBrowser) {
-  // We'll happily accept any kind of IPCTabContext here; we don't need to
-  // check that it's of a certain type for security purposes, because we
-  // believe whatever the parent process tells us.
-
-  MaybeInvalidTabContext tc(aContext);
-  if (!tc.IsValid()) {
-    NS_ERROR(nsPrintfCString("Received an invalid TabContext from "
-                             "the parent process. (%s)  Crashing...",
-                             tc.GetInvalidReason())
-                 .get());
-    MOZ_CRASH("Invalid TabContext received from the parent process.");
-  }
-
-  RefPtr<TabChild> child = TabChild::Create(this, aTabId, aSameTabGroupAs,
-                                            tc.GetTabContext(), aChromeFlags);
-
-  // The ref here is released in DeallocPBrowserChild.
-  return child.forget().take();
-}
-
-bool nsIContentChild::DeallocPBrowserChild(PBrowserChild* aIframe) {
-  TabChild* child = static_cast<TabChild*>(aIframe);
-  NS_RELEASE(child);
-  return true;
-}
-
-mozilla::ipc::IPCResult nsIContentChild::RecvPBrowserConstructor(
-    PBrowserChild* aActor, const TabId& aTabId, const TabId& aSameTabGroupAs,
-    const IPCTabContext& aContext, const uint32_t& aChromeFlags,
-    const ContentParentId& aCpID, const bool& aIsForBrowser) {
-  // This runs after AllocPBrowserChild() returns and the IPC machinery for this
-  // PBrowserChild has been set up.
-
-  auto tabChild = static_cast<TabChild*>(static_cast<TabChild*>(aActor));
-
-  if (NS_WARN_IF(NS_FAILED(tabChild->Init(/* aOpener */ nullptr)))) {
-    return IPC_FAIL(tabChild, "TabChild::Init failed");
-  }
-
-  nsCOMPtr<nsIObserverService> os = services::GetObserverService();
-  if (os) {
-    os->NotifyObservers(static_cast<nsITabChild*>(tabChild),
-                        "tab-child-created", nullptr);
-  }
-  // Notify parent that we are ready to handle input events.
-  tabChild->SendRemoteIsReadyToHandleInputEvents();
-  return IPC_OK();
-}
-
-PIPCBlobInputStreamChild* nsIContentChild::AllocPIPCBlobInputStreamChild(
-    const nsID& aID, const uint64_t& aSize) {
-  // IPCBlobInputStreamChild is refcounted. Here it's created and in
-  // DeallocPIPCBlobInputStreamChild is released.
-
-  RefPtr<IPCBlobInputStreamChild> actor =
-      new IPCBlobInputStreamChild(aID, aSize);
-  return actor.forget().take();
-}
-
-bool nsIContentChild::DeallocPIPCBlobInputStreamChild(
-    PIPCBlobInputStreamChild* aActor) {
-  RefPtr<IPCBlobInputStreamChild> actor =
-      dont_AddRef(static_cast<IPCBlobInputStreamChild*>(aActor));
-  return true;
-}
-
-PChildToParentStreamChild* nsIContentChild::AllocPChildToParentStreamChild() {
-  MOZ_CRASH("PChildToParentStreamChild actors should be manually constructed!");
-}
-
-bool nsIContentChild::DeallocPChildToParentStreamChild(
-    PChildToParentStreamChild* aActor) {
-  delete aActor;
-  return true;
-}
-
-PParentToChildStreamChild* nsIContentChild::AllocPParentToChildStreamChild() {
-  return mozilla::ipc::AllocPParentToChildStreamChild();
-}
-
-bool nsIContentChild::DeallocPParentToChildStreamChild(
-    PParentToChildStreamChild* aActor) {
-  delete aActor;
-  return true;
-}
-
-PFileDescriptorSetChild* nsIContentChild::AllocPFileDescriptorSetChild(
-    const FileDescriptor& aFD) {
-  return new FileDescriptorSetChild(aFD);
-}
-
-bool nsIContentChild::DeallocPFileDescriptorSetChild(
-    PFileDescriptorSetChild* aActor) {
-  delete static_cast<FileDescriptorSetChild*>(aActor);
-  return true;
-}
-
-mozilla::ipc::IPCResult nsIContentChild::RecvAsyncMessage(
-    const nsString& aMsg, InfallibleTArray<CpowEntry>&& aCpows,
-    const IPC::Principal& aPrincipal, const ClonedMessageData& aData) {
-  AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(
-      "nsIContentChild::RecvAsyncMessage", OTHER, aMsg);
-  MMPrinter::Print("nsIContentChild::RecvAsyncMessage", aMsg, aData);
-
-  CrossProcessCpowHol