Merge autoland to mozilla-central. a=merge
authorNarcis Beleuzu <nbeleuzu@mozilla.com>
Sat, 12 Jan 2019 11:39:04 +0200
changeset 510707 cb35977ae7a4a5a1d2fe6a543e15d49cd2fd47c8
parent 510624 3aec75953c2888e37b511f0fdba10c9a3b26a272 (current diff)
parent 510706 434b6906676e7dbad6ea265af5528d5eea2f6f8f (diff)
child 510726 a44934afe25e7ac1fcccc5ca63b90406a29fbee2
push id10547
push userffxbld-merge
push dateMon, 21 Jan 2019 13:03:58 +0000
treeherdermozilla-beta@24ec1916bffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone66.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 autoland to mozilla-central. a=merge
taskcluster/docker/debian7-build/Dockerfile
taskcluster/scripts/misc/minidump_stackwalk.sh
--- a/.flake8
+++ b/.flake8
@@ -18,8 +18,9 @@ exclude =
     memory/moz.configure,
     mobile/android/*.configure,
     node_modules,
     security/nss/,
     testing/mochitest/pywebsocket,
     tools/lint/test/files,
     build/build-infer/build-infer.py,
     tools/infer/test/*.configure,    tools/infer/test/*.configure,
+    tools/crashreporter/*.configure,
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1751,21 +1751,22 @@ pref("app.normandy.shieldLearnMoreUrl", 
 pref("app.normandy.remotesettings.enabled", false);
 #ifdef MOZ_DATA_REPORTING
 pref("app.shield.optoutstudies.enabled", true);
 #else
 pref("app.shield.optoutstudies.enabled", false);
 #endif
 
 // Multi-lingual preferences
+#ifdef RELEASE_OR_BETA
+pref("intl.multilingual.enabled", true);
+pref("intl.multilingual.downloadEnabled", true);
+#else
 pref("intl.multilingual.enabled", false);
 // AMO only serves language packs for release and beta versions.
-#ifdef RELEASE_OR_BETA
-pref("intl.multilingual.downloadEnabled", true);
-#else
 pref("intl.multilingual.downloadEnabled", false);
 #endif
 
 // Simulate conditions that will happen when the browser
 // is running with Fission enabled. This is meant to assist
 // development and testing of Fission.
 // The current simulated conditions are:
 // - Don't propagate events from subframes to JS child actors
--- a/browser/components/sessionstore/ContentSessionStore.jsm
+++ b/browser/components/sessionstore/ContentSessionStore.jsm
@@ -324,17 +324,18 @@ SessionHistoryListener.prototype.QueryIn
  *
  * Example:
  *   {scroll: "100,100", children: [null, null, {scroll: "200,200"}]}
  */
 class ScrollPositionListener extends Handler {
   constructor(store) {
     super(store);
 
-    ssu.addDynamicFrameFilteredListener(this.mm, "scroll", this, false);
+    ssu.addDynamicFrameFilteredListener(this.mm, "mozvisualscroll", this,
+                                        /* capture */ false, /* system group */ true);
     this.stateChangeNotifier.addObserver(this);
   }
 
   handleEvent() {
     this.messageQueue.push("scroll", () => this.collect());
   }
 
   onPageLoadCompleted() {
--- a/browser/components/sessionstore/test/content.js
+++ b/browser/components/sessionstore/test/content.js
@@ -119,34 +119,36 @@ addMessageListener("ss-test:setUsePrivat
   sendAsyncMessage("ss-test:setUsePrivateBrowsing");
 });
 
 addMessageListener("ss-test:getScrollPosition", function(msg) {
   let frame = content;
   if (msg.data.hasOwnProperty("frame")) {
     frame = content.frames[msg.data.frame];
   }
-  let {scrollX: x, scrollY: y} = frame;
-  sendAsyncMessage("ss-test:getScrollPosition", {x, y});
+  let x = {}, y = {};
+  frame.windowUtils.getVisualViewportOffset(x, y);
+  sendAsyncMessage("ss-test:getScrollPosition", {x: x.value, y: y.value});
 });
 
 addMessageListener("ss-test:setScrollPosition", function(msg) {
   let frame = content;
   let {x, y} = msg.data;
   if (msg.data.hasOwnProperty("frame")) {
     frame = content.frames[msg.data.frame];
   }
   frame.scrollTo(x, y);
 
-  frame.addEventListener("scroll", function onScroll(event) {
-    if (frame.document == event.target) {
-      frame.removeEventListener("scroll", onScroll);
+  frame.addEventListener("mozvisualscroll", function onScroll(event) {
+    if (frame.document.ownerGlobal.visualViewport == event.target) {
+      frame.removeEventListener("mozvisualscroll", onScroll,
+                                { mozSystemGroup: true });
       sendAsyncMessage("ss-test:setScrollPosition");
     }
-  });
+  }, { mozSystemGroup: true });
 });
 
 addMessageListener("ss-test:click", function({data}) {
   content.document.getElementById(data.id).click();
   sendAsyncMessage("ss-test:click");
 });
 
 addEventListener("load", function(event) {
--- a/build/moz.configure/bindgen.configure
+++ b/build/moz.configure/bindgen.configure
@@ -1,14 +1,48 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+# cbindgen is needed by the style system build.
+cbindgen = check_prog('CBINDGEN', ['cbindgen'], paths=toolchain_search_path,
+                      when=depends(build_project)
+                      (lambda build_project: build_project != 'js'))
+
+
+@depends_if(cbindgen)
+@checking('cbindgen version')
+@imports(_from='textwrap', _import='dedent')
+def cbindgen_version(cbindgen):
+    cbindgen_min_version = Version('0.6.7')
+
+    # cbindgen x.y.z
+    version = Version(check_cmd_output(cbindgen, '--version').strip().split(" ")[1])
+
+    if version < cbindgen_min_version:
+        die(dedent('''\
+        cbindgen version {} is too old. At least version {} is required.
+
+        Please update using 'cargo install cbindgen --force' or running
+        './mach bootstrap', after removing the existing executable located at
+        {}.
+        '''.format(version, cbindgen_min_version, cbindgen)))
+
+    return version
+
+
+# Bindgen can use rustfmt to format Rust file, but it's not required.
+js_option(env='RUSTFMT', nargs=1, help='Path to the rustfmt program')
+
+rustfmt = check_prog('RUSTFMT', ['rustfmt'], paths=toolchain_search_path,
+                     input='RUSTFMT', allow_missing=True)
+
+
 # We support setting up the appropriate options for bindgen via setting
 # LLVM_CONFIG or by providing explicit configure options.  The Windows
 # installer of LLVM/Clang doesn't provide llvm-config, so we need both
 # methods to support all of our tier-1 platforms.
 @depends(host)
 @imports('which')
 @imports('os')
 @imports('subprocess')
--- a/build/moz.configure/pkg.configure
+++ b/build/moz.configure/pkg.configure
@@ -1,19 +1,19 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 
-@depends('--enable-compile-environment')
-def pkg_config(compile_env):
-    if compile_env:
-        return ('pkg-config',)
+@depends(toolchain_prefix, when=compile_environment)
+def pkg_config(prefixes):
+    return tuple('{}pkg-config'.format(p)
+                 for p in (prefixes or ()) + ('',))
 
 
 pkg_config = check_prog('PKG_CONFIG', pkg_config, allow_missing=True)
 
 
 @depends_if(pkg_config)
 @checking('for pkg-config version')
 @imports('subprocess')
--- a/build/moz.configure/rust.configure
+++ b/build/moz.configure/rust.configure
@@ -1,30 +1,23 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 
-@imports(_from='os.path', _import='expanduser')
-def add_rustup_path(what):
-    # rustup installs rustc/cargo into ~/.cargo/bin by default,
-    # so look there if the binaries aren't in $PATH.
-    return [what, os.path.join(expanduser('~/.cargo/bin'), what)]
-
-
 # Rust is required by `rust_compiler` below. We allow_missing here
 # to propagate failures to the better error message there.
 js_option(env='RUSTC', nargs=1, help='Path to the rust compiler')
 js_option(env='CARGO', nargs=1, help='Path to the Cargo package manager')
 
-rustc = check_prog('RUSTC', add_rustup_path('rustc'),
+rustc = check_prog('RUSTC', ['rustc'], paths=toolchain_search_path,
                    input='RUSTC', allow_missing=True)
-cargo = check_prog('CARGO', add_rustup_path('cargo'),
+cargo = check_prog('CARGO', ['cargo'], paths=toolchain_search_path,
                    input='CARGO', allow_missing=True)
 
 
 @depends_if(rustc)
 @checking('rustc version', lambda info: info.version)
 def rustc_info(rustc):
     out = check_cmd_output(rustc, '--version', '--verbose').splitlines()
     info = dict((s.strip() for s in line.split(':', 1)) for line in out[1:])
@@ -260,17 +253,17 @@ set_config('RUST_TARGET_ENV_NAME', rust_
 set_config('RUSTC_COMMIT', depends(rustc_info)(lambda i: i.commit))
 
 # Until we remove all the other Rust checks in old-configure.
 add_old_configure_assignment('RUSTC', rustc)
 
 # Rustdoc is required by Rust tests below.
 js_option(env='RUSTDOC', nargs=1, help='Path to the rustdoc program')
 
-rustdoc = check_prog('RUSTDOC', add_rustup_path('rustdoc'),
+rustdoc = check_prog('RUSTDOC', ['rustdoc'], paths=toolchain_search_path,
                      input='RUSTDOC', allow_missing=True)
 
 # This option is separate from --enable-tests because Rust tests are particularly
 # expensive in terms of compile time (especially for code in libxul).
 option('--enable-rust-tests',
        help='Enable building and running of Rust tests during `make check`')
 
 
@@ -278,49 +271,16 @@ option('--enable-rust-tests',
 def rust_tests(enable_rust_tests, rustdoc):
     if enable_rust_tests and not rustdoc:
         die('--enable-rust-tests requires rustdoc')
     return bool(enable_rust_tests)
 
 
 set_config('MOZ_RUST_TESTS', rust_tests)
 
-# cbindgen is needed by the style system build.
-cbindgen = check_prog('CBINDGEN', add_rustup_path('cbindgen'), paths=toolchain_search_path,
-                      when=depends(build_project)
-                      (lambda build_project: build_project != 'js'))
-
-
-@depends_if(cbindgen)
-@checking('cbindgen version')
-@imports(_from='textwrap', _import='dedent')
-def cbindgen_version(cbindgen):
-    cbindgen_min_version = Version('0.6.7')
-
-    # cbindgen x.y.z
-    version = Version(check_cmd_output(cbindgen, '--version').strip().split(" ")[1])
-
-    if version < cbindgen_min_version:
-        die(dedent('''\
-        cbindgen version {} is too old. At least version {} is required.
-
-        Please update using 'cargo install cbindgen --force' or running
-        './mach bootstrap', after removing the existing executable located at
-        {}.
-        '''.format(version, cbindgen_min_version, cbindgen)))
-
-    return version
-
-
-# Bindgen can use rustfmt to format Rust file, but it's not required.
-js_option(env='RUSTFMT', nargs=1, help='Path to the rustfmt program')
-
-rustfmt = check_prog('RUSTFMT', add_rustup_path('rustfmt'),
-                     input='RUSTFMT', allow_missing=True)
-
 js_option(env='WIN64_LINK', nargs=1, help='Path to link.exe that targets win64')
 js_option(env='WIN64_LIB', nargs=1, help='Paths to libraries for the win64 linker')
 
 set_config('WIN64_LINK', depends('WIN64_LINK')(lambda x: x))
 set_config('WIN64_LIB', depends('WIN64_LIB')(lambda x: x))
 
 
 @depends(target, rustc_info, c_compiler, 'WIN64_LINK', 'WIN64_LIB')
--- a/build/moz.configure/toolchain.configure
+++ b/build/moz.configure/toolchain.configure
@@ -62,36 +62,16 @@ yasm = check_prog('YASM', ['yasm'], allo
 def yasm_version(yasm):
     version = check_cmd_output(
         yasm, '--version',
         onerror=lambda: die('Failed to get yasm version.')
     ).splitlines()[0].split()[1]
     return Version(version)
 
 
-@depends_if(yasm_version)
-def yasm_major_version(yasm_version):
-    return str(yasm_version.major)
-
-
-@depends_if(yasm_version)
-def yasm_minor_version(yasm_version):
-    return str(yasm_version.minor)
-
-
-set_config('YASM_MAJOR_VERSION', yasm_major_version)
-set_config('YASM_MINOR_VERSION', yasm_minor_version)
-# Until we move all the yasm consumers out of old-configure.
-# bug 1257904
-add_old_configure_assignment('_YASM_MAJOR_VERSION',
-                             yasm_version.major)
-add_old_configure_assignment('_YASM_MINOR_VERSION',
-                             yasm_version.minor)
-
-
 @depends(yasm, target)
 def yasm_asflags(yasm, target):
     if yasm:
         asflags = {
             ('OSX', 'x86'): ['-f', 'macho32'],
             ('OSX', 'x86_64'): ['-f', 'macho64'],
             ('WINNT', 'x86'): ['-f', 'win32'],
             ('WINNT', 'x86_64'): ['-f', 'x64'],
@@ -215,22 +195,42 @@ with only_when(target_is_osx):
     # MacOS SDK
     # =========
 
     option('--with-macos-sdk', env='MACOS_SDK_DIR', nargs=1,
            help='Location of platform SDK to use')
 
     @depends_if('--with-macos-sdk')
     @imports(_from='os.path', _import='isdir')
+    @imports(_from='plistlib', _import='readPlist')
     def macos_sdk(value):
+        sdk_min_version = Version('10.11')
+        sdk_max_version = Version('10.13')
+
         if not isdir(value[0]):
             die('SDK not found in %s. When using --with-macos-sdk, you must specify a '
                 'valid SDK. SDKs are installed when the optional cross-development '
                 'tools are selected during the Xcode/Developer Tools installation.'
                 % value[0])
+        obj = readPlist(os.path.join(value[0], 'SDKSettings.plist'))
+        if not obj:
+            die('Error parsing SDKSettings.plist in the SDK directory: %s' % value[0])
+        if 'Version' not in obj:
+            die('Error finding Version information in SDKSettings.plist from the SDK: %s' % value[0])
+        version = Version(obj['Version'])
+        if version < sdk_min_version:
+            die('SDK version "%s" is too old. Please upgrade to at least %s. '
+                'You may need to point to it using --with-macos-sdk=<path> in your '
+                'mozconfig. Various SDK versions are available from '
+                'https://github.com/phracker/MacOSX-SDKs' % (version, sdk_min_version))
+        if version > sdk_max_version:
+            die('SDK version "%s" is unsupported. Please downgrade to version '
+                '%s. You may need to point to it using --with-macos-sdk=<path> in '
+                'your mozconfig. Various SDK versions are available from '
+                'https://github.com/phracker/MacOSX-SDKs' % (version, sdk_max_version))
         return value[0]
 
     set_config('MACOS_SDK_DIR', macos_sdk)
 
     with only_when(cross_compiling):
         option('--with-macos-private-frameworks',
                env="MACOS_PRIVATE_FRAMEWORKS_DIR", nargs=1,
                help='Location of private frameworks to use')
@@ -789,16 +789,20 @@ def toolchain_search_path_for(host_or_ta
         mozbuild_state_dir = environ.get('MOZBUILD_STATE_PATH',
                                          os.path.expanduser(os.path.join('~', '.mozbuild')))
         bootstrap_clang_path = os.path.join(mozbuild_state_dir, 'clang', 'bin')
         result.append(bootstrap_clang_path)
 
         bootstrap_cbindgen_path = os.path.join(mozbuild_state_dir, 'cbindgen')
         result.append(bootstrap_cbindgen_path)
 
+        # Also add the rustup install directory for cargo/rustc.
+        rustup_path = os.path.expanduser(os.path.join('~', '.cargo', 'bin'))
+        result.append(rustup_path)
+
         return result
     return toolchain_search_path
 
 
 toolchain_search_path = toolchain_search_path_for(target)
 host_toolchain_search_path = toolchain_search_path_for(host)
 
 
--- a/build/moz.configure/windows.configure
+++ b/build/moz.configure/windows.configure
@@ -151,25 +151,16 @@ def valid_windows_sdk_dir(compiler, wind
     return namespace(
         path=sdk.path,
         include=sdk.include,
         lib=sdk.lib,
         version=biggest_version,
     )
 
 
-add_old_configure_assignment(
-    'WINDOWSSDKDIR',
-    valid_windows_sdk_dir.path)
-add_old_configure_assignment(
-    'MOZ_WINSDK_MAXVER',
-    depends(valid_windows_sdk_dir)(
-        lambda x: '0x%04X0000' % x.version if x else None))
-
-
 @imports(_from='mozbuild.shellutil', _import='quote')
 def valid_ucrt_sdk_dir_result(value):
     if value:
         return '%s in %s' % (value.version, quote(value.path))
 
 
 @depends(windows_sdk_dir, 'WINDOWSSDKDIR', c_compiler)
 @checking('for Universal CRT SDK', valid_ucrt_sdk_dir_result)
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -5618,39 +5618,22 @@ nsDocShell::SetTitle(const nsAString& aT
   if (mOSHE && mLoadType != LOAD_BYPASS_HISTORY &&
       mLoadType != LOAD_ERROR_PAGE) {
     mOSHE->SetTitle(mTitle);
   }
 
   return NS_OK;
 }
 
-nsresult nsDocShell::GetCurScrollPos(int32_t aScrollOrientation,
-                                     int32_t* aCurPos) {
-  NS_ENSURE_ARG_POINTER(aCurPos);
-
-  nsIScrollableFrame* sf = GetRootScrollFrame();
-  if (!sf) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsPoint pt = sf->GetScrollPosition();
-
-  switch (aScrollOrientation) {
-    case ScrollOrientation_X:
-      *aCurPos = pt.x;
-      return NS_OK;
-
-    case ScrollOrientation_Y:
-      *aCurPos = pt.y;
-      return NS_OK;
-
-    default:
-      NS_ENSURE_TRUE(false, NS_ERROR_INVALID_ARG);
-  }
+nsPoint nsDocShell::GetCurScrollPos() {
+  nsPoint scrollPos;
+  if (nsIScrollableFrame* sf = GetRootScrollFrame()) {
+    scrollPos = sf->GetVisualViewportOffset();
+  }
+  return scrollPos;
 }
 
 nsresult nsDocShell::SetCurScrollPosEx(int32_t aCurHorizontalPos,
                                        int32_t aCurVerticalPos) {
   nsIScrollableFrame* sf = GetRootScrollFrame();
   NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
 
   nsIScrollableFrame::ScrollMode scrollMode = nsIScrollableFrame::INSTANT;
@@ -9150,19 +9133,17 @@ nsresult nsDocShell::InternalLoad(nsDocS
     // short-circuited loads.
     bool doShortCircuitedLoad =
         (historyNavBetweenSameDoc && mOSHE != aLoadState->SHEntry()) ||
         (!aLoadState->SHEntry() && !aLoadState->PostDataStream() &&
          sameExceptHashes && newURIHasRef);
 
     if (doShortCircuitedLoad) {
       // Save the position of the scrollers.
-      nscoord cx = 0, cy = 0;
-      GetCurScrollPos(ScrollOrientation_X, &cx);
-      GetCurScrollPos(ScrollOrientation_Y, &cy);
+      nsPoint scrollPos = GetCurScrollPos();
 
       // Reset mLoadType to its original value once we exit this block,
       // because this short-circuited load might have started after a
       // normal, network load, and we don't want to clobber its load type.
       // See bug 737307.
       AutoRestore<uint32_t> loadTypeResetter(mLoadType);
 
       // If a non-short-circuit load (i.e., a network load) is pending,
@@ -9215,17 +9196,17 @@ nsresult nsDocShell::InternalLoad(nsDocS
                newURIPrincipalToInherit, mLoadType, true, true, true);
 
       nsCOMPtr<nsIInputStream> postData;
       uint32_t cacheKey = 0;
 
       bool scrollRestorationIsManual = false;
       if (mOSHE) {
         /* save current position of scroller(s) (bug 59774) */
-        mOSHE->SetScrollPosition(cx, cy);
+        mOSHE->SetScrollPosition(scrollPos.x, scrollPos.y);
         scrollRestorationIsManual = mOSHE->GetScrollRestorationIsManual();
         // Get the postdata and page ident from the current page, if
         // the new load is being done via normal means.  Note that
         // "normal means" can be checked for just by checking for
         // LOAD_CMD_NORMAL, given the loadType and allowScroll check
         // above -- it filters out some LOAD_CMD_NORMAL cases that we
         // wouldn't want here.
         if (aLoadState->LoadType() & LOAD_CMD_NORMAL) {
@@ -11052,20 +11033,18 @@ nsDocShell::AddState(JS::Handle<JS::Valu
   NS_ENSURE_TRUE(mOSHE, NS_ERROR_FAILURE);
   nsCOMPtr<nsISHEntry> oldOSHE = mOSHE;
 
   mLoadType = LOAD_PUSHSTATE;
 
   nsCOMPtr<nsISHEntry> newSHEntry;
   if (!aReplace) {
     // Save the current scroll position (bug 590573).
-    nscoord cx = 0, cy = 0;
-    GetCurScrollPos(ScrollOrientation_X, &cx);
-    GetCurScrollPos(ScrollOrientation_Y, &cy);
-    mOSHE->SetScrollPosition(cx, cy);
+    nsPoint scrollPos = GetCurScrollPos();
+    mOSHE->SetScrollPosition(scrollPos.x, scrollPos.y);
 
     bool scrollRestorationIsManual = mOSHE->GetScrollRestorationIsManual();
 
     // Since we're not changing which page we have loaded, pass
     // true for aCloneChildren.
     rv = AddToSessionHistory(newURI, nullptr,
                              document->NodePrincipal(),  // triggeringPrincipal
                              nullptr, true, getter_AddRefs(newSHEntry));
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -861,19 +861,19 @@ class nsDocShell final : public nsDocLoa
   nsresult CheckLoadingPermissions();
   nsresult PersistLayoutHistoryState();
   nsresult LoadHistoryEntry(nsISHEntry* aEntry, uint32_t aLoadType);
   nsresult SetBaseUrlForWyciwyg(nsIContentViewer* aContentViewer);
   nsresult GetHttpChannel(nsIChannel* aChannel, nsIHttpChannel** aReturn);
   nsresult ConfirmRepost(bool* aRepost);
   nsresult GetPromptAndStringBundle(nsIPrompt** aPrompt,
                                     nsIStringBundle** aStringBundle);
-  nsresult GetCurScrollPos(int32_t aScrollOrientation, int32_t* aCurPos);
   nsresult SetCurScrollPosEx(int32_t aCurHorizontalPos,
                              int32_t aCurVerticalPos);
+  nsPoint GetCurScrollPos();
 
   already_AddRefed<mozilla::dom::ChildSHistory> GetRootSessionHistory();
 
   inline bool UseErrorPages() {
     return (mObserveErrorPages ? sUseErrorPages : mUseErrorPages);
   }
 
   bool CSSErrorReportingEnabled() const { return mCSSErrorReportingEnabled; }
--- a/docshell/base/timeline/TimelineConsumers.cpp
+++ b/docshell/base/timeline/TimelineConsumers.cpp
@@ -29,17 +29,20 @@ StaticRefPtr<TimelineConsumers> Timeline
 // in these cases.
 bool TimelineConsumers::sInShutdown = false;
 
 already_AddRefed<TimelineConsumers> TimelineConsumers::Get() {
   // Using this class is not supported yet for other processes other than
   // parent or content. To avoid accidental checks to methods like `IsEmpty`,
   // which would probably always be true in those cases, assert here.
   // Remember, there will be different singletons available to each process.
-  MOZ_ASSERT(XRE_IsContentProcess() || XRE_IsParentProcess());
+
+  // TODO: we have to avoid calling this function in socket process.
+  MOZ_ASSERT(XRE_IsContentProcess() || XRE_IsParentProcess() ||
+             XRE_IsSocketProcess());
 
   // If we are shutting down, don't bother doing anything. Note: we can only
   // know whether or not we're in shutdown if we're instantiated.
   if (sInShutdown) {
     return nullptr;
   }
 
   // Note: We don't simply check `sInstance` for null-ness here, since otherwise
--- a/docshell/shistory/nsISHEntry.idl
+++ b/docshell/shistory/nsISHEntry.idl
@@ -221,17 +221,20 @@ interface nsISHEntry : nsISupports
 
     /**
      * When an entry is serving is within nsISHistory's array of entries, this
      * property specifies if it should persist. If not it will be replaced by
      * new additions to the list.
      */
     [infallible] attribute boolean persist;
 
-    /** Set/Get scrollers' position in anchored pages */
+    /**
+     * Set/Get the visual viewport scroll position if session history is
+     * changed through anchor navigation or pushState.
+     */
     void setScrollPosition(in long x, in long y);
     void getScrollPosition(out long x, out long y);
 
     /**
      * Saved position and dimensions of the content viewer; we must adjust the
      * root view's widget accordingly if this has changed when the presentation
      * is restored.
      */
--- a/dom/base/CustomElementRegistry.cpp
+++ b/dom/base/CustomElementRegistry.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/CustomElementRegistry.h"
 
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/dom/CustomElementRegistryBinding.h"
 #include "mozilla/dom/HTMLElementBinding.h"
+#include "mozilla/dom/ShadowIncludingTreeIterator.h"
 #include "mozilla/dom/XULElementBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/WebComponentsBinding.h"
 #include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/CustomEvent.h"
 #include "mozilla/dom/ShadowRoot.h"
 #include "nsHTMLTags.h"
 #include "jsapi.h"
@@ -552,18 +553,16 @@ namespace {
 
 class CandidateFinder {
  public:
   CandidateFinder(nsTHashtable<nsRefPtrHashKey<nsIWeakReference>>& aCandidates,
                   Document* aDoc);
   nsTArray<nsCOMPtr<Element>> OrderedCandidates();
 
  private:
-  bool Traverse(Element* aRoot, nsTArray<nsCOMPtr<Element>>& aOrderedElements);
-
   nsCOMPtr<Document> mDoc;
   nsInterfaceHashtable<nsPtrHashKey<Element>, Element> mCandidates;
 };
 
 CandidateFinder::CandidateFinder(
     nsTHashtable<nsRefPtrHashKey<nsIWeakReference>>& aCandidates,
     Document* aDoc)
     : mDoc(aDoc), mCandidates(aCandidates.Count()) {
@@ -585,57 +584,34 @@ nsTArray<nsCOMPtr<Element>> CandidateFin
     for (auto iter = mCandidates.Iter(); !iter.Done(); iter.Next()) {
       nsTArray<nsCOMPtr<Element>> rval({std::move(iter.Data())});
       iter.Remove();
       return rval;
     }
   }
 
   nsTArray<nsCOMPtr<Element>> orderedElements(mCandidates.Count());
-  for (Element* child = mDoc->GetFirstElementChild(); child;
-       child = child->GetNextElementSibling()) {
-    if (!Traverse(child, orderedElements)) {
-      break;
+  for (nsINode* node : ShadowIncludingTreeIterator(*mDoc)) {
+    Element* element = Element::FromNode(node);
+    if (!element) {
+      continue;
+    }
+
+    nsCOMPtr<Element> elem;
+    if (mCandidates.Remove(element, getter_AddRefs(elem))) {
+      orderedElements.AppendElement(std::move(elem));
+      if (mCandidates.Count() == 0) {
+        break;
+      }
     }
   }
 
   return orderedElements;
 }
 
-bool CandidateFinder::Traverse(Element* aRoot,
-                               nsTArray<nsCOMPtr<Element>>& aOrderedElements) {
-  nsCOMPtr<Element> elem;
-  if (mCandidates.Remove(aRoot, getter_AddRefs(elem))) {
-    aOrderedElements.AppendElement(std::move(elem));
-    if (mCandidates.Count() == 0) {
-      return false;
-    }
-  }
-
-  if (ShadowRoot* root = aRoot->GetShadowRoot()) {
-    // First iterate the children of the shadow root if aRoot is a shadow host.
-    for (Element* child = root->GetFirstElementChild(); child;
-         child = child->GetNextElementSibling()) {
-      if (!Traverse(child, aOrderedElements)) {
-        return false;
-      }
-    }
-  }
-
-  // Iterate the explicit children of aRoot.
-  for (Element* child = aRoot->GetFirstElementChild(); child;
-       child = child->GetNextElementSibling()) {
-    if (!Traverse(child, aOrderedElements)) {
-      return false;
-    }
-  }
-
-  return true;
-}
-
 }  // namespace
 
 void CustomElementRegistry::UpgradeCandidates(
     nsAtom* aKey, CustomElementDefinition* aDefinition, ErrorResult& aRv) {
   DocGroup* docGroup = mWindow->GetDocGroup();
   if (!docGroup) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return;
@@ -1000,48 +976,38 @@ void CustomElementRegistry::SetElementCr
     return;
   }
 
   RefPtr<CustomElementCreationCallback> callback = &aCallback;
   mElementCreationCallbacks.Put(nameAtom, callback.forget());
   return;
 }
 
-static void TryUpgrade(nsINode& aNode) {
-  Element* element = aNode.IsElement() ? aNode.AsElement() : nullptr;
-  if (element) {
+void CustomElementRegistry::Upgrade(nsINode& aRoot) {
+  for (nsINode* node : ShadowIncludingTreeIterator(aRoot)) {
+    Element* element = Element::FromNode(node);
+    if (!element) {
+      continue;
+    }
+
     CustomElementData* ceData = element->GetCustomElementData();
     if (ceData) {
       NodeInfo* nodeInfo = element->NodeInfo();
       nsAtom* typeAtom = ceData->GetCustomElementType();
       CustomElementDefinition* definition =
           nsContentUtils::LookupCustomElementDefinition(
               nodeInfo->GetDocument(), nodeInfo->NameAtom(),
               nodeInfo->NamespaceID(), typeAtom);
       if (definition) {
         nsContentUtils::EnqueueUpgradeReaction(element, definition);
       }
     }
-
-    if (ShadowRoot* root = element->GetShadowRoot()) {
-      for (Element* child = root->GetFirstElementChild(); child;
-           child = child->GetNextElementSibling()) {
-        TryUpgrade(*child);
-      }
-    }
-  }
-
-  for (Element* child = aNode.GetFirstElementChild(); child;
-       child = child->GetNextElementSibling()) {
-    TryUpgrade(*child);
   }
 }
 
-void CustomElementRegistry::Upgrade(nsINode& aRoot) { TryUpgrade(aRoot); }
-
 void CustomElementRegistry::Get(JSContext* aCx, const nsAString& aName,
                                 JS::MutableHandle<JS::Value> aRetVal) {
   RefPtr<nsAtom> nameAtom(NS_Atomize(aName));
   CustomElementDefinition* data = mCustomDefinitions.GetWeak(nameAtom);
 
   if (!data) {
     aRetVal.setUndefined();
     return;
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -70,16 +70,17 @@
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/FeaturePolicy.h"
 #include "mozilla/dom/FramingChecker.h"
 #include "mozilla/dom/HTMLSharedElement.h"
 #include "mozilla/dom/Navigator.h"
 #include "mozilla/dom/Performance.h"
 #include "mozilla/dom/ServiceWorkerContainer.h"
 #include "mozilla/dom/ScriptLoader.h"
+#include "mozilla/dom/ShadowIncludingTreeIterator.h"
 #include "mozilla/dom/StyleSheetList.h"
 #include "mozilla/dom/SVGUseElement.h"
 #include "nsGenericHTMLElement.h"
 #include "mozilla/dom/CDATASection.h"
 #include "mozilla/dom/ProcessingInstruction.h"
 #include "nsDOMString.h"
 #include "nsNodeUtils.h"
 #include "nsLayoutUtils.h"  // for GetFrameForPoint
@@ -3542,31 +3543,31 @@ void Document::TryChannelCharset(nsIChan
         return;
       } else if (aExecutor && !charsetVal.IsEmpty()) {
         aExecutor->ComplainAboutBogusProtocolCharset(this);
       }
     }
   }
 }
 
-static inline void AssertNoStaleServoDataIn(const nsINode& aSubtreeRoot) {
+static inline void AssertNoStaleServoDataIn(nsINode& aSubtreeRoot) {
 #ifdef DEBUG
-  for (const nsINode* node = &aSubtreeRoot; node;
-       node = node->GetNextNode(&aSubtreeRoot)) {
+  for (nsINode* node : ShadowIncludingTreeIterator(aSubtreeRoot)) {
     const Element* element = Element::FromNode(node);
     if (!element) {
       continue;
     }
     MOZ_ASSERT(!element->HasServoData());
-    if (auto* shadow = element->GetShadowRoot()) {
-      AssertNoStaleServoDataIn(*shadow);
-    }
     if (nsXBLBinding* binding = element->GetXBLBinding()) {
       if (nsXBLBinding* bindingWithContent = binding->GetBindingWithContent()) {
         nsIContent* content = bindingWithContent->GetAnonymousContent();
+        // Need to do this instead of just AssertNoStaleServoDataIn(*content),
+        // because the parent of the children of the <content> element isn't the
+        // <content> element, but the bound element, and that confuses
+        // GetNextNode a lot.
         MOZ_ASSERT(!content->AsElement()->HasServoData());
         for (nsINode* child = content->GetFirstChild(); child;
              child = child->GetNextSibling()) {
           AssertNoStaleServoDataIn(*child);
         }
       }
     }
   }
@@ -3576,17 +3577,17 @@ static inline void AssertNoStaleServoDat
 already_AddRefed<nsIPresShell> Document::CreateShell(
     nsPresContext* aContext, nsViewManager* aViewManager,
     UniquePtr<ServoStyleSet> aStyleSet) {
   NS_ASSERTION(!mPresShell, "We have a presshell already!");
 
   NS_ENSURE_FALSE(GetBFCacheEntry(), nullptr);
 
   FillStyleSet(aStyleSet.get());
-  AssertNoStaleServoDataIn(static_cast<nsINode&>(*this));
+  AssertNoStaleServoDataIn(*this);
 
   RefPtr<PresShell> shell = new PresShell;
   // Note: we don't hold a ref to the shell (it holds a ref to us)
   mPresShell = shell;
   shell->Init(this, aContext, aViewManager, std::move(aStyleSet));
 
   // Make sure to never paint if we belong to an invisible DocShell.
   nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
@@ -3699,17 +3700,17 @@ void Document::DeleteShell() {
   MarkUserFontSetDirty();
 
   nsIPresShell* oldShell = mPresShell;
   mPresShell = nullptr;
   UpdateFrameRequestCallbackSchedulingState(oldShell);
   mStyleSetFilled = false;
 
   ClearStaleServoData();
-  AssertNoStaleServoDataIn(static_cast<nsINode&>(*this));
+  AssertNoStaleServoDataIn(*this);
 }
 
 void Document::SetBFCacheEntry(nsIBFCacheEntry* aEntry) {
   MOZ_ASSERT(IsBFCachingAllowed() || !aEntry, "You should have checked!");
 
   if (mPresShell) {
     if (aEntry) {
       mPresShell->StopObservingRefreshDriver();
@@ -11686,16 +11687,26 @@ void Document::MaybeNotifyAutoplayBlocke
   // This event is used to notify front-end side that we've blocked autoplay,
   // so front-end side should show blocking icon as well.
   RefPtr<AsyncEventDispatcher> asyncDispatcher = new AsyncEventDispatcher(
       topLevelDoc, NS_LITERAL_STRING("GloballyAutoplayBlocked"),
       CanBubble::eYes, ChromeOnlyDispatch::eYes);
   asyncDispatcher->PostDOMEvent();
 }
 
+void Document::ClearUserGestureActivation() {
+  Document* doc = this;
+  while (doc) {
+    MOZ_LOG(gUserInteractionPRLog, LogLevel::Debug,
+          ("Reset user activation flag for document %p.", this));
+    doc->mUserGestureActivated = false;
+    doc = doc->GetSameTypeParentDocument();
+  }
+}
+
 void Document::SetDocTreeHadAudibleMedia() {
   Document* topLevelDoc = GetTopLevelContentDocument();
   if (!topLevelDoc) {
     return;
   }
 
   topLevelDoc->mDocTreeHadAudibleMedia = true;
 }
--- a/dom/base/Document.h
+++ b/dom/base/Document.h
@@ -3290,16 +3290,19 @@ class Document : public nsINode,
   // This should be called when this document receives events which are likely
   // to be user interaction with the document, rather than the byproduct of
   // interaction with the browser (i.e. a keypress to scroll the view port,
   // keyboard shortcuts, etc). This is used to decide whether we should
   // permit autoplay audible media. This also gesture activates all other
   // content documents in this tab.
   void NotifyUserGestureActivation();
 
+  // This function is used for mochitest only.
+  void ClearUserGestureActivation();
+
   // Return true if NotifyUserGestureActivation() has been called on any
   // document in the document tree.
   bool HasBeenUserGestureActivated();
 
   // This document is a WebExtension page, it might be a background page, a
   // popup, a visible tab, a visible iframe ...e.t.c.
   bool IsExtensionPage() const;
 
new file mode 100644
--- /dev/null
+++ b/dom/base/ShadowIncludingTreeIterator.h
@@ -0,0 +1,113 @@
+/* -*- 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/. */
+
+/**
+ * Implementation of
+ * https://dom.spec.whatwg.org/#concept-shadow-including-tree-order in iterator
+ * form.  This can and should be used to avoid recursion on the stack and lots
+ * of function calls during shadow-including tree iteration.
+ */
+
+#ifndef mozilla_dom_ShadowIncludingTreeIterator_h
+#define mozilla_dom_ShadowIncludingTreeIterator_h
+
+#include "nsINode.h"
+#include "nsTArray.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/ShadowRoot.h"
+
+namespace mozilla {
+namespace dom {
+
+class ShadowIncludingTreeIterator {
+ public:
+  /**
+   * Initialize an iterator with aRoot.  After that it can be iterated with a
+   * range-based for loop.  At the moment, that's the only supported form of use
+   * for this iterator.
+   */
+  explicit ShadowIncludingTreeIterator(nsINode& aRoot) : mCurrent(&aRoot) {
+    mRoots.AppendElement(&aRoot);
+  }
+
+#ifdef DEBUG
+  ~ShadowIncludingTreeIterator() {
+    MOZ_ASSERT(
+        !mMutationGuard.Mutated(0),
+        "Don't mutate the DOM while using a ShadowIncludingTreeIterator");
+  }
+#endif  // DEBUG
+
+  // Basic support for range-based for loops.  This will modify the iterator as
+  // it goes.
+  ShadowIncludingTreeIterator& begin() { return *this; }
+
+  ShadowIncludingTreeIterator end() { return ShadowIncludingTreeIterator(); }
+
+  bool operator!=(const ShadowIncludingTreeIterator& aOther) {
+    return mCurrent != aOther.mCurrent;
+  }
+
+  void operator++() { Next(); }
+
+  nsINode* operator*() { return mCurrent; }
+
+ private:
+  // Constructor used only for end() to represent a drained iterator.
+  ShadowIncludingTreeIterator() : mCurrent(nullptr) {}
+
+  void Next() {
+    MOZ_ASSERT(mCurrent, "Don't call Next() after we have no current node");
+
+    // We walk shadow roots immediately after their shadow host.
+    if (Element* element = Element::FromNode(mCurrent)) {
+      if (ShadowRoot* shadowRoot = element->GetShadowRoot()) {
+        mCurrent = shadowRoot;
+        mRoots.AppendElement(shadowRoot);
+        return;
+      }
+    }
+
+    mCurrent = mCurrent->GetNextNode(mRoots.LastElement());
+    while (!mCurrent) {
+      // Nothing left under this root.  Keep trying to pop the stack until we
+      // find a node or run out of stack.
+      nsINode* root = mRoots.PopLastElement();
+      if (mRoots.IsEmpty()) {
+        // No more roots to step out of; we're done.  mCurrent is already set to
+        // null.
+        return;
+      }
+      mCurrent =
+          ShadowRoot::FromNode(root)->Host()->GetNextNode(mRoots.LastElement());
+    }
+  }
+
+  // The current node we're at.
+  nsINode* mCurrent;
+
+  // Stack of roots that we're inside of right now.  An empty stack can only
+  // happen when mCurrent is null (and hence we are done iterating).
+  //
+  // The default array size here is picked based on gut feeling.  We want at
+  // least 1, since we will always add something to it in our constructor.
+  // Having a few more entries probably makes sense, because this is commonly
+  // used in cases when we know we have custom elements, and hence likely have
+  // shadow DOMs.  But the exact value "4" was just picked because it sounded
+  // not too big, not too small.  Feel free to replace it with something else
+  // based on actual data.
+  AutoTArray<nsINode*, 4> mRoots;
+
+#ifdef DEBUG
+  // Make sure no one mutates the DOM while we're walking over it.
+  nsMutationGuard mMutationGuard;
+#endif  // DEBUG
+};
+
+}  // namespace dom
+}  // namespace mozilla
+
+#endif  // mozilla_dom_ShadowIncludingTreeIterator_h
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -215,16 +215,17 @@ EXPORTS.mozilla.dom += [
     'Pose.h',
     'PostMessageEvent.h',
     'ProcessMessageManager.h',
     'ResponsiveImageSelector.h',
     'SameProcessMessageQueue.h',
     'ScreenLuminance.h',
     'ScreenOrientation.h',
     'Selection.h',
+    'ShadowIncludingTreeIterator.h',
     'ShadowRoot.h',
     'StructuredCloneBlob.h',
     'StructuredCloneHolder.h',
     'StructuredCloneTags.h',
     'StructuredCloneTester.h',
     'StyleSheetList.h',
     'SubtleCrypto.h',
     'SyncMessageSender.h',
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -1427,16 +1427,35 @@ nsDOMWindowUtils::GetVisualViewportOffse
   nsPoint offset = presShell->GetVisualViewportOffsetRelativeToLayoutViewport();
   *aOffsetX = nsPresContext::AppUnitsToFloatCSSPixels(offset.x);
   *aOffsetY = nsPresContext::AppUnitsToFloatCSSPixels(offset.y);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsDOMWindowUtils::GetVisualViewportOffset(int32_t* aOffsetX,
+                                          int32_t* aOffsetY) {
+  *aOffsetX = 0;
+  *aOffsetY = 0;
+
+  nsCOMPtr<Document> doc = GetDocument();
+  NS_ENSURE_STATE(doc);
+
+  nsIPresShell* presShell = doc->GetShell();
+  NS_ENSURE_TRUE(presShell, NS_ERROR_NOT_AVAILABLE);
+
+  nsPoint offset = presShell->GetVisualViewportOffset();
+  *aOffsetX = nsPresContext::AppUnitsToIntCSSPixels(offset.x);
+  *aOffsetY = nsPresContext::AppUnitsToIntCSSPixels(offset.y);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDOMWindowUtils::GetScrollbarSize(bool aFlushLayout, int32_t* aWidth,
                                    int32_t* aHeight) {
   *aWidth = 0;
   *aHeight = 0;
 
   nsCOMPtr<Document> doc = GetDocument();
   NS_ENSURE_STATE(doc);
 
--- a/dom/base/test/test_window_define_nonconfigurable.html
+++ b/dom/base/test/test_window_define_nonconfigurable.html
@@ -5,73 +5,105 @@ https://bugzilla.mozilla.org/show_bug.cg
 -->
 <head>
   <meta charset="utf-8">
   <title>Test for Bug 1107443</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript">
 
+  var {AppConstants} = SpecialPowers.Cu.import("resource://gre/modules/AppConstants.jsm", {});
+
   /**
    * Test for Bug 1107443, modified when it was backed out in bug 1329323.
    * This is now testing the _current_ behavior, not the desired one; expect
    * failures in this test and needing to update it when bug 1329324 is
    * fixed.
    */
   var retval = Object.defineProperty(window, "nosuchprop",
                                      { value: 5, configurable: false });
-  is(retval, false,
-     "Should return false when 'failing' to define non-configurable property via Object.defineProperty.")
+  if (AppConstants.RELEASE_OR_BETA) {
+    todo_is(retval, false,
+            "Should return false when 'failing' to define non-configurable property via Object.defineProperty.")
+  } else {
+    is(retval, false,
+       "Should return false when 'failing' to define non-configurable property via Object.defineProperty.")
+  }
   var desc = Object.getOwnPropertyDescriptor(window, "nosuchprop");
   is(typeof(desc), "object", "Should have a property 'nosuchprop' now");
-  is(desc.configurable, true,
-     "Property 'nosuchprop' should be configurable");
+  if (AppConstants.RELEASE_OR_BETA) {
+    todo_is(desc.configurable, true,
+            "Property 'nosuchprop' should be configurable");
+  } else {
+    is(desc.configurable, true,
+       "Property 'nosuchprop' should be configurable");
+  }
   is(desc.writable, false, "Property 'nosuchprop' should be readonly");
   is(desc.value, 5, "Property 'nosuchprop' should have the right value");
 
   retval = Object.defineProperty(window, "nosuchprop2", { value: 6 });
   is(retval, window,
      "Should return object when succesfully defining 'nosuchprop2'");
   desc = Object.getOwnPropertyDescriptor(window, "nosuchprop2");
   is(typeof(desc), "object", "Should have a property 'nosuchprop2' now");
-  is(desc.configurable, true,
-     "Property 'nosuchprop2' should be configurable");
+  if (AppConstants.RELEASE_OR_BETA) {
+    todo_is(desc.configurable, true,
+            "Property 'nosuchprop2' should be configurable");
+  } else {
+    is(desc.configurable, true,
+       "Property 'nosuchprop2' should be configurable");
+  }
   is(desc.writable, false, "Property 'nosuchprop2' should be readonly");
   is(desc.value, 6, "Property 'nosuchprop2' should have the right value");
 
   retval = Object.defineProperty(window, "nosuchprop3",
                                  { value: 7, configurable: true });
   is(retval, window,
      "Should return object when succesfully defining 'nosuchprop3'");
   desc = Object.getOwnPropertyDescriptor(window, "nosuchprop3");
   is(typeof(desc), "object", "Should have a property 'nosuchprop3' now");
   is(desc.configurable, true,
           "Property 'nosuchprop3' should be configurable");
   is(desc.writable, false, "Property 'nosuchprop3' should be readonly");
   is(desc.value, 7, "Property 'nosuchprop3' should have the right value");
 
   retval = Reflect.defineProperty(window, "nosuchprop4",
                                   { value: 8, configurable: false });
-  is(retval, false,
-     "Should not be able to Reflect.defineProperty if non-configurable");
+  if (AppConstants.RELEASE_OR_BETA) {
+    todo_is(retval, false,
+            "Should not be able to Reflect.defineProperty if non-configurable");
+  } else {
+    is(retval, false,
+       "Should not be able to Reflect.defineProperty if non-configurable");
+  }
   desc = Object.getOwnPropertyDescriptor(window, "nosuchprop4");
   is(typeof(desc), "object", "Should have a property 'nosuchprop4' now");
-  is(desc.configurable, true,
-     "Property 'nosuchprop4' should be configurable");
+  if (AppConstants.RELEASE_OR_BETA) {
+    todo_is(desc.configurable, true,
+            "Property 'nosuchprop4' should be configurable");
+  } else {
+    is(desc.configurable, true,
+       "Property 'nosuchprop4' should be configurable");
+  }
   is(desc.writable, false, "Property 'nosuchprop4' should be readonly");
   is(desc.value, 8, "Property 'nosuchprop4' should have the right value");
 
   retval = Reflect.defineProperty(window, "nosuchprop5",
                                   { value: 9 });
   is(retval, true,
      "Should be able to Reflect.defineProperty with default configurability");
   desc = Object.getOwnPropertyDescriptor(window, "nosuchprop5");
   is(typeof(desc), "object", "Should have a property 'nosuchprop5' now");
-  is(desc.configurable, true,
-     "Property 'nosuchprop5' should be configurable");
+  if (AppConstants.RELEASE_OR_BETA) {
+    todo_is(desc.configurable, true,
+            "Property 'nosuchprop5' should be configurable");
+  } else {
+    is(desc.configurable, true,
+       "Property 'nosuchprop5' should be configurable");
+  }
   is(desc.writable, false, "Property 'nosuchprop5' should be readonly");
   is(desc.value, 9, "Property 'nosuchprop5' should have the right value");
 
   retval = Reflect.defineProperty(window, "nosuchprop6",
                                   { value: 10, configurable: true });
   is(retval, true,
      "Should be able to Reflect.defineProperty if configurable");
   desc = Object.getOwnPropertyDescriptor(window, "nosuchprop6");
--- a/dom/chrome-webidl/MatchPattern.webidl
+++ b/dom/chrome-webidl/MatchPattern.webidl
@@ -52,16 +52,22 @@ interface MatchPattern {
 
   /**
    * Returns true if this pattern will match any host which would be matched
    * by the given pattern.
    */
   boolean subsumes(MatchPattern pattern);
 
   /**
+   * Returns true if this pattern will match any host which would be matched
+   * by the given pattern, ignoring the scheme.
+   */
+  boolean subsumesDomain(MatchPattern pattern);
+
+  /**
    * Returns true if there is any host which would be matched by both this
    * pattern and the given pattern.
    */
   boolean overlaps(MatchPattern pattern);
 
   /**
    * The match pattern string represented by this pattern.
    */
@@ -94,16 +100,22 @@ interface MatchPatternSet {
   boolean matchesCookie(Cookie cookie);
 
   /**
    * Returns true if any sub-pattern subsumes the given pattern.
    */
   boolean subsumes(MatchPattern pattern);
 
   /**
+   * Returns true if any sub-pattern subsumes the given pattern,
+   * ignoring any of the schemes in the patterns.
+   */
+  boolean subsumesDomain(MatchPattern pattern);
+
+  /**
    * Returns true if any sub-pattern overlaps the given pattern.
    */
   boolean overlaps(MatchPattern pattern);
 
   /**
    * Returns true if any sub-pattern overlaps any sub-pattern the given
    * pattern set.
    */
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -3183,16 +3183,28 @@ already_AddRefed<DOMMediaStream> HTMLMed
   if (!stream) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   return stream.forget();
 }
 
+RefPtr<GenericNonExclusivePromise> HTMLMediaElement::GetAllowedToPlayPromise() {
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!mOutputStreams.IsEmpty(),
+             "This method should only be called during stream capturing!");
+  if (AutoplayPolicy::IsAllowedToPlay(*this)) {
+    AUTOPLAY_LOG("MediaElement %p has allowed to play, resolve promise", this);
+    return GenericNonExclusivePromise::CreateAndResolve(true, __func__);
+  }
+  AUTOPLAY_LOG("create allow-to-play promise for MediaElement %p", this);
+  return mAllowedToPlayPromise.Ensure(__func__);
+}
+
 already_AddRefed<DOMMediaStream> HTMLMediaElement::MozCaptureStream(
     ErrorResult& aRv) {
   MediaStreamGraph::GraphDriverType graphDriverType =
       HasAudio() ? MediaStreamGraph::AUDIO_THREAD_DRIVER
                  : MediaStreamGraph::SYSTEM_THREAD_DRIVER;
 
   nsPIDOMWindowInner* window = OwnerDoc()->GetInnerWindow();
   if (!window) {
@@ -3477,16 +3489,18 @@ HTMLMediaElement::~HTMLMediaElement() {
   mShutdownObserver->Unsubscribe();
 
   if (mVideoFrameContainer) {
     mVideoFrameContainer->ForgetElement();
   }
   UnregisterActivityObserver();
 
   mSetCDMRequest.DisconnectIfExists();
+  mAllowedToPlayPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
+
   if (mDecoder) {
     ShutdownDecoder();
   }
   if (mProgressTimer) {
     StopProgress();
   }
   if (mVideoDecodeSuspendTimer) {
     mVideoDecodeSuspendTimer->Cancel();
@@ -3629,16 +3643,17 @@ already_AddRefed<Promise> HTMLMediaEleme
 
   UpdateHadAudibleAutoplayState();
 
   const bool handlingUserInput = EventStateManager::IsHandlingUserInput();
   mPendingPlayPromises.AppendElement(promise);
 
   if (AutoplayPolicy::IsAllowedToPlay(*this)) {
     AUTOPLAY_LOG("allow MediaElement %p to play", this);
+    mAllowedToPlayPromise.ResolveIfExists(true, __func__);
     PlayInternal(handlingUserInput);
     UpdateCustomPolicyAfterPlayed();
   } else {
     AUTOPLAY_LOG("reject MediaElement %p to play", this);
     AsyncRejectPendingPlayPromises(NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR);
   }
   return promise.forget();
 }
@@ -5556,16 +5571,17 @@ void HTMLMediaElement::CheckAutoplayData
   }
 
   UpdateHadAudibleAutoplayState();
   if (!AutoplayPolicy::IsAllowedToPlay(*this)) {
     DispatchEventsWhenPlayWasNotAllowed();
     return;
   }
 
+  mAllowedToPlayPromise.ResolveIfExists(true, __func__);
   mPaused = false;
   // We changed mPaused which can affect AddRemoveSelfReference
   AddRemoveSelfReference();
   UpdateSrcMediaStreamPlaying();
   UpdateAudioChannelPlayingState();
 
   if (mDecoder) {
     SetPlayedOrSeeked(true);
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -718,16 +718,21 @@ class HTMLMediaElement : public nsGeneri
                                       ErrorResult& aRv);
   // Get the sink id of the device that audio is being played. Initial value is
   // empty and the default device is being used.
   void GetSinkId(nsString& aSinkId) {
     MOZ_ASSERT(NS_IsMainThread());
     aSinkId = mSink.first();
   }
 
+  // This is used to notify MediaElementAudioSourceNode that media element is
+  // allowed to play when media element is used as a source for web audio, so
+  // that we can start AudioContext if it was not allowed to start.
+  RefPtr<GenericNonExclusivePromise> GetAllowedToPlayPromise();
+
  protected:
   virtual ~HTMLMediaElement();
 
   class AudioChannelAgentCallback;
   class ChannelLoader;
   class ErrorSink;
   class MediaLoadListener;
   class MediaStreamTrackListener;
@@ -1653,16 +1658,21 @@ class HTMLMediaElement : public nsGeneri
   // Note this flag is false when the element is in a phase after creation and
   // before attaching to the DOM tree.
   bool mUnboundFromTree = false;
 
   // True if the autoplay media was blocked because it hadn't loaded metadata
   // yet.
   bool mBlockedAsWithoutMetadata = false;
 
+  // This promise is used to notify MediaElementAudioSourceNode that media
+  // element is allowed to play when MediaElement is used as a source for web
+  // audio.
+  MozPromiseHolder<GenericNonExclusivePromise> mAllowedToPlayPromise;
+
  public:
   // Helper class to measure times for MSE telemetry stats
   class TimeDurationAccumulator {
    public:
     TimeDurationAccumulator() : mCount(0) {}
     void Start() {
       if (IsStarted()) {
         return;
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -843,16 +843,21 @@ interface nsIDOMWindowUtils : nsISupport
  
   /**
    * Returns the offset of the window's visual viewport relative to the
    * layout viewport.
    */
   void getVisualViewportOffsetRelativeToLayoutViewport(out float aOffsetX,
                                                        out float aOffsetY);
 
+  /**
+   * Returns the scroll position of the window's visual viewport.
+   */
+  void getVisualViewportOffset(out long aOffsetX, out long aOffsetY);
+
   const long FLUSH_NONE = -1;
   const long FLUSH_STYLE = 0;
   const long FLUSH_LAYOUT = 1;
   const long FLUSH_DISPLAY = 2;
 
   /**
    * Returns true if a flush of the given type is needed.
    */
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -68,16 +68,17 @@
 #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"
+#include "mozilla/layers/SynchronousTask.h"  // for LaunchRDDProcess
 #include "mozilla/loader/ScriptCacheActors.h"
 #include "mozilla/net/NeckoChild.h"
 #include "mozilla/net/CookieServiceChild.h"
 #include "mozilla/net/CaptivePortalService.h"
 #include "mozilla/PerformanceMetricsCollector.h"
 #include "mozilla/PerformanceUtils.h"
 #include "mozilla/plugins/PluginInstanceParent.h"
 #include "mozilla/plugins/PluginModuleParent.h"
@@ -1111,16 +1112,32 @@ nsresult ContentChild::ProvideWindowComm
   MOZ_ASSERT_IF(NS_SUCCEEDED(rv), *aReturn);
   return rv;
 }
 
 void ContentChild::GetProcessName(nsAString& aName) const {
   aName.Assign(mProcessName);
 }
 
+void ContentChild::LaunchRDDProcess() {
+  SynchronousTask task("LaunchRDDProcess");
+  SystemGroup::Dispatch(
+      TaskCategory::Other,
+      NS_NewRunnableFunction("LaunchRDDProcess", [&task, this] {
+        AutoCompleteTask complete(&task);
+        nsresult rv;
+        Endpoint<PRemoteDecoderManagerChild> endpoint;
+        Unused << SendLaunchRDDProcess(&rv, &endpoint);
+        if (rv == NS_OK) {
+          RemoteDecoderManagerChild::InitForContent(std::move(endpoint));
+        }
+      }));
+  task.Wait();
+}
+
 bool ContentChild::IsAlive() const { return mIsAlive; }
 
 bool ContentChild::IsShuttingDown() const { return mShuttingDown; }
 
 void ContentChild::GetProcessName(nsACString& aName) const {
   aName.Assign(NS_ConvertUTF16toUTF8(mProcessName));
 }
 
@@ -1435,22 +1452,16 @@ mozilla::ipc::IPCResult ContentChild::Re
   for (const auto& tabChild : tabs) {
     if (tabChild->GetLayersId().IsValid()) {
       tabChild->ReinitRenderingForDeviceReset();
     }
   }
   return IPC_OK();
 }
 
-mozilla::ipc::IPCResult ContentChild::RecvInitRemoteDecoder(
-    Endpoint<PRemoteDecoderManagerChild>&& aRemoteManager) {
-  RemoteDecoderManagerChild::InitForContent(std::move(aRemoteManager));
-  return IPC_OK();
-}
-
 #if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
 extern "C" {
 CGError CGSSetDenyWindowServerConnections(bool);
 void CGSShutdownServerConnections();
 };
 
 static bool StartMacOSContentSandbox() {
   int sandboxLevel = GetEffectiveContentSandboxLevel();
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -128,16 +128,18 @@ class ContentChild final : public PConte
   const AppInfo& GetAppInfo() { return mAppInfo; }
 
   void SetProcessName(const nsAString& aName);
 
   void GetProcessName(nsAString& aName) const;
 
   void GetProcessName(nsACString& aName) const;
 
+  void LaunchRDDProcess();
+
 #if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
   void GetProfileDir(nsIFile** aProfileDir) const {
     *aProfileDir = mProfileDir;
     NS_IF_ADDREF(*aProfileDir);
   }
 
   void SetProfileDir(nsIFile* aProfileDir) { mProfileDir = aProfileDir; }
 #endif
@@ -183,19 +185,16 @@ class ContentChild final : public PConte
       Endpoint<PVRManagerChild>&& aVRBridge,
       Endpoint<PVideoDecoderManagerChild>&& aVideoManager,
       nsTArray<uint32_t>&& namespaces) override;
 
   virtual mozilla::ipc::IPCResult RecvAudioDefaultDeviceChange() override;
 
   mozilla::ipc::IPCResult RecvReinitRenderingForDeviceReset() override;
 
-  virtual mozilla::ipc::IPCResult RecvInitRemoteDecoder(
-      Endpoint<PRemoteDecoderManagerChild>&& aRemoteManager) override;
-
   virtual mozilla::ipc::IPCResult RecvSetProcessSandbox(
       const MaybeFileDesc& aBroker) override;
 
   virtual PBrowserChild* AllocPBrowserChild(const TabId& aTabId,
                                             const TabId& aSameTabGroupAs,
                                             const IPCTabContext& aContext,
                                             const uint32_t& aChromeFlags,
                                             const ContentParentId& aCpID,
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/DebugOnly.h"
 
 #include "base/basictypes.h"
 #include "base/shared_memory.h"
 
 #include "ContentParent.h"
+#include "ProcessUtils.h"
 #include "TabParent.h"
 
 #if defined(ANDROID) || defined(LINUX)
 #include <sys/time.h>
 #include <sys/resource.h>
 #endif
 
 #include "chrome/common/process_watcher.h"
@@ -1114,16 +1115,41 @@ mozilla::ipc::IPCResult ContentParent::R
   // pointer and just throw it away.
   uint32_t dummy = 0;
   if (!mozilla::plugins::SetupBridge(aPluginId, this, aRv, &dummy, aEndpoint)) {
     return IPC_FAIL(this, "SetupBridge failed");
   }
   return IPC_OK();
 }
 
+mozilla::ipc::IPCResult ContentParent::RecvLaunchRDDProcess(
+    nsresult* aRv, Endpoint<PRemoteDecoderManagerChild>* aEndpoint) {
+  *aRv = NS_OK;
+
+  if (XRE_IsParentProcess() &&
+      BrowserTabsRemoteAutostart() &&  // only do rdd process if e10s on
+      Preferences::GetBool("media.rdd-process.enabled", false)) {
+    RDDProcessManager* rdd = RDDProcessManager::Get();
+    if (rdd) {
+      rdd->LaunchRDDProcess();
+
+      bool rddOpened = rdd->CreateContentBridge(OtherPid(), aEndpoint);
+      MOZ_ASSERT(rddOpened);
+
+      if (!rddOpened) {
+        *aRv = NS_ERROR_NOT_AVAILABLE;
+      }
+    } else {
+      *aRv = NS_ERROR_NOT_AVAILABLE;
+    }
+  }
+
+  return IPC_OK();
+}
+
 /*static*/ TabParent* ContentParent::CreateBrowser(
     const TabContext& aContext, Element* aFrameElement,
     ContentParent* aOpenerContentParent, TabParent* aSameTabGroupAs,
     uint64_t aNextTabParentId) {
   AUTO_PROFILER_LABEL("ContentParent::CreateBrowser", OTHER);
 
   if (!sCanLaunchSubprocesses) {
     return nullptr;
@@ -2106,82 +2132,65 @@ void ContentParent::LaunchSubprocessInte
   char idStr[21];
   SprintfLiteral(idStr, "%" PRId64, static_cast<uint64_t>(mChildID));
   extraArgs.push_back(idStr);
   extraArgs.push_back(IsForBrowser() ? "-isForBrowser" : "-notForBrowser");
 
   // Prefs information is passed via anonymous shared memory to avoid bloating
   // the command line.
 
-  size_t prefMapSize;
-  auto prefMapHandle =
-      Preferences::EnsureSnapshot(&prefMapSize).ClonePlatformHandle();
-
-  // Serialize the early prefs.
-  nsAutoCStringN<1024> prefs;
-  Preferences::SerializePreferences(prefs);
-
-  // Set up the shared memory.
-  base::SharedMemory shm;
-  if (!shm.Create(prefs.Length())) {
-    NS_ERROR("failed to create shared memory in the parent");
+  SharedPreferenceSerializer prefSerializer;
+  if (!prefSerializer.SerializeToSharedMemory()) {
     MarkAsDead();
     earlyReject();
     return;
   }
-  if (!shm.Map(prefs.Length())) {
-    NS_ERROR("failed to map shared memory in the parent");
-    MarkAsDead();
-    earlyReject();
-    return;
-  }
-
-  // Copy the serialized prefs into the shared memory.
-  memcpy(static_cast<char*>(shm.memory()), prefs.get(), prefs.Length());
 
   // Register ContentParent as an observer for changes to any pref
   // whose prefix matches the empty string, i.e. all of them.  The
   // observation starts here in order to capture pref updates that
   // happen during async launch.
   Preferences::AddStrongObserver(this, "");
 
   // Formats a pointer or pointer-sized-integer as a string suitable for passing
   // in an arguments list.
   auto formatPtrArg = [](auto arg) {
     return nsPrintfCString("%zu", uintptr_t(arg));
   };
 
 #if defined(XP_WIN)
   // Record the handle as to-be-shared, and pass it via a command flag. This
   // works because Windows handles are system-wide.
-  HANDLE prefsHandle = shm.handle();
+  HANDLE prefsHandle = prefSerializer.GetSharedMemoryHandle();
   mSubprocess->AddHandleToShare(prefsHandle);
-  mSubprocess->AddHandleToShare(prefMapHandle.get());
+  mSubprocess->AddHandleToShare(prefSerializer.GetPrefMapHandle().get());
   extraArgs.push_back("-prefsHandle");
   extraArgs.push_back(formatPtrArg(prefsHandle).get());
   extraArgs.push_back("-prefMapHandle");
-  extraArgs.push_back(formatPtrArg(prefMapHandle.get()).get());
+  extraArgs.push_back(
+      formatPtrArg(prefSerializer.GetPrefMapHandle().get()).get());
 #else
   // In contrast, Unix fds are per-process. So remap the fd to a fixed one that
   // will be used in the child.
   // XXX: bug 1440207 is about improving how fixed fds are used.
   //
   // Note: on Android, AddFdToRemap() sets up the fd to be passed via a Parcel,
   // and the fixed fd isn't used. However, we still need to mark it for
   // remapping so it doesn't get closed in the child.
-  mSubprocess->AddFdToRemap(shm.handle().fd, kPrefsFileDescriptor);
-  mSubprocess->AddFdToRemap(prefMapHandle.get(), kPrefMapFileDescriptor);
+  mSubprocess->AddFdToRemap(prefSerializer.GetSharedMemoryHandle().fd,
+                            kPrefsFileDescriptor);
+  mSubprocess->AddFdToRemap(prefSerializer.GetPrefMapHandle().get(),
+                            kPrefMapFileDescriptor);
 #endif
 
   // Pass the lengths via command line flags.
   extraArgs.push_back("-prefsLen");
-  extraArgs.push_back(formatPtrArg(prefs.Length()).get());
-
+  extraArgs.push_back(formatPtrArg(prefSerializer.GetPrefLength()).get());
   extraArgs.push_back("-prefMapSize");
-  extraArgs.push_back(formatPtrArg(prefMapSize).get());
+  extraArgs.push_back(formatPtrArg(prefSerializer.GetPrefMapSize()).get());
 
   // Scheduler prefs need to be handled differently because the scheduler needs
   // to start up in the content process before the normal preferences service.
   nsPrintfCString schedulerPrefs = Scheduler::GetPrefs();
   extraArgs.push_back("-schedulerPrefs");
   extraArgs.push_back(schedulerPrefs.get());
 
   if (gSafeMode) {
@@ -2231,19 +2240,18 @@ void ContentParent::LaunchSubprocessInte
   // ContentParent then owns the GeckoChildProcessHost (and that
   // ownership is not exposed to the cycle collector).  Therefore,
   // this all stays alive until the promise is resolved or rejected.
 
   auto resolve = [self, this, aInitialPriority, isSync,
                   // Transfer ownership of RAII file descriptor/handle
                   // holders so that they won't be closed before the
                   // child can inherit them.
-                  shm = std::move(shm),
-                  prefMapHandle =
-                      std::move(prefMapHandle)](base::ProcessHandle handle) {
+                  prefSerializer =
+                      std::move(prefSerializer)](base::ProcessHandle handle) {
     AUTO_PROFILER_LABEL("ContentParent::LaunchSubprocess::resolve", OTHER);
     const auto launchResumeTS = TimeStamp::Now();
 
     base::ProcessId procId = base::GetProcId(handle);
     Open(mSubprocess->GetChannel(), procId);
 #ifdef MOZ_CODE_COVERAGE
     Unused << SendShareCodeCoverageMutex(
         CodeCoverageHandler::Get()->GetMutexHandle(procId));
@@ -2607,32 +2615,16 @@ void ContentParent::InitInternal(Process
   MOZ_ASSERT(opened);
 
   Unused << SendInitRendering(std::move(compositor), std::move(imageBridge),
                               std::move(vrBridge), std::move(videoManager),
                               namespaces);
 
   gpm->AddListener(this);
 
-  if (StaticPrefs::MediaRddProcessEnabled() && BrowserTabsRemoteAutostart()) {
-    RDDProcessManager* rdd = RDDProcessManager::Get();
-
-    Endpoint<PRemoteDecoderManagerChild> remoteManager;
-    bool rddOpened = rdd->CreateContentBridge(OtherPid(), &remoteManager);
-    MOZ_ASSERT(rddOpened);
-
-    if (rddOpened) {
-      // not using std::move here (like in SendInitRendering above) because
-      // clang-tidy says:
-      // Warning: Passing result of std::move() as a const reference
-      // argument; no move will actually happen
-      Unused << SendInitRemoteDecoder(remoteManager);
-    }
-  }
-
   nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance();
   if (sheetService) {
     // This looks like a lot of work, but in a normal browser session we just
     // send two loads.
     //
     // The URIs of the Gecko and Servo sheets should be the same, so it
     // shouldn't matter which we look at.
 
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -297,16 +297,19 @@ class ContentParent final : public PCont
       Endpoint<PPluginModuleParent>* aEndpoint) override;
 
   virtual mozilla::ipc::IPCResult RecvMaybeReloadPlugins() override;
 
   virtual mozilla::ipc::IPCResult RecvConnectPluginBridge(
       const uint32_t& aPluginId, nsresult* aRv,
       Endpoint<PPluginModuleParent>* aEndpoint) override;
 
+  virtual mozilla::ipc::IPCResult RecvLaunchRDDProcess(
+      nsresult* aRv, Endpoint<PRemoteDecoderManagerChild>* aEndpoint) override;
+
   virtual mozilla::ipc::IPCResult RecvUngrabPointer(
       const uint32_t& aTime) override;
 
   virtual mozilla::ipc::IPCResult RecvRemovePermission(
       const IPC::Principal& aPrincipal, const nsCString& aPermissionType,
       nsresult* aRv) override;
 
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ContentParent, nsIObserver)
--- a/dom/ipc/ContentProcess.cpp
+++ b/dom/ipc/ContentProcess.cpp
@@ -73,52 +73,29 @@ static void SetUpSandboxEnvironment() {
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
 
   SetTmpEnvironmentVariable(sandboxedContentTemp);
 }
 #endif
 
-#ifdef ANDROID
-static int gPrefsFd = -1;
-static int gPrefMapFd = -1;
-
-void SetPrefsFd(int aFd) { gPrefsFd = aFd; }
-
-void SetPrefMapFd(int aFd) { gPrefMapFd = aFd; }
-#endif
-
 bool ContentProcess::Init(int aArgc, char* aArgv[]) {
   Maybe<uint64_t> childID;
   Maybe<bool> isForBrowser;
-  Maybe<base::SharedMemoryHandle> prefsHandle;
-  Maybe<FileDescriptor> prefMapHandle;
-  Maybe<size_t> prefsLen;
-  Maybe<size_t> prefMapSize;
   Maybe<const char*> schedulerPrefs;
   Maybe<const char*> parentBuildID;
+  char* prefsHandle = nullptr;
+  char* prefMapHandle = nullptr;
+  char* prefsLen = nullptr;
+  char* prefMapSize = nullptr;
 #if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
   nsCOMPtr<nsIFile> profileDir;
 #endif
 
-  // Parses an arg containing a pointer-sized-integer.
-  auto parseUIntPtrArg = [](char*& aArg) {
-    // ContentParent uses %zu to print a word-sized unsigned integer. So
-    // even though strtoull() returns a long long int, it will fit in a
-    // uintptr_t.
-    return uintptr_t(strtoull(aArg, &aArg, 10));
-  };
-
-#ifdef XP_WIN
-  auto parseHandleArg = [&](char*& aArg) {
-    return HANDLE(parseUIntPtrArg(aArg));
-  };
-#endif
-
   for (int i = 1; i < aArgc; i++) {
     if (!aArgv[i]) {
       continue;
     }
 
     if (strcmp(aArgv[i], "-appdir") == 0) {
       if (++i == aArgc) {
         return false;
@@ -142,57 +119,34 @@ bool ContentProcess::Init(int aArgc, cha
     } else if (strcmp(aArgv[i], "-notForBrowser") == 0) {
       isForBrowser = Some(false);
 
 #ifdef XP_WIN
     } else if (strcmp(aArgv[i], "-prefsHandle") == 0) {
       if (++i == aArgc) {
         return false;
       }
-      char* str = aArgv[i];
-      prefsHandle = Some(parseHandleArg(str));
-      if (str[0] != '\0') {
-        return false;
-      }
-
+      prefsHandle = aArgv[i];
     } else if (strcmp(aArgv[i], "-prefMapHandle") == 0) {
       if (++i == aArgc) {
         return false;
       }
-      char* str = aArgv[i];
-      // The FileDescriptor constructor will clone this handle when constructed,
-      // so store it in a UniquePlatformHandle to make sure the original gets
-      // closed.
-      FileDescriptor::UniquePlatformHandle handle(parseHandleArg(str));
-      prefMapHandle.emplace(handle.get());
-      if (str[0] != '\0') {
-        return false;
-      }
+      prefMapHandle = aArgv[i];
 #endif
 
     } else if (strcmp(aArgv[i], "-prefsLen") == 0) {
       if (++i == aArgc) {
         return false;
       }
-      char* str = aArgv[i];
-      prefsLen = Some(parseUIntPtrArg(str));
-      if (str[0] != '\0') {
-        return false;
-      }
-
+      prefsLen = aArgv[i];
     } else if (strcmp(aArgv[i], "-prefMapSize") == 0) {
       if (++i == aArgc) {
         return false;
       }
-      char* str = aArgv[i];
-      prefMapSize = Some(parseUIntPtrArg(str));
-      if (str[0] != '\0') {
-        return false;
-      }
-
+      prefMapSize = aArgv[i];
     } else if (strcmp(aArgv[i], "-schedulerPrefs") == 0) {
       if (++i == aArgc) {
         return false;
       }
       schedulerPrefs = Some(aArgv[i]);
 
     } else if (strcmp(aArgv[i], "-safeMode") == 0) {
       gSafeMode = true;
@@ -213,64 +167,34 @@ bool ContentProcess::Init(int aArgc, cha
       if (NS_FAILED(rv) || NS_FAILED(profileDir->Exists(&flag)) || !flag) {
         NS_WARNING("Invalid profile directory passed to content process.");
         profileDir = nullptr;
       }
 #endif /* XP_MACOSX && MOZ_CONTENT_SANDBOX */
     }
   }
 
-#ifdef ANDROID
-  // Android is different; get the FD via gPrefsFd instead of a fixed fd.
-  MOZ_RELEASE_ASSERT(gPrefsFd != -1);
-  prefsHandle = Some(base::FileDescriptor(gPrefsFd, /* auto_close */ true));
-
-  FileDescriptor::UniquePlatformHandle handle(gPrefMapFd);
-  prefMapHandle.emplace(handle.get());
-#elif XP_UNIX
-  prefsHandle = Some(base::FileDescriptor(kPrefsFileDescriptor,
-                                          /* auto_close */ true));
-
-  // The FileDescriptor constructor will clone this handle when constructed,
-  // so store it in a UniquePlatformHandle to make sure the original gets
-  // closed.
-  FileDescriptor::UniquePlatformHandle handle(kPrefMapFileDescriptor);
-  prefMapHandle.emplace(handle.get());
-#endif
-
   // Did we find all the mandatory flags?
   if (childID.isNothing() || isForBrowser.isNothing() ||
-      prefsHandle.isNothing() || prefsLen.isNothing() ||
-      prefMapHandle.isNothing() || prefMapSize.isNothing() ||
       schedulerPrefs.isNothing() || parentBuildID.isNothing()) {
     return false;
   }
 
-  // Init the shared-memory base preference mapping first, so that only changed
-  // preferences wind up in heap memory.
-  Preferences::InitSnapshot(prefMapHandle.ref(), *prefMapSize);
-
-  // Set up early prefs from the shared memory.
-  base::SharedMemory shm;
-  if (!shm.SetHandle(*prefsHandle, /* read_only */ true)) {
-    NS_ERROR("failed to open shared memory in the child");
+  SharedPreferenceDeserializer deserializer;
+  if (!deserializer.DeserializeFromSharedMemory(prefsHandle, prefMapHandle,
+                                                prefsLen, prefMapSize)) {
     return false;
   }
-  if (!shm.Map(*prefsLen)) {
-    NS_ERROR("failed to map shared memory in the child");
-    return false;
-  }
-  Preferences::DeserializePreferences(static_cast<char*>(shm.memory()),
-                                      *prefsLen);
 
   Scheduler::SetPrefs(*schedulerPrefs);
 
   if (recordreplay::IsMiddleman()) {
     recordreplay::parent::InitializeMiddleman(aArgc, aArgv, ParentPid(),
-                                              *prefsHandle, *prefMapHandle);
+                                              deserializer.GetPrefsHandle(),
+                                              deserializer.GetPrefMapHandle());
   }
 
   mContent.Init(IOThreadChild::message_loop(), ParentPid(), *parentBuildID,
                 IOThreadChild::channel(), *childID, *isForBrowser);
 
   mXREEmbed.Start();
 #if (defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
   mContent.SetProfileDir(profileDir);
--- a/dom/ipc/ContentProcess.h
+++ b/dom/ipc/ContentProcess.h
@@ -40,19 +40,12 @@ class ContentProcess : public mozilla::i
 #if defined(XP_WIN)
   // This object initializes and configures COM.
   mozilla::mscom::MainThreadRuntime mCOMRuntime;
 #endif
 
   DISALLOW_EVIL_CONSTRUCTORS(ContentProcess);
 };
 
-#ifdef ANDROID
-// Android doesn't use -prefsHandle or -prefMapHandle. It gets those FDs
-// another way.
-void SetPrefsFd(int aFd);
-void SetPrefMapFd(int aFd);
-#endif
-
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // ifndef dom_tabs_ContentThread_h
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -55,32 +55,32 @@ include PluginTypes;
 include ProtocolTypes;
 include PBackgroundSharedTypes;
 include PContentPermission;
 include ServiceWorkerConfiguration;
 include GraphicsMessages;
 include MemoryReportTypes;
 include ClientIPCTypes;
 include HangTypes;
+include PrefsTypes;
 
 // 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";
 using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h";
-using struct mozilla::null_t from "ipc/IPCMessageUtils.h";
 using mozilla::a11y::IHandlerControlHolder from "mozilla/a11y/IPCTypes.h";
 using mozilla::dom::NativeThreadId from "mozilla/dom/TabMessageUtils.h";
 using mozilla::hal::ProcessPriority from "mozilla/HalTypes.h";
 using mozilla::gfx::IntSize from "mozilla/gfx/2D.h";
 using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h";
 using mozilla::dom::ContentParentId from "mozilla/dom/ipc/IdType.h";
 using mozilla::LayoutDeviceIntPoint from "Units.h";
 using struct LookAndFeelInt from "mozilla/widget/WidgetMessageUtils.h";
@@ -141,40 +141,16 @@ struct FontPatternListEntry {
 
 // Wrap the Font*ListEntry records in a union so the SetXPCOMProcessAttributes
 // message can pass an array of either type.
 union SystemFontListEntry {
     FontFamilyListEntry;
     FontPatternListEntry;
 };
 
-union PrefValue {
-  nsCString;
-  int32_t;
-  bool;
-};
-
-union MaybePrefValue {
-  PrefValue;
-  null_t;
-};
-
-// This serialization form mirrors that used in mozilla::Pref in
-// Preferences.cpp. The two should be kept in sync, e.g. if something is added
-// to one it should also be added to the other.
-//
-// Note: there is no need to pass the isSticky attribute because that's an
-// immutable attribute obtained from file at startup.
-struct Pref {
-  nsCString name;
-  bool isLocked;
-  MaybePrefValue defaultValue;
-  MaybePrefValue userValue;
-};
-
 struct DataStorageItem {
   nsCString key;
   nsCString value;
   DataStorageType type;
 };
 
 struct DataStorageEntry {
   DataStorageItem[] items;
@@ -430,18 +406,16 @@ child:
       Endpoint<PVideoDecoderManagerChild> video,
       uint32_t[] namespaces);
 
     async AudioDefaultDeviceChange();
 
     // Re-create the rendering stack for a device reset.
     async ReinitRenderingForDeviceReset();
 
-    async InitRemoteDecoder(Endpoint<PRemoteDecoderManagerChild> decoder);
-
     /**
      * Enable system-level sandboxing features, if available.  Can
      * usually only be performed zero or one times.  The child may
      * abnormally exit if this fails; the details are OS-specific.
      */
     async SetProcessSandbox(MaybeFileDesc aBroker);
 
     async RequestMemoryReport(uint32_t generation,
@@ -800,16 +774,20 @@ parent:
      * This call is used by asynchronous plugin instantiation to notify the
      * content parent that it is now safe to initiate the plugin bridge for
      * the specified plugin id. The endpoint for the content process part of the
      * bridge is returned.
      */
     sync ConnectPluginBridge(uint32_t aPluginId)
         returns (nsresult rv, Endpoint<PPluginModuleParent> aEndpoint);
 
+    // See Bug 1518344 - Investigate using async for PContent::LaunchRDDProcess
+    sync LaunchRDDProcess()
+        returns (nsresult rv, Endpoint<PRemoteDecoderManagerChild> aEndpoint);
+
     async PJavaScript();
 
     async PRemoteSpellcheckEngine();
 
     async InitCrashReporter(Shmem shmem, NativeThreadId tid);
 
     sync IsSecureURI(uint32_t aType, URIParams aURI, uint32_t aFlags,
                      OriginAttributes aOriginAttributes)
new file mode 100644
--- /dev/null
+++ b/dom/ipc/PrefsTypes.ipdlh
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+using struct mozilla::null_t from "ipc/IPCMessageUtils.h";
+
+namespace mozilla {
+namespace dom {
+
+union PrefValue {
+  nsCString;
+  int32_t;
+  bool;
+};
+
+union MaybePrefValue {
+  PrefValue;
+  null_t;
+};
+
+// This serialization form mirrors that used in mozilla::Pref in
+// Preferences.cpp. The two should be kept in sync, e.g. if something is added
+// to one it should also be added to the other.
+//
+// Note: there is no need to pass the isSticky attribute because that's an
+// immutable attribute obtained from file at startup.
+struct Pref {
+  nsCString name;
+  bool isLocked;
+  MaybePrefValue defaultValue;
+  MaybePrefValue userValue;
+};
+
+} // namespace dom
+} // namespace mozilla
--- a/dom/ipc/moz.build
+++ b/dom/ipc/moz.build
@@ -110,16 +110,17 @@ IPDL_SOURCES += [
     'PContentBridge.ipdl',
     'PContentPermission.ipdlh',
     'PContentPermissionRequest.ipdl',
     'PCycleCollectWithLogs.ipdl',
     'PFilePicker.ipdl',
     'PLoginReputation.ipdl',
     'PPluginWidget.ipdl',
     'PProcessHangMonitor.ipdl',
+    'PrefsTypes.ipdlh',
     'PTabContext.ipdlh',
     'PURLClassifier.ipdl',
     'PURLClassifierInfo.ipdlh',
     'PURLClassifierLocal.ipdl',
     'PWindowGlobal.ipdl',
     'ServiceWorkerConfiguration.ipdlh',
 ]
 
--- a/dom/media/ipc/RemoteDecoderModule.cpp
+++ b/dom/media/ipc/RemoteDecoderModule.cpp
@@ -1,39 +1,34 @@
 /* -*- 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 "RemoteDecoderModule.h"
 
 #include "base/thread.h"
+#include "mozilla/dom/ContentChild.h"  // for launching RDD w/ ContentChild
 #include "mozilla/layers/SynchronousTask.h"
 #include "mozilla/StaticPrefs.h"
 
 #ifdef MOZ_AV1
 #include "AOMDecoder.h"
 #endif
 #include "RemoteDecoderManagerChild.h"
 #include "RemoteMediaDataDecoder.h"
 #include "RemoteVideoDecoderChild.h"
 
 namespace mozilla {
 
 using base::Thread;
+using dom::ContentChild;
 using namespace ipc;
 using namespace layers;
 
-nsresult RemoteDecoderModule::Startup() {
-  if (!RemoteDecoderManagerChild::GetManagerThread()) {
-    return NS_ERROR_FAILURE;
-  }
-  return NS_OK;
-}
-
 bool RemoteDecoderModule::SupportsMimeType(
     const nsACString& aMimeType, DecoderDoctorDiagnostics* aDiagnostics) const {
   bool supports = false;
 
 #ifdef MOZ_AV1
   if (StaticPrefs::MediaAv1Enabled()) {
     supports |= AOMDecoder::IsAV1(aMimeType);
   }
@@ -41,16 +36,25 @@ bool RemoteDecoderModule::SupportsMimeTy
   MOZ_LOG(
       sPDMLog, LogLevel::Debug,
       ("Sandbox decoder %s requested type", supports ? "supports" : "rejects"));
   return supports;
 }
 
 already_AddRefed<MediaDataDecoder> RemoteDecoderModule::CreateVideoDecoder(
     const CreateDecoderParams& aParams) {
+  if (XRE_IsContentProcess()) {
+    ContentChild* contentChild = ContentChild::GetSingleton();
+    contentChild->LaunchRDDProcess();
+  }
+
+  if (!RemoteDecoderManagerChild::GetManagerThread()) {
+    return nullptr;
+  }
+
   RemoteVideoDecoderChild* child = new RemoteVideoDecoderChild();
   RefPtr<RemoteMediaDataDecoder> object = new RemoteMediaDataDecoder(
       child, RemoteDecoderManagerChild::GetManagerThread(),
       RemoteDecoderManagerChild::GetManagerAbstractThread());
 
   // (per Matt Woodrow) We can't use NS_DISPATCH_SYNC here since that
   // can spin the event loop while it waits.
   SynchronousTask task("InitIPDL");
--- a/dom/media/ipc/RemoteDecoderModule.h
+++ b/dom/media/ipc/RemoteDecoderModule.h
@@ -12,18 +12,16 @@ namespace mozilla {
 // A PDM implementation that creates a RemoteMediaDataDecoder (a
 // MediaDataDecoder) that proxies to a RemoteVideoDecoderChild.
 // A decoder child will talk to its respective decoder parent
 // (RemoteVideoDecoderParent) on the RDD process.
 class RemoteDecoderModule : public PlatformDecoderModule {
  public:
   RemoteDecoderModule() = default;
 
-  nsresult Startup() override;
-
   bool SupportsMimeType(const nsACString& aMimeType,
                         DecoderDoctorDiagnostics* aDiagnostics) const override;
 
   already_AddRefed<MediaDataDecoder> CreateVideoDecoder(
       const CreateDecoderParams& aParams) override;
 
   already_AddRefed<MediaDataDecoder> CreateAudioDecoder(
       const CreateDecoderParams& aParams) override {
--- a/dom/media/test/mochitest.ini
+++ b/dom/media/test/mochitest.ini
@@ -719,16 +719,18 @@ skip-if = android_version >= '23' # bug 
 [test_autoplay_policy_key_blacklist.html]
 skip-if = android_version >= '23' || (verify && debug && (os == 'win')) # bug 1424903
 [test_autoplay_policy_unmute_pauses.html]
 skip-if = android_version >= '23' # bug 1424903
 [test_autoplay_policy_play_before_loadedmetadata.html]
 skip-if = android_version >= '23' # bug 1424903
 [test_autoplay_policy_permission.html]
 skip-if = android_version >= '23' # bug 1424903
+[test_autoplay_policy_web_audio_mediaElementAudioSourceNode.html]
+skip-if = android_version >= '23' # bug 1424903
 [test_buffered.html]
 skip-if = android_version == '22' # bug 1308388, android(bug 1232305)
 [test_bug448534.html]
 [test_bug463162.xhtml]
 [test_bug465498.html]
 skip-if = toolkit == 'android' # android(bug 1232305)
 [test_bug495145.html]
 skip-if = toolkit == 'android' # android(bug 1232305)
new file mode 100644
--- /dev/null
+++ b/dom/media/test/test_autoplay_policy_web_audio_mediaElementAudioSourceNode.html
@@ -0,0 +1,104 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Autoplay policy test : use media element as source for web audio</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="text/javascript" src="manifest.js"></script>
+</head>
+<body>
+<script>
+
+/**
+ * This test is used to ensure blocked AudioContext would be resumed when the
+ * source media element of MediaElementAudioSouceNode which has been created and
+ * connected to destinationnode starts.
+ */
+
+SimpleTest.waitForExplicitFinish();
+
+(async function testResumeAudioContextWhenMediaElementSourceStarted() {
+  await setupTestPreferences();
+
+  info(`- create audio context -`);
+  createAudioContext();
+
+  info(`- AudioContext is not allowed to start in beginning -`);
+  await audioContextShouldBeBlocked();
+
+  info(`- create a source for web audio and start the source -`);
+  await useMediaElementAsSourceAndPlayMediaElement();
+
+  info(`- AudioContext should be allowed to start after MediaElementAudioSourceNode started -`);
+  await audioContextShouldBeAllowedToStart();
+
+  endTest();
+})();
+
+/**
+ * Test utility functions
+ */
+
+function setupTestPreferences() {
+  return SpecialPowers.pushPrefEnv({"set": [
+    ["media.autoplay.default", SpecialPowers.Ci.nsIAutoplay.BLOCKED],
+    ["media.autoplay.enabled.user-gestures-needed", true],
+    ["media.autoplay.block-webaudio", true],
+    ["media.autoplay.block-event.enabled", true],
+  ]});
+}
+
+function createAudioContext() {
+  window.ac = new AudioContext();
+
+  ac.allowedToStart = new Promise(resolve => {
+    ac.addEventListener("statechange", function() {
+      if (ac.state === "running") {
+        resolve();
+      }
+    }, {once: true});
+  });
+
+  ac.notAllowedToStart = new Promise(resolve => {
+    ac.addEventListener("blocked", async function() {
+      resolve();
+    }, {once: true});
+  });
+}
+
+async function audioContextShouldBeBlocked() {
+  await ac.notAllowedToStart;
+  is(ac.state, "suspended", `AudioContext is blocked.`);
+}
+
+async function useMediaElementAsSourceAndPlayMediaElement() {
+  let video = document.createElement('video');
+  video.src = "gizmo.mp4";
+
+  let source = ac.createMediaElementSource(video);
+  source.connect(ac.destination);
+  // simulate user gesture in order to start video.
+  SpecialPowers.wrap(document).notifyUserGestureActivation();
+  await playVideo(video);
+}
+
+async function playVideo(video) {
+  video.play();
+  await once(video, "play");
+  ok(true, `video started.`);
+  removeNodeAndSource(video);
+}
+
+async function audioContextShouldBeAllowedToStart() {
+  await ac.allowedToStart;
+  is(ac.state, "running", `AudioContext is allowed to start.`);
+}
+
+function endTest() {
+  // reset the activation flag in order not to interfere following test in the
+  // verify mode which would run the test using same document couple times.
+  SpecialPowers.wrap(document).clearUserGestureActivation();
+  SimpleTest.finish();
+}
+
+</script>
--- a/dom/media/webaudio/AudioBufferSourceNode.cpp
+++ b/dom/media/webaudio/AudioBufferSourceNode.cpp
@@ -700,17 +700,17 @@ void AudioBufferSourceNode::Start(double
     SendOffsetAndDurationParametersToStream(ns);
   }
 
   // Don't set parameter unnecessarily
   if (aWhen > 0.0) {
     ns->SetDoubleParameter(START, aWhen);
   }
 
-  Context()->NotifyScheduledSourceNodeStarted();
+  Context()->StartBlockedAudioContextIfAllowed();
 }
 
 void AudioBufferSourceNode::Start(double aWhen, ErrorResult& aRv) {
   Start(aWhen, 0 /* offset */, Optional<double>(), aRv);
 }
 
 void AudioBufferSourceNode::SendBufferParameterToStream(JSContext* aCx) {
   AudioNodeStream* ns = mStream;
--- a/dom/media/webaudio/AudioContext.cpp
+++ b/dom/media/webaudio/AudioContext.cpp
@@ -156,39 +156,38 @@ AudioContext::AudioContext(nsPIDOMWindow
       mWasEverAllowedToStart(false),
       mWasEverBlockedToStart(false),
       mWouldBeAllowedToStart(true) {
   bool mute = aWindow->AddAudioContext(this);
 
   // Note: AudioDestinationNode needs an AudioContext that must already be
   // bound to the window.
   const bool allowedToStart = AutoplayPolicy::IsAllowedToPlay(*this);
+  // If an AudioContext is not allowed to start, we would postpone its state
+  // transition from `suspended` to `running` until sites explicitly call
+  // AudioContext.resume() or AudioScheduledSourceNode.start().
+  if (!allowedToStart) {
+    AUTOPLAY_LOG("AudioContext %p is not allowed to start", this);
+    mSuspendCalled = true;
+    ReportBlocked();
+  }
   mDestination = new AudioDestinationNode(this, aIsOffline, allowedToStart,
                                           aNumberOfChannels, aLength);
 
   // The context can't be muted until it has a destination.
   if (mute) {
     Mute();
   }
 
-  // If an AudioContext is not allowed to start, we would postpone its state
-  // transition from `suspended` to `running` until sites explicitly call
-  // AudioContext.resume() or AudioScheduledSourceNode.start().
-  if (!allowedToStart) {
-    AUTOPLAY_LOG("AudioContext %p is not allowed to start", this);
-    SuspendInternal(nullptr);
-    ReportBlocked();
-  }
-
   UpdateAutoplayAssumptionStatus();
 
   FFTBlock::MainThreadInit();
 }
 
-void AudioContext::NotifyScheduledSourceNodeStarted() {
+void AudioContext::StartBlockedAudioContextIfAllowed() {
   MOZ_ASSERT(NS_IsMainThread());
   MaybeUpdateAutoplayTelemetry();
   // Only try to start AudioContext when AudioContext was not allowed to start.
   if (mWasAllowedToStart) {
     return;
   }
 
   const bool isAllowedToPlay = AutoplayPolicy::IsAllowedToPlay(*this);
--- a/dom/media/webaudio/AudioContext.h
+++ b/dom/media/webaudio/AudioContext.h
@@ -178,19 +178,19 @@ class AudioContext final : public DOMEve
   AudioListener* Listener();
 
   AudioContextState State() const { return mAudioContextState; }
 
   Worklet* GetAudioWorklet(ErrorResult& aRv);
 
   bool IsRunning() const;
 
-  // Called when an AudioScheduledSourceNode started, this method might resume
-  // the AudioContext if it was not allowed to start.
-  void NotifyScheduledSourceNodeStarted();
+  // Called when an AudioScheduledSourceNode started or the source node starts,
+  // this method might resume the AudioContext if it was not allowed to start.
+  void StartBlockedAudioContextIfAllowed();
 
   // Those three methods return a promise to content, that is resolved when an
   // (possibly long) operation is completed on the MSG (and possibly other)
   // thread(s). To avoid having to match the calls and asychronous result when
   // the operation is completed, we keep a reference to the promises on the main
   // thread, and then send the promises pointers down the MSG thread, as a void*
   // (to make it very clear that the pointer is to merely be treated as an ID).
   // When back on the main thread, we can resolve or reject the promise, by
--- a/dom/media/webaudio/ConstantSourceNode.cpp
+++ b/dom/media/webaudio/ConstantSourceNode.cpp
@@ -219,17 +219,17 @@ void ConstantSourceNode::Start(double aW
   if (!mStream) {
     return;
   }
 
   mStream->SetStreamTimeParameter(ConstantSourceNodeEngine::START, Context(),
                                   aWhen);
 
   MarkActive();
-  Context()->NotifyScheduledSourceNodeStarted();
+  Context()->StartBlockedAudioContextIfAllowed();
 }
 
 void ConstantSourceNode::Stop(double aWhen, ErrorResult& aRv) {
   if (!WebAudioUtils::IsTimeValid(aWhen)) {
     aRv.ThrowRangeError<MSG_VALUE_OUT_OF_RANGE>(NS_LITERAL_STRING("stop time"));
     return;
   }
 
--- a/dom/media/webaudio/MediaElementAudioSourceNode.cpp
+++ b/dom/media/webaudio/MediaElementAudioSourceNode.cpp
@@ -38,18 +38,40 @@ MediaElementAudioSourceNode::Create(
     return nullptr;
   }
 
   node->Init(stream, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
+  node->ListenForAllowedToPlay(aOptions);
   return node.forget();
 }
 
 JSObject* MediaElementAudioSourceNode::WrapObject(
     JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
   return MediaElementAudioSourceNode_Binding::Wrap(aCx, this, aGivenProto);
 }
 
+void MediaElementAudioSourceNode::ListenForAllowedToPlay(
+    const MediaElementAudioSourceOptions& aOptions) {
+  aOptions.mMediaElement->GetAllowedToPlayPromise()
+      ->Then(
+          AbstractMainThread(), __func__,
+          // Capture by reference to bypass the mozilla-refcounted-inside-lambda
+          // static analysis. We capture a non-owning reference so as to allow
+          // cycle collection of the node. The reference is cleared via
+          // DisconnectIfExists() from Destroy() when the node is collected.
+          [& self = *this]() {
+            self.Context()->StartBlockedAudioContextIfAllowed();
+            self.mAllowedToPlayRequest.Complete();
+          })
+      ->Track(mAllowedToPlayRequest);
+}
+
+void MediaElementAudioSourceNode::Destroy() {
+  mAllowedToPlayRequest.DisconnectIfExists();
+  MediaStreamAudioSourceNode::Destroy();
+}
+
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/media/webaudio/MediaElementAudioSourceNode.h
+++ b/dom/media/webaudio/MediaElementAudioSourceNode.h
@@ -39,14 +39,22 @@ class MediaElementAudioSourceNode final 
   }
 
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override {
     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   }
 
  private:
   explicit MediaElementAudioSourceNode(AudioContext* aContext);
+
+  void Destroy() override;
+
+  // If AudioContext was not allowed to start, we would try to start it when
+  // source starts.
+  void ListenForAllowedToPlay(const MediaElementAudioSourceOptions& aOptions);
+
+  MozPromiseRequestHolder<GenericNonExclusivePromise> mAllowedToPlayRequest;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif
--- a/dom/media/webaudio/MediaStreamAudioSourceNode.h
+++ b/dom/media/webaudio/MediaStreamAudioSourceNode.h
@@ -88,17 +88,17 @@ class MediaStreamAudioSourceNode
   void NotifyTrackRemoved(const RefPtr<MediaStreamTrack>& aTrack) override;
 
   // From PrincipalChangeObserver<MediaStreamTrack>.
   void PrincipalChanged(MediaStreamTrack* aMediaStreamTrack) override;
 
  protected:
   explicit MediaStreamAudioSourceNode(AudioContext* aContext);
   void Init(DOMMediaStream* aMediaStream, ErrorResult& aRv);
-  void Destroy();
+  virtual void Destroy();
   virtual ~MediaStreamAudioSourceNode();
 
  private:
   RefPtr<MediaInputPort> mInputPort;
   RefPtr<DOMMediaStream> mInputStream;
 
   // On construction we set this to the first audio track of mInputStream.
   RefPtr<MediaStreamTrack> mInputTrack;
--- a/dom/media/webaudio/OscillatorNode.cpp
+++ b/dom/media/webaudio/OscillatorNode.cpp
@@ -483,17 +483,17 @@ void OscillatorNode::Start(double aWhen,
     return;
   }
 
   // TODO: Perhaps we need to do more here.
   mStream->SetStreamTimeParameter(OscillatorNodeEngine::START, Context(),
                                   aWhen);
 
   MarkActive();
-  Context()->NotifyScheduledSourceNodeStarted();
+  Context()->StartBlockedAudioContextIfAllowed();
 }
 
 void OscillatorNode::Stop(double aWhen, ErrorResult& aRv) {
   if (!WebAudioUtils::IsTimeValid(aWhen)) {
     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return;
   }
 
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -498,16 +498,19 @@ partial interface Document {
   [ChromeOnly] readonly attribute boolean userHasInteracted;
 };
 
 // Extension to give chrome JS the ability to simulate activate the docuement
 // by user gesture.
 partial interface Document {
   [ChromeOnly]
   void notifyUserGestureActivation();
+  // For testing only.
+  [ChromeOnly]
+  void clearUserGestureActivation();
 };
 
 // Extension to give chrome JS the ability to set an event handler which is
 // called with certain events that happened while events were suppressed in the
 // document or one of its subdocuments.
 partial interface Document {
   [ChromeOnly]
   void setSuppressedEventListener(EventListener? aListener);
--- a/gfx/layers/apz/test/mochitest/test_group_touchevents.html
+++ b/gfx/layers/apz/test/mochitest/test_group_touchevents.html
@@ -8,18 +8,17 @@
   <script type="application/javascript" src="apz_test_utils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript">
 
 var basic_pan_prefs = getPrefs("TOUCH_EVENTS:PAN");
 
 var subtests = [
   // Simple tests to exercise basic panning behaviour
-  // The visual viewport isn't yet enabled by default and we want to test its events, too.
-  {"file": "helper_basic_pan.html", "prefs": basic_pan_prefs.concat([["dom.visualviewport.enabled", true]])},
+  {"file": "helper_basic_pan.html", "prefs": basic_pan_prefs},
   {"file": "helper_div_pan.html", "prefs": basic_pan_prefs},
   {"file": "helper_iframe_pan.html", "prefs": basic_pan_prefs},
 
   // Simple test to exercise touch-tapping behaviour
   {"file": "helper_tap.html"},
   // Tapping, but with a full-zoom applied
   {"file": "helper_tap_fullzoom.html"},
 
--- a/gfx/layers/apz/test/mochitest/test_group_zoom.html
+++ b/gfx/layers/apz/test/mochitest/test_group_zoom.html
@@ -34,18 +34,16 @@ var prefs = [
   ["dom.meta-viewport.enabled", true],
   // Pinch-zooming currently requires container scrolling (this requirement
   // will eventually be removed).
   ["layout.scroll.root-frame-containers", 1],
   // Retained displaylists don't work well with container scrolling, so
   // they too need to be disabled for now.
   ["layout.display-list.retain", false],
   ["layout.display-list.retain.chrome", false],
-  // The VisualViewport API currently isn't enabled by default.
-  ["dom.visualviewport.enabled", true],
 ];
 
 // Increase the tap timeouts so the double-tap is still detected in case of
 // random delays during testing.
 var doubletap_prefs = [
   ...prefs,
   ["ui.click_hold_context_menus.delay", 10000],
   ["apz.max_tap_time", 10000],
--- a/gfx/layers/apz/util/APZCCallbackHelper.cpp
+++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp
@@ -169,19 +169,21 @@ static ScreenMargin ScrollFrame(nsIConte
   // Scroll the window to the desired spot
   nsIScrollableFrame* sf =
       nsLayoutUtils::FindScrollableFrameFor(aRequest.GetScrollId());
   if (sf) {
     sf->ResetScrollInfoIfGeneration(aRequest.GetScrollGeneration());
     sf->SetScrollableByAPZ(!aRequest.IsScrollInfoLayer());
     if (sf->IsRootScrollFrameOfDocument()) {
       if (nsCOMPtr<nsIPresShell> shell = GetPresShell(aContent)) {
-        shell->SetVisualViewportOffset(
-            CSSPoint::ToAppUnits(aRequest.GetScrollOffset()),
-            shell->GetLayoutViewportOffset());
+        if (shell->SetVisualViewportOffset(
+                CSSPoint::ToAppUnits(aRequest.GetScrollOffset()),
+                shell->GetLayoutViewportOffset())) {
+          sf->MarkEverScrolled();
+        }
       }
     }
   }
   bool scrollUpdated = false;
   ScreenMargin displayPortMargins = aRequest.GetDisplayPortMargins();
   CSSPoint apzScrollOffset = aRequest.GetScrollOffset();
   CSSPoint actualScrollOffset = ScrollFrameTo(sf, aRequest, scrollUpdated);
 
--- a/gfx/layers/wr/WebRenderCommandBuilder.cpp
+++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp
@@ -1076,24 +1076,24 @@ static bool IsItemProbablyActive(nsDispl
   switch (aItem->GetType()) {
     case DisplayItemType::TYPE_TRANSFORM: {
       nsDisplayTransform* transformItem =
           static_cast<nsDisplayTransform*>(aItem);
       const Matrix4x4Flagged& t = transformItem->GetTransform();
       Matrix t2d;
       bool is2D = t.Is2D(&t2d);
       GP("active: %d\n", transformItem->MayBeAnimated(aDisplayListBuilder));
-      return transformItem->MayBeAnimated(aDisplayListBuilder) || !is2D ||
-             HasActiveChildren(*transformItem->GetChildren(),
-                               aDisplayListBuilder);
+      return transformItem->MayBeAnimated(aDisplayListBuilder, false) ||
+             !is2D || HasActiveChildren(*transformItem->GetChildren(),
+                                        aDisplayListBuilder);
     }
     case DisplayItemType::TYPE_OPACITY: {
       nsDisplayOpacity* opacityItem = static_cast<nsDisplayOpacity*>(aItem);
       bool active = opacityItem->NeedsActiveLayer(aDisplayListBuilder,
-                                                  opacityItem->Frame());
+                                                  opacityItem->Frame(), false);
       GP("active: %d\n", active);
       return active || HasActiveChildren(*opacityItem->GetChildren(),
                                          aDisplayListBuilder);
     }
     case DisplayItemType::TYPE_FOREIGN_OBJECT: {
       return true;
     }
     case DisplayItemType::TYPE_WRAP_LIST:
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -926,25 +926,16 @@ void gfxPlatform::Init() {
   }
   gPlatform->InitOMTPConfig();
 
   if (gfxConfig::IsEnabled(Feature::GPU_PROCESS)) {
     GPUProcessManager* gpu = GPUProcessManager::Get();
     gpu->LaunchGPUProcess();
   }
 
-  if (XRE_IsParentProcess() &&
-      BrowserTabsRemoteAutostart() &&  // only do rdd process if e10s on
-      Preferences::GetBool("media.rdd-process.enabled", false)) {
-    RDDProcessManager* rdd = RDDProcessManager::Get();
-    if (rdd) {
-      rdd->LaunchRDDProcess();
-    }
-  }
-
   gLastUsedFrameRate = ForceSoftwareVsync() ? GetSoftwareVsyncRate() : -1;
   auto updateFrameRateCallback = [](const GfxPrefValue& aValue) -> void {
     int32_t newRate = ForceSoftwareVsync() ? GetSoftwareVsyncRate() : -1;
     if (newRate != gLastUsedFrameRate) {
       gLastUsedFrameRate = newRate;
       ReInitFrameRate();
     }
   };
--- a/gfx/wr/webrender/Cargo.toml
+++ b/gfx/wr/webrender/Cargo.toml
@@ -12,16 +12,17 @@ default = ["freetype-lib"]
 freetype-lib = ["freetype/servo-freetype-sys"]
 profiler = ["thread_profiler/thread_profiler", "debug_renderer"]
 debugger = ["ws", "serde_json", "serde", "image", "base64", "debug_renderer"]
 capture = ["webrender_api/serialize", "ron", "serde", "debug_renderer"]
 replay = ["webrender_api/deserialize", "ron", "serde"]
 debug_renderer = []
 pathfinder = ["pathfinder_font_renderer", "pathfinder_gfx_utils", "pathfinder_partitioner", "pathfinder_path_utils"]
 serialize_program = ["serde", "webrender_build/serialize_program"]
+no_static_freetype = []
 
 [build-dependencies]
 webrender_build = { version = "0.0.1", path = "../webrender_build" }
 
 [dependencies]
 app_units = "0.7"
 base64 = { optional = true, version = "0.10" }
 bincode = "1.0"
--- a/gfx/wr/webrender/src/platform/unix/font.rs
+++ b/gfx/wr/webrender/src/platform/unix/font.rs
@@ -20,17 +20,17 @@ use freetype::freetype::{FT_LOAD_NO_BITM
 use freetype::freetype::{FT_FACE_FLAG_SCALABLE, FT_FACE_FLAG_FIXED_SIZES};
 use freetype::freetype::{FT_FACE_FLAG_MULTIPLE_MASTERS};
 use freetype::succeeded;
 use glyph_rasterizer::{FontInstance, GlyphFormat, GlyphKey};
 use glyph_rasterizer::{GlyphRasterError, GlyphRasterResult, RasterizedGlyph};
 #[cfg(feature = "pathfinder")]
 use glyph_rasterizer::NativeFontHandleWrapper;
 use internal_types::{FastHashMap, ResourceCacheError};
-#[cfg(not(target_os = "android"))]
+#[cfg(any(not(target_os = "android"), feature = "no_static_freetype"))]
 use libc::{dlsym, RTLD_DEFAULT};
 use libc::free;
 #[cfg(feature = "pathfinder")]
 use pathfinder_font_renderer::freetype as pf_freetype;
 use std::{cmp, mem, ptr, slice};
 use std::cmp::max;
 use std::collections::hash_map::Entry;
 use std::ffi::CString;
@@ -72,17 +72,17 @@ struct FT_MM_Var {
 }
 
 #[inline]
 pub fn unimplemented(error: FT_Error) -> bool {
     error == FT_Err_Unimplemented_Feature as FT_Error
 }
 
 // Use dlsym to check for symbols. If not available. just return an unimplemented error.
-#[cfg(not(target_os = "android"))]
+#[cfg(any(not(target_os = "android"), feature = "no_static_freetype"))]
 macro_rules! ft_dyn_fn {
     ($func_name:ident($($arg_name:ident:$arg_type:ty),*) -> FT_Error) => {
         #[allow(non_snake_case)]
         unsafe fn $func_name($($arg_name:$arg_type),*) -> FT_Error {
             extern "C" fn unimpl_func($(_:$arg_type),*) -> FT_Error {
                 FT_Err_Unimplemented_Feature as FT_Error
             }
             lazy_static! {
@@ -95,17 +95,17 @@ macro_rules! ft_dyn_fn {
                 };
             }
             (*func)($($arg_name),*)
         }
     }
 }
 
 // On Android, just statically link in the symbols...
-#[cfg(target_os = "android")]
+#[cfg(all(target_os = "android", not(feature = "no_static_freetype")))]
 macro_rules! ft_dyn_fn {
     ($($proto:tt)+) => { extern "C" { fn $($proto)+; } }
 }
 
 ft_dyn_fn!(FT_Get_MM_Var(face: FT_Face, desc: *mut *mut FT_MM_Var) -> FT_Error);
 ft_dyn_fn!(FT_Done_MM_Var(library: FT_Library, desc: *mut FT_MM_Var) -> FT_Error);
 ft_dyn_fn!(FT_Set_Var_Design_Coordinates(face: FT_Face, num_vals: FT_UInt, vals: *mut FT_Fixed) -> FT_Error);
 
--- a/image/decoders/icon/nsIconURI.cpp
+++ b/image/decoders/icon/nsIconURI.cpp
@@ -413,20 +413,18 @@ NS_IMETHODIMP
 nsMozIconURI::EqualsExceptRef(nsIURI* other, bool* result) {
   // GetRef/SetRef not supported by nsMozIconURI, so
   // EqualsExceptRef() is the same as Equals().
   return Equals(other, result);
 }
 
 NS_IMETHODIMP
 nsMozIconURI::SchemeIs(const char* aScheme, bool* aEquals) {
-  NS_ENSURE_ARG_POINTER(aEquals);
-  if (!aScheme) {
-    return NS_ERROR_INVALID_ARG;
-  }
+  MOZ_ASSERT(aScheme);
+  MOZ_ASSERT(aEquals, "null pointer");
 
   *aEquals = PL_strcasecmp("moz-icon", aScheme) ? false : true;
   return NS_OK;
 }
 
 nsresult nsMozIconURI::Clone(nsIURI** result) {
   nsCOMPtr<nsIURL> newIconURL;
   if (mIconURL) {
--- a/ipc/glue/BackgroundChild.h
+++ b/ipc/glue/BackgroundChild.h
@@ -16,55 +16,70 @@ namespace mozilla {
 namespace dom {
 
 class BlobImpl;
 class ContentChild;
 class ContentParent;
 
 }  // namespace dom
 
+namespace net {
+
+class SocketProcessImpl;
+
+}  // namespace net
+
 namespace ipc {
 
 class PBackgroundChild;
 
 // This class allows access to the PBackground protocol. PBackground allows
 // communication between any thread (in the parent or a child process) and a
 // single background thread in the parent process. Each PBackgroundChild
 // instance is tied to the thread on which it is created and must not be shared
 // across threads. Each PBackgroundChild is unique and valid as long as its
 // designated thread lives.
 //
 // Creation of PBackground is synchronous. GetOrCreateForCurrentThread will
 // create the actor if it doesn't exist yet. Thereafter (assuming success)
 // GetForCurrentThread() will return the same actor every time.
 //
+// GetOrCreateSocketActorForCurrentThread, which is like
+// GetOrCreateForCurrentThread, is used to get or create PBackground actor
+// between child process and socket process.
+//
 // CloseForCurrentThread() will close the current PBackground actor.  Subsequent
 // calls to GetForCurrentThread will return null.  CloseForCurrentThread() may
 // only be called exactly once for each thread-specific actor.  Currently it is
 // illegal to call this before the PBackground actor has been created.
 //
 // The PBackgroundChild actor and all its sub-protocol actors will be
 // automatically destroyed when its designated thread completes.
 class BackgroundChild final {
   friend class mozilla::dom::ContentChild;
   friend class mozilla::dom::ContentParent;
+  friend class mozilla::net::SocketProcessImpl;
 
   typedef mozilla::ipc::Transport Transport;
 
  public:
   // See above.
   static PBackgroundChild* GetForCurrentThread();
 
   // See above.
   static PBackgroundChild* GetOrCreateForCurrentThread(
       nsIEventTarget* aMainEventTarget = nullptr);
 
   // See above.
   static void CloseForCurrentThread();
 
+  // See above.
+  static PBackgroundChild* GetOrCreateSocketActorForCurrentThread(
+      nsIEventTarget* aMainEventTarget = nullptr);
+
  private:
   // Only called by ContentChild or ContentParent.
   static void Startup();
 };
 
 }  // namespace ipc
 }  // namespace mozilla
 
--- a/ipc/glue/BackgroundImpl.cpp
+++ b/ipc/glue/BackgroundImpl.cpp
@@ -22,16 +22,17 @@
 #include "mozilla/StaticPtr.h"
 #include "mozilla/Unused.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/WorkerRef.h"
 #include "mozilla/ipc/ProtocolTypes.h"
+#include "mozilla/net/SocketProcessBridgeChild.h"
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsIEventTarget.h"
 #include "nsIMutable.h"
 #include "nsIObserver.h"
 #include "nsIObserverService.h"
 #include "nsIRunnable.h"
 #include "nsISupportsImpl.h"
@@ -39,16 +40,18 @@
 #include "nsITimer.h"
 #include "nsTArray.h"
 #include "nsThreadUtils.h"
 #include "nsTraceRefcnt.h"
 #include "nsXULAppAPI.h"
 #include "nsXPCOMPrivate.h"
 #include "prthread.h"
 
+#include <functional>
+
 #ifdef RELEASE_OR_BETA
 #define THREADSAFETY_ASSERT MOZ_ASSERT
 #else
 #define THREADSAFETY_ASSERT MOZ_RELEASE_ASSERT
 #endif
 
 #define CRASH_IN_CHILD_PROCESS(_msg) \
   do {                               \
@@ -57,27 +60,32 @@
     } else {                         \
       MOZ_CRASH(_msg);               \
     }                                \
   } while (0)
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::ipc;
+using namespace mozilla::net;
 
 namespace {
 
 class ChildImpl;
 
 // -----------------------------------------------------------------------------
 // Utility Functions
 // -----------------------------------------------------------------------------
 
 void AssertIsInMainProcess() { MOZ_ASSERT(XRE_IsParentProcess()); }
 
+void AssertIsInMainOrSocketProcess() {
+  MOZ_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess());
+}
+
 void AssertIsOnMainThread() { THREADSAFETY_ASSERT(NS_IsMainThread()); }
 
 void AssertIsNotOnMainThread() { THREADSAFETY_ASSERT(!NS_IsMainThread()); }
 
 // -----------------------------------------------------------------------------
 // ParentImpl Declaration
 // -----------------------------------------------------------------------------
 
@@ -96,17 +104,17 @@ class ParentImpl final : public Backgrou
   class CreateActorHelper;
 
   struct MOZ_STACK_CLASS TimerCallbackClosure {
     nsIThread* mThread;
     nsTArray<ParentImpl*>* mLiveActors;
 
     TimerCallbackClosure(nsIThread* aThread, nsTArray<ParentImpl*>* aLiveActors)
         : mThread(aThread), mLiveActors(aLiveActors) {
-      AssertIsInMainProcess();
+      AssertIsInMainOrSocketProcess();
       AssertIsOnMainThread();
       MOZ_ASSERT(aThread);
       MOZ_ASSERT(aLiveActors);
     }
   };
 
   // The length of time we will wait at shutdown for all actors to clean
   // themselves up before forcing them to be destroyed.
@@ -215,31 +223,30 @@ class ParentImpl final : public Backgrou
   }
 
   // For other-process actors.
   explicit ParentImpl(ContentParent* aContent)
       : mContent(aContent),
         mLiveActorArray(nullptr),
         mIsOtherProcessActor(true),
         mActorDestroyed(false) {
-    AssertIsInMainProcess();
+    MOZ_ASSERT((XRE_IsParentProcess() && aContent) || XRE_IsSocketProcess());
     AssertIsOnMainThread();
-    MOZ_ASSERT(aContent);
   }
 
   ~ParentImpl() {
-    AssertIsInMainProcess();
+    AssertIsInMainOrSocketProcess();
     AssertIsOnMainThread();
     MOZ_ASSERT(!mContent);
   }
 
   void MainThreadActorDestroy();
 
   void SetLiveActorArray(nsTArray<ParentImpl*>* aLiveActorArray) {
-    AssertIsInMainProcess();
+    AssertIsInMainOrSocketProcess();
     AssertIsOnBackgroundThread();
     MOZ_ASSERT(aLiveActorArray);
     MOZ_ASSERT(!aLiveActorArray->Contains(this));
     MOZ_ASSERT(!mLiveActorArray);
     MOZ_ASSERT(mIsOtherProcessActor);
 
     mLiveActorArray = aLiveActorArray;
     mLiveActorArray->AppendElement(this);
@@ -265,16 +272,17 @@ class ChildImpl final : public Backgroun
 
   // A thread-local index that is not valid.
   static const unsigned int kBadThreadLocalIndex =
       static_cast<unsigned int>(-1);
 
   // This is only modified on the main thread. It is the thread-local index that
   // we use to store the BackgroundChild for each thread.
   static unsigned int sThreadLocalIndex;
+  static unsigned int sThreadLocalIndexForSocketProcess;
 
   struct ThreadLocalInfo {
     ThreadLocalInfo()
 #ifdef DEBUG
         : mClosed(false)
 #endif
     {
     }
@@ -287,32 +295,36 @@ class ChildImpl final : public Backgroun
 #endif
   };
 
   // On the main thread, we store TLS in this global instead of in
   // sThreadLocalIndex. That way, cooperative main threads all share the same
   // thread info.
   static ThreadLocalInfo* sMainThreadInfo;
 
+  static ThreadLocalInfo* sMainThreadInfoForSocketProcess;
+
   // This is only modified on the main thread. It prevents us from trying to
   // create the background thread after application shutdown has started.
   static bool sShutdownHasStarted;
 
 #if defined(DEBUG) || !defined(RELEASE_OR_BETA)
   nsISerialEventTarget* mOwningEventTarget;
 #endif
 
 #ifdef DEBUG
   bool mActorWasAlive;
   bool mActorDestroyed;
 #endif
 
  public:
   static void Shutdown();
 
+  static void ShutdownWithThreadLocalIndex(unsigned int aThreadLocalIndex);
+
   void AssertIsOnOwningThread() {
     THREADSAFETY_ASSERT(mOwningEventTarget);
 
 #ifdef RELEASE_OR_BETA
     DebugOnly<bool> current;
 #else
     bool current;
 #endif
@@ -352,23 +364,33 @@ class ChildImpl final : public Backgroun
 
  private:
   // Forwarded from BackgroundChild.
   static void Startup();
 
   // Forwarded from BackgroundChild.
   static PBackgroundChild* GetForCurrentThread();
 
+  // Helper function for getting PBackgroundChild from thread info.
+  static PBackgroundChild* GetFromThreadInfo(nsIEventTarget* aMainEventTarget,
+                                             ThreadLocalInfo* aThreadLocalInfo);
+
   // Forwarded from BackgroundChild.
   static PBackgroundChild* GetOrCreateForCurrentThread(
       nsIEventTarget* aMainEventTarget);
 
   // Forwarded from BackgroundChild.
+  static PBackgroundChild* GetOrCreateSocketActorForCurrentThread(
+      nsIEventTarget* aMainEventTarget);
+
+  // Forwarded from BackgroundChild.
   static void CloseForCurrentThread();
 
+  static void CloseThreadWithIndex(unsigned int aThreadLocalIndex);
+
   // Forwarded from BackgroundChildImpl.
   static BackgroundChildImpl::ThreadLocal* GetThreadLocalForCurrentThread();
 
   static void ThreadLocalDestructor(void* aThreadLocal);
 
   // This class is reference counted.
   ~ChildImpl() { MOZ_ASSERT_IF(mActorWasAlive, mActorDestroyed); }
 
@@ -395,32 +417,32 @@ class ParentImpl::RequestMessageLoopRunn
   nsCOMPtr<nsIThread> mTargetThread;
   MessageLoop* mMessageLoop;
 
  public:
   explicit RequestMessageLoopRunnable(nsIThread* aTargetThread)
       : Runnable("Background::ParentImpl::RequestMessageLoopRunnable"),
         mTargetThread(aTargetThread),
         mMessageLoop(nullptr) {
-    AssertIsInMainProcess();
+    AssertIsInMainOrSocketProcess();
     AssertIsOnMainThread();
     MOZ_ASSERT(aTargetThread);
   }
 
  private:
   ~RequestMessageLoopRunnable() {}
 
   NS_DECL_NSIRUNNABLE
 };
 
 class ParentImpl::ShutdownBackgroundThreadRunnable final : public Runnable {
  public:
   ShutdownBackgroundThreadRunnable()
       : Runnable("Background::ParentImpl::ShutdownBackgroundThreadRunnable") {
-    AssertIsInMainProcess();
+    AssertIsInMainOrSocketProcess();
     AssertIsOnMainThread();
   }
 
  private:
   ~ShutdownBackgroundThreadRunnable() {}
 
   NS_DECL_NSIRUNNABLE
 };
@@ -428,17 +450,17 @@ class ParentImpl::ShutdownBackgroundThre
 class ParentImpl::ForceCloseBackgroundActorsRunnable final : public Runnable {
   nsTArray<ParentImpl*>* mActorArray;
 
  public:
   explicit ForceCloseBackgroundActorsRunnable(
       nsTArray<ParentImpl*>* aActorArray)
       : Runnable("Background::ParentImpl::ForceCloseBackgroundActorsRunnable"),
         mActorArray(aActorArray) {
-    AssertIsInMainProcess();
+    AssertIsInMainOrSocketProcess();
     AssertIsOnMainThread();
     MOZ_ASSERT(aActorArray);
   }
 
  private:
   ~ForceCloseBackgroundActorsRunnable() {}
 
   NS_DECL_NSIRUNNABLE
@@ -452,24 +474,24 @@ class ParentImpl::ConnectActorRunnable f
  public:
   ConnectActorRunnable(ParentImpl* aActor,
                        Endpoint<PBackgroundParent>&& aEndpoint,
                        nsTArray<ParentImpl*>* aLiveActorArray)
       : Runnable("Background::ParentImpl::ConnectActorRunnable"),
         mActor(aActor),
         mEndpoint(std::move(aEndpoint)),
         mLiveActorArray(aLiveActorArray) {
-    AssertIsInMainProcess();
+    AssertIsInMainOrSocketProcess();
     AssertIsOnMainThread();
     MOZ_ASSERT(mEndpoint.IsValid());
     MOZ_ASSERT(aLiveActorArray);
   }
 
  private:
-  ~ConnectActorRunnable() { AssertIsInMainProcess(); }
+  ~ConnectActorRunnable() { AssertIsInMainOrSocketProcess(); }
 
   NS_DECL_NSIRUNNABLE
 };
 
 class ParentImpl::CreateActorHelper final : public Runnable {
   mozilla::Monitor mMonitor;
   RefPtr<ParentImpl> mParentActor;
   nsCOMPtr<nsIThread> mThread;
@@ -477,26 +499,26 @@ class ParentImpl::CreateActorHelper fina
   bool mWaiting;
 
  public:
   explicit CreateActorHelper()
       : Runnable("Background::ParentImpl::CreateActorHelper"),
         mMonitor("CreateActorHelper::mMonitor"),
         mMainThreadResultCode(NS_OK),
         mWaiting(true) {
-    AssertIsInMainProcess();
+    AssertIsInMainOrSocketProcess();
     AssertIsNotOnMainThread();
   }
 
   nsresult BlockAndGetResults(nsIEventTarget* aMainEventTarget,
                               RefPtr<ParentImpl>& aParentActor,
                               nsCOMPtr<nsIThread>& aThread);
 
  private:
-  ~CreateActorHelper() { AssertIsInMainProcess(); }
+  ~CreateActorHelper() { AssertIsInMainOrSocketProcess(); }
 
   nsresult RunOnMainThread();
 
   NS_DECL_NSIRUNNABLE
 };
 
 class NS_NO_VTABLE ParentImpl::CreateCallback {
  public:
@@ -527,35 +549,40 @@ class ChildImpl::ShutdownObserver final 
 };
 
 class ChildImpl::SendInitBackgroundRunnable final : public CancelableRunnable {
   nsCOMPtr<nsISerialEventTarget> mOwningEventTarget;
   RefPtr<StrongWorkerRef> mWorkerRef;
   Endpoint<PBackgroundParent> mParent;
   mozilla::Mutex mMutex;
   bool mSentInitBackground;
+  std::function<void(Endpoint<PBackgroundParent>&& aParent)> mSendInitfunc;
 
  public:
   static already_AddRefed<SendInitBackgroundRunnable> Create(
-      Endpoint<PBackgroundParent>&& aParent);
+      Endpoint<PBackgroundParent>&& aParent,
+      std::function<void(Endpoint<PBackgroundParent>&& aParent)>&& aFunc);
 
   void ClearEventTarget() {
     mWorkerRef = nullptr;
 
     mozilla::MutexAutoLock lock(mMutex);
     mOwningEventTarget = nullptr;
   }
 
  private:
-  explicit SendInitBackgroundRunnable(Endpoint<PBackgroundParent>&& aParent)
+  explicit SendInitBackgroundRunnable(
+      Endpoint<PBackgroundParent>&& aParent,
+      std::function<void(Endpoint<PBackgroundParent>&& aParent)>&& aFunc)
       : CancelableRunnable("Background::ChildImpl::SendInitBackgroundRunnable"),
         mOwningEventTarget(GetCurrentThreadSerialEventTarget()),
         mParent(std::move(aParent)),
         mMutex("SendInitBackgroundRunnable::mMutex"),
-        mSentInitBackground(false) {}
+        mSentInitBackground(false),
+        mSendInitfunc(std::move(aFunc)) {}
 
   ~SendInitBackgroundRunnable() {}
 
   NS_DECL_NSIRUNNABLE
 };
 
 }  // namespace
 
@@ -627,16 +654,22 @@ PBackgroundChild* BackgroundChild::GetFo
 
 // static
 PBackgroundChild* BackgroundChild::GetOrCreateForCurrentThread(
     nsIEventTarget* aMainEventTarget) {
   return ChildImpl::GetOrCreateForCurrentThread(aMainEventTarget);
 }
 
 // static
+PBackgroundChild* BackgroundChild::GetOrCreateSocketActorForCurrentThread(
+    nsIEventTarget* aMainEventTarget) {
+  return ChildImpl::GetOrCreateSocketActorForCurrentThread(aMainEventTarget);
+}
+
+// static
 void BackgroundChild::CloseForCurrentThread() {
   ChildImpl::CloseForCurrentThread();
 }
 
 // -----------------------------------------------------------------------------
 // BackgroundChildImpl Public Methods
 // -----------------------------------------------------------------------------
 
@@ -666,16 +699,18 @@ bool ParentImpl::sShutdownObserverRegist
 
 bool ParentImpl::sShutdownHasStarted = false;
 
 // -----------------------------------------------------------------------------
 // ChildImpl Static Members
 // -----------------------------------------------------------------------------
 
 unsigned int ChildImpl::sThreadLocalIndex = kBadThreadLocalIndex;
+unsigned int ChildImpl::sThreadLocalIndexForSocketProcess =
+    kBadThreadLocalIndex;
 
 bool ChildImpl::sShutdownHasStarted = false;
 
 // -----------------------------------------------------------------------------
 // ParentImpl Implementation
 // -----------------------------------------------------------------------------
 
 // static
@@ -771,17 +806,17 @@ bool ParentImpl::GetLiveActorArray(
   }
 
   return true;
 }
 
 // static
 bool ParentImpl::Alloc(ContentParent* aContent,
                        Endpoint<PBackgroundParent>&& aEndpoint) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnMainThread();
   MOZ_ASSERT(aEndpoint.IsValid());
 
   if (!sBackgroundThread && !CreateBackgroundThread()) {
     NS_WARNING("Failed to create background thread!");
     return false;
   }
 
@@ -862,17 +897,17 @@ already_AddRefed<ChildImpl> ParentImpl::
   // Now that Open() has succeeded transfer the ownership of the actors to IPDL.
   Unused << parentActor.forget();
 
   return childActor.forget();
 }
 
 // static
 bool ParentImpl::CreateBackgroundThread() {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnMainThread();
   MOZ_ASSERT(!sBackgroundThread);
   MOZ_ASSERT(!sLiveActorsForBackgroundThread);
 
   if (sShutdownHasStarted) {
     NS_WARNING(
         "Trying to create background thread after shutdown has "
         "already begun!");
@@ -926,17 +961,17 @@ bool ParentImpl::CreateBackgroundThread(
     sShutdownTimer = newShutdownTimer;
   }
 
   return true;
 }
 
 // static
 void ParentImpl::ShutdownBackgroundThread() {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnMainThread();
   MOZ_ASSERT(sShutdownHasStarted);
   MOZ_ASSERT_IF(!sBackgroundThread, !sLiveActorCount);
   MOZ_ASSERT_IF(sBackgroundThread, sShutdownTimer);
 
   nsCOMPtr<nsITimer> shutdownTimer = sShutdownTimer.get();
   sShutdownTimer = nullptr;
 
@@ -971,17 +1006,17 @@ void ParentImpl::ShutdownBackgroundThrea
     MOZ_ALWAYS_SUCCEEDS(thread->Dispatch(shutdownRunnable, NS_DISPATCH_NORMAL));
 
     MOZ_ALWAYS_SUCCEEDS(thread->Shutdown());
   }
 }
 
 // static
 void ParentImpl::ShutdownTimerCallback(nsITimer* aTimer, void* aClosure) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnMainThread();
   MOZ_ASSERT(sShutdownHasStarted);
   MOZ_ASSERT(sLiveActorCount);
 
   auto closure = static_cast<TimerCallbackClosure*>(aClosure);
   MOZ_ASSERT(closure);
 
   // Don't let the stack unwind until the ForceCloseBackgroundActorsRunnable has
@@ -992,40 +1027,40 @@ void ParentImpl::ShutdownTimerCallback(n
       new ForceCloseBackgroundActorsRunnable(closure->mLiveActors);
   MOZ_ALWAYS_SUCCEEDS(
       closure->mThread->Dispatch(forceCloseRunnable, NS_DISPATCH_NORMAL));
 }
 
 void ParentImpl::Destroy() {
   // May be called on any thread!
 
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
 
   MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(
       NewNonOwningRunnableMethod("ParentImpl::MainThreadActorDestroy", this,
                                  &ParentImpl::MainThreadActorDestroy)));
 }
 
 void ParentImpl::MainThreadActorDestroy() {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnMainThread();
-  MOZ_ASSERT_IF(mIsOtherProcessActor, mContent);
+  MOZ_ASSERT_IF(mIsOtherProcessActor && XRE_IsParentProcess(), mContent);
   MOZ_ASSERT_IF(!mIsOtherProcessActor, !mContent);
 
   mContent = nullptr;
 
   MOZ_ASSERT(sLiveActorCount);
   sLiveActorCount--;
 
   // This may be the last reference!
   Release();
 }
 
 void ParentImpl::ActorDestroy(ActorDestroyReason aWhy) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(!mActorDestroyed);
   MOZ_ASSERT_IF(mIsOtherProcessActor, mLiveActorArray);
 
   BackgroundParentImpl::ActorDestroy(aWhy);
 
   mActorDestroyed = true;
 
@@ -1046,35 +1081,39 @@ void ParentImpl::ActorDestroy(ActorDestr
       "ParentImpl::Destroy", this, &ParentImpl::Destroy)));
 }
 
 NS_IMPL_ISUPPORTS(ParentImpl::ShutdownObserver, nsIObserver)
 
 NS_IMETHODIMP
 ParentImpl::ShutdownObserver::Observe(nsISupports* aSubject, const char* aTopic,
                                       const char16_t* aData) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnMainThread();
   MOZ_ASSERT(!sShutdownHasStarted);
   MOZ_ASSERT(!strcmp(aTopic, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID));
 
   sShutdownHasStarted = true;
 
   // Do this first before calling (and spinning the event loop in)
   // ShutdownBackgroundThread().
-  ChildImpl::Shutdown();
+  // Since we didn't call BackgroundChild::Startup() in socket process,
+  // we can't call ChildImpl::Shutdown() here.
+  if (!XRE_IsSocketProcess()) {
+    ChildImpl::Shutdown();
+  }
 
   ShutdownBackgroundThread();
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 ParentImpl::RequestMessageLoopRunnable::Run() {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   MOZ_ASSERT(mTargetThread);
 
   if (NS_IsMainThread()) {
     MOZ_ASSERT(mMessageLoop);
 
     if (!sBackgroundThread ||
         !SameCOMIdentity(mTargetThread.get(), sBackgroundThread.get())) {
       return NS_OK;
@@ -1110,29 +1149,29 @@ ParentImpl::RequestMessageLoopRunnable::
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 ParentImpl::ShutdownBackgroundThreadRunnable::Run() {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
 
   // It is possible that another background thread was created while this thread
   // was shutting down. In that case we can't assert anything about
   // sBackgroundPRThread and we should not modify it here.
   sBackgroundPRThread.compareExchange(PR_GetCurrentThread(), nullptr);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 ParentImpl::ForceCloseBackgroundActorsRunnable::Run() {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   MOZ_ASSERT(mActorArray);
 
   if (NS_IsMainThread()) {
     MOZ_ASSERT(sLiveActorCount);
     sLiveActorCount--;
     return NS_OK;
   }
 
@@ -1149,17 +1188,17 @@ ParentImpl::ForceCloseBackgroundActorsRu
 
   MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 ParentImpl::ConnectActorRunnable::Run() {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
 
   // Transfer ownership to this thread. If Open() fails then we will release
   // this reference in Destroy.
   ParentImpl* actor;
   mActor.forget(&actor);
 
   Endpoint<PBackgroundParent> endpoint = std::move(mEndpoint);
@@ -1241,67 +1280,92 @@ ParentImpl::CreateActorHelper::Run() {
 
 // static
 void ChildImpl::Startup() {
   // This happens on the main thread but before XPCOM has started so we can't
   // assert that we're being called on the main thread here.
 
   MOZ_ASSERT(sThreadLocalIndex == kBadThreadLocalIndex,
              "BackgroundChild::Startup() called more than once!");
+  MOZ_ASSERT(sThreadLocalIndexForSocketProcess == kBadThreadLocalIndex,
+             "BackgroundChild::Startup() called more than once!");
 
   PRStatus status =
       PR_NewThreadPrivateIndex(&sThreadLocalIndex, ThreadLocalDestructor);
   MOZ_RELEASE_ASSERT(status == PR_SUCCESS, "PR_NewThreadPrivateIndex failed!");
 
+  status = PR_NewThreadPrivateIndex(&sThreadLocalIndexForSocketProcess,
+                                    ThreadLocalDestructor);
+  MOZ_RELEASE_ASSERT(status == PR_SUCCESS, "PR_NewThreadPrivateIndex failed!");
+
   MOZ_ASSERT(sThreadLocalIndex != kBadThreadLocalIndex);
+  MOZ_ASSERT(sThreadLocalIndexForSocketProcess != kBadThreadLocalIndex);
 
   nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
   MOZ_RELEASE_ASSERT(observerService);
 
   nsCOMPtr<nsIObserver> observer = new ShutdownObserver();
 
   nsresult rv = observerService->AddObserver(
       observer, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, false);
   MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
 }
 
-// static
-void ChildImpl::Shutdown() {
-  AssertIsOnMainThread();
-
-  if (sShutdownHasStarted) {
-    MOZ_ASSERT_IF(sThreadLocalIndex != kBadThreadLocalIndex,
-                  !PR_GetThreadPrivate(sThreadLocalIndex));
-    return;
-  }
-
-  sShutdownHasStarted = true;
-
-  MOZ_ASSERT(sThreadLocalIndex != kBadThreadLocalIndex);
+void ChildImpl::ShutdownWithThreadLocalIndex(unsigned int aThreadLocalIndex) {
+  MOZ_ASSERT(aThreadLocalIndex != kBadThreadLocalIndex);
 
   ThreadLocalInfo* threadLocalInfo;
 #ifdef DEBUG
   threadLocalInfo =
-      static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(sThreadLocalIndex));
+      static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(aThreadLocalIndex));
   MOZ_ASSERT(!threadLocalInfo);
 #endif
-  threadLocalInfo = sMainThreadInfo;
+  threadLocalInfo = aThreadLocalIndex == sThreadLocalIndex
+                        ? sMainThreadInfo
+                        : sMainThreadInfoForSocketProcess;
 
   if (threadLocalInfo) {
 #ifdef DEBUG
     MOZ_ASSERT(!threadLocalInfo->mClosed);
     threadLocalInfo->mClosed = true;
 #endif
 
     ThreadLocalDestructor(threadLocalInfo);
-    sMainThreadInfo = nullptr;
+    if (aThreadLocalIndex == sThreadLocalIndex) {
+      sMainThreadInfo = nullptr;
+    } else {
+      sMainThreadInfoForSocketProcess = nullptr;
+    }
+  }
+}
+
+// static
+void ChildImpl::Shutdown() {
+  AssertIsOnMainThread();
+
+  if (sShutdownHasStarted) {
+    MOZ_ASSERT_IF(sThreadLocalIndex != kBadThreadLocalIndex,
+                  !PR_GetThreadPrivate(sThreadLocalIndex));
+    MOZ_ASSERT_IF(sThreadLocalIndexForSocketProcess != kBadThreadLocalIndex,
+                  !PR_GetThreadPrivate(sThreadLocalIndexForSocketProcess));
+    return;
+  }
+
+  sShutdownHasStarted = true;
+
+  ShutdownWithThreadLocalIndex(sThreadLocalIndex);
+
+  if (sThreadLocalIndexForSocketProcess != kBadThreadLocalIndex) {
+    ShutdownWithThreadLocalIndex(sThreadLocalIndexForSocketProcess);
   }
 }
 
 ChildImpl::ThreadLocalInfo* ChildImpl::sMainThreadInfo = nullptr;
+ChildImpl::ThreadLocalInfo* ChildImpl::sMainThreadInfoForSocketProcess =
+    nullptr;
 
 // static
 PBackgroundChild* ChildImpl::GetForCurrentThread() {
   MOZ_ASSERT(sThreadLocalIndex != kBadThreadLocalIndex);
 
   auto threadLocalInfo = NS_IsMainThread()
                              ? sMainThreadInfo
                              : static_cast<ThreadLocalInfo*>(
@@ -1310,16 +1374,51 @@ PBackgroundChild* ChildImpl::GetForCurre
   if (!threadLocalInfo) {
     return nullptr;
   }
 
   return threadLocalInfo->mActor;
 }
 
 /* static */
+PBackgroundChild* ChildImpl::GetFromThreadInfo(
+    nsIEventTarget* aMainEventTarget, ThreadLocalInfo* aThreadLocalInfo) {
+  MOZ_ASSERT(aThreadLocalInfo);
+
+  if (aThreadLocalInfo->mActor) {
+    RefPtr<SendInitBackgroundRunnable>& runnable =
+        aThreadLocalInfo->mSendInitBackgroundRunnable;
+
+    if (aMainEventTarget && runnable) {
+      // The SendInitBackgroundRunnable was already dispatched to the main
+      // thread to finish initialization of a new background child actor.
+      // However, the caller passed a custom main event target which indicates
+      // that synchronous blocking of the main thread is happening (done by
+      // creating a nested event target and spinning the event loop).
+      // It can happen that the SendInitBackgroundRunnable didn't have a chance
+      // to run before the synchronous blocking has occured. Unblocking of the
+      // main thread can depend on an IPC message received on this thread, so
+      // we have to dispatch the SendInitBackgroundRunnable to the custom main
+      // event target too, otherwise IPC will be only queueing messages on this
+      // thread. The runnable will run twice in the end, but that's a harmless
+      // race between the main and nested event queue of the main thread.
+      // There's a guard in the runnable implementation for calling
+      // SendInitBackground only once.
+
+      MOZ_ALWAYS_SUCCEEDS(
+          aMainEventTarget->Dispatch(runnable, NS_DISPATCH_NORMAL));
+    }
+
+    return aThreadLocalInfo->mActor;
+  }
+
+  return nullptr;
+}
+
+/* static */
 PBackgroundChild* ChildImpl::GetOrCreateForCurrentThread(
     nsIEventTarget* aMainEventTarget) {
   MOZ_ASSERT_IF(NS_IsMainThread(), !aMainEventTarget);
 
   MOZ_ASSERT(sThreadLocalIndex != kBadThreadLocalIndex,
              "BackgroundChild::Startup() was never called!");
 
   if (NS_IsMainThread() && sShutdownHasStarted) {
@@ -1341,41 +1440,20 @@ PBackgroundChild* ChildImpl::GetOrCreate
         CRASH_IN_CHILD_PROCESS("PR_SetThreadPrivate failed!");
         return nullptr;
       }
     }
 
     threadLocalInfo = newInfo.forget();
   }
 
-  if (threadLocalInfo->mActor) {
-    RefPtr<SendInitBackgroundRunnable>& runnable =
-        threadLocalInfo->mSendInitBackgroundRunnable;
-
-    if (aMainEventTarget && runnable) {
-      // The SendInitBackgroundRunnable was already dispatched to the main
-      // thread to finish initialization of a new background child actor.
-      // However, the caller passed a custom main event target which indicates
-      // that synchronous blocking of the main thread is happening (done by
-      // creating a nested event target and spinning the event loop).
-      // It can happen that the SendInitBackgroundRunnable didn't have a chance
-      // to run before the synchronous blocking has occured. Unblocking of the
-      // main thread can depend on an IPC message received on this thread, so
-      // we have to dispatch the SendInitBackgroundRunnable to the custom main
-      // event target too, otherwise IPC will be only queueing messages on this
-      // thread. The runnable will run twice in the end, but that's a harmless
-      // race between the main and nested event queue of the main thread.
-      // There's a guard in the runnable implementation for calling
-      // SendInitBackground only once.
-
-      MOZ_ALWAYS_SUCCEEDS(
-          aMainEventTarget->Dispatch(runnable, NS_DISPATCH_NORMAL));
-    }
-
-    return threadLocalInfo->mActor;
+  PBackgroundChild* bgChild =
+      GetFromThreadInfo(aMainEventTarget, threadLocalInfo);
+  if (bgChild) {
+    return bgChild;
   }
 
   if (XRE_IsParentProcess()) {
     RefPtr<ChildImpl> strongActor =
         ParentImpl::CreateActorForSameProcess(aMainEventTarget);
     if (NS_WARN_IF(!strongActor)) {
       return nullptr;
     }
@@ -1402,17 +1480,25 @@ PBackgroundChild* ChildImpl::GetOrCreate
                                     base::GetCurrentProcId(), &parent, &child);
   if (NS_FAILED(rv)) {
     NS_WARNING("Failed to create top level actor!");
     return nullptr;
   }
 
   RefPtr<SendInitBackgroundRunnable> runnable;
   if (!NS_IsMainThread()) {
-    runnable = SendInitBackgroundRunnable::Create(std::move(parent));
+    runnable = SendInitBackgroundRunnable::Create(
+        std::move(parent), [](Endpoint<PBackgroundParent>&& aParent) {
+          RefPtr<ContentChild> content = ContentChild::GetSingleton();
+          MOZ_ASSERT(content);
+
+          if (!content->SendInitBackground(std::move(aParent))) {
+            MOZ_CRASH("Failed to create top level actor!");
+          }
+        });
     if (!runnable) {
       return nullptr;
     }
   }
 
   RefPtr<ChildImpl> strongActor = new ChildImpl();
 
   if (!child.Bind(strongActor)) {
@@ -1440,44 +1526,156 @@ PBackgroundChild* ChildImpl::GetOrCreate
   }
 
   RefPtr<ChildImpl>& actor = threadLocalInfo->mActor;
   strongActor.swap(actor);
 
   return actor;
 }
 
-// static
-void ChildImpl::CloseForCurrentThread() {
-  MOZ_ASSERT(!NS_IsMainThread(),
-             "PBackground for the main thread should be shut down via "
-             "ChildImpl::Shutdown().");
+/* static */
+PBackgroundChild* ChildImpl::GetOrCreateSocketActorForCurrentThread(
+    nsIEventTarget* aMainEventTarget) {
+  MOZ_ASSERT_IF(NS_IsMainThread(), !aMainEventTarget);
 
-  if (sThreadLocalIndex == kBadThreadLocalIndex) {
-    return;
+  MOZ_ASSERT(sThreadLocalIndexForSocketProcess != kBadThreadLocalIndex,
+             "BackgroundChild::Startup() was never called!");
+
+  if (NS_IsMainThread() && sShutdownHasStarted) {
+    return nullptr;
   }
 
   auto threadLocalInfo =
-      static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(sThreadLocalIndex));
+      NS_IsMainThread() ? sMainThreadInfoForSocketProcess
+                        : static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(
+                              sThreadLocalIndexForSocketProcess));
+
+  if (!threadLocalInfo) {
+    nsAutoPtr<ThreadLocalInfo> newInfo(new ThreadLocalInfo());
+
+    if (NS_IsMainThread()) {
+      sMainThreadInfoForSocketProcess = newInfo;
+    } else {
+      if (PR_SetThreadPrivate(sThreadLocalIndexForSocketProcess, newInfo) !=
+          PR_SUCCESS) {
+        CRASH_IN_CHILD_PROCESS("PR_SetThreadPrivate failed!");
+        return nullptr;
+      }
+    }
+
+    threadLocalInfo = newInfo.forget();
+  }
+
+  PBackgroundChild* bgChild =
+      GetFromThreadInfo(aMainEventTarget, threadLocalInfo);
+  if (bgChild) {
+    return bgChild;
+  }
+
+  RefPtr<SocketProcessBridgeChild> bridgeChild =
+      SocketProcessBridgeChild::GetSingleton();
+
+  if (!bridgeChild || bridgeChild->IsShuttingDown()) {
+    // The transport for SocketProcessBridgeChild is shut down
+    // and can't be used to open PBackground.
+    return nullptr;
+  }
+
+  Endpoint<PBackgroundParent> parent;
+  Endpoint<PBackgroundChild> child;
+  nsresult rv;
+  rv = PBackground::CreateEndpoints(bridgeChild->SocketProcessPid(),
+                                    base::GetCurrentProcId(), &parent, &child);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Failed to create top level actor!");
+    return nullptr;
+  }
+
+  RefPtr<SendInitBackgroundRunnable> runnable;
+  if (!NS_IsMainThread()) {
+    runnable = SendInitBackgroundRunnable::Create(
+        std::move(parent), [](Endpoint<PBackgroundParent>&& aParent) {
+          RefPtr<SocketProcessBridgeChild> bridgeChild =
+              SocketProcessBridgeChild::GetSingleton();
+
+          if (!bridgeChild->SendInitBackground(std::move(aParent))) {
+            MOZ_CRASH("Failed to create top level actor!");
+          }
+        });
+    if (!runnable) {
+      return nullptr;
+    }
+  }
+
+  RefPtr<ChildImpl> strongActor = new ChildImpl();
+
+  if (!child.Bind(strongActor)) {
+    CRASH_IN_CHILD_PROCESS("Failed to bind ChildImpl!");
+
+    return nullptr;
+  }
+
+  strongActor->SetActorAlive();
+
+  if (NS_IsMainThread()) {
+    if (!bridgeChild->SendInitBackground(std::move(parent))) {
+      NS_WARNING("Failed to create top level actor!");
+      return nullptr;
+    }
+  } else {
+    if (aMainEventTarget) {
+      MOZ_ALWAYS_SUCCEEDS(
+          aMainEventTarget->Dispatch(runnable, NS_DISPATCH_NORMAL));
+    } else {
+      MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable));
+    }
+
+    threadLocalInfo->mSendInitBackgroundRunnable = runnable;
+  }
+
+  RefPtr<ChildImpl>& actor = threadLocalInfo->mActor;
+  strongActor.swap(actor);
+
+  return actor;
+}
+
+// static
+void ChildImpl::CloseThreadWithIndex(unsigned int aThreadLocalIndex) {
+  auto threadLocalInfo =
+      static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(aThreadLocalIndex));
 
   if (!threadLocalInfo) {
     return;
   }
 
 #ifdef DEBUG
   MOZ_ASSERT(!threadLocalInfo->mClosed);
   threadLocalInfo->mClosed = true;
 #endif
 
   // Clearing the thread local will synchronously close the actor.
-  DebugOnly<PRStatus> status = PR_SetThreadPrivate(sThreadLocalIndex, nullptr);
+  DebugOnly<PRStatus> status = PR_SetThreadPrivate(aThreadLocalIndex, nullptr);
   MOZ_ASSERT(status == PR_SUCCESS);
 }
 
 // static
+void ChildImpl::CloseForCurrentThread() {
+  MOZ_ASSERT(!NS_IsMainThread(),
+             "PBackground for the main thread should be shut down via "
+             "ChildImpl::Shutdown().");
+
+  if (sThreadLocalIndex != kBadThreadLocalIndex) {
+    CloseThreadWithIndex(sThreadLocalIndex);
+  }
+  if (sThreadLocalIndexForSocketProcess != kBadThreadLocalIndex) {
+    CloseThreadWithIndex(sThreadLocalIndexForSocketProcess);
+  }
+}
+
+// static
 BackgroundChildImpl::ThreadLocal* ChildImpl::GetThreadLocalForCurrentThread() {
   MOZ_ASSERT(sThreadLocalIndex != kBadThreadLocalIndex,
              "BackgroundChild::Startup() was never called!");
 
   auto threadLocalInfo = NS_IsMainThread()
                              ? sMainThreadInfo
                              : static_cast<ThreadLocalInfo*>(
                                    PR_GetThreadPrivate(sThreadLocalIndex));
@@ -1536,21 +1734,22 @@ ChildImpl::ShutdownObserver::Observe(nsI
   ChildImpl::Shutdown();
 
   return NS_OK;
 }
 
 // static
 already_AddRefed<ChildImpl::SendInitBackgroundRunnable>
 ChildImpl::SendInitBackgroundRunnable::Create(
-    Endpoint<PBackgroundParent>&& aParent) {
+    Endpoint<PBackgroundParent>&& aParent,
+    std::function<void(Endpoint<PBackgroundParent>&& aParent)>&& aFunc) {
   MOZ_ASSERT(!NS_IsMainThread());
 
   RefPtr<SendInitBackgroundRunnable> runnable =
-      new SendInitBackgroundRunnable(std::move(aParent));
+      new SendInitBackgroundRunnable(std::move(aParent), std::move(aFunc));
 
   WorkerPrivate* workerPrivate = mozilla::dom::GetCurrentThreadWorkerPrivate();
   if (!workerPrivate) {
     return runnable.forget();
   }
 
   workerPrivate->AssertIsOnWorkerThread();
 
@@ -1567,22 +1766,17 @@ NS_IMETHODIMP
 ChildImpl::SendInitBackgroundRunnable::Run() {
   if (NS_IsMainThread()) {
     if (mSentInitBackground) {
       return NS_OK;
     }
 
     mSentInitBackground = true;
 
-    RefPtr<ContentChild> content = ContentChild::GetSingleton();
-    MOZ_ASSERT(content);
-
-    if (!content->SendInitBackground(std::move(mParent))) {
-      MOZ_CRASH("Failed to create top level actor!");
-    }
+    mSendInitfunc(std::move(mParent));
 
     nsCOMPtr<nsISerialEventTarget> owningEventTarget;
     {
       mozilla::MutexAutoLock lock(mMutex);
       owningEventTarget = mOwningEventTarget;
     }
 
     if (!owningEventTarget) {
--- a/ipc/glue/BackgroundParent.h
+++ b/ipc/glue/BackgroundParent.h
@@ -10,16 +10,23 @@
 #include "base/process.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ipc/Transport.h"
 
 template <class>
 struct already_AddRefed;
 
 namespace mozilla {
+
+namespace net {
+
+class SocketProcessBridgeParent;
+
+}  // namespace net
+
 namespace dom {
 
 class BlobImpl;
 class ContentParent;
 
 }  // namespace dom
 
 namespace ipc {
@@ -33,16 +40,17 @@ class Endpoint;
 // member functions.
 class BackgroundParent final {
   friend class mozilla::dom::ContentParent;
 
   typedef base::ProcessId ProcessId;
   typedef mozilla::dom::BlobImpl BlobImpl;
   typedef mozilla::dom::ContentParent ContentParent;
   typedef mozilla::ipc::Transport Transport;
+  friend class mozilla::net::SocketProcessBridgeParent;
 
  public:
   // This function allows the caller to determine if the given parent actor
   // corresponds to a child actor from another process or a child actor from a
   // different thread in the same process.
   // This function may only be called on the background thread.
   static bool IsOtherProcessActor(PBackgroundParent* aBackgroundActor);
 
@@ -68,16 +76,19 @@ class BackgroundParent final {
 
   static bool GetLiveActorArray(PBackgroundParent* aBackgroundActor,
                                 nsTArray<PBackgroundParent*>& aLiveActorArray);
 
  private:
   // Only called by ContentParent for cross-process actors.
   static bool Alloc(ContentParent* aContent,
                     Endpoint<PBackgroundParent>&& aEndpoint);
+
+  // Only called by SocketProcessBridgeParent for cross-process actors.
+  static bool Alloc(Endpoint<PBackgroundParent>&& aEndpoint);
 };
 
 // Implemented in BackgroundImpl.cpp.
 bool IsOnBackgroundThread();
 
 #ifdef DEBUG
 
 // Implemented in BackgroundImpl.cpp.
@@ -86,12 +97,16 @@ void AssertIsOnBackgroundThread();
 #else
 
 inline void AssertIsOnBackgroundThread() {}
 
 #endif  // DEBUG
 
 inline void AssertIsInMainProcess() { MOZ_ASSERT(XRE_IsParentProcess()); }
 
+inline void AssertIsInMainOrSocketProcess() {
+  MOZ_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess());
+}
+
 }  // namespace ipc
 }  // namespace mozilla
 
 #endif  // mozilla_ipc_backgroundparent_h__
--- a/ipc/glue/BackgroundParentImpl.cpp
+++ b/ipc/glue/BackgroundParentImpl.cpp
@@ -113,359 +113,359 @@ class TestParent final : public mozilla:
 namespace mozilla {
 namespace ipc {
 
 using mozilla::dom::BroadcastChannelParent;
 using mozilla::dom::ContentParent;
 using mozilla::dom::ServiceWorkerRegistrationData;
 
 BackgroundParentImpl::BackgroundParentImpl() {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnMainThread();
 
   MOZ_COUNT_CTOR(mozilla::ipc::BackgroundParentImpl);
 }
 
 BackgroundParentImpl::~BackgroundParentImpl() {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnMainThread();
 
   MOZ_COUNT_DTOR(mozilla::ipc::BackgroundParentImpl);
 }
 
 void BackgroundParentImpl::ActorDestroy(ActorDestroyReason aWhy) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
 }
 
 BackgroundParentImpl::PBackgroundTestParent*
 BackgroundParentImpl::AllocPBackgroundTestParent(const nsCString& aTestArg) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
 
   return new TestParent();
 }
 
 mozilla::ipc::IPCResult BackgroundParentImpl::RecvPBackgroundTestConstructor(
     PBackgroundTestParent* aActor, const nsCString& aTestArg) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   if (!PBackgroundTestParent::Send__delete__(aActor, aTestArg)) {
     return IPC_FAIL_NO_REASON(this);
   }
   return IPC_OK();
 }
 
 bool BackgroundParentImpl::DeallocPBackgroundTestParent(
     PBackgroundTestParent* aActor) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   delete static_cast<TestParent*>(aActor);
   return true;
 }
 
 auto BackgroundParentImpl::AllocPBackgroundIDBFactoryParent(
     const LoggingInfo& aLoggingInfo) -> PBackgroundIDBFactoryParent* {
   using mozilla::dom::indexedDB::AllocPBackgroundIDBFactoryParent;
 
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
 
   return AllocPBackgroundIDBFactoryParent(aLoggingInfo);
 }
 
 mozilla::ipc::IPCResult
 BackgroundParentImpl::RecvPBackgroundIDBFactoryConstructor(
     PBackgroundIDBFactoryParent* aActor, const LoggingInfo& aLoggingInfo) {
   using mozilla::dom::indexedDB::RecvPBackgroundIDBFactoryConstructor;
 
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   if (!RecvPBackgroundIDBFactoryConstructor(aActor, aLoggingInfo)) {
     return IPC_FAIL_NO_REASON(this);
   }
   return IPC_OK();
 }
 
 bool BackgroundParentImpl::DeallocPBackgroundIDBFactoryParent(
     PBackgroundIDBFactoryParent* aActor) {
   using mozilla::dom::indexedDB::DeallocPBackgroundIDBFactoryParent;
 
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   return DeallocPBackgroundIDBFactoryParent(aActor);
 }
 
 auto BackgroundParentImpl::AllocPBackgroundIndexedDBUtilsParent()
     -> PBackgroundIndexedDBUtilsParent* {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
 
   return mozilla::dom::indexedDB::AllocPBackgroundIndexedDBUtilsParent();
 }
 
 bool BackgroundParentImpl::DeallocPBackgroundIndexedDBUtilsParent(
     PBackgroundIndexedDBUtilsParent* aActor) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   return mozilla::dom::indexedDB::DeallocPBackgroundIndexedDBUtilsParent(
       aActor);
 }
 
 mozilla::ipc::IPCResult BackgroundParentImpl::RecvFlushPendingFileDeletions() {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
 
   if (!mozilla::dom::indexedDB::RecvFlushPendingFileDeletions()) {
     return IPC_FAIL_NO_REASON(this);
   }
   return IPC_OK();
 }
 
 BackgroundParentImpl::PBackgroundSDBConnectionParent*
 BackgroundParentImpl::AllocPBackgroundSDBConnectionParent(
     const PrincipalInfo& aPrincipalInfo) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
 
   return mozilla::dom::AllocPBackgroundSDBConnectionParent(aPrincipalInfo);
 }
 
 mozilla::ipc::IPCResult
 BackgroundParentImpl::RecvPBackgroundSDBConnectionConstructor(
     PBackgroundSDBConnectionParent* aActor,
     const PrincipalInfo& aPrincipalInfo) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   if (!mozilla::dom::RecvPBackgroundSDBConnectionConstructor(aActor,
                                                              aPrincipalInfo)) {
     return IPC_FAIL_NO_REASON(this);
   }
   return IPC_OK();
 }
 
 bool BackgroundParentImpl::DeallocPBackgroundSDBConnectionParent(
     PBackgroundSDBConnectionParent* aActor) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   return mozilla::dom::DeallocPBackgroundSDBConnectionParent(aActor);
 }
 
 BackgroundParentImpl::PBackgroundLSDatabaseParent*
 BackgroundParentImpl::AllocPBackgroundLSDatabaseParent(
     const PrincipalInfo& aPrincipalInfo, const uint32_t& aPrivateBrowsingId,
     const uint64_t& aDatastoreId) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
 
   return mozilla::dom::AllocPBackgroundLSDatabaseParent(
       aPrincipalInfo, aPrivateBrowsingId, aDatastoreId);
 }
 
 mozilla::ipc::IPCResult
 BackgroundParentImpl::RecvPBackgroundLSDatabaseConstructor(
     PBackgroundLSDatabaseParent* aActor, const PrincipalInfo& aPrincipalInfo,
     const uint32_t& aPrivateBrowsingId, const uint64_t& aDatastoreId) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   if (!mozilla::dom::RecvPBackgroundLSDatabaseConstructor(
           aActor, aPrincipalInfo, aPrivateBrowsingId, aDatastoreId)) {
     return IPC_FAIL_NO_REASON(this);
   }
   return IPC_OK();
 }
 
 bool BackgroundParentImpl::DeallocPBackgroundLSDatabaseParent(
     PBackgroundLSDatabaseParent* aActor) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   return mozilla::dom::DeallocPBackgroundLSDatabaseParent(aActor);
 }
 
 BackgroundParentImpl::PBackgroundLSObserverParent*
 BackgroundParentImpl::AllocPBackgroundLSObserverParent(
     const uint64_t& aObserverId) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
 
   return mozilla::dom::AllocPBackgroundLSObserverParent(aObserverId);
 }
 
 mozilla::ipc::IPCResult
 BackgroundParentImpl::RecvPBackgroundLSObserverConstructor(
     PBackgroundLSObserverParent* aActor, const uint64_t& aObserverId) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   if (!mozilla::dom::RecvPBackgroundLSObserverConstructor(aActor,
                                                           aObserverId)) {
     return IPC_FAIL_NO_REASON(this);
   }
   return IPC_OK();
 }
 
 bool BackgroundParentImpl::DeallocPBackgroundLSObserverParent(
     PBackgroundLSObserverParent* aActor) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   return mozilla::dom::DeallocPBackgroundLSObserverParent(aActor);
 }
 
 BackgroundParentImpl::PBackgroundLSRequestParent*
 BackgroundParentImpl::AllocPBackgroundLSRequestParent(
     const LSRequestParams& aParams) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
 
   return mozilla::dom::AllocPBackgroundLSRequestParent(this, aParams);
 }
 
 mozilla::ipc::IPCResult
 BackgroundParentImpl::RecvPBackgroundLSRequestConstructor(
     PBackgroundLSRequestParent* aActor, const LSRequestParams& aParams) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   if (!mozilla::dom::RecvPBackgroundLSRequestConstructor(aActor, aParams)) {
     return IPC_FAIL_NO_REASON(this);
   }
   return IPC_OK();
 }
 
 bool BackgroundParentImpl::DeallocPBackgroundLSRequestParent(
     PBackgroundLSRequestParent* aActor) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   return mozilla::dom::DeallocPBackgroundLSRequestParent(aActor);
 }
 
 BackgroundParentImpl::PBackgroundLSSimpleRequestParent*
 BackgroundParentImpl::AllocPBackgroundLSSimpleRequestParent(
     const LSSimpleRequestParams& aParams) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
 
   return mozilla::dom::AllocPBackgroundLSSimpleRequestParent(aParams);
 }
 
 mozilla::ipc::IPCResult
 BackgroundParentImpl::RecvPBackgroundLSSimpleRequestConstructor(
     PBackgroundLSSimpleRequestParent* aActor,
     const LSSimpleRequestParams& aParams) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   if (!mozilla::dom::RecvPBackgroundLSSimpleRequestConstructor(aActor,
                                                                aParams)) {
     return IPC_FAIL_NO_REASON(this);
   }
   return IPC_OK();
 }
 
 bool BackgroundParentImpl::DeallocPBackgroundLSSimpleRequestParent(
     PBackgroundLSSimpleRequestParent* aActor) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   return mozilla::dom::DeallocPBackgroundLSSimpleRequestParent(aActor);
 }
 
 BackgroundParentImpl::PBackgroundLocalStorageCacheParent*
 BackgroundParentImpl::AllocPBackgroundLocalStorageCacheParent(
     const PrincipalInfo& aPrincipalInfo, const nsCString& aOriginKey,
     const uint32_t& aPrivateBrowsingId) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
 
   return mozilla::dom::AllocPBackgroundLocalStorageCacheParent(
       aPrincipalInfo, aOriginKey, aPrivateBrowsingId);
 }
 
 mozilla::ipc::IPCResult
 BackgroundParentImpl::RecvPBackgroundLocalStorageCacheConstructor(
     PBackgroundLocalStorageCacheParent* aActor,
     const PrincipalInfo& aPrincipalInfo, const nsCString& aOriginKey,
     const uint32_t& aPrivateBrowsingId) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   return mozilla::dom::RecvPBackgroundLocalStorageCacheConstructor(
       this, aActor, aPrincipalInfo, aOriginKey, aPrivateBrowsingId);
 }
 
 bool BackgroundParentImpl::DeallocPBackgroundLocalStorageCacheParent(
     PBackgroundLocalStorageCacheParent* aActor) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   return mozilla::dom::DeallocPBackgroundLocalStorageCacheParent(aActor);
 }
 
 auto BackgroundParentImpl::AllocPBackgroundStorageParent(
     const nsString& aProfilePath) -> PBackgroundStorageParent* {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
 
   return mozilla::dom::AllocPBackgroundStorageParent(aProfilePath);
 }
 
 mozilla::ipc::IPCResult BackgroundParentImpl::RecvPBackgroundStorageConstructor(
     PBackgroundStorageParent* aActor, const nsString& aProfilePath) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   return mozilla::dom::RecvPBackgroundStorageConstructor(aActor, aProfilePath);
 }
 
 bool BackgroundParentImpl::DeallocPBackgroundStorageParent(
     PBackgroundStorageParent* aActor) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   return mozilla::dom::DeallocPBackgroundStorageParent(aActor);
 }
 
 PPendingIPCBlobParent* BackgroundParentImpl::AllocPPendingIPCBlobParent(
     const IPCBlob& aBlob) {
   MOZ_CRASH("PPendingIPCBlobParent actors should be manually constructed!");
 }
 
 bool BackgroundParentImpl::DeallocPPendingIPCBlobParent(
     PPendingIPCBlobParent* aActor) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   delete aActor;
   return true;
 }
 
 mozilla::dom::PRemoteWorkerParent*
@@ -540,17 +540,17 @@ mozilla::ipc::IPCResult BackgroundParent
 bool BackgroundParentImpl::DeallocPTemporaryIPCBlobParent(
     PTemporaryIPCBlobParent* aActor) {
   delete aActor;
   return true;
 }
 
 PIPCBlobInputStreamParent* BackgroundParentImpl::AllocPIPCBlobInputStreamParent(
     const nsID& aID, const uint64_t& aSize) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
 
   RefPtr<mozilla::dom::IPCBlobInputStreamParent> actor =
       mozilla::dom::IPCBlobInputStreamParent::Create(aID, aSize, this);
   return actor.forget().take();
 }
 
 mozilla::ipc::IPCResult
@@ -561,36 +561,36 @@ BackgroundParentImpl::RecvPIPCBlobInputS
     return IPC_FAIL_NO_REASON(this);
   }
 
   return IPC_OK();
 }
 
 bool BackgroundParentImpl::DeallocPIPCBlobInputStreamParent(
     PIPCBlobInputStreamParent* aActor) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   RefPtr<mozilla::dom::IPCBlobInputStreamParent> actor =
       dont_AddRef(static_cast<mozilla::dom::IPCBlobInputStreamParent*>(aActor));
   return true;
 }
 
 PFileDescriptorSetParent* BackgroundParentImpl::AllocPFileDescriptorSetParent(
     const FileDescriptor& aFileDescriptor) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
 
   return new FileDescriptorSetParent(aFileDescriptor);
 }
 
 bool BackgroundParentImpl::DeallocPFileDescriptorSetParent(
     PFileDescriptorSetParent* aActor) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   delete static_cast<FileDescriptorSetParent*>(aActor);
   return true;
 }
 
 PChildToParentStreamParent*
@@ -612,53 +612,53 @@ BackgroundParentImpl::AllocPParentToChil
 
 bool BackgroundParentImpl::DeallocPParentToChildStreamParent(
     PParentToChildStreamParent* aActor) {
   delete aActor;
   return true;
 }
 
 BackgroundParentImpl::PVsyncParent* BackgroundParentImpl::AllocPVsyncParent() {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
 
   RefPtr<mozilla::layout::VsyncParent> actor =
       mozilla::layout::VsyncParent::Create();
   // There still has one ref-count after return, and it will be released in
   // DeallocPVsyncParent().
   return actor.forget().take();
 }
 
 bool BackgroundParentImpl::DeallocPVsyncParent(PVsyncParent* aActor) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   // This actor already has one ref-count. Please check AllocPVsyncParent().
   RefPtr<mozilla::layout::VsyncParent> actor =
       dont_AddRef(static_cast<mozilla::layout::VsyncParent*>(aActor));
   return true;
 }
 
 camera::PCamerasParent* BackgroundParentImpl::AllocPCamerasParent() {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
 
 #ifdef MOZ_WEBRTC
   RefPtr<mozilla::camera::CamerasParent> actor =
       mozilla::camera::CamerasParent::Create();
   return actor.forget().take();
 #else
   return nullptr;
 #endif
 }
 
 bool BackgroundParentImpl::DeallocPCamerasParent(
     camera::PCamerasParent* aActor) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
 #ifdef MOZ_WEBRTC
   RefPtr<mozilla::camera::CamerasParent> actor =
       dont_AddRef(static_cast<mozilla::camera::CamerasParent*>(aActor));
 #endif
   return true;
@@ -670,17 +670,17 @@ auto BackgroundParentImpl::AllocPUDPSock
   RefPtr<UDPSocketParent> p = new UDPSocketParent(this);
 
   return p.forget().take();
 }
 
 mozilla::ipc::IPCResult BackgroundParentImpl::RecvPUDPSocketConstructor(
     PUDPSocketParent* aActor, const OptionalPrincipalInfo& aOptionalPrincipal,
     const nsCString& aFilter) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
 
   if (aOptionalPrincipal.type() == OptionalPrincipalInfo::TPrincipalInfo) {
     // Support for checking principals (for non-mtransport use) will be handled
     // in bug 1167039
     return IPC_FAIL_NO_REASON(this);
   }
   // No principal - This must be from mtransport (WebRTC/ICE) - We'd want
@@ -708,17 +708,17 @@ bool BackgroundParentImpl::DeallocPUDPSo
   p->Release();
   return true;
 }
 
 mozilla::dom::PBroadcastChannelParent*
 BackgroundParentImpl::AllocPBroadcastChannelParent(
     const PrincipalInfo& aPrincipalInfo, const nsCString& aOrigin,
     const nsString& aChannel) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
 
   nsString originChannelKey;
 
   // The format of originChannelKey is:
   //  <channelName>|<origin+OriginAttributes>
 
   originChannelKey.Assign(aChannel);
@@ -745,17 +745,17 @@ class CheckPrincipalRunnable final : pub
  public:
   CheckPrincipalRunnable(already_AddRefed<ContentParent> aParent,
                          const PrincipalInfo& aPrincipalInfo,
                          const nsCString& aOrigin)
       : Runnable("ipc::CheckPrincipalRunnable"),
         mContentParent(aParent),
         mPrincipalInfo(aPrincipalInfo),
         mOrigin(aOrigin) {
-    AssertIsInMainProcess();
+    AssertIsInMainOrSocketProcess();
     AssertIsOnBackgroundThread();
 
     MOZ_ASSERT(mContentParent);
   }
 
   NS_IMETHOD Run() override {
     MOZ_ASSERT(NS_IsMainThread());
 
@@ -786,17 +786,17 @@ class CheckPrincipalRunnable final : pub
   nsCString mOrigin;
 };
 
 }  // namespace
 
 mozilla::ipc::IPCResult BackgroundParentImpl::RecvPBroadcastChannelConstructor(
     PBroadcastChannelParent* actor, const PrincipalInfo& aPrincipalInfo,
     const nsCString& aOrigin, const nsString& aChannel) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
 
   RefPtr<ContentParent> parent = BackgroundParent::GetContentParent(this);
 
   // If the ContentParent is null we are dealing with a same-process actor.
   if (!parent) {
     return IPC_OK();
   }
@@ -805,49 +805,49 @@ mozilla::ipc::IPCResult BackgroundParent
       new CheckPrincipalRunnable(parent.forget(), aPrincipalInfo, aOrigin);
   MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable));
 
   return IPC_OK();
 }
 
 bool BackgroundParentImpl::DeallocPBroadcastChannelParent(
     PBroadcastChannelParent* aActor) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   delete static_cast<BroadcastChannelParent*>(aActor);
   return true;
 }
 
 mozilla::dom::PServiceWorkerManagerParent*
 BackgroundParentImpl::AllocPServiceWorkerManagerParent() {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
 
   RefPtr<dom::ServiceWorkerManagerParent> agent =
       new dom::ServiceWorkerManagerParent();
   return agent.forget().take();
 }
 
 bool BackgroundParentImpl::DeallocPServiceWorkerManagerParent(
     PServiceWorkerManagerParent* aActor) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   RefPtr<dom::ServiceWorkerManagerParent> parent =
       dont_AddRef(static_cast<dom::ServiceWorkerManagerParent*>(aActor));
   MOZ_ASSERT(parent);
   return true;
 }
 
 mozilla::ipc::IPCResult
 BackgroundParentImpl::RecvShutdownServiceWorkerRegistrar() {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
 
   if (BackgroundParent::IsOtherProcessActor(this)) {
     return IPC_FAIL_NO_REASON(this);
   }
 
   RefPtr<dom::ServiceWorkerRegistrar> service =
       dom::ServiceWorkerRegistrar::Get();
@@ -889,96 +889,96 @@ bool BackgroundParentImpl::DeallocPCache
     PCacheStreamControlParent* aActor) {
   dom::cache::DeallocPCacheStreamControlParent(aActor);
   return true;
 }
 
 PMessagePortParent* BackgroundParentImpl::AllocPMessagePortParent(
     const nsID& aUUID, const nsID& aDestinationUUID,
     const uint32_t& aSequenceID) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
 
   return new MessagePortParent(aUUID);
 }
 
 mozilla::ipc::IPCResult BackgroundParentImpl::RecvPMessagePortConstructor(
     PMessagePortParent* aActor, const nsID& aUUID, const nsID& aDestinationUUID,
     const uint32_t& aSequenceID) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
 
   MessagePortParent* mp = static_cast<MessagePortParent*>(aActor);
   if (!mp->Entangle(aDestinationUUID, aSequenceID)) {
     return IPC_FAIL_NO_REASON(this);
   }
   return IPC_OK();
 }
 
 bool BackgroundParentImpl::DeallocPMessagePortParent(
     PMessagePortParent* aActor) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   delete static_cast<MessagePortParent*>(aActor);
   return true;
 }
 
 mozilla::ipc::IPCResult BackgroundParentImpl::RecvMessagePortForceClose(
     const nsID& aUUID, const nsID& aDestinationUUID,
     const uint32_t& aSequenceID) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
 
   if (!MessagePortParent::ForceClose(aUUID, aDestinationUUID, aSequenceID)) {
     return IPC_FAIL_NO_REASON(this);
   }
   return IPC_OK();
 }
 
 PAsmJSCacheEntryParent* BackgroundParentImpl::AllocPAsmJSCacheEntryParent(
     const dom::asmjscache::OpenMode& aOpenMode,
     const dom::asmjscache::WriteParams& aWriteParams,
     const PrincipalInfo& aPrincipalInfo) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
 
   return dom::asmjscache::AllocEntryParent(aOpenMode, aWriteParams,
                                            aPrincipalInfo);
 }
 
 bool BackgroundParentImpl::DeallocPAsmJSCacheEntryParent(
     PAsmJSCacheEntryParent* aActor) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
 
   dom::asmjscache::DeallocEntryParent(aActor);
   return true;
 }
 
 BackgroundParentImpl::PQuotaParent* BackgroundParentImpl::AllocPQuotaParent() {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
 
   return mozilla::dom::quota::AllocPQuotaParent();
 }
 
 bool BackgroundParentImpl::DeallocPQuotaParent(PQuotaParent* aActor) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   return mozilla::dom::quota::DeallocPQuotaParent(aActor);
 }
 
 dom::PFileSystemRequestParent*
 BackgroundParentImpl::AllocPFileSystemRequestParent(
     const FileSystemParams& aParams) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
 
   RefPtr<FileSystemRequestParent> result = new FileSystemRequestParent();
 
   if (NS_WARN_IF(!result->Initialize(aParams))) {
     return nullptr;
   }
 
@@ -988,17 +988,17 @@ BackgroundParentImpl::AllocPFileSystemRe
 mozilla::ipc::IPCResult BackgroundParentImpl::RecvPFileSystemRequestConstructor(
     PFileSystemRequestParent* aActor, const FileSystemParams& params) {
   static_cast<FileSystemRequestParent*>(aActor)->Start();
   return IPC_OK();
 }
 
 bool BackgroundParentImpl::DeallocPFileSystemRequestParent(
     PFileSystemRequestParent* aDoomed) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
 
   RefPtr<FileSystemRequestParent> parent =
       dont_AddRef(static_cast<FileSystemRequestParent*>(aDoomed));
   return true;
 }
 
 // Gamepad API Background IPC
@@ -1044,89 +1044,89 @@ bool BackgroundParentImpl::DeallocPWebAu
   MOZ_ASSERT(aActor);
   delete aActor;
   return true;
 }
 
 net::PHttpBackgroundChannelParent*
 BackgroundParentImpl::AllocPHttpBackgroundChannelParent(
     const uint64_t& aChannelId) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
 
   RefPtr<net::HttpBackgroundChannelParent> actor =
       new net::HttpBackgroundChannelParent();
 
   // hold extra refcount for IPDL
   return actor.forget().take();
 }
 
 mozilla::ipc::IPCResult
 BackgroundParentImpl::RecvPHttpBackgroundChannelConstructor(
     net::PHttpBackgroundChannelParent* aActor, const uint64_t& aChannelId) {
   MOZ_ASSERT(aActor);
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
 
   net::HttpBackgroundChannelParent* aParent =
       static_cast<net::HttpBackgroundChannelParent*>(aActor);
 
   if (NS_WARN_IF(NS_FAILED(aParent->Init(aChannelId)))) {
     return IPC_FAIL_NO_REASON(this);
   }
 
   return IPC_OK();
 }
 
 bool BackgroundParentImpl::DeallocPHttpBackgroundChannelParent(
     net::PHttpBackgroundChannelParent* aActor) {
   MOZ_ASSERT(aActor);
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
 
   // release extra refcount hold by AllocPHttpBackgroundChannelParent
   RefPtr<net::HttpBackgroundChannelParent> actor =
       dont_AddRef(static_cast<net::HttpBackgroundChannelParent*>(aActor));
 
   return true;
 }
 
 PMIDIPortParent* BackgroundParentImpl::AllocPMIDIPortParent(
     const MIDIPortInfo& aPortInfo, const bool& aSysexEnabled) {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
 
   RefPtr<MIDIPortParent> result = new MIDIPortParent(aPortInfo, aSysexEnabled);
   return result.forget().take();
 }
 
 bool BackgroundParentImpl::DeallocPMIDIPortParent(PMIDIPortParent* aActor) {
   MOZ_ASSERT(aActor);
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
 
   RefPtr<MIDIPortParent> parent =
       dont_AddRef(static_cast<MIDIPortParent*>(aActor));
   parent->Teardown();
   return true;
 }
 
 PMIDIManagerParent* BackgroundParentImpl::AllocPMIDIManagerParent() {
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
 
   RefPtr<MIDIManagerParent> result = new MIDIManagerParent();
   MIDIPlatformService::Get()->AddManager(result);
   return result.forget().take();
 }
 
 bool BackgroundParentImpl::DeallocPMIDIManagerParent(
     PMIDIManagerParent* aActor) {
   MOZ_ASSERT(aActor);
-  AssertIsInMainProcess();
+  AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
 
   RefPtr<MIDIManagerParent> parent =
       dont_AddRef(static_cast<MIDIManagerParent*>(aActor));
   parent->Teardown();
   return true;
 }
 
@@ -1240,11 +1240,11 @@ mozilla::ipc::IPCResult BackgroundParent
 
   return IPC_OK();
 }
 
 }  // namespace ipc
 }  // namespace mozilla
 
 void TestParent::ActorDestroy(ActorDestroyReason aWhy) {
-  mozilla::ipc::AssertIsInMainProcess();
+  mozilla::ipc::AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
 }
--- a/ipc/glue/CrashReporterHost.cpp
+++ b/ipc/glue/CrashReporterHost.cpp
@@ -31,19 +31,21 @@ static_assert(nsICrashService::PROCESS_T
 static_assert(nsICrashService::PROCESS_TYPE_GMPLUGIN == (int)GeckoProcessType_GMPlugin,
               "GeckoProcessType enum is out of sync with nsICrashService!");
 static_assert(nsICrashService::PROCESS_TYPE_GPU == (int)GeckoProcessType_GPU,
               "GeckoProcessType enum is out of sync with nsICrashService!");
 static_assert(nsICrashService::PROCESS_TYPE_VR == (int)GeckoProcessType_VR,
               "GeckoProcessType enum is out of sync with nsICrashService!");
 static_assert(nsICrashService::PROCESS_TYPE_RDD == (int)GeckoProcessType_RDD,
               "GeckoProcessType enum is out of sync with nsICrashService!");
+static_assert(nsICrashService::PROCESS_TYPE_SOCKET == (int)GeckoProcessType_Socket,
+              "GeckoProcessType enum is out of sync with nsICrashService!");
 // Add new static asserts here if you add more process types.
 // Update this static assert as well.
-static_assert(nsICrashService::PROCESS_TYPE_RDD + 1 == (int)GeckoProcessType_End,
+static_assert(nsICrashService::PROCESS_TYPE_SOCKET + 1 == (int)GeckoProcessType_End,
               "GeckoProcessType enum is out of sync with nsICrashService!");
 
 
 namespace mozilla {
 namespace ipc {
 
 CrashReporterHost::CrashReporterHost(GeckoProcessType aProcessType,
                                      const Shmem& aShmem,
--- a/ipc/glue/GeckoChildProcessHost.cpp
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -149,17 +149,18 @@ GeckoChildProcessHost::~GeckoChildProces
 
 // static
 auto GeckoChildProcessHost::GetPathToBinary(FilePath& exePath,
                                             GeckoProcessType processType)
     -> BinaryPathType {
   if (sRunSelfAsContentProc && (processType == GeckoProcessType_Content ||
                                 processType == GeckoProcessType_GPU ||
                                 processType == GeckoProcessType_VR ||
-                                processType == GeckoProcessType_RDD)) {
+                                processType == GeckoProcessType_RDD ||
+                                processType == GeckoProcessType_Socket)) {
 #if defined(OS_WIN)
     wchar_t exePathBuf[MAXPATHLEN];
     if (!::GetModuleFileNameW(nullptr, exePathBuf, MAXPATHLEN)) {
       MOZ_CRASH("GetModuleFileNameW failed (FIXME)");
     }
 #if defined(MOZ_SANDBOX)
     // We need to start the child process using the real path, so that the
     // sandbox policy rules will match for DLLs loaded from the bin dir after
@@ -971,16 +972,19 @@ bool GeckoChildProcessHost::PerformAsync
     case GeckoProcessType_RDD:
       if (!PR_GetEnv("MOZ_DISABLE_RDD_SANDBOX")) {
         if (!mSandboxBroker.SetSecurityLevelForRDDProcess()) {
           return false;
         }
         shouldSandboxCurrentProcess = true;
       }
       break;
+    case GeckoProcessType_Socket:
+      // TODO - setup sandboxing for the socket process.
+      break;
     case GeckoProcessType_Default:
     default:
       MOZ_CRASH("Bad process type in GeckoChildProcessHost");
       break;
   };
 
   if (shouldSandboxCurrentProcess) {
     for (auto it = mAllowedFilesRead.begin(); it != mAllowedFilesRead.end();
--- a/ipc/glue/ProcessUtils.h
+++ b/ipc/glue/ProcessUtils.h
@@ -2,19 +2,77 @@
 /* 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_ipc_ProcessUtils_h
 #define mozilla_ipc_ProcessUtils_h
 
+#include "FileDescriptor.h"
+#include "base/shared_memory.h"
+
 namespace mozilla {
 namespace ipc {
 
 // You probably should call ContentChild::SetProcessName instead of calling
 // this directly.
-void SetThisProcessName(const char *aName);
+void SetThisProcessName(const char* aName);
+
+class SharedPreferenceSerializer final {
+ public:
+  SharedPreferenceSerializer();
+  SharedPreferenceSerializer(SharedPreferenceSerializer&& aOther);
+  ~SharedPreferenceSerializer();
+
+  bool SerializeToSharedMemory();
+
+  base::SharedMemoryHandle GetSharedMemoryHandle() const {
+    return mShm.handle();
+  }
+
+  const FileDescriptor::UniquePlatformHandle& GetPrefMapHandle() const {
+    return mPrefMapHandle;
+  }
+
+  nsACString::size_type GetPrefLength() const { return mPrefs.Length(); }
+
+  size_t GetPrefMapSize() const { return mPrefMapSize; }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SharedPreferenceSerializer);
+  size_t mPrefMapSize;
+  FileDescriptor::UniquePlatformHandle mPrefMapHandle;
+  base::SharedMemory mShm;
+  nsAutoCStringN<1024> mPrefs;
+};
+
+class SharedPreferenceDeserializer final {
+ public:
+  SharedPreferenceDeserializer();
+  ~SharedPreferenceDeserializer();
+
+  bool DeserializeFromSharedMemory(char* aPrefsHandleStr,
+                                   char* aPrefMapHandleStr, char* aPrefsLenStr,
+                                   char* aPrefMapSizeStr);
+
+  const base::SharedMemoryHandle& GetPrefsHandle() const;
+  const FileDescriptor& GetPrefMapHandle() const;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SharedPreferenceDeserializer);
+  Maybe<base::SharedMemoryHandle> mPrefsHandle;
+  Maybe<FileDescriptor> mPrefMapHandle;
+  Maybe<size_t> mPrefsLen;
+  Maybe<size_t> mPrefMapSize;
+};
+
+#ifdef ANDROID
+// Android doesn't use -prefsHandle or -prefMapHandle. It gets those FDs
+// another way.
+void SetPrefsFd(int aFd);
+void SetPrefMapFd(int aFd);
+#endif
 
 }  // namespace ipc
 }  // namespace mozilla
 
 #endif  // ifndef mozilla_ipc_ProcessUtils_h
new file mode 100644
--- /dev/null
+++ b/ipc/glue/ProcessUtils_common.cpp
@@ -0,0 +1,171 @@
+/* -*- 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 "ProcessUtils.h"
+
+#include "mozilla/Preferences.h"
+
+namespace mozilla {
+namespace ipc {
+
+SharedPreferenceSerializer::SharedPreferenceSerializer() : mPrefMapSize(0) {
+  MOZ_COUNT_CTOR(SharedPreferenceSerializer);
+}
+
+SharedPreferenceSerializer::~SharedPreferenceSerializer() {
+  MOZ_COUNT_DTOR(SharedPreferenceSerializer);
+}
+
+SharedPreferenceSerializer::SharedPreferenceSerializer(
+    SharedPreferenceSerializer&& aOther)
+    : mPrefMapSize(aOther.mPrefMapSize),
+      mPrefMapHandle(std::move(aOther.mPrefMapHandle)),
+      mShm(std::move(aOther.mShm)),
+      mPrefs(std::move(aOther.mPrefs)) {
+  MOZ_COUNT_CTOR(SharedPreferenceSerializer);
+}
+
+bool SharedPreferenceSerializer::SerializeToSharedMemory() {
+  mPrefMapHandle =
+      Preferences::EnsureSnapshot(&mPrefMapSize).ClonePlatformHandle();
+
+  // Serialize the early prefs.
+  Preferences::SerializePreferences(mPrefs);
+
+  // Set up the shared memory.
+  if (!mShm.Create(mPrefs.Length())) {
+    NS_ERROR("failed to create shared memory in the parent");
+    return false;
+  }
+  if (!mShm.Map(mPrefs.Length())) {
+    NS_ERROR("failed to map shared memory in the parent");
+    return false;
+  }
+
+  // Copy the serialized prefs into the shared memory.
+  memcpy(static_cast<char*>(mShm.memory()), mPrefs.get(), mPrefs.Length());
+
+  return true;
+}
+
+#ifdef ANDROID
+static int gPrefsFd = -1;
+static int gPrefMapFd = -1;
+
+void SetPrefsFd(int aFd) { gPrefsFd = aFd; }
+
+void SetPrefMapFd(int aFd) { gPrefMapFd = aFd; }
+#endif
+
+SharedPreferenceDeserializer::SharedPreferenceDeserializer() {
+  MOZ_COUNT_CTOR(SharedPreferenceDeserializer);
+}
+
+SharedPreferenceDeserializer::~SharedPreferenceDeserializer() {
+  MOZ_COUNT_DTOR(SharedPreferenceDeserializer);
+}
+
+bool SharedPreferenceDeserializer::DeserializeFromSharedMemory(
+    char* aPrefsHandleStr, char* aPrefMapHandleStr, char* aPrefsLenStr,
+    char* aPrefMapSizeStr) {
+  // Parses an arg containing a pointer-sized-integer.
+  auto parseUIntPtrArg = [](char*& aArg) {
+    // ContentParent uses %zu to print a word-sized unsigned integer. So
+    // even though strtoull() returns a long long int, it will fit in a
+    // uintptr_t.
+    return uintptr_t(strtoull(aArg, &aArg, 10));
+  };
+
+#ifdef XP_WIN
+  auto parseHandleArg = [&](char*& aArg) {
+    return HANDLE(parseUIntPtrArg(aArg));
+  };
+
+  mPrefsHandle = Some(parseHandleArg(aPrefsHandleStr));
+  if (aPrefsHandleStr[0] != '\0') {
+    return false;
+  }
+
+  // The FileDescriptor constructor will clone this handle when constructed,
+  // so store it in a UniquePlatformHandle to make sure the original gets
+  // closed.
+  FileDescriptor::UniquePlatformHandle handle(
+      parseHandleArg(aPrefMapHandleStr));
+  if (aPrefMapHandleStr[0] != '\0') {
+    return false;
+  }
+
+  mPrefMapHandle.emplace(handle.get());
+#endif
+
+  mPrefsLen = Some(parseUIntPtrArg(aPrefsLenStr));
+  if (aPrefsLenStr[0] != '\0') {
+    return false;
+  }
+
+  mPrefMapSize = Some(parseUIntPtrArg(aPrefMapSizeStr));
+  if (aPrefMapSizeStr[0] != '\0') {
+    return false;
+  }
+
+#ifdef ANDROID
+  // Android is different; get the FD via gPrefsFd instead of a fixed fd.
+  MOZ_RELEASE_ASSERT(gPrefsFd != -1);
+  mPrefsHandle = Some(base::FileDescriptor(gPrefsFd, /* auto_close */ true));
+
+  FileDescriptor::UniquePlatformHandle handle(gPrefMapFd);
+  mPrefMapHandle.emplace(handle.get());
+#elif XP_UNIX
+  mPrefsHandle = Some(base::FileDescriptor(kPrefsFileDescriptor,
+                                           /* auto_close */ true));
+
+  // The FileDescriptor constructor will clone this handle when constructed,
+  // so store it in a UniquePlatformHandle to make sure the original gets
+  // closed.
+  FileDescriptor::UniquePlatformHandle handle(kPrefMapFileDescriptor);
+  mPrefMapHandle.emplace(handle.get());
+#endif
+
+  if (mPrefsHandle.isNothing() || mPrefsLen.isNothing() ||
+      mPrefMapHandle.isNothing() || mPrefMapSize.isNothing()) {
+    return false;
+  }
+
+  // Init the shared-memory base preference mapping first, so that only changed
+  // preferences wind up in heap memory.
+  Preferences::InitSnapshot(mPrefMapHandle.ref(), *mPrefMapSize);
+
+  // Set up early prefs from the shared memory.
+  base::SharedMemory shm;
+  if (!shm.SetHandle(*mPrefsHandle, /* read_only */ true)) {
+    NS_ERROR("failed to open shared memory in the child");
+    return false;
+  }
+  if (!shm.Map(*mPrefsLen)) {
+    NS_ERROR("failed to map shared memory in the child");
+    return false;
+  }
+  Preferences::DeserializePreferences(static_cast<char*>(shm.memory()),
+                                      *mPrefsLen);
+
+  return true;
+}
+
+const base::SharedMemoryHandle& SharedPreferenceDeserializer::GetPrefsHandle()
+    const {
+  MOZ_ASSERT(mPrefsHandle.isSome());
+
+  return mPrefsHandle.ref();
+}
+
+const FileDescriptor& SharedPreferenceDeserializer::GetPrefMapHandle() const {
+  MOZ_ASSERT(mPrefMapHandle.isSome());
+
+  return mPrefMapHandle.ref();
+}
+
+}  // namespace ipc
+}  // namespace mozilla
\ No newline at end of file
--- a/ipc/glue/moz.build
+++ b/ipc/glue/moz.build
@@ -158,16 +158,17 @@ UNIFIED_SOURCES += [
     'IPCStreamDestination.cpp',
     'IPCStreamParent.cpp',
     'IPCStreamSource.cpp',
     'IPCStreamUtils.cpp',
     'MessageChannel.cpp',
     'MessageLink.cpp',
     'MessagePump.cpp',
     'ProcessChild.cpp',
+    'ProcessUtils_common.cpp',
     'ProtocolUtils.cpp',
     'ScopedXREEmbed.cpp',
     'SharedMemory.cpp',
     'Shmem.cpp',
     'StringUtil.cpp',
     'URIUtils.cpp',
 ]
 
--- a/ipc/ipdl/sync-messages.ini
+++ b/ipc/ipdl/sync-messages.ini
@@ -909,16 +909,18 @@ description =
 [PGMPService::GetGMPNodeId]
 description =
 [PGMPVideoDecoder::NeedShmem]
 description =
 [PGMPVideoEncoder::NeedShmem]
 description =
 [PRemoteDecoderManager::PRemoteVideoDecoder]
 description = See Bug 1505976 - investigate changing to async instead of matching GPU pattern
+[PContent::LaunchRDDProcess]
+description = See Bug 1518344 - investigate using async for PContent::LaunchRDDProcess
 [PVideoDecoderManager::PVideoDecoder]
 description =
 [PVideoDecoderManager::Readback]
 description =
 [PBackgroundStorage::Preload]
 description =
 [PBackgroundLSDatabase::PBackgroundLSSnapshot]
 description = See corresponding comment in PBackgroundLSDatabase.ipdl
--- a/js/moz.configure
+++ b/js/moz.configure
@@ -13,16 +13,18 @@ def building_js(build_project):
 
 # Exception to the rule above: JS_STANDALONE is a special option that doesn't
 # want the js_option treatment. When we're done merging js/src/configure and
 # top-level configure, it can go away, although the JS_STANDALONE config
 # will still need to be set depending on building_js above.
 option(env='JS_STANDALONE', default=building_js,
        help='Reserved for internal use')
 
+include('../build/moz.configure/bindgen.configure',
+        when='--enable-compile-environment')
 include('../build/moz.configure/rust.configure',
         when='--enable-compile-environment')
 
 @depends('JS_STANDALONE')
 def js_standalone(value):
     if value:
         return True
 set_config('JS_STANDALONE', js_standalone)
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/for-of/throw-during-break.js
@@ -0,0 +1,42 @@
+var whoCaught = "nobody"
+
+function* wrapNoThrow() {
+    let iter = {
+      [Symbol.iterator]() {
+        return this;
+      },
+      next() {
+        return { value: 10, done: false };
+      },
+      return() { throw "nonsense"; }
+    };
+    for (const i of iter)
+      yield i;
+  }
+function foo() {
+    try {
+	l2:
+	for (j of wrapNoThrow()) {
+	    try {
+		for (i of [1,2,3]) {
+		    try {
+			break l2;
+		    } catch(e) {
+			whoCaught = "inner"
+		    }
+		}
+	    } catch (e) {
+		whoCaught = "inner2"
+	    }
+	}
+    } catch (e) {
+	whoCaught = "correct"
+    }
+}
+
+try {
+    foo();
+} catch (e) { whoCaught = "outer" }
+
+
+assertEq(whoCaught, "correct");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/promise/unhandled-rejections-different-realm.js
@@ -0,0 +1,19 @@
+// |jit-test| error:Unhandled rejection: "some reason"
+
+// Test JS shell's unhandled rejection tracking.
+
+var z = newGlobal();
+
+Promise.prototype.then = z.Promise.prototype.then;
+
+// Add unhandled rejection from other realm.
+evalcx("var p = (async function() { throw 'some reason' })()", z);
+
+// Add unhandled rejection from this realm.
+var p = (async function f() { throw 'other reason'; })();
+
+// Remove unhandled rejection from this realm.
+p.then();
+
+// Remove unhandled rejection from other realm.
+evalcx("p.then()", z);
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -128,35 +128,35 @@ static void CloseLiveIteratorIon(JSConte
     } else {
       IteratorCloseForException(cx, iterObject);
     }
   } else {
     UnwindIteratorForUncatchableException(iterObject);
   }
 }
 
-class IonFrameStackDepthOp {
+class IonTryNoteFilter {
   uint32_t depth_;
 
  public:
-  explicit IonFrameStackDepthOp(const InlineFrameIterator& frame) {
+  explicit IonTryNoteFilter(const InlineFrameIterator& frame) {
     uint32_t base = NumArgAndLocalSlots(frame);
     SnapshotIterator si = frame.snapshotIterator();
     MOZ_ASSERT(si.numAllocations() >= base);
     depth_ = si.numAllocations() - base;
   }
 
-  uint32_t operator()() { return depth_; }
+  bool operator()(const JSTryNote* note) { return note->stackDepth <= depth_; }
 };
 
-class TryNoteIterIon : public TryNoteIter<IonFrameStackDepthOp> {
+class TryNoteIterIon : public TryNoteIter<IonTryNoteFilter> {
  public:
   TryNoteIterIon(JSContext* cx, const InlineFrameIterator& frame)
       : TryNoteIter(cx, frame.script(), frame.pc(),
-                    IonFrameStackDepthOp(frame)) {}
+                    IonTryNoteFilter(frame)) {}
 };
 
 static void HandleExceptionIon(JSContext* cx, const InlineFrameIterator& frame,
                                ResumeFromException* rfe,
                                bool* hitBailoutException) {
   if (cx->realm()->isDebuggee()) {
     // We need to bail when there is a catchable exception, and we are the
     // debuggee of a Debugger with a live onExceptionUnwind hook, or if a
@@ -195,53 +195,29 @@ static void HandleExceptionIon(JSContext
     }
   }
 
   RootedScript script(cx, frame.script());
   if (!script->hasTrynotes()) {
     return;
   }
 
-  bool inForOfIterClose = false;
-
   for (TryNoteIterIon tni(cx, frame); !tni.done(); ++tni) {
     const JSTryNote* tn = *tni;
-
     switch (tn->kind) {
       case JSTRY_FOR_IN:
       case JSTRY_DESTRUCTURING:
-        // See corresponding comment in ProcessTryNotes.
-        if (inForOfIterClose) {
-          break;
-        }
-
         MOZ_ASSERT_IF(tn->kind == JSTRY_FOR_IN,
                       JSOp(*(script->offsetToPC(tn->start + tn->length))) ==
                           JSOP_ENDITER);
         CloseLiveIteratorIon(cx, frame, tn);
         break;
 
-      case JSTRY_FOR_OF_ITERCLOSE:
-        inForOfIterClose = true;
-        break;
-
-      case JSTRY_FOR_OF:
-        inForOfIterClose = false;
-        break;
-
-      case JSTRY_LOOP:
-        break;
-
       case JSTRY_CATCH:
         if (cx->isExceptionPending()) {
-          // See corresponding comment in ProcessTryNotes.
-          if (inForOfIterClose) {
-            break;
-          }
-
           // Ion can compile try-catch, but bailing out to catch
           // exceptions is slow. Reset the warm-up counter so that if we
           // catch many exceptions we won't Ion-compile the script.
           script->resetWarmUpCounter();
 
           if (*hitBailoutException) {
             break;
           }
@@ -260,16 +236,21 @@ static void HandleExceptionIon(JSContext
             return;
           }
 
           *hitBailoutException = true;
           MOZ_ASSERT(cx->isExceptionPending());
         }
         break;
 
+      case JSTRY_FOR_OF:
+      case JSTRY_LOOP:
+        break;
+
+      // JSTRY_FOR_OF_ITERCLOSE is handled internally by the try note iterator.
       default:
         MOZ_CRASH("Unexpected try note");
     }
   }
 }
 
 static void OnLeaveBaselineFrame(JSContext* cx, const JSJitFrameIter& frame,
                                  jsbytecode* pc, ResumeFromException* rfe,
@@ -322,97 +303,78 @@ struct AutoBaselineHandlingException {
     frame->setOverridePc(pc);
   }
   ~AutoBaselineHandlingException() {
     frame->unsetIsHandlingException();
     frame->clearOverridePc();
   }
 };
 
-class BaselineFrameStackDepthOp {
+class BaselineTryNoteFilter {
   BaselineFrame* frame_;
 
  public:
-  explicit BaselineFrameStackDepthOp(BaselineFrame* frame) : frame_(frame) {}
-  uint32_t operator()() {
+  explicit BaselineTryNoteFilter(BaselineFrame* frame) : frame_(frame) {}
+  bool operator()(const JSTryNote* note) {
     MOZ_ASSERT(frame_->numValueSlots() >= frame_->script()->nfixed());
-    return frame_->numValueSlots() - frame_->script()->nfixed();
+    uint32_t currDepth = frame_->numValueSlots() - frame_->script()->nfixed();
+    return note->stackDepth <= currDepth;
   }
 };
 
-class TryNoteIterBaseline : public TryNoteIter<BaselineFrameStackDepthOp> {
+class TryNoteIterBaseline : public TryNoteIter<BaselineTryNoteFilter> {
  public:
   TryNoteIterBaseline(JSContext* cx, BaselineFrame* frame, jsbytecode* pc)
-      : TryNoteIter(cx, frame->script(), pc, BaselineFrameStackDepthOp(frame)) {
+      : TryNoteIter(cx, frame->script(), pc, BaselineTryNoteFilter(frame)) {
   }
 };
 
 // Close all live iterators on a BaselineFrame due to exception unwinding. The
 // pc parameter is updated to where the envs have been unwound to.
 static void CloseLiveIteratorsBaselineForUncatchableException(
     JSContext* cx, const JSJitFrameIter& frame, jsbytecode* pc) {
-  bool inForOfIterClose = false;
   for (TryNoteIterBaseline tni(cx, frame.baselineFrame(), pc); !tni.done();
        ++tni) {
     const JSTryNote* tn = *tni;
     switch (tn->kind) {
       case JSTRY_FOR_IN: {
-        // See corresponding comment in ProcessTryNotes.
-        if (inForOfIterClose) {
-          break;
-        }
-
         uint8_t* framePointer;
         uint8_t* stackPointer;
         BaselineFrameAndStackPointersFromTryNote(tn, frame, &framePointer,
                                                  &stackPointer);
         Value iterValue(*(Value*)stackPointer);
         RootedObject iterObject(cx, &iterValue.toObject());
         UnwindIteratorForUncatchableException(iterObject);
         break;
       }
 
-      case JSTRY_FOR_OF_ITERCLOSE:
-        inForOfIterClose = true;
-        break;
-
-      case JSTRY_FOR_OF:
-        inForOfIterClose = false;
-        break;
-
       default:
         break;
     }
   }
 }
 
 static bool ProcessTryNotesBaseline(JSContext* cx, const JSJitFrameIter& frame,
                                     EnvironmentIter& ei,
                                     ResumeFromException* rfe, jsbytecode** pc) {
   RootedScript script(cx, frame.baselineFrame()->script());
-  bool inForOfIterClose = false;
 
   for (TryNoteIterBaseline tni(cx, frame.baselineFrame(), *pc); !tni.done();
        ++tni) {
     const JSTryNote* tn = *tni;
 
     MOZ_ASSERT(cx->isExceptionPending());
     switch (tn->kind) {
       case JSTRY_CATCH: {
         // If we're closing a legacy generator, we have to skip catch
         // blocks.
         if (cx->isClosingGenerator()) {
           break;
         }
 
-        // See corresponding comment in ProcessTryNotes.
-        if (inForOfIterClose) {
-          break;
-        }
-
         SettleOnTryNote(cx, tn, frame, ei, rfe, pc);
 
         // Ion can compile try-catch, but bailing out to catch
         // exceptions is slow. Reset the warm-up counter so that if we
         // catch many exceptions we won't Ion-compile the script.
         script->resetWarmUpCounter();
 
         // Resume at the start of the catch block.
@@ -420,58 +382,43 @@ static bool ProcessTryNotesBaseline(JSCo
         rfe->kind = ResumeFromException::RESUME_CATCH;
         rfe->target =
             script->baselineScript()->nativeCodeForPC(script, *pc, &slotInfo);
         MOZ_ASSERT(slotInfo.isStackSynced());
         return true;
       }
 
       case JSTRY_FINALLY: {
-        // See corresponding comment in ProcessTryNotes.
-        if (inForOfIterClose) {
-          break;
-        }
-
         PCMappingSlotInfo slotInfo;
         SettleOnTryNote(cx, tn, frame, ei, rfe, pc);
         rfe->kind = ResumeFromException::RESUME_FINALLY;
         rfe->target =
             script->baselineScript()->nativeCodeForPC(script, *pc, &slotInfo);
         MOZ_ASSERT(slotInfo.isStackSynced());
         // Drop the exception instead of leaking cross compartment data.
         if (!cx->getPendingException(
                 MutableHandleValue::fromMarkedLocation(&rfe->exception))) {
           rfe->exception = UndefinedValue();
         }
         cx->clearPendingException();
         return true;
       }
 
       case JSTRY_FOR_IN: {
-        // See corresponding comment in ProcessTryNotes.
-        if (inForOfIterClose) {
-          break;
-        }
-
         uint8_t* framePointer;
         uint8_t* stackPointer;
         BaselineFrameAndStackPointersFromTryNote(tn, frame, &framePointer,
                                                  &stackPointer);
         Value iterValue(*reinterpret_cast<Value*>(stackPointer));
         JSObject* iterObject = &iterValue.toObject();
         CloseIterator(iterObject);
         break;
       }
 
       case JSTRY_DESTRUCTURING: {
-        // See corresponding comment in ProcessTryNotes.
-        if (inForOfIterClose) {
-          break;
-        }
-
         uint8_t* framePointer;
         uint8_t* stackPointer;
         BaselineFrameAndStackPointersFromTryNote(tn, frame, &framePointer,
                                                  &stackPointer);
         RootedValue doneValue(cx, *(reinterpret_cast<Value*>(stackPointer)));
         bool done = ToBoolean(doneValue);
         if (!done) {
           Value iterValue(*(reinterpret_cast<Value*>(stackPointer) + 1));
@@ -479,27 +426,21 @@ static bool ProcessTryNotesBaseline(JSCo
           if (!IteratorCloseForException(cx, iterObject)) {
             SettleOnTryNote(cx, tn, frame, ei, rfe, pc);
             return false;
           }
         }
         break;
       }
 
-      case JSTRY_FOR_OF_ITERCLOSE:
-        inForOfIterClose = true;
-        break;
-
       case JSTRY_FOR_OF:
-        inForOfIterClose = false;
-        break;
-
       case JSTRY_LOOP:
         break;
 
+      // JSTRY_FOR_OF_ITERCLOSE is handled internally by the try note iterator.
       default:
         MOZ_CRASH("Invalid try note");
     }
   }
   return true;
 }
 
 static void HandleExceptionBaseline(JSContext* cx, const JSJitFrameIter& frame,
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -1072,22 +1072,19 @@ static bool TrackUnhandledRejections(JSC
     sc->unhandledRejectedPromises = SetObject::create(cx);
     if (!sc->unhandledRejectedPromises) {
       return false;
     }
   }
 
   RootedValue promiseVal(cx, ObjectValue(*promise));
 
-  Maybe<AutoRealm> ar;
-  if (cx->realm() != sc->unhandledRejectedPromises->realm()) {
-    ar.emplace(cx, sc->unhandledRejectedPromises);
-    if (!cx->compartment()->wrap(cx, &promiseVal)) {
-      return false;
-    }
+  AutoRealm ar(cx, sc->unhandledRejectedPromises);
+  if (!cx->compartment()->wrap(cx, &promiseVal)) {
+    return false;
   }
 
   switch (state) {
     case JS::PromiseRejectionHandlingState::Unhandled:
       if (!SetObject::add(cx, sc->unhandledRejectedPromises, promiseVal)) {
         return false;
       }
       break;
@@ -1125,17 +1122,17 @@ static void ForwardingPromiseRejectionTr
   args[0].setObject(*promise);
   args[1].setInt32(static_cast<int32_t>(state));
 
   if (!JS_WrapValue(cx, args[0])) {
     return;
   }
 
   RootedValue rval(cx);
-  (void) Call(cx, callback, UndefinedHandleValue, args, &rval);
+  (void)Call(cx, callback, UndefinedHandleValue, args, &rval);
 }
 
 static bool SetPromiseRejectionTrackerCallback(JSContext* cx, unsigned argc,
                                                Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
   if (!IsFunctionObject(args.get(0))) {
     JS_ReportErrorASCII(
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -1094,174 +1094,113 @@ static void SettleOnTryNote(JSContext* c
   UnwindEnvironment(cx, ei, UnwindEnvironmentToTryPc(regs.fp()->script(), tn));
 
   // Set pc to the first bytecode after the the try note to point
   // to the beginning of catch or finally.
   regs.pc = regs.fp()->script()->offsetToPC(tn->start + tn->length);
   regs.sp = regs.spForStackDepth(tn->stackDepth);
 }
 
-class InterpreterFrameStackDepthOp {
+class InterpreterTryNoteFilter {
   const InterpreterRegs& regs_;
 
  public:
-  explicit InterpreterFrameStackDepthOp(const InterpreterRegs& regs)
+  explicit InterpreterTryNoteFilter(const InterpreterRegs& regs)
       : regs_(regs) {}
-  uint32_t operator()() { return regs_.stackDepth(); }
+  bool operator()(const JSTryNote* note) {
+    return note->stackDepth <= regs_.stackDepth();
+  }
 };
 
 class TryNoteIterInterpreter
-    : public TryNoteIter<InterpreterFrameStackDepthOp> {
+    : public TryNoteIter<InterpreterTryNoteFilter> {
  public:
   TryNoteIterInterpreter(JSContext* cx, const InterpreterRegs& regs)
       : TryNoteIter(cx, regs.fp()->script(), regs.pc,
-                    InterpreterFrameStackDepthOp(regs)) {}
+                    InterpreterTryNoteFilter(regs)) {}
 };
 
 static void UnwindIteratorsForUncatchableException(
     JSContext* cx, const InterpreterRegs& regs) {
   // c.f. the regular (catchable) TryNoteIterInterpreter loop in
   // ProcessTryNotes.
-  bool inForOfIterClose = false;
   for (TryNoteIterInterpreter tni(cx, regs); !tni.done(); ++tni) {
     const JSTryNote* tn = *tni;
     switch (tn->kind) {
       case JSTRY_FOR_IN: {
-        // See corresponding comment in ProcessTryNotes.
-        if (inForOfIterClose) {
-          break;
-        }
-
         Value* sp = regs.spForStackDepth(tn->stackDepth);
         UnwindIteratorForUncatchableException(&sp[-1].toObject());
         break;
       }
-
-      case JSTRY_FOR_OF_ITERCLOSE:
-        inForOfIterClose = true;
-        break;
-
-      case JSTRY_FOR_OF:
-        inForOfIterClose = false;
-        break;
-
       default:
         break;
     }
   }
 }
 
 enum HandleErrorContinuation {
   SuccessfulReturnContinuation,
   ErrorReturnContinuation,
   CatchContinuation,
   FinallyContinuation
 };
 
 static HandleErrorContinuation ProcessTryNotes(JSContext* cx,
                                                EnvironmentIter& ei,
                                                InterpreterRegs& regs) {
-  bool inForOfIterClose = false;
   for (TryNoteIterInterpreter tni(cx, regs); !tni.done(); ++tni) {
     const JSTryNote* tn = *tni;
 
     switch (tn->kind) {
       case JSTRY_CATCH:
         /* Catch cannot intercept the closing of a generator. */
         if (cx->isClosingGenerator()) {
           break;
         }
 
-        // If IteratorClose due to abnormal completion threw inside a
-        // for-of loop, it is not catchable by try statements inside of
-        // the for-of loop.
-        //
-        // This is handled by this weirdness in the exception handler
-        // instead of in bytecode because it is hard to do so in bytecode:
-        //
-        //   1. IteratorClose emitted due to abnormal completion (break,
-        //   throw, return) are emitted inline, at the source location of
-        //   the break, throw, or return statement. For example:
-        //
-        //     for (x of iter) {
-        //       try { return; } catch (e) { }
-        //     }
-        //
-        //   From the try-note nesting's perspective, the IteratorClose
-        //   resulting from |return| is covered by the inner try, when it
-        //   should not be.
-        //
-        //   2. Try-catch notes cannot be disjoint. That is, we can't have
-        //   multiple notes with disjoint pc ranges jumping to the same
-        //   catch block.
-        if (inForOfIterClose) {
-          break;
-        }
         SettleOnTryNote(cx, tn, ei, regs);
         return CatchContinuation;
 
       case JSTRY_FINALLY:
-        // See note above.
-        if (inForOfIterClose) {
-          break;
-        }
         SettleOnTryNote(cx, tn, ei, regs);
         return FinallyContinuation;
 
       case JSTRY_FOR_IN: {
-        // Don't let (extra) values pushed on the stack while closing a
-        // for-of iterator confuse us into thinking we still have to close
-        // an inner for-in iterator.
-        if (inForOfIterClose) {
-          break;
-        }
-
         /* This is similar to JSOP_ENDITER in the interpreter loop. */
         DebugOnly<jsbytecode*> pc =
             regs.fp()->script()->offsetToPC(tn->start + tn->length);
         MOZ_ASSERT(JSOp(*pc) == JSOP_ENDITER);
         Value* sp = regs.spForStackDepth(tn->stackDepth);
         JSObject* obj = &sp[-1].toObject();
         CloseIterator(obj);
         break;
       }
 
       case JSTRY_DESTRUCTURING: {
-        // See note above.
-        if (inForOfIterClose) {
-          break;
-        }
-
         // Whether the destructuring iterator is done is at the top of the
         // stack. The iterator object is second from the top.
         MOZ_ASSERT(tn->stackDepth > 1);
         Value* sp = regs.spForStackDepth(tn->stackDepth);
         RootedValue doneValue(cx, sp[-1]);
         bool done = ToBoolean(doneValue);
         if (!done) {
           RootedObject iterObject(cx, &sp[-2].toObject());
           if (!IteratorCloseForException(cx, iterObject)) {
             SettleOnTryNote(cx, tn, ei, regs);
             return ErrorReturnContinuation;
           }
         }
         break;
       }
 
-      case JSTRY_FOR_OF_ITERCLOSE:
-        inForOfIterClose = true;
-        break;
-
       case JSTRY_FOR_OF:
-        inForOfIterClose = false;
-        break;
-
       case JSTRY_LOOP:
         break;
 
+      // JSTRY_FOR_OF_ITERCLOSE is handled internally by the try note iterator.
       default:
         MOZ_CRASH("Invalid try note");
     }
   }
 
   return SuccessfulReturnContinuation;
 }
 
--- a/js/src/vm/Interpreter.h
+++ b/js/src/vm/Interpreter.h
@@ -304,32 +304,67 @@ extern void UnwindEnvironment(JSContext*
 // Unwind all environments.
 extern void UnwindAllEnvironmentsInFrame(JSContext* cx, EnvironmentIter& ei);
 
 // Compute the pc needed to unwind the scope to the beginning of the block
 // pointed to by the try note.
 extern jsbytecode* UnwindEnvironmentToTryPc(JSScript* script,
                                             const JSTryNote* tn);
 
-template <class StackDepthOp>
+template <class TryNoteFilter>
 class MOZ_STACK_CLASS TryNoteIter {
   RootedScript script_;
   uint32_t pcOffset_;
-  StackDepthOp getStackDepth_;
+  TryNoteFilter isTryNoteValid_;
 
   const JSTryNote* tn_;
   const JSTryNote* tnEnd_;
 
   void settle() {
     for (; tn_ != tnEnd_; ++tn_) {
-      /* If pc is out of range, try the next one. */
-      if (pcOffset_ - tn_->start >= tn_->length) {
+      if (!pcInRange()) {
         continue;
       }
 
+      /*  Try notes cannot be disjoint. That is, we can't have
+       *  multiple notes with disjoint pc ranges jumping to the same
+       *  catch block. This interacts awkwardly with for-of loops, in
+       *  which calls to IteratorClose emitted due to abnormal
+       *  completion (break, throw, return) are emitted inline, at the
+       *  source location of the break, throw, or return
+       *  statement. For example:
+       *
+       *      for (x of iter) {
+       *        try { return; } catch (e) { }
+       *      }
+       *
+       *  From the try-note nesting's perspective, the IteratorClose
+       *  resulting from |return| is covered by the inner try, when it
+       *  should not be. If IteratorClose throws, we don't want to
+       *  catch it here.
+       *
+       *  To make this work, we use JSTRY_FOR_OF_ITERCLOSE try-notes,
+       *  which cover the range of the abnormal completion. When
+       *  looking up trynotes, a for-of iterclose note indicates that
+       *  the enclosing for-of has just been terminated. As a result,
+       *  trynotes within that for-of are no longer active. When we
+       *  see a for-of-iterclose, we skip ahead in the trynotes list
+       *  until we see the matching for-of.
+       */
+      if (tn_->kind == JSTRY_FOR_OF_ITERCLOSE) {
+        do {
+          ++tn_;
+          MOZ_ASSERT(tn_ != tnEnd_);
+          MOZ_ASSERT_IF(pcInRange(), tn_->kind != JSTRY_FOR_OF_ITERCLOSE);
+        } while (!(pcInRange() && tn_->kind == JSTRY_FOR_OF));
+
+        // Advance to trynote following the enclosing for-of.
+        ++tn_;
+      }
+
       /*
        * We have a note that covers the exception pc but we must check
        * whether the interpreter has already executed the corresponding
        * handler. This is possible when the executed bytecode implements
        * break or return from inside a for-in loop.
        *
        * In this case the emitter generates additional [enditer] and [gosub]
        * opcodes to close all outstanding iterators and execute the finally
@@ -339,28 +374,28 @@ class MOZ_STACK_CLASS TryNoteIter {
        * invoked the finally blocks.
        *
        * To address this, we make [enditer] always decrease the stack even
        * when its implementation throws an exception. Thus already executed
        * [enditer] and [gosub] opcodes will have try notes with the stack
        * depth exceeding the current one and this condition is what we use to
        * filter them out.
        */
-      if (tn_->stackDepth <= getStackDepth_()) {
-        break;
+      if (tn_ == tnEnd_ || isTryNoteValid_(tn_)) {
+        return;
       }
     }
   }
 
  public:
   TryNoteIter(JSContext* cx, JSScript* script, jsbytecode* pc,
-              StackDepthOp getStackDepth)
+              TryNoteFilter isTryNoteValid)
       : script_(cx, script),
         pcOffset_(script->pcToOffset(pc)),
-        getStackDepth_(getStackDepth) {
+        isTryNoteValid_(isTryNoteValid) {
     if (script->hasTrynotes()) {
       // NOTE: The Span is a temporary so we can't use begin()/end()
       // here or the iterator will outlive the span.
       auto trynotes = script->trynotes();
       tn_ = trynotes.data();
       tnEnd_ = tn_ + trynotes.size();
     } else {
       tn_ = tnEnd_ = nullptr;
@@ -368,16 +403,25 @@ class MOZ_STACK_CLASS TryNoteIter {
     settle();
   }
 
   void operator++() {
     ++tn_;
     settle();
   }
 
+  bool pcInRange() const {
+    // This checks both ends of the range at once
+    // because unsigned integers wrap on underflow.
+    uint32_t offset = pcOffset_;
+    uint32_t start = tn_->start;
+    uint32_t length = tn_->length;
+    return offset - start < length;
+
+  }
   bool done() const { return tn_ == tnEnd_; }
   const JSTryNote* operator*() const { return tn_; }
 };
 
 bool HandleClosingGeneratorReturn(JSContext* cx, AbstractFramePtr frame,
                                   bool ok);
 
 /************************************************************************/
--- a/js/src/vm/JSScript.h
+++ b/js/src/vm/JSScript.h
@@ -88,29 +88,48 @@ namespace detail {
 bool CopyScript(JSContext* cx, HandleScript src, HandleScript dst,
                 MutableHandle<GCVector<Scope*>> scopes);
 
 }  // namespace detail
 
 }  // namespace js
 
 /*
- * Type of try note associated with each catch or finally block, and also with
- * for-in and other kinds of loops. Non-for-in loops do not need these notes
- * for exception unwinding, but storing their boundaries here is helpful for
- * heuristics that need to know whether a given op is inside a loop.
+ * [SMDOC] Try Notes
+ *
+ * Trynotes are attached to regions that are involved with
+ * exception unwinding. They can be broken up into four categories:
+ *
+ * 1. CATCH and FINALLY: Basic exception handling. A CATCH trynote
+ *    covers the range of the associated try. A FINALLY trynote covers
+ *    the try and the catch.
+
+ * 2. FOR_IN and DESTRUCTURING: These operations create an iterator
+ *    which must be cleaned up (by calling IteratorClose) during
+ *    exception unwinding.
+ *
+ * 3. FOR_OF and FOR_OF_ITERCLOSE: For-of loops handle unwinding using
+ *    catch blocks. These trynotes are used for for-of breaks/returns,
+ *    which create regions that are lexically within a for-of block,
+ *    but logically outside of it. See TryNoteIter::settle for more
+ *    details.
+ *
+ * 4. LOOP: This represents normal for/while/do-while loops. It is
+ *    unnecessary for exception unwinding, but storing the boundaries
+ *    of loops here is helpful for heuristics that need to know
+ *    whether a given op is inside a loop.
  */
 enum JSTryNoteKind {
   JSTRY_CATCH,
   JSTRY_FINALLY,
   JSTRY_FOR_IN,
+  JSTRY_DESTRUCTURING,
   JSTRY_FOR_OF,
-  JSTRY_LOOP,
   JSTRY_FOR_OF_ITERCLOSE,
-  JSTRY_DESTRUCTURING
+  JSTRY_LOOP
 };
 
 /*
  * Exception handling record.
  */
 struct JSTryNote {
   uint8_t kind;        /* one of JSTryNoteKind */
   uint32_t stackDepth; /* stack depth upon exception handler entry */
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -71,16 +71,17 @@
 #include "nsFrameManager.h"
 #include "nsXPCOM.h"
 #include "nsILayoutHistoryState.h"
 #include "nsILineIterator.h"  // for ScrollContentIntoView
 #include "PLDHashTable.h"
 #include "mozilla/dom/Touch.h"
 #include "mozilla/dom/TouchEvent.h"
 #include "mozilla/dom/PointerEventBinding.h"
+#include "mozilla/dom/ShadowIncludingTreeIterator.h"
 #include "nsIObserverService.h"
 #include "nsDocShell.h"  // for reflow observation
 #include "nsIBaseWindow.h"
 #include "nsError.h"
 #include "nsLayoutUtils.h"
 #include "nsViewportInfo.h"
 #include "nsCSSRendering.h"
 // for |#ifdef DEBUG| code
@@ -1506,34 +1507,43 @@ void PresShell::RemovePreferenceStyles()
     mStyleSet->RemoveStyleSheet(SheetType::Agent, mPrefStyleSheet);
     mPrefStyleSheet = nullptr;
   }
 }
 
 void PresShell::AddUserSheet(StyleSheet* aSheet) {
   // Make sure this does what nsDocumentViewer::CreateStyleSet does wrt
   // ordering. We want this new sheet to come after all the existing stylesheet
-  // service sheets, but before other user sheets; see nsIStyleSheetService.idl
-  // for the ordering.  Just remove and readd all the nsStyleSheetService
-  // sheets.
-  nsCOMPtr<nsIStyleSheetService> dummy =
-      do_GetService(NS_STYLESHEETSERVICE_CONTRACTID);
-
-  nsStyleSheetService* sheetService = nsStyleSheetService::gInstance;
+  // service sheets (which are at the start), but before other user sheets; see
+  // nsIStyleSheetService.idl for the ordering.
+
+  nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance();
   nsTArray<RefPtr<StyleSheet>>& userSheets = *sheetService->UserStyleSheets();
-  // Iterate forwards when removing so the searches for RemoveStyleSheet are as
-  // short as possible.
-  for (StyleSheet* sheet : userSheets) {
-    mStyleSet->RemoveStyleSheet(SheetType::User, sheet);
-  }
-
-  // Now iterate backwards, so that the order of userSheets will be the same as
-  // the order of sheets from it in the style set.
-  for (StyleSheet* sheet : Reversed(userSheets)) {
-    mStyleSet->PrependStyleSheet(SheetType::User, sheet);
+
+  // Search for the place to insert the new user sheet. Since all of the
+  // stylesheet service provided user sheets should be at the start of the style
+  // set's list, and aSheet should be at the end of userSheets. Given that, we
+  // can find the right place to insert the new sheet based on the length of
+  // userSheets.
+  MOZ_ASSERT(aSheet);
+  MOZ_ASSERT(userSheets.LastElement() == aSheet);
+
+  size_t index = userSheets.Length() - 1;
+
+  // Assert that all of userSheets (except for the last, new element) matches up
+  // with what's in the style set.
+  for (size_t i = 0; i < index; ++i) {
+    MOZ_ASSERT(mStyleSet->StyleSheetAt(SheetType::User, i) == userSheets[i]);
+  }
+
+  if (index == static_cast<size_t>(mStyleSet->SheetCount(SheetType::User))) {
+    mStyleSet->AppendStyleSheet(SheetType::User, aSheet);
+  } else {
+    StyleSheet* ref = mStyleSet->StyleSheetAt(SheetType::User, index);
+    mStyleSet->InsertStyleSheetBefore(SheetType::User, aSheet, ref);
   }
 
   ApplicableStylesChanged();
 }
 
 void PresShell::AddAgentSheet(StyleSheet* aSheet) {
   // Make sure this does what nsDocumentViewer::CreateStyleSet does
   // wrt ordering.
@@ -2874,21 +2884,19 @@ void nsIPresShell::SlotAssignmentWillCha
       aNewSlot->SetHasDirtyDescendantsForServo();
       aNewSlot->NoteDirtySubtreeForServo();
     }
   }
 }
 
 #ifdef DEBUG
 static void AssertNoFramesInSubtree(nsIContent* aContent) {
-  for (nsIContent* c = aContent; c; c = c->GetNextNode(aContent)) {
+  for (nsINode* node : ShadowIncludingTreeIterator(*aContent)) {
+    nsIContent* c = nsIContent::FromNode(node);
     MOZ_ASSERT(!c->GetPrimaryFrame());
-    if (auto* shadowRoot = c->GetShadowRoot()) {
-      AssertNoFramesInSubtree(shadowRoot);
-    }
     if (auto* binding = c->GetXBLBinding()) {
       if (auto* bindingWithContent = binding->GetBindingWithContent()) {
         nsIContent* anonContent = bindingWithContent->GetAnonymousContent();
         MOZ_ASSERT(!anonContent->GetPrimaryFrame());
 
         // Need to do this instead of just AssertNoFramesInSubtree(anonContent),
         // because the parent of the children of the <content> element isn't the
         // <content> element, but the bound element, and that confuses
@@ -8246,17 +8254,17 @@ nsresult PresShell::GetAgentStyleSheets(
 }
 
 nsresult PresShell::SetAgentStyleSheets(
     const nsTArray<RefPtr<StyleSheet>>& aSheets) {
   return mStyleSet->ReplaceSheets(SheetType::Agent, aSheets);
 }
 
 nsresult PresShell::AddOverrideStyleSheet(StyleSheet* aSheet) {
-  return mStyleSet->PrependStyleSheet(SheetType::Override, aSheet);
+  return mStyleSet->AppendStyleSheet(SheetType::Override, aSheet);
 }
 
 nsresult PresShell::RemoveOverrideStyleSheet(StyleSheet* aSheet) {
   return mStyleSet->RemoveStyleSheet(SheetType::Override, aSheet);
 }
 
 static void FreezeElement(nsISupports* aSupports, void* /* unused */) {
   nsCOMPtr<nsIObjectLoadingContent> olc(do_QueryInterface(aSupports));
@@ -10086,27 +10094,30 @@ void nsIPresShell::SetVisualViewportSize
     MarkFixedFramesForReflow(nsIPresShell::eResize);
 
     if (auto* window = nsGlobalWindowInner::Cast(mDocument->GetInnerWindow())) {
       window->VisualViewport()->PostResizeEvent();
     }
   }
 }
 
-void nsIPresShell::SetVisualViewportOffset(
+bool nsIPresShell::SetVisualViewportOffset(
     const nsPoint& aScrollOffset, const nsPoint& aPrevLayoutScrollPos) {
+  bool didChange = false;
   if (mVisualViewportOffset != aScrollOffset) {
     nsPoint prevOffset = mVisualViewportOffset;
     mVisualViewportOffset = aScrollOffset;
+    didChange = true;
 
     if (auto* window = nsGlobalWindowInner::Cast(mDocument->GetInnerWindow())) {
       window->VisualViewport()->PostScrollEvent(prevOffset,
                                                 aPrevLayoutScrollPos);
     }
   }
+  return didChange;
 }
 
 nsPoint nsIPresShell::GetVisualViewportOffsetRelativeToLayoutViewport() const {
   return GetVisualViewportOffset() - GetLayoutViewportOffset();
 }
 
 nsPoint nsIPresShell::GetLayoutViewportOffset() const {
   nsPoint result;
--- a/layout/base/PresState.ipdlh
+++ b/layout/base/PresState.ipdlh
@@ -31,16 +31,18 @@ union PresContentData {
   CheckedContentData;
   // We can need to serialize blobs in order to transmit this type, so we need
   // to handle that in a custom handler.
   FileContentData[];
 };
 
 struct PresState {
   PresContentData contentData;
+  // For frames where layout and visual viewport aren't one and the same thing,
+  // scrollState will store the position of the *visual* viewport.
   nsPoint scrollState;
   bool allowScrollOriginDowngrade;
   float resolution;
   bool disabledSet;
   bool disabled;
   bool droppedDown;
 };
 
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -2283,80 +2283,81 @@ nsDocumentViewer::RequestWindowClose(boo
   } else
 #endif
     *aCanClose = true;
 
   return NS_OK;
 }
 
 UniquePtr<ServoStyleSet> nsDocumentViewer::CreateStyleSet(Document* aDocument) {
-  // Make sure this does the same thing as PresShell::AddSheet wrt ordering.
-
-  // this should eventually get expanded to allow for creating
-  // different sets for different media
-
-  UniquePtr<ServoStyleSet> styleSet = MakeUnique<ServoStyleSet>();
+  // Make sure this does the same thing as PresShell::Add{User,Agent}Sheet wrt
+  // ordering.
 
   // The document will fill in the document sheets when we create the presshell
   auto cache = nsLayoutStylesheetCache::Singleton();
-
-  // Handle the user sheets.
+  nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance();
+
+  auto styleSet = MakeUnique<ServoStyleSet>();
+
+  // User sheets
+
+  for (StyleSheet* sheet : *sheetService->UserStyleSheets()) {
+    styleSet->AppendStyleSheet(SheetType::User, sheet);
+  }
+
   StyleSheet* sheet = nsContentUtils::IsInChromeDocshell(aDocument)
                           ? cache->GetUserChromeSheet()
                           : cache->GetUserContentSheet();
 
   if (sheet) {
     styleSet->AppendStyleSheet(SheetType::User, sheet);
   }
 
-  // Append chrome sheets (scrollbars + forms).
-  styleSet->PrependStyleSheet(SheetType::Agent, cache->ScrollbarsSheet());
-  styleSet->PrependStyleSheet(SheetType::Agent, cache->FormsSheet());
-
-  // Only load the full XUL sheet if we'll need it.
-  if (aDocument->LoadsFullXULStyleSheetUpFront()) {
-    styleSet->PrependStyleSheet(SheetType::Agent, cache->XULSheet());
+  // Agent sheets
+
+  styleSet->AppendStyleSheet(SheetType::Agent, cache->UASheet());
+
+  if (MOZ_LIKELY(mDocument->NodeInfoManager()->MathMLEnabled())) {
+    styleSet->AppendStyleSheet(SheetType::Agent, cache->MathMLSheet());
+  }
+
+  if (MOZ_LIKELY(mDocument->NodeInfoManager()->SVGEnabled())) {
+    styleSet->AppendStyleSheet(SheetType::Agent, cache->SVGSheet());
   }
 
+  styleSet->AppendStyleSheet(SheetType::Agent, cache->HTMLSheet());
+
+  // We don't add quirk.css here; nsPresContext::CompatibilityModeChanged will
+  // append it if needed.
+
+  if (nsLayoutUtils::ShouldUseNoFramesSheet(aDocument)) {
+    styleSet->AppendStyleSheet(SheetType::Agent, cache->NoFramesSheet());
+  }
+
+  if (nsLayoutUtils::ShouldUseNoScriptSheet(aDocument)) {
+    styleSet->AppendStyleSheet(SheetType::Agent, cache->NoScriptSheet());
+  }
+
+  styleSet->AppendStyleSheet(SheetType::Agent, cache->CounterStylesSheet());
+
   // Load the minimal XUL rules for scrollbars and a few other XUL things
   // that non-XUL (typically HTML) documents commonly use.
-  styleSet->PrependStyleSheet(SheetType::Agent, cache->MinimalXULSheet());
-
-  styleSet->PrependStyleSheet(SheetType::Agent, cache->CounterStylesSheet());
-
-  if (nsLayoutUtils::ShouldUseNoScriptSheet(aDocument)) {
-    styleSet->PrependStyleSheet(SheetType::Agent, cache->NoScriptSheet());
-  }
-
-  if (nsLayoutUtils::ShouldUseNoFramesSheet(aDocument)) {
-    styleSet->PrependStyleSheet(SheetType::Agent, cache->NoFramesSheet());
+  styleSet->AppendStyleSheet(SheetType::Agent, cache->MinimalXULSheet());
+
+  // Only load the full XUL sheet if we'll need it.
+  if (aDocument->LoadsFullXULStyleSheetUpFront()) {
+    styleSet->AppendStyleSheet(SheetType::Agent, cache->XULSheet());
   }
 
-  // We don't add quirk.css here; nsPresContext::CompatibilityModeChanged will
-  // append it if needed.
-
-  styleSet->PrependStyleSheet(SheetType::Agent, cache->HTMLSheet());
-
-  if (MOZ_LIKELY(mDocument->NodeInfoManager()->SVGEnabled())) {
-    styleSet->PrependStyleSheet(SheetType::Agent, cache->SVGSheet());
-  }
-
-  if (MOZ_LIKELY(mDocument->NodeInfoManager()->MathMLEnabled())) {
-    styleSet->PrependStyleSheet(SheetType::Agent, cache->MathMLSheet());
-  }
-
-  styleSet->PrependStyleSheet(SheetType::Agent, cache->UASheet());
-
-  if (nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance()) {
-    for (StyleSheet* sheet : *sheetService->AgentStyleSheets()) {
-      styleSet->AppendStyleSheet(SheetType::Agent, sheet);
-    }
-    for (StyleSheet* sheet : Reversed(*sheetService->UserStyleSheets())) {
-      styleSet->PrependStyleSheet(SheetType::User, sheet);
-    }
+  // Append chrome sheets (scrollbars + forms).
+  styleSet->AppendStyleSheet(SheetType::Agent, cache->FormsSheet());
+  styleSet->AppendStyleSheet(SheetType::Agent, cache->ScrollbarsSheet());
+
+  for (StyleSheet* sheet : *sheetService->AgentStyleSheets()) {
+    styleSet->AppendStyleSheet(SheetType::Agent, sheet);
   }
 
   return styleSet;
 }
 
 NS_IMETHODIMP
 nsDocumentViewer::ClearHistoryEntry() {
   if (mDocument) {
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -1645,17 +1645,20 @@ class nsIPresShell : public nsStubDocume
   void SetVisualViewportSize(nscoord aWidth, nscoord aHeight);
   bool IsVisualViewportSizeSet() { return mVisualViewportSizeSet; }
   nsSize GetVisualViewportSize() {
     NS_ASSERTION(mVisualViewportSizeSet,
                  "asking for visual viewport size when its not set?");
     return mVisualViewportSize;
   }
 
-  void SetVisualViewportOffset(const nsPoint& aScrollOffset,
+  /**
+   * The return value indicates whether the offset actually changed.
+   */
+  bool SetVisualViewportOffset(const nsPoint& aScrollOffset,
                                const nsPoint& aPrevLayoutScrollPos);
 
   nsPoint GetVisualViewportOffset() const { return mVisualViewportOffset; }
 
   nsPoint GetVisualViewportOffsetRelativeToLayoutViewport() const;
 
   // Ask APZ in the next transaction to scroll to the given visual viewport 
   // offset (relative to the document).
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -2431,16 +2431,24 @@ static void RemoveDisplayPortCallback(ns
   nsLayoutUtils::RemoveDisplayPort(helper->mOuter->GetContent());
   nsLayoutUtils::ExpireDisplayPortOnAsyncScrollableAncestor(helper->mOuter);
   helper->mOuter->SchedulePaint();
   // Be conservative and unflag this this scrollframe as being scrollable by
   // APZ. If it is still scrollable this will get flipped back soon enough.
   helper->mScrollableByAPZ = false;
 }
 
+void ScrollFrameHelper::MarkEverScrolled() {
+  // Mark this frame as having been scrolled. If this is the root
+  // scroll frame of a content document, then IsAlwaysActive()
+  // will return true from now on and MarkNotRecentlyScrolled() won't
+  // have any effect.
+  mHasBeenScrolled = true;
+}
+
 void ScrollFrameHelper::MarkNotRecentlyScrolled() {
   if (!mHasBeenScrolledRecently) return;
 
   mHasBeenScrolledRecently = false;
   mOuter->SchedulePaint();
 }
 
 void ScrollFrameHelper::MarkRecentlyScrolled() {
@@ -2495,21 +2503,17 @@ void ScrollFrameHelper::TriggerDisplayPo
 
   if (!mDisplayPortExpiryTimer) {
     mDisplayPortExpiryTimer = NS_NewTimer();
   }
   ResetDisplayPortExpiryTimer();
 }
 
 void ScrollFrameHelper::ScrollVisual() {
-  // Mark this frame as having been scrolled. If this is the root
-  // scroll frame of a content document, then IsAlwaysActive()
-  // will return true from now on and MarkNotRecentlyScrolled() won't
-  // have any effect.
-  mHasBeenScrolled = true;
+  MarkEverScrolled();
 
   AdjustViews(mScrolledFrame);
   // We need to call this after fixing up the view positions
   // to be consistent with the frame hierarchy.
   MarkRecentlyScrolled();
 }
 
 /**
@@ -4314,46 +4318,46 @@ void ScrollFrameHelper::ScrollToRestored
   // it. if it does then the user must have moved it, and we no longer
   // need to restore.
   //
   // In the RTL case, we check whether the scroll position changed using the
   // logical scroll position, but we scroll to the physical scroll position in
   // all cases
 
   // if we didn't move, we still need to restore
-  if (GetLogicalScrollPosition() == mLastPos) {
+  if (GetLogicalVisualViewportOffset() == mLastPos) {
     // if our desired position is different to the scroll position, scroll.
     // remember that we could be incrementally loading so we may enter
     // and scroll many times.
-    if (mRestorePos != mLastPos /* GetLogicalScrollPosition() */) {
+    if (mRestorePos != mLastPos /* GetLogicalVisualViewportOffset() */) {
       LoadingState state = GetPageLoadingState();
       if (state == LoadingState::Stopped && !NS_SUBTREE_DIRTY(mOuter)) {
         return;
       }
       nsPoint scrollToPos = mRestorePos;
       if (!IsPhysicalLTR()) {
         // convert from logical to physical scroll position
-        scrollToPos.x = mScrollPort.x - (mScrollPort.XMost() - scrollToPos.x -
-                                         mScrolledFrame->GetRect().width);
+        scrollToPos.x -=
+            (GetVisualViewportSize().width - mScrolledFrame->GetRect().width);
       }
       AutoWeakFrame weakFrame(mOuter);
       // It's very important to pass nsGkAtoms::restore here, so
       // ScrollToWithOrigin won't clear out mRestorePos.
       ScrollToWithOrigin(scrollToPos, nsIScrollableFrame::INSTANT,
                          nsGkAtoms::restore, nullptr);
       if (!weakFrame.IsAlive()) {
         return;
       }
       if (state == LoadingState::Loading || NS_SUBTREE_DIRTY(mOuter)) {
         // If we're trying to do a history scroll restore, then we want to
         // keep trying this until we succeed, because the page can be loading
         // incrementally. So re-get the scroll position for the next iteration,
         // it might not be exactly equal to mRestorePos due to rounding and
         // clamping.
-        mLastPos = GetLogicalScrollPosition();
+        mLastPos = GetLogicalVisualViewportOffset();
         return;
       }
     }
     // If we get here, either we reached the desired position (mLastPos ==
     // mRestorePos) or we're not trying to do a history scroll restore, so
     // we can stop after the scroll attempt above.
     mRestorePos.y = -1;
     mLastPos.x = -1;
@@ -6135,17 +6139,17 @@ UniquePtr<PresState> ScrollFrameHelper::
   // valid and we haven't moved since the last update of mLastPos (same check
   // that ScrollToRestoredPosition uses). This ensures if a reframe occurs
   // while we're in the process of loading content to scroll to a restored
   // position, we'll keep trying after the reframe. Similarly, if we're in the
   // middle of a smooth scroll, store the destination so that when we restore
   // we'll jump straight to the end of the scroll animation, rather than
   // effectively dropping it. Note that the mRestorePos will override the
   // smooth scroll destination if both are present.
-  nsPoint pt = GetLogicalScrollPosition();
+  nsPoint pt = GetLogicalVisualViewportOffset();
   if (isInSmoothScroll) {
     pt = mDestination;
     allowScrollOriginDowngrade = false;
   }
   if (mRestorePos.y != -1 && pt == mLastPos) {
     pt = mRestorePos;
   }
   state->scrollState() = pt;
@@ -6158,17 +6162,17 @@ UniquePtr<PresState> ScrollFrameHelper::
   return state;
 }
 
 void ScrollFrameHelper::RestoreState(PresState* aState) {
   mRestorePos = aState->scrollState();
   MOZ_ASSERT(mLastScrollOrigin == nsGkAtoms::other);
   mAllowScrollOriginDowngrade = aState->allowScrollOriginDowngrade();
   mDidHistoryRestore = true;
-  mLastPos = mScrolledFrame ? GetLogicalScrollPosition() : nsPoint(0, 0);
+  mLastPos = mScrolledFrame ? GetLogicalVisualViewportOffset() : nsPoint(0, 0);
 
   // Resolution properties should only exist on root scroll frames.
   MOZ_ASSERT(mIsRoot || aState->resolution() == 1.0);
 
   if (mIsRoot) {
     nsIPresShell* presShell = mOuter->PresShell();
     presShell->SetResolutionAndScaleTo(aState->resolution(),
                                        nsIPresShell::ChangeOrigin::eMainThread);
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -215,16 +215,28 @@ class ScrollFrameHelper : public nsIRefl
     return pt;
   }
   nsPoint GetApzScrollPosition() const { return mApzScrollPos; }
   nsRect GetScrollRange() const;
   // Get the scroll range assuming the viewport has size (aWidth, aHeight).
   nsRect GetScrollRange(nscoord aWidth, nscoord aHeight) const;
   nsSize GetVisualViewportSize() const;
   nsPoint GetVisualViewportOffset() const;
+  /**
+   * For LTR frames, this is the same as GetVisualViewportOffset().
+   * For RTL frames, we take the offset from the top right corner of the frame
+   * to the top right corner of the visual viewport.
+   */
+  nsPoint GetLogicalVisualViewportOffset() const {
+    nsPoint pt = GetVisualViewportOffset();
+    if (!IsPhysicalLTR()) {
+      pt.x += GetVisualViewportSize().width - mScrolledFrame->GetRect().width;
+    }
+    return pt;
+  }
   void ScrollSnap(
       nsIScrollableFrame::ScrollMode aMode = nsIScrollableFrame::SMOOTH_MSD);
   void ScrollSnap(
       const nsPoint& aDestination,
       nsIScrollableFrame::ScrollMode aMode = nsIScrollableFrame::SMOOTH_MSD);
 
  protected:
   nsRect GetScrollRangeForClamping() const;
@@ -405,16 +417,17 @@ class ScrollFrameHelper : public nsIRefl
   void LayoutScrollbars(nsBoxLayoutState& aState, const nsRect& aContentArea,
                         const nsRect& aOldScrollArea);
 
   void MarkScrollbarsDirtyForReflow() const;
 
   bool ShouldClampScrollPosition() const;
 
   bool IsAlwaysActive() const;
+  void MarkEverScrolled();
   void MarkRecentlyScrolled();
   void MarkNotRecentlyScrolled();
   nsExpirationState* GetExpirationState() { return &mActivityExpirationState; }
 
   void SetTransformingByAPZ(bool aTransforming) {
     if (mTransformingByAPZ && !aTransforming) {
       PostScrollEndEvent();
     }
@@ -536,20 +549,25 @@ class ScrollFrameHelper : public nsIRefl
   // just the current scroll position. ScrollBy will choose its
   // destination based on this value.
   nsPoint mDestination;
 
   // A goal position to try to scroll to as content loads. As long as mLastPos
   // matches the current logical scroll position, we try to scroll to
   // mRestorePos after every reflow --- because after each time content is
   // loaded/added to the scrollable element, there will be a reflow.
+  // Note that for frames where layout and visual viewport aren't one and the
+  // same thing, this scroll position will be the logical scroll position of
+  // the *visual* viewport, as its position will be more relevant to the user.
   nsPoint mRestorePos;
   // The last logical position we scrolled to while trying to restore
   // mRestorePos, or 0,0 when this is a new frame. Set to -1,-1 once we've
   // scrolled for any reason other than trying to restore mRestorePos.
+  // Just as with mRestorePos, this position will be the logical position of
+  // the *visual* viewport where available.
   nsPoint mLastPos;
 
   // The latest scroll position we've sent or received from APZ. This
   // represents the main thread's best knowledge of the APZ scroll position,
   // and is used to calculate relative scroll offset updates.
   nsPoint mApzScrollPos;
 
   nsExpirationState mActivityExpirationState;
@@ -961,16 +979,17 @@ class nsHTMLScrollFrame : public nsConta
     mHelper.ResetScrollPositionForLayerPixelAlignment();
   }
   virtual bool DidHistoryRestore() const override {
     return mHelper.mDidHistoryRestore;
   }
   virtual void ClearDidHistoryRestore() override {
     mHelper.mDidHistoryRestore = false;
   }
+  virtual void MarkEverScrolled() override { mHelper.MarkEverScrolled(); }
   virtual bool IsRectNearlyVisible(const nsRect& aRect) override {
     return mHelper.IsRectNearlyVisible(aRect);
   }
   virtual nsRect ExpandRectToNearlyVisible(const nsRect& aRect) const override {
     return mHelper.ExpandRectToNearlyVisible(aRect);
   }
   virtual nsAtom* LastScrollOrigin() override {
     return mHelper.LastScrollOrigin();
@@ -1428,16 +1447,17 @@ class nsXULScrollFrame final : public ns
     mHelper.ResetScrollPositionForLayerPixelAlignment();
   }
   virtual bool DidHistoryRestore() const override {
     return mHelper.mDidHistoryRestore;
   }
   virtual void ClearDidHistoryRestore() override {
     mHelper.mDidHistoryRestore = false;
   }
+  virtual void MarkEverScrolled() override { mHelper.MarkEverScrolled(); }
   virtual bool IsRectNearlyVisible(const nsRect& aRect) override {
     return mHelper.IsRectNearlyVisible(aRect);
   }
   virtual nsRect ExpandRectToNearlyVisible(const nsRect& aRect) const override {
     return mHelper.ExpandRectToNearlyVisible(aRect);
   }
   virtual nsAtom* LastScrollOrigin() override {
     return mHelper.LastScrollOrigin();
--- a/layout/generic/nsIScrollableFrame.h
+++ b/layout/generic/nsIScrollableFrame.h
@@ -371,16 +371,21 @@ class nsIScrollableFrame : public nsIScr
   virtual bool DidHistoryRestore() const = 0;
   /**
    * Clear the flag so that DidHistoryRestore() returns false until the next
    * RestoreState call.
    * @see nsIStatefulFrame::RestoreState
    */
   virtual void ClearDidHistoryRestore() = 0;
   /**
+   * Mark the frame as having been scrolled at least once, so that it remains
+   * active and we can also start storing its scroll position when saving state.
+   */
+  virtual void MarkEverScrolled() = 0;
+  /**
    * Determine if the passed in rect is nearly visible according to the frame
    * visibility heuristics for how close it is to the visible scrollport.
    */
   virtual bool IsRectNearlyVisible(const nsRect& aRect) = 0;
   /**
    * Expand the given rect taking into account which directions we can scroll
    * and how far we want to expand for frame visibility purposes.
    */
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -5829,22 +5829,23 @@ static bool IsItemTooSmallForActiveLayer
       aFrame->GetVisualOverflowRectRelativeToSelf().ToOutsidePixels(
           aFrame->PresContext()->AppUnitsPerDevPixel());
   return visibleDevPixels.Size() <
          nsIntSize(gfxPrefs::LayoutMinActiveLayerSize(),
                    gfxPrefs::LayoutMinActiveLayerSize());
 }
 
 /* static */ bool nsDisplayOpacity::NeedsActiveLayer(
-    nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) {
+    nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
+    bool aEnforceMinimumSize) {
   if (EffectCompositor::HasAnimationsForCompositor(aFrame,
                                                    eCSSProperty_opacity) ||
       (ActiveLayerTracker::IsStyleAnimated(aBuilder, aFrame,
                                            eCSSProperty_opacity) &&
-       !IsItemTooSmallForActiveLayer(aFrame))) {
+       !(aEnforceMinimumSize && IsItemTooSmallForActiveLayer(aFrame)))) {
     return true;
   }
   return false;
 }
 
 void nsDisplayOpacity::ApplyOpacity(nsDisplayListBuilder* aBuilder,
                                     float aOpacity,
                                     const DisplayItemClipChain* aClip) {
@@ -7957,29 +7958,30 @@ already_AddRefed<Layer> nsDisplayTransfo
   } else {
     container->RemoveUserData(nsIFrame::LayerIsPrerenderedDataKey());
     container->SetContentFlags(container->GetContentFlags() &
                                ~Layer::CONTENT_MAY_CHANGE_TRANSFORM);
   }
   return container.forget();
 }
 
-bool nsDisplayTransform::MayBeAnimated(nsDisplayListBuilder* aBuilder) const {
+bool nsDisplayTransform::MayBeAnimated(nsDisplayListBuilder* aBuilder,
+                                       bool aEnforceMinimumSize) const {
   // If EffectCompositor::HasAnimationsForCompositor() is true then we can
   // completely bypass the main thread for this animation, so it is always
   // worthwhile.
   // For ActiveLayerTracker::IsStyleAnimated() cases the main thread is
   // already involved so there is less to be gained.
   // Therefore we check that the *post-transform* bounds of this item are
   // big enough to justify an active layer.
   if (EffectCompositor::HasAnimationsForCompositor(mFrame,
                                                    eCSSProperty_transform) ||
       (ActiveLayerTracker::IsStyleAnimated(aBuilder, mFrame,
                                            eCSSProperty_transform) &&
-       !IsItemTooSmallForActiveLayer(mFrame))) {
+       !(aEnforceMinimumSize && IsItemTooSmallForActiveLayer(mFrame)))) {
     return true;
   }
   return false;
 }
 
 nsDisplayItem::LayerState nsDisplayTransform::GetLayerState(
     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
     const ContainerLayerParameters& aParameters) {
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -5329,18 +5329,18 @@ class nsDisplayOpacity : public nsDispla
 
   /**
    * Returns true if ShouldFlattenAway() applied opacity to children.
    */
   bool OpacityAppliedToChildren() const {
     return mChildOpacityState == ChildOpacityState::Applied;
   }
 
-  static bool NeedsActiveLayer(nsDisplayListBuilder* aBuilder,
-                               nsIFrame* aFrame);
+  static bool NeedsActiveLayer(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
+                               bool aEnforceMinimumSize = true);
   void WriteDebugInfo(std::stringstream& aStream) override;
   bool CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) override;
   bool CreateWebRenderCommands(
       mozilla::wr::DisplayListBuilder& aBuilder,
       mozilla::wr::IpcResourceUpdateQueue& aResources,
       const StackingContextHelper& aSc,
       mozilla::layers::RenderRootStateManager* aManager,
       nsDisplayListBuilder* aDisplayListBuilder) override;
@@ -6700,17 +6700,18 @@ class nsDisplayTransform : public nsDisp
    * PartialPrerender if some but not all of the contents should be prerendered,
    * or NoPrerender if only the visible area should be rendered.
    * |aDirtyRect| is updated to the area that should be prerendered.
    */
   static PrerenderDecision ShouldPrerenderTransformedContent(
       nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsRect* aDirtyRect);
   bool CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) override;
 
-  bool MayBeAnimated(nsDisplayListBuilder* aBuilder) const;
+  bool MayBeAnimated(nsDisplayListBuilder* aBuilder,
+                     bool aEnforceMinimumSize = true) const;
 
   void WriteDebugInfo(std::stringstream& aStream) override;
 
   // Force the layer created for this item not to extend 3D context.
   // See nsIFrame::BuildDisplayListForStackingContext()
   void SetNoExtendContext() { mNoExtendContext = true; }
 
   void DoUpdateBoundsPreserves3D(nsDisplayListBuilder* aBuilder) override {
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -139,19 +139,16 @@ mozilla::MediumFeaturesChangedResult Ser
     bool may_affect_default_style);
 
 void Servo_StyleSet_Drop(RawServoStyleSetOwned set);
 void Servo_StyleSet_CompatModeChanged(RawServoStyleSetBorrowed raw_data);
 
 void Servo_StyleSet_AppendStyleSheet(RawServoStyleSetBorrowed set,
                                      const mozilla::StyleSheet* gecko_sheet);
 
-void Servo_StyleSet_PrependStyleSheet(RawServoStyleSetBorrowed set,
-                                      const mozilla::StyleSheet* gecko_sheet);
-
 void Servo_StyleSet_RemoveStyleSheet(RawServoStyleSetBorrowed set,
                                      const mozilla::StyleSheet* gecko_sheet);
 
 void Servo_StyleSet_InsertStyleSheetBefore(
     RawServoStyleSetBorrowed set, const mozilla::StyleSheet* gecko_sheet,
     const mozilla::StyleSheet* before);
 
 void Servo_StyleSet_FlushStyleSheets(
--- a/layout/style/ServoStyleSet.cpp
+++ b/layout/style/ServoStyleSet.cpp
@@ -651,41 +651,16 @@ nsresult ServoStyleSet::AppendStyleSheet
 
   if (mStyleRuleMap) {
     mStyleRuleMap->SheetAdded(*aSheet);
   }
 
   return NS_OK;
 }
 
-nsresult ServoStyleSet::PrependStyleSheet(SheetType aType, StyleSheet* aSheet) {
-  MOZ_ASSERT(aSheet);
-  MOZ_ASSERT(aSheet->IsApplicable());
-  MOZ_ASSERT(IsCSSSheetType(aType));
-  MOZ_ASSERT(aSheet->RawContents(),
-             "Raw sheet should be in place before insertion.");
-
-  RemoveSheetOfType(aType, aSheet);
-  PrependSheetOfType(aType, aSheet);
-
-  if (mRawSet) {
-    // Maintain a mirrored list of sheets on the servo side.
-    // Servo will remove aSheet from its original position as part of the call
-    // to Servo_StyleSet_PrependStyleSheet.
-    Servo_StyleSet_PrependStyleSheet(mRawSet.get(), aSheet);
-    SetStylistStyleSheetsDirty();
-  }
-
-  if (mStyleRuleMap) {
-    mStyleRuleMap->SheetAdded(*aSheet);
-  }
-
-  return NS_OK;
-}
-
 nsresult ServoStyleSet::RemoveStyleSheet(SheetType aType, StyleSheet* aSheet) {
   MOZ_ASSERT(aSheet);
   MOZ_ASSERT(IsCSSSheetType(aType));
 
   RemoveSheetOfType(aType, aSheet);
   if (mRawSet) {
     // Maintain a mirrored list of sheets on the servo side.
     Servo_StyleSet_RemoveStyleSheet(mRawSet.get(), aSheet);
--- a/layout/style/ServoStyleSet.h
+++ b/layout/style/ServoStyleSet.h
@@ -203,17 +203,16 @@ class ServoStyleSet {
 #ifdef MOZ_XUL
   already_AddRefed<ComputedStyle> ResolveXULTreePseudoStyle(
       dom::Element* aParentElement, nsCSSAnonBoxPseudoStaticAtom* aPseudoTag,
       ComputedStyle* aParentContext, const AtomArray& aInputWord);
 #endif
 
   // manage the set of style sheets in the style set
   nsresult AppendStyleSheet(SheetType aType, StyleSheet* aSheet);
-  nsresult PrependStyleSheet(SheetType aType, StyleSheet* aSheet);
   nsresult RemoveStyleSheet(SheetType aType, StyleSheet* aSheet);
   nsresult ReplaceSheets(SheetType aType,
                          const nsTArray<RefPtr<StyleSheet>>& aNewSheets);
   nsresult InsertStyleSheetBefore(SheetType aType, StyleSheet* aNewSheet,
                                   StyleSheet* aReferenceSheet);
 
   int32_t SheetCount(SheetType aType) const;
   StyleSheet* StyleSheetAt(SheetType aType, int32_t aIndex) const;
--- a/layout/style/test/test_transitions_per_property.html
+++ b/layout/style/test/test_transitions_per_property.html
@@ -322,16 +322,23 @@ if (IsCSSPropertyPrefEnabled("layout.css
 }
 
 if (IsCSSPropertyPrefEnabled("layout.css.scrollbar-color.enabled")) {
   supported_properties["scrollbar-color"] = [
     test_scrollbar_color_transition,
   ];
 }
 
+// For properties which are well-tested by web-platform-tests, we don't need to
+// test animations/transitions again on them.
+var skipped_transitionable_properties = [
+  "grid-template-columns",
+  "grid-template-rows",
+]
+
 // Logical properties.
 for (const logical_side of ["inline-start", "inline-end", "block-start", "block-end"]) {
   supported_properties["border-" + logical_side + "-color"] = supported_properties["border-top-color"];
   supported_properties["border-" + logical_side + "-width"] = supported_properties["border-top-width"];
   supported_properties["margin-" + logical_side] = supported_properties["margin-top"];
   supported_properties["padding-" + logical_side] = supported_properties["padding-top"];
   supported_properties["inset-" + logical_side] = supported_properties["top"];
 }
@@ -1086,16 +1093,17 @@ function sample_array(array, count) {
 }
 
 // Test that transitions don't do anything (i.e., aren't supported) on
 // the properties not in our test list above (and not transition
 // properties themselves).
 for (prop in gCSSProperties) {
   var info = gCSSProperties[prop];
   if (!(prop in supported_properties) &&
+      !skipped_transitionable_properties.includes(prop) &&
       info.type != CSS_TYPE_TRUE_SHORTHAND &&
       !("alias_for" in info) &&
       !prop.match(/^transition-/) &&
       // FIXME (Bug 119078): THIS SHOULD REALLY NOT BE NEEDED!
       prop != "-moz-binding" &&
       prop != "mask") {
 
     if ("prerequisites" in info) {
--- a/media/ffvpx/ffvpxcommon.mozbuild
+++ b/media/ffvpx/ffvpxcommon.mozbuild
@@ -6,49 +6,26 @@
 
 # Add assembler flags and includes
 ASFLAGS += CONFIG['FFVPX_ASFLAGS']
 ASFLAGS += ['-I%s/media/ffvpx/' % TOPSRCDIR]
 ASFLAGS += ['-I%s/media/ffvpx/libavcodec/x86/' % TOPSRCDIR]
 ASFLAGS += ['-I%s/media/ffvpx/libavutil/x86/' % TOPSRCDIR]
 
 if CONFIG['FFVPX_ASFLAGS']:
+    if CONFIG['FFVPX_USE_YASM']:
+        USE_YASM = True
+
     if CONFIG['OS_ARCH'] == 'WINNT':
-        USE_YASM = True
        # Fix inline symbols and math defines for windows.
         DEFINES['_USE_MATH_DEFINES'] = True
         DEFINES['inline'] = "__inline"
-        # 32-bit windows need to prefix symbols with an underscore.
-        if CONFIG['CPU_ARCH'] == 'x86':
-            ASFLAGS += ['-DPREFIX']
-            ASFLAGS += ['-Pconfig_win32.asm']
-        else:
-            ASFLAGS += ['-Pconfig_win64.asm']
-    elif CONFIG['OS_ARCH'] == 'Darwin':
-        USE_YASM = True
-        # 32/64-bit macosx assemblers need to prefix symbols with an underscore.
-        ASFLAGS += [
-            '-Pconfig_darwin64.asm',
-            '-DPREFIX'
-        ]
-    elif CONFIG['CPU_ARCH'] != 'arm' and CONFIG['CPU_ARCH'] != 'aarch4':
-        USE_YASM = True
-        # Default to unix, similar to how ASFLAGS setup works in configure.in
-        ASFLAGS += ['-Pconfig_unix64.asm']
 
-    if USE_YASM:
-        # default disabled components
-        ASFLAGS += ['-Pdefaults_disabled.asm']
-        if int(CONFIG['YASM_MAJOR_VERSION']) == 1 and int(CONFIG['YASM_MINOR_VERSION']) < 2:
-            DEFINES['YASM_MISSING_AVX2'] = True
-            ASFLAGS += [
-                '-DHAVE_AVX2=0',
-                '-DHAVE_AVX2_INTERNAL=0',
-                '-DHAVE_AVX2_EXTERNAL=0',
-            ]
+    if USE_YASM and not CONFIG['YASM_HAS_AVX2']:
+        DEFINES['YASM_MISSING_AVX2'] = True
 
 
 LOCAL_INCLUDES += ['/media/ffvpx']
 
 # We allow warnings for third-party code that can be updated from upstream.
 AllowCompilerWarnings()
 
 # Suppress warnings in third-party code.
--- a/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp
+++ b/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp
@@ -903,22 +903,22 @@ nsresult JsepSessionImpl::SetRemoteDescr
           SdpAttribute::kIceOptionsAttribute)) {
     iceOptions = parsed->GetAttributeList().GetIceOptions().mValues;
   }
 
   // Save in case we need to rollback.
   if (type == kJsepSdpOffer) {
     mOldTransceivers.clear();
     for (const auto& transceiver : mTransceivers) {
+      mOldTransceivers.push_back(new JsepTransceiver(*transceiver));
       if (!transceiver->IsNegotiated()) {
         // We chose a level for this transceiver, but never negotiated it.
         // Discard this state.
         transceiver->ClearLevel();
       }
-      mOldTransceivers.push_back(new JsepTransceiver(*transceiver));
     }
   }
 
   // TODO(bug 1095780): Note that we create remote tracks even when
   // They contain only codecs we can't negotiate or other craziness.
   rv = UpdateTransceiversFromRemoteDescription(*parsed);
   NS_ENSURE_SUCCESS(rv, rv);
 
--- a/media/webrtc/signaling/src/jsep/JsepTransceiver.h
+++ b/media/webrtc/signaling/src/jsep/JsepTransceiver.h
@@ -52,17 +52,17 @@ class JsepTransceiver {
         mStopped(orig.mStopped),
         mRemoved(orig.mRemoved),
         mNegotiated(orig.mNegotiated) {}
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(JsepTransceiver);
 
   void Rollback(JsepTransceiver& oldTransceiver) {
     MOZ_ASSERT(oldTransceiver.GetMediaType() == GetMediaType());
-    MOZ_ASSERT(!oldTransceiver.HasLevel() ||
+    MOZ_ASSERT(!oldTransceiver.HasLevel() || !HasLevel() ||
                oldTransceiver.GetLevel() == GetLevel());
     mTransport = oldTransceiver.mTransport;
     mLevel = oldTransceiver.mLevel;
     mBundleLevel = oldTransceiver.mBundleLevel;
     mRecvTrack = oldTransceiver.mRecvTrack;
 
     // stop() caused by a disabled m-section in a remote offer cannot be
     // rolled back.
--- a/mfbt/moz.build
+++ b/mfbt/moz.build
@@ -2,18 +2,16 @@
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 with Files("**"):
     BUG_COMPONENT = ("Core", "MFBT")
 
-TEST_DIRS += ['tests']
-
 Library('mfbt')
 
 EXPORTS.mozilla = [
     'Algorithm.h',
     'Alignment.h',
     'AllocPolicy.h',
     'AlreadyAddRefed.h',
     'Array.h',
@@ -145,23 +143,32 @@ UNIFIED_SOURCES += [
     'double-conversion/double-conversion/fast-dtoa.cc',
     'double-conversion/double-conversion/fixed-dtoa.cc',
     'double-conversion/double-conversion/strtod.cc',
     'FloatingPoint.cpp',
     'HashFunctions.cpp',
     'JSONWriter.cpp',
     'Poison.cpp',
     'RandomNum.cpp',
-    'RecordReplay.cpp',
     'SHA1.cpp',
     'TaggedAnonymousMemory.cpp',
     'Unused.cpp',
     'Utf8.cpp',
 ]
 
+if CONFIG['MOZ_BUILD_APP'] != 'tools/crashreporter':
+    # RecordReplay.cpp uses files from js/
+    UNIFIED_SOURCES += [
+        'RecordReplay.cpp',
+    ]
+
+    # Building MFBT tests adds a large overhead when building
+    # tools/crashreporter.
+    TEST_DIRS += ['tests']
+
 DEFINES['IMPL_MFBT'] = True
 
 SOURCES += [
     'Compression.cpp',
     'decimal/Decimal.cpp',
     'lz4.c',
 ]
 
--- a/mobile/android/chrome/geckoview/GeckoViewScrollChild.js
+++ b/mobile/android/chrome/geckoview/GeckoViewScrollChild.js
@@ -3,37 +3,39 @@
  * 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/. */
 
 ChromeUtils.import("resource://gre/modules/GeckoViewChildModule.jsm");
 
 class GeckoViewScrollChild extends GeckoViewChildModule {
   onEnable() {
     debug `onEnable`;
-    addEventListener("scroll", this, false);
+    addEventListener("mozvisualscroll", this, { mozSystemGroup: true });
   }
 
   onDisable() {
     debug `onDisable`;
-    removeEventListener("scroll", this);
+    removeEventListener("mozvisualscroll", { mozSystemGroup: true });
   }
 
   handleEvent(aEvent) {
-    if (aEvent.originalTarget.defaultView != content) {
+    if (aEvent.originalTarget.ownerGlobal != content) {
       return;
     }
 
     debug `handleEvent: ${aEvent.type}`;
 
     switch (aEvent.type) {
-      case "scroll":
+      case "mozvisualscroll":
+        let x = {}, y = {};
+        content.windowUtils.getVisualViewportOffset(x, y);
         this.eventDispatcher.sendRequest({
           type: "GeckoView:ScrollChanged",
-          scrollX: Math.round(content.scrollX),
-          scrollY: Math.round(content.scrollY),
+          scrollX: x.value,
+          scrollY: y.value,
         });
         break;
     }
   }
 }
 
 let {debug, warn} = GeckoViewScrollChild.initLogging("GeckoViewScroll");
 let module = GeckoViewScrollChild.create(this);
--- a/mobile/android/components/SessionStore.js
+++ b/mobile/android/components/SessionStore.js
@@ -541,18 +541,18 @@ SessionStore.prototype = {
           this._formdataSavePending =
             window.setTimeout(() => {
               this._formdataSavePending = null;
               this.onTabInput(window, browser);
             }, 2000);
         }
         break;
       }
-      case "resize":
-      case "scroll": {
+      case "mozvisualresize":
+      case "mozvisualscroll": {
         let browser = aEvent.currentTarget;
         // Duplicated logging check to avoid calling getTabForBrowser on each scroll event.
         if (loggingEnabled) {
           log(aEvent.type + " for tab " + window.BrowserApp.getTabForBrowser(browser).id);
         }
         if (!this._scrollSavePending) {
           this._scrollSavePending =
             window.setTimeout(() => {
@@ -645,18 +645,20 @@ SessionStore.prototype = {
     aBrowser.addEventListener("pageshow", this, true);
     aBrowser.addEventListener("AboutReaderContentReady", this, true);
 
     // Use a combination of events to watch for text data changes
     aBrowser.addEventListener("input", this, true);
     aBrowser.addEventListener("DOMAutoComplete", this, true);
 
     // Record the current scroll position and zoom level.
-    aBrowser.addEventListener("scroll", this, true);
-    aBrowser.addEventListener("resize", this, true);
+    aBrowser.addEventListener("mozvisualscroll", this,
+                              { capture: true, mozSystemGroup: true });
+    aBrowser.addEventListener("mozvisualresize", this,
+                              { capture: true, mozSystemGroup: true });
 
     log("onTabAdd() ran for tab " + aWindow.BrowserApp.getTabForBrowser(aBrowser).id +
         ", aNoNotification = " + aNoNotification);
     if (!aNoNotification) {
       if (this._loadState == STATE_QUITTING) {
         // A tab arrived just as were starting to shut down. Since we haven't yet received
         // application-quit, we refresh the window data one more time before the window closes.
         this._forEachBrowserWindow((aWindow) => {
@@ -671,18 +673,20 @@ SessionStore.prototype = {
   onTabRemove(aWindow, aBrowser, aNoNotification) {
     // Cleanup event listeners
     aBrowser.removeEventListener("DOMTitleChanged", this, true);
     aBrowser.removeEventListener("load", this, true);
     aBrowser.removeEventListener("pageshow", this, true);
     aBrowser.removeEventListener("AboutReaderContentReady", this, true);
     aBrowser.removeEventListener("input", this, true);
     aBrowser.removeEventListener("DOMAutoComplete", this, true);
-    aBrowser.removeEventListener("scroll", this, true);
-    aBrowser.removeEventListener("resize", this, true);
+    aBrowser.removeEventListener("mozvisualscroll", this,
+                                 { capture: true, mozSystemGroup: true });
+    aBrowser.removeEventListener("mozvisualresize", this,
+                                 { capture: true, mozSystemGroup: true });
 
     if (aBrowser.__SS_historyChange) {
       aWindow.clearTimeout(aBrowser.__SS_historyChange);
       delete aBrowser.__SS_historyChange;
     }
 
     delete aBrowser.__SS_data;
 
--- a/mobile/android/geckoview/api.txt
+++ b/mobile/android/geckoview/api.txt
@@ -445,17 +445,16 @@ package org.mozilla.geckoview {
     field public final java.lang.String issuerCommonName;
     field public final java.lang.String issuerOrganization;
     field public final int mixedModeActive;
     field public final int mixedModePassive;
     field public final java.lang.String organization;
     field public final java.lang.String origin;
     field public final int securityMode;
     field public final java.lang.String subjectName;
-    field public final int trackingMode;
   }
 
   public static interface GeckoSession.PromptDelegate {
     method @android.support.annotation.UiThread public void onAlert(@android.support.annotation.NonNull org.mozilla.geckoview.GeckoSession, @android.support.annotation.Nullable java.lang.String, @android.support.annotation.Nullable java.lang.String, @android.support.annotation.NonNull org.mozilla.geckoview.GeckoSession.PromptDelegate.AlertCallback);
     method @android.support.annotation.UiThread public void onAuthPrompt(@android.support.annotation.NonNull org.mozilla.geckoview.GeckoSession, @android.support.annotation.Nullable java.lang.String, @android.support.annotation.Nullable java.lang.String, @android.support.annotation.NonNull org.mozilla.geckoview.GeckoSession.PromptDelegate.AuthOptions, @android.support.annotation.NonNull org.mozilla.geckoview.GeckoSession.PromptDelegate.AuthCallback);
     method @android.support.annotation.UiThread public void onButtonPrompt(@android.support.annotation.NonNull org.mozilla.geckoview.GeckoSession, @android.support.annotation.Nullable java.lang.String, @android.support.annotation.Nullable java.lang.String, @android.support.annotation.Nullable java.lang.String[], @android.support.annotation.NonNull org.mozilla.geckoview.GeckoSession.PromptDelegate.ButtonCallback);
     method @android.support.annotation.UiThread public void onChoicePrompt(@android.support.annotation.NonNull org.mozilla.geckoview.GeckoSession, @android.support.annotation.Nullable java.lang.String, @android.support.annotation.Nullable java.lang.String, int, @android.support.annotation.NonNull org.mozilla.geckoview.GeckoSession.PromptDelegate.Choice[], @android.support.annotation.NonNull org.mozilla.geckoview.GeckoSession.PromptDelegate.ChoiceCallback);
     method @android.support.annotation.UiThread public void onColorPrompt(@android.support.annotation.NonNull org.mozilla.geckoview.GeckoSession, @android.support.annotation.Nullable java.lang.String, @android.support.annotation.Nullable java.lang.String, @android.support.annotation.NonNull org.mozilla.geckoview.GeckoSession.PromptDelegate.TextCallback);
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ContentDelegateTest.kt
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ContentDelegateTest.kt
@@ -169,17 +169,18 @@ class ContentDelegateTest : BaseSessionT
 
     @WithDevToolsAPI
     @WithDisplay(width = 400, height = 400)
     @Test fun saveAndRestoreState() {
         val startUri = createTestUrl(SAVE_STATE_PATH)
         mainSession.loadUri(startUri)
         sessionRule.waitForPageStop()
 
-        mainSession.evaluateJS("$('#name').value = 'the name'; window.scrollBy(0, 100);")
+        mainSession.evaluateJS("$('#name').value = 'the name'; window.setTimeout(() => window.scrollBy(0, 100),0);")
+        sessionRule.waitUntilCalled(Callbacks.ScrollDelegate::class, "onScrollChanged")
 
         val state = sessionRule.waitForResult(mainSession.saveState())
         assertThat("State should not be null", state, notNullValue())
 
         mainSession.loadUri("about:blank")
         sessionRule.waitForPageStop()
 
         mainSession.restoreState(state)
@@ -192,17 +193,17 @@ class ContentDelegateTest : BaseSessionT
             }
         })
 
         assertThat("'name' field should match",
                 mainSession.evaluateJS("$('#name').value").toString(),
                 equalTo("the name"))
 
         assertThat("Scroll position should match",
-                mainSession.evaluateJS("window.scrollY") as Double,
+                mainSession.evaluateJS("window.visualViewport.pageTop") as Double,
                 closeTo(100.0, .5))
     }
 
     @Test fun saveStateSync() {
         val startUri = createTestUrl(SAVE_STATE_PATH)
         mainSession.loadUri(startUri)
         sessionRule.waitForPageStop()
 
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ProgressDelegateTest.kt
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ProgressDelegateTest.kt
@@ -61,19 +61,16 @@ class ProgressDelegateTest : BaseSession
 
             @AssertCalled(count = 1, order = [2])
             override fun onSecurityChange(session: GeckoSession,
                                           securityInfo: GeckoSession.ProgressDelegate.SecurityInformation) {
                 assertThat("Session should not be null", session, notNullValue())
                 assertThat("Security info should not be null", securityInfo, notNullValue())
 
                 assertThat("Should not be secure", securityInfo.isSecure, equalTo(false))
-                assertThat("Tracking mode should match",
-                           securityInfo.trackingMode,
-                           equalTo(GeckoSession.ProgressDelegate.SecurityInformation.CONTENT_UNKNOWN))
             }
 
             @AssertCalled(count = 1, order = [3])
             override fun onPageStop(session: GeckoSession, success: Boolean) {
                 assertThat("Session should not be null", session, notNullValue())
                 assertThat("Load should succeed", success, equalTo(true))
             }
         })
@@ -210,19 +207,16 @@ class ProgressDelegateTest : BaseSession
                            securityInfo.securityMode,
                            equalTo(GeckoSession.ProgressDelegate.SecurityInformation.SECURITY_MODE_IDENTIFIED))
                 assertThat("Active mixed mode should match",
                            securityInfo.mixedModeActive,
                            equalTo(GeckoSession.ProgressDelegate.SecurityInformation.CONTENT_UNKNOWN))
                 assertThat("Passive mixed mode should match",
                            securityInfo.mixedModePassive,
                            equalTo(GeckoSession.ProgressDelegate.SecurityInformation.CONTENT_UNKNOWN))
-                assertThat("Tracking mode should match",
-                           securityInfo.trackingMode,
-                           equalTo(GeckoSession.ProgressDelegate.SecurityInformation.CONTENT_UNKNOWN))
             }
         })
     }
 
     @LargeTest
     @Test fun correctSecurityInfoForValidTLS_local() {
         assumeThat(sessionRule.env.isAutomation, equalTo(false))
 
@@ -259,19 +253,16 @@ class ProgressDelegateTest : BaseSession
                            securityInfo.securityMode,
                            equalTo(GeckoSession.ProgressDelegate.SecurityInformation.SECURITY_MODE_IDENTIFIED))
                 assertThat("Active mixed mode should match",
                            securityInfo.mixedModeActive,
                            equalTo(GeckoSession.ProgressDelegate.SecurityInformation.CONTENT_UNKNOWN))
                 assertThat("Passive mixed mode should match",
                            securityInfo.mixedModePassive,
                            equalTo(GeckoSession.ProgressDelegate.SecurityInformation.CONTENT_UNKNOWN))
-                assertThat("Tracking mode should match",
-                           securityInfo.trackingMode,
-                           equalTo(GeckoSession.ProgressDelegate.SecurityInformation.CONTENT_UNKNOWN))
             }
         })
     }
 
     @LargeTest
     @Test fun noSecurityInfoForExpiredTLS() {
         sessionRule.session.loadUri(if (sessionRule.env.isAutomation)
                                         "https://expired.example.com"
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
@@ -2491,28 +2491,22 @@ public class GeckoSession implements Par
              * CONTENT_UNKNOWN, CONTENT_BLOCKED, and CONTENT_LOADED.
              */
             public final @ContentType int mixedModePassive;
             /**
              * Indicates the presence of active mixed content; possible values are
              * CONTENT_UNKNOWN, CONTENT_BLOCKED, and CONTENT_LOADED.
              */
             public final @ContentType int mixedModeActive;
-            /**
-             * Indicates the status of tracking protection; possible values are
-             * CONTENT_UNKNOWN, CONTENT_BLOCKED, and CONTENT_LOADED.
-             */
-            public final @ContentType int trackingMode;
 
             /* package */ SecurityInformation(GeckoBundle identityData) {
                 final GeckoBundle mode = identityData.getBundle("mode");
 
                 mixedModePassive = mode.getInt("mixed_display");
                 mixedModeActive = mode.getInt("mixed_active");
-                trackingMode = mode.getInt("tracking");
 
                 securityMode = mode.getInt("identity");
 
                 isSecure = identityData.getBoolean("secure");
                 isException = identityData.getBoolean("securityException");
                 origin = identityData.getString("origin");
                 host = identityData.getString("host");
                 organization = identityData.getString("organization");
@@ -2522,17 +2516,16 @@ public class GeckoSession implements Par
             }
 
             /**
              * Empty constructor for tests
              */
             protected SecurityInformation() {
                 mixedModePassive = 0;
                 mixedModeActive = 0;
-                trackingMode = 0;
                 securityMode = 0;
                 isSecure = false;
                 isException = false;
                 origin = "";
                 host = "";
                 organization = "";
                 subjectName = "";
                 issuerCommonName = "";
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/doc-files/CHANGELOG.md
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/doc-files/CHANGELOG.md
@@ -1,15 +1,19 @@
 ---
 layout: geckoview
 ---
 
 <h1> GeckoView API Changelog. </h1>
 
 ## v66
+- Removed redundant field `GeckoSession.ProgressDelegate.SecurityInformation.trackingMode`.
+  Use `GeckoSession.TrackingProtectionDelegate.onTrackerBlocked` for
+  notification of blocked elements during page load.
+
 - Added [`@NonNull`][66.1] or [`@Nullable`][66.2] to all APIs.
 
 [66.1]: https://developer.android.com/reference/android/support/annotation/NonNull
 [66.2]: https://developer.android.com/reference/android/support/annotation/Nullable
 
 - Added methods for each setting in [`GeckoSessionSettings`][66.3]
 
 [66.3]: ../GeckoSessionSettings.html
@@ -100,9 +104,9 @@ layout: geckoview
 [65.23]: ../GeckoSession.FinderResult.html
 
 - Update [`CrashReporter#sendCrashReport`][65.24] to return the crash ID as a
   [`GeckoResult<String>`][65.25].
 
 [65.24]: ../CrashReporter.html#sendCrashReport-android.content.Context-android.os.Bundle-java.lang.String-
 [65.25]: ../GeckoResult.html
 
-[api-version]: 5957a5943b39ae0e56b7e892bd824a16bb71e811
+[api-version]: 9fb5334f4d7d0c79f963ef9c8e14795e4965e852
--- a/mobile/android/modules/geckoview/GeckoViewProgress.jsm
+++ b/mobile/android/modules/geckoview/GeckoViewProgress.jsm
@@ -34,39 +34,16 @@ var IdentityHandler = {
   MIXED_MODE_UNKNOWN: 0,
 
   // Blocked active mixed content.
   MIXED_MODE_CONTENT_BLOCKED: 1,
 
   // Loaded active mixed content.
   MIXED_MODE_CONTENT_LOADED: 2,
 
-  // The following tracking content modes are only used if tracking protection
-  // is enabled. Our Java frontend coalesces them into one indicator.
-
-  // No tracking content information. No tracking content icon is shown.
-  TRACKING_MODE_UNKNOWN: 0,
-
-  // Blocked active tracking content. Shield icon is shown, with a popup option to load content.
-  TRACKING_MODE_CONTENT_BLOCKED: 1,
-
-  // Loaded active tracking content. Yellow triangle icon is shown.
-  TRACKING_MODE_CONTENT_LOADED: 2,
-
-  _useTrackingProtection: false,
-  _usePrivateMode: false,
-
-  setUseTrackingProtection: function(aUse) {
-    this._useTrackingProtection = aUse;
-  },
-
-  setUsePrivateMode: function(aUse) {
-    this._usePrivateMode = aUse;
-  },
-
   /**
    * Determines the identity mode corresponding to the icon we show in the urlbar.
    */
   getIdentityMode: function getIdentityMode(aState) {
     if (aState & Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL) {
       return this.IDENTITY_MODE_VERIFIED;
     }
 
@@ -98,44 +75,29 @@ var IdentityHandler = {
 
     if (aState & Ci.nsIWebProgressListener.STATE_BLOCKED_MIXED_ACTIVE_CONTENT) {
       return this.MIXED_MODE_CONTENT_BLOCKED;
     }
 
     return this.MIXED_MODE_UNKNOWN;
   },
 
-  getTrackingMode: function getTrackingMode(aState) {
-    if (aState & Ci.nsIWebProgressListener.STATE_BLOCKED_TRACKING_CONTENT) {
-      return this.TRACKING_MODE_CONTENT_BLOCKED;
-    }
-
-    // Only show an indicator for loaded tracking content if the pref to block it is enabled
-    if ((aState & Ci.nsIWebProgressListener.STATE_LOADED_TRACKING_CONTENT) && this._useTrackingProtection) {
-      return this.TRACKING_MODE_CONTENT_LOADED;
-    }
-
-    return this.TRACKING_MODE_UNKNOWN;
-  },
-
   /**
    * Determine the identity of the page being displayed by examining its SSL cert
    * (if available). Return the data needed to update the UI.
    */
   checkIdentity: function checkIdentity(aState, aBrowser) {
     let identityMode = this.getIdentityMode(aState);
     let mixedDisplay = this.getMixedDisplayMode(aState);
     let mixedActive = this.getMixedActiveMode(aState);
-    let trackingMode = this.getTrackingMode(aState);
     let result = {
       mode: {
         identity: identityMode,
         mixed_display: mixedDisplay,
         mixed_active: mixedActive,
-        tracking: trackingMode,
       },
     };
 
     if (aBrowser.contentPrincipal) {
       result.origin = aBrowser.contentPrincipal.originNoSuffix;
     }
 
     // Don't show identity data for pages with an unknown identity or if any
@@ -205,19 +167,16 @@ class GeckoViewProgress extends GeckoVie
     }
 
     Services.obs.removeObserver(this, "oop-frameloader-crashed");
   }
 
   onSettingsUpdate() {
     const settings = this.settings;
     debug `onSettingsUpdate: ${settings}`;
-
-    IdentityHandler.setUseTrackingProtection(!!settings.useTrackingProtection);
-    IdentityHandler.setUsePrivateMode(!!settings.usePrivateMode);
   }
 
   onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
     debug `onStateChange: isTopLevel=${aWebProgress.isTopLevel},
                           flags=${aStateFlags}, status=${aStatus}
                           loadType=${aWebProgress.loadType}`;
 
 
--- a/mobile/android/tests/browser/chrome/chrome.ini
+++ b/mobile/android/tests/browser/chrome/chrome.ini
@@ -6,16 +6,17 @@ support-files =
   basic_article_mobile.html
   basic_article_mobile_2x.html
   browser_scrollPositions_sample.html
   browser_scrollPositions_sample2.html
   browser_scrollPositions_sample_frameset.html
   desktopmode_user_agent.sjs
   devicesearch.xml
   head.js
+  head_scroll.js
   head_search.js
   media_playback.html
   session_formdata_sample.html
   simpleservice.xml
   video_controls.html
   video_discovery.html
   video_discovery.sjs
   web_channel.html
@@ -45,15 +46,19 @@ skip-if = true # Bug 1241478
 [test_resource_substitutions.html]
 [test_restricted_profiles.html]
 [test_select_disabled.html]
 [test_selectoraddtab.html]
 [test_session_clear_history.html]
 [test_session_form_data.html]
 [test_session_parentid.html]
 [test_session_scroll_position.html]
+[test_session_scroll_visual_viewport.html]
+support-files =
+  ../../../../../gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js
+  ../../../../../gfx/layers/apz/test/mochitest/apz_test_utils.js
 [test_session_undo_close_tab.html]
 [test_session_zombification.html]
 [test_settings_fontinflation.html]
 [test_shared_preferences.html]
 [test_simple_discovery.html]
 [test_video_discovery.html]
 [test_web_channel.html]
--- a/mobile/android/tests/browser/chrome/head.js
+++ b/mobile/android/tests/browser/chrome/head.js
@@ -1,34 +1,35 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-function fuzzyEquals(a, b) {
-  return (Math.abs(a - b) < 1e-6);
-}
-
-function promiseBrowserEvent(browserOrFrame, eventType, options) {
+function promiseBrowserEvent(browserOrFrame, eventType, options = {}) {
+  let listenerOptions = { capture: true };
+  if (options.mozSystemGroup) {
+    listenerOptions.mozSystemGroup = true;
+  }
   return new Promise((resolve) => {
     function handle(event) {
       // Since we'll be redirecting, don't make assumptions about the given URL and the loaded URL
-      if (event.target != (browserOrFrame.contentDocument || browserOrFrame.document) ||
-                          event.target.location.href == "about:blank") {
-        info("Skipping spurious '" + eventType + "' event" + " for " + event.target.location.href);
+      let document = browserOrFrame.contentDocument || browserOrFrame.document;
+      if (event.target != document && event.target != document.ownerGlobal.visualViewport ||
+          document.location.href == "about:blank") {
+        info("Skipping spurious '" + eventType + "' event" + " for " + document.location.href);
         return;
       }
       info("Received event " + eventType + " from browser");
-      browserOrFrame.removeEventListener(eventType, handle, true);
-      if (options && options.resolveAtNextTick) {
+      browserOrFrame.removeEventListener(eventType, handle, listenerOptions);
+      if (options.resolveAtNextTick) {
         Services.tm.dispatchToMainThread(() => resolve(event));
       } else {
         resolve(event);
       }
     }
 
-    browserOrFrame.addEventListener(eventType, handle, true);
+    browserOrFrame.addEventListener(eventType, handle, listenerOptions);
     info("Now waiting for " + eventType + " event from browser");
   });
 }
 
 function promiseTabEvent(container, eventType) {
   return new Promise((resolve) => {
     function handle(event) {
       info("Received event " + eventType + " from container");
new file mode 100644
--- /dev/null
+++ b/mobile/android/tests/browser/chrome/head_scroll.js
@@ -0,0 +1,70 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function fuzzyEquals(a, b) {
+  return (Math.abs(a - b) < 1e-6);
+}
+
+function getFrame(browser, { frame = null }) {
+  let window;
+  if (frame !== null) {
+    window = browser.contentWindow.frames[frame];
+  } else {
+    window = browser.contentWindow;
+  }
+  return window;
+}
+
+function setScrollPosition(browser,
+                           { x = 0, y = 0, zoom = 0, frame }) {
+  let window = getFrame(browser, {frame});
+  if (zoom) {
+    browser.contentWindow.windowUtils.setResolutionAndScaleTo(zoom);
+  }
+  window.scrollTo(x, y);
+}
+
+function checkScroll(browser, data) {
+  let {x, y, zoom} = data;
+  let scrollPos = getScrollPosition(browser, data);
+
+  if (data.hasOwnProperty("x")) {
+    is(scrollPos.x, x, "scrollX set correctly");
+  }
+  if (data.hasOwnProperty("y")) {
+    is(scrollPos.y, y, "scrollY set correctly");
+  }
+  if (zoom) {
+    ok(fuzzyEquals(scrollPos.zoom, zoom), "zoom set correctly");
+  }
+}
+
+function getScrollPosition(browser, data = {}) {
+  let utils = getFrame(browser, data).windowUtils;
+  let x = {}, y = {};
+
+  let zoom = utils.getResolution();
+  utils.getVisualViewportOffset(x, y);
+
+  return { x: x.value, y: y.value, zoom };
+}
+
+function getScrollString({ x = 0, y = 0 }) {
+  return x + "," + y;
+}
+
+function presStateToCSSPx(presState) {
+  // App units aren't commonly exposed to JS, so we can't just call a helper function
+  // and have to convert them ourselves instead.
+  // Conversion factor taken from gfx/src/AppUnits.h.
+  const APP_UNITS_PER_CSS_PX = 60;
+
+  let state = {...presState};
+  if (state.scroll) {
+    let scroll = state.scroll.split(",").map(pos => parseInt(pos, 10) || 0);
+    scroll = scroll.map(appUnits => Math.round(appUnits / APP_UNITS_PER_CSS_PX));
+    state.scroll = getScrollString({ x: scroll[0], y: scroll[1] });
+  }
+
+  return state;
+}
--- a/mobile/android/tests/browser/chrome/test_session_scroll_position.html
+++ b/mobile/android/tests/browser/chrome/test_session_scroll_position.html
@@ -10,16 +10,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <head>
   <meta charset="utf-8">
   <title>Various scroll position/zoom level tests for the mobile session store</title>
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://global/skin"/>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
   <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="head_scroll.js"></script>
   <script type="application/javascript">
 
   /** Tests for Bug 810981, 1282902, 1301016, 1265818, 1498892 **/
 
   "use strict";
 
   ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
   ChromeUtils.import("resource://gre/modules/Services.jsm");
@@ -44,61 +45,16 @@ https://bugzilla.mozilla.org/show_bug.cg
   const SCROLL_X = Math.round(100 * (1 + Math.random()));
   const SCROLL_Y = Math.round(200 * (1 + Math.random()));
   const ZOOM = 1 + 0.5 * Math.random();
 
   const SCROLL2_X = Math.round(300 * (1 + Math.random()));
   const SCROLL2_Y = Math.round(400 * (1 + Math.random()));
   const ZOOM2 = 1.5 + 0.5 * Math.random();
 
-  function getFrame(browser, data) {
-    let frame = browser.contentWindow;
-    if (data.hasOwnProperty("frame")) {
-      frame = browser.contentWindow.frames[data.frame];
-    }
-    return frame;
-  }
-
-  function setScrollPosition(browser, data) {
-    let {x, y, zoom} = data;
-    x = x || 0;
-    y = y || 0;
-    let frame = getFrame(browser, data);
-    if (data.hasOwnProperty("zoom")) {
-      browser.contentWindow.windowUtils.setResolutionAndScaleTo(zoom);
-    }
-    frame.scrollTo(x, y);
-  }
-
-  function checkScroll(browser, data) {
-    let {x, y, zoom} = data;
-    let utils = getFrame(browser, data).windowUtils;
-
-    let actualX = {}, actualY = {};
-    let actualZoom = utils.getResolution();
-    utils.getScrollXY(false, actualX, actualY);
-
-    if (data.hasOwnProperty("x")) {
-      is(actualX.value, x, "scrollX set correctly");
-    }
-    if (data.hasOwnProperty("y")) {
-      is(actualY.value, y, "scrollY set correctly");
-    }
-    if (data.hasOwnProperty("zoom")) {
-      ok(fuzzyEquals(actualZoom, zoom), "zoom set correctly");
-    }
-  }
-
-  function getScrollString(data) {
-    let {x, y} = data;
-    x = x || 0;
-    y = y || 0;
-    return x + "," + y;
-  }
-
   // Track the tabs where the tests are happening.
   let tabScroll;
 
   function cleanupTabs() {
     if (tabScroll) {
       BrowserApp.closeTab(tabScroll);
       tabScroll = null;
     }
@@ -188,28 +144,30 @@ https://bugzilla.mozilla.org/show_bug.cg
     let [{scrolldata}] = state;
     is(scrolldata.scroll, getScrollString(testData2), "stored scroll position is correct");
     ok(fuzzyEquals(scrolldata.zoom.resolution, ZOOM2), "stored zoom level is correct");
 
     // Restore the closed tab.
     let closedTabData = ss.getClosedTabs(chromeWin)[0];
     let browser = ss.undoCloseTab(chromeWin, closedTabData);
     let pageshow = promiseBrowserEvent(browser, "pageshow");
-    let scroll = promiseBrowserEvent(browser, "scroll");
+    let scroll = promiseBrowserEvent(browser, "mozvisualscroll",
+                                     { mozSystemGroup: true });
     await pageshow;
     await scroll;
 
     // Check the scroll position and zoom level.
     checkScroll(browser, testData2);
 
     // Now go back in history and check that the scroll position
     // is restored there as well.
     is(browser.canGoBack, true, "can go back");
     pageshow = promiseBrowserEvent(browser, "pageshow");
-    scroll = promiseBrowserEvent(browser, "scroll");
+    scroll = promiseBrowserEvent(browser, "mozvisualscroll",
+                                 { mozSystemGroup: true });
     browser.goBack();
     await pageshow;
     await scroll;
 
     checkScroll(browser, testData1);
 
     // Remove the tab.
     BrowserApp.closeTab(BrowserApp.getTabForBrowser(browser));
@@ -305,19 +263,25 @@ https://bugzilla.mozilla.org/show_bug.cg
 
     // Restore the closed tab.
     let closedTabData = ss.getClosedTabs(chromeWin)[0];
     let browser = ss.undoCloseTab(chromeWin, closedTabData);
     let pageshow = promiseBrowserEvent(browser, "pageshow");
     // We can't add event listeners for the frames until we're sure that they've actually loaded.
     let load = promiseBrowserEvent(browser, "load");
     await load;
-    let scrollParent = promiseBrowserEvent(getFrame(browser, testDataParent), "scroll");
-    let scroll1 = promiseBrowserEvent(getFrame(browser, testData1), "scroll");
-    let scroll2 = promiseBrowserEvent(getFrame(browser, testData2), "scroll");
+    let scrollParent = promiseBrowserEvent(getFrame(browser, testDataParent),
+                                           "mozvisualscroll",
+                                           { mozSystemGroup: true });
+    let scroll1 = promiseBrowserEvent(getFrame(browser, testData1),
+                                      "mozvisualscroll",
+                                      { mozSystemGroup: true });
+    let scroll2 = promiseBrowserEvent(getFrame(browser, testData2),
+                                      "mozvisualscroll",
+                                      { mozSystemGroup: true });
     await pageshow;
     await scrollParent;
     await scroll1;
     await scroll2;
 
     // Check the scroll position and zoom level.
     checkScroll(browser, testDataParent);
     checkScroll(browser, testData1);
new file mode 100644
--- /dev/null
+++ b/mobile/android/tests/browser/chrome/test_session_scroll_visual_viewport.html
@@ -0,0 +1,190 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1498812
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Various scroll position tests for the mobile session store, dealing specifically with the Visual Viewport</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/AddTask.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://global/skin"/>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="head_scroll.js"></script>
+  <script type="application/javascript" src="apz_test_utils.js"></script>
+  <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+  <script type="application/javascript">
+  /* import-globals-from ../../../../../gfx/layers/apz/test/mochitest/apz_test_utils.js */
+  /* import-globals-from ../../../../../gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js */
+
+  /** Tests for Bug 1498812 **/
+
+  "use strict";
+
+  ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+  ChromeUtils.import("resource://gre/modules/Services.jsm");
+  ChromeUtils.import("resource://gre/modules/Messaging.jsm");
+
+  // The chrome window and friends.
+  let chromeWin = Services.wm.getMostRecentWindow("navigator:browser");
+  let BrowserApp = chromeWin.BrowserApp;
+
+  const BASE = "http://example.org/chrome/mobile/android/tests/browser/chrome/";
+  // This is a plain desktop page without any meta viewport tags,
+  // so the layout viewport will always fill the full page width.
+  const URL = BASE + "basic_article.html";
+  // A mobile page using width=device-width, which leads to the same result.
+  const URL2 = BASE + "basic_article_mobile.html";
+
+  async function scrollRight(window) {
+    // This listener will trigger the test to continue once APZ is done with
+    // processing the scroll.
+    let transformEnd = promiseNotification("APZ:TransformEnd");
+
+    let scroll = [
+        [ { x: 125, y: 100 } ],
+        [ { x: 120, y: 100 } ],
+        [ { x: 115, y: 100 } ],
+        [ { x: 110, y: 100 } ],
+        [ { x: 105, y: 100 } ],
+        [ { x: 100, y: 100 } ],
+    ];
+
+    let touchIds = [0];
+    let doScroll = synthesizeNativeTouchSequences(document.body, scroll, null, touchIds);
+    while (!doScroll.next().done);
+
+    await transformEnd;
+
+    await promiseApzRepaintsFlushed(window);
+  }
+
+  // Track the tabs where the tests are happening.
+  let tabScroll;
+
+  function cleanupTabs() {
+    if (tabScroll) {
+      BrowserApp.closeTab(tabScroll);
+      tabScroll = null;
+    }
+  }
+
+  SimpleTest.registerCleanupFunction(function() {
+    cleanupTabs();
+  });
+
+  let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
+
+  add_task(async function test_sessionStoreScrollPositionVisualViewport() {
+    let zoomIn = {x: 0, y: 0, zoom: 4 };
+    let scrollPos1, scrollPos2;
+    // Creates a tab, sets a scroll position and zoom level and closes the tab.
+    async function createAndRemoveTab() {
+        // Create a new tab.
+        tabScroll = BrowserApp.addTab(URL);
+        let browser = tabScroll.browser;
+        await promiseBrowserEvent(browser, "pageshow");
+
+        // Zoom in, so we can scroll to the right.
+        let scrolled = promiseTabEvent(browser, "SSTabScrollCaptured");
+        setScrollPosition(browser, zoomIn);
+        await scrolled;
+
+        // Check that we've actually zoomed.
+        checkScroll(browser, zoomIn);
+
+        scrolled = promiseTabEvent(browser, "SSTabScrollCaptured");
+        await scrollRight(browser.contentWindow);
+        await scrolled;
+
+        scrollPos1 = getScrollPosition(browser);
+        isnot(scrollPos1.x, 0, "we should be scrolled to the right");
+        is(scrollPos1.y, 0, "we scrolled horizontally");
+
+        // Navigate to a different page and scroll/zoom there as well.
+        browser.loadURI(URL2);
+        await promiseBrowserEvent(browser, "pageshow");
+
+        scrolled = promiseTabEvent(browser, "SSTabScrollCaptured");
+        setScrollPosition(browser, zoomIn);
+        await scrolled;
+        checkScroll(browser, zoomIn);
+
+        scrolled = promiseTabEvent(browser, "SSTabScrollCaptured");
+        await scrollRight(browser.contentWindow);
+        await scrolled;
+
+        scrollPos2 = getScrollPosition(browser);
+        isnot(scrollPos2.x, 0, "we should be scrolled to the right");
+        is(scrollPos2.y, 0, "we scrolled horizontally");
+
+        // Remove the tab.
+        let closed = promiseTabEvent(browser, "SSTabCloseProcessed");
+        BrowserApp.closeTab(tabScroll);
+        await closed;
+    }
+
+    await createAndRemoveTab();
+
+    // Check the live scroll data for the current history entry...
+    let tabData = ss.getClosedTabs(chromeWin)[0];
+    let {scrolldata} = tabData;
+    is(scrolldata.scroll, getScrollString(scrollPos2), "stored scroll position is correct");
+    ok(fuzzyEquals(scrolldata.zoom.resolution, scrollPos2.zoom), "stored zoom level is correct");
+
+    // ... and the presState from the previous history entry.
+    let {index} = tabData;
+    index -= 1; // session history uses 1-based index
+    let {entries} = tabData;
+    let prevPage = entries[index - 1];
+    ok(prevPage.presState, "presState exists");
+    if (prevPage.presState) {
+      let presState = prevPage.presState[0];
+      // The presState operates in app units, while all other scroll positions
+      // in JS-land use CSS pixels.
+      presState = presStateToCSSPx(presState);
+      is(presState.scroll, getScrollString(scrollPos1), "stored scroll position for previous page is correct");
+      ok(fuzzyEquals(presState.res, scrollPos1.zoom), "stored zoom level for previous page is correct");
+    }
+
+    // Restore the closed tab.
+    let browser = ss.undoCloseTab(chromeWin, tabData);
+    tabScroll = BrowserApp.getTabForBrowser(browser);
+    let pageshow = promiseBrowserEvent(browser, "pageshow");
+    let scroll = promiseBrowserEvent(browser, "mozvisualscroll",
+                                     { mozSystemGroup: true });
+    await pageshow;
+    await scroll;
+
+    // Check the scroll position and zoom level.
+    checkScroll(browser, scrollPos2);
+
+    // Now go back in history and check that the scroll position
+    // is restored there as well.
+    is(browser.canGoBack, true, "can go back");
+    pageshow = promiseBrowserEvent(browser, "pageshow");
+    scroll = promiseBrowserEvent(browser, "mozvisualscroll",
+                                 { mozSystemGroup: true });
+    browser.goBack();
+    await pageshow;
+    await scroll;
+
+    checkScroll(browser, scrollPos1);
+
+    // Remove the tab.
+    cleanupTabs();
+  });
+
+  </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1498812">Mozilla Bug 1498812</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
--- a/modules/libjar/nsJARURI.cpp
+++ b/modules/libjar/nsJARURI.cpp
@@ -470,24 +470,20 @@ nsJARURI::EqualsExceptRef(nsIURI *other,
 
   return refHandlingMode == eHonorRef
              ? mJAREntry->Equals(otherJAR->mJAREntry, result)
              : mJAREntry->EqualsExceptRef(otherJAR->mJAREntry, result);
 }
 
 NS_IMETHODIMP
 nsJARURI::SchemeIs(const char *i_Scheme, bool *o_Equals) {
-  NS_ENSURE_ARG_POINTER(o_Equals);
-  if (!i_Scheme) return NS_ERROR_INVALID_ARG;
+  MOZ_ASSERT(i_Scheme);
+  MOZ_ASSERT(o_Equals);
 
-  if (*i_Scheme == 'j' || *i_Scheme == 'J') {
-    *o_Equals = PL_strcasecmp("jar", i_Scheme) ? false : true;
-  } else {
-    *o_Equals = false;
-  }
+  *o_Equals = PL_strcasecmp("jar", i_Scheme) ? false : true;
   return NS_OK;
 }
 
 nsresult nsJARURI::Clone(nsIURI **result) {
   nsresult rv;
 
   nsCOMPtr<nsIJARURI> uri;
   rv = CloneWithJARFileInternal(mJARFile, eHonorRef, getter_AddRefs(uri));
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1941,16 +1941,22 @@ pref("network.sts.poll_busy_wait_period_
 pref("network.sts.max_time_for_pr_close_during_shutdown", 5000);
 
 // When the polling socket pair we use to wake poll() up on demand doesn't
 // get signalled (is not readable) within this timeout, we try to repair it.
 // This timeout can be disabled by setting this pref to 0.
 // The value is expected in seconds.
 pref("network.sts.pollable_event_timeout", 6);
 
+// Start a separate socket process. Performing networking on the socket process
+// is control by a sepparate pref
+// ("network.http.network_access_on_socket_process.enabled").
+// Changing these prefs requires a restart.
+pref("network.process.enabled", false);
+
 // Enable/disable sni encryption.
 pref("network.security.esni.enabled", false);
 
 // 2147483647 == PR_INT32_MAX == ~2 GB
 pref("network.websocket.max-message-size", 2147483647);
 
 // Should we automatically follow http 3xx redirects during handshake
 pref("network.websocket.auto-follow-http-redirects", false);
--- a/moz.configure
+++ b/moz.configure
@@ -118,23 +118,24 @@ def debug_rust():
 
 set_config('MOZ_DEBUG_RUST', debug_rust)
 set_define('MOZ_DEBUG_RUST', debug_rust)
 
 js_option(env='MOZ_PGO', help='Build with profile guided optimizations')
 
 set_config('MOZ_PGO', depends('MOZ_PGO')(lambda x: bool(x)))
 
+include('build/moz.configure/toolchain.configure',
+        when='--enable-compile-environment')
+
 include('build/moz.configure/pkg.configure')
 # Make this assignment here rather than in pkg.configure to avoid
 # requiring this file in unit tests.
 add_old_configure_assignment('PKG_CONFIG', pkg_config)
 
-include('build/moz.configure/toolchain.configure',
-        when='--enable-compile-environment')
 include('build/moz.configure/memory.configure',
         when='--enable-compile-environment')
 include('build/moz.configure/headers.configure',
         when='--enable-compile-environment')
 include('build/moz.configure/warnings.configure',
         when='--enable-compile-environment')
 include('build/moz.configure/flags.configure',
         when='--enable-compile-environment')
@@ -232,19 +233,16 @@ set_config('RUST_LIB_PREFIX', library_na
 set_config('RUST_LIB_SUFFIX', library_name_info.rust_lib.suffix)
 set_config('OBJ_SUFFIX', library_name_info.obj.suffix)
 # Lots of compilation tests depend on this variable being present.
 add_old_configure_assignment('OBJ_SUFFIX', library_name_info.obj.suffix)
 set_config('IMPORT_LIB_SUFFIX', library_name_info.import_lib.suffix)
 set_define('MOZ_DLL_PREFIX', depends(library_name_info.dll.prefix)(lambda s: '"%s"' % s))
 set_define('MOZ_DLL_SUFFIX', depends(library_name_info.dll.suffix)(lambda s: '"%s"' % s))
 
-# Depends on host_library_name_info, so needs to go here.
-include('build/moz.configure/bindgen.configure',
-        when='--enable-compile-environment')
 include(include_project_configure)
 
 @depends('--help')
 @imports(_from='mozbuild.backend', _import='backends')
 def build_backends_choices(_):
     return tuple(backends)
 
 
--- a/netwerk/base/nsIOService.cpp
+++ b/netwerk/base/nsIOService.cpp
@@ -49,16 +49,18 @@
 #include "mozilla/ipc/URIUtils.h"
 #include "mozilla/net/NeckoChild.h"
 #include "mozilla/net/NeckoParent.h"
 #include "mozilla/dom/ClientInfo.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ServiceWorkerDescriptor.h"
 #include "mozilla/net/CaptivePortalService.h"
 #include "mozilla/net/NetworkConnectivityService.h"
+#include "mozilla/net/SocketProcessHost.h"
+#include "mozilla/net/SocketProcessParent.h"
 #include "mozilla/Unused.h"
 #include "ReferrerPolicy.h"
 #include "nsContentSecurityManager.h"
 #include "nsContentUtils.h"
 
 namespace mozilla {
 namespace net {
 
@@ -73,16 +75,17 @@ using mozilla::dom::ServiceWorkerDescrip
 
 // Nb: these have been misnomers since bug 715770 removed the buffer cache.
 // "network.segment.count" and "network.segment.size" would be better names,
 // but the old names are still used to preserve backward compatibility.
 #define NECKO_BUFFER_CACHE_COUNT_PREF "network.buffer.cache.count"
 #define NECKO_BUFFER_CACHE_SIZE_PREF "network.buffer.cache.size"
 #define NETWORK_NOTIFY_CHANGED_PREF "network.notify.changed"
 #define NETWORK_CAPTIVE_PORTAL_PREF "network.captive-portal-service.enabled"
+#define WEBRTC_PREF_PREFIX "media.peerconnection."
 
 #define MAX_RECURSION_COUNT 50
 
 nsIOService *gIOService;
 static bool gHasWarnedUploadChannel2;
 static bool gCaptivePortalEnabled = false;
 static LazyLogModule gIOServiceLog("nsIOService");
 #undef LOG
@@ -184,39 +187,46 @@ bool nsIOService::sBlockFTPSubresources 
 nsIOService::nsIOService()
     : mOffline(true),
       mOfflineForProfileChange(false),
       mManageLinkStatus(false),
       mConnectivity(true),
       mOfflineMirrorsConnectivity(true),
       mSettingOffline(false),
       mSetOfflineValue(false),
+      mSocketProcessLaunchComplete(false),
       mShutdown(false),
       mHttpHandlerAlreadyShutingDown(false),
       mNetworkLinkServiceInitialized(false),
       mChannelEventSinks(NS_CHANNEL_EVENT_SINK_CATEGORY),
       mNetworkNotifyChanged(true),
       mTotalRequests(0),
       mCacheWon(0),
       mNetWon(0),
       mLastOfflineStateChange(PR_IntervalNow()),
       mLastConnectivityChange(PR_IntervalNow()),
       mLastNetworkLinkChange(PR_IntervalNow()),
-      mNetTearingDownStarted(0) {}
+      mNetTearingDownStarted(0),
+      mSocketProcess(nullptr) {}
 
 static const char *gCallbackPrefs[] = {
     PORT_PREF_PREFIX,
     MANAGE_OFFLINE_STATUS_PREF,
     NECKO_BUFFER_CACHE_COUNT_PREF,
     NECKO_BUFFER_CACHE_SIZE_PREF,
     NETWORK_NOTIFY_CHANGED_PREF,
     NETWORK_CAPTIVE_PORTAL_PREF,
     nullptr,
 };
 
+static const char *gCallbackPrefsForSocketProcess[] = {
+    WEBRTC_PREF_PREFIX,
+    nullptr,
+};
+
 nsresult nsIOService::Init() {
   // XXX hack until xpidl supports error info directly (bug 13423)
   nsCOMPtr<nsIErrorService> errorService = nsErrorService::GetOrCreate();
   MOZ_ALWAYS_TRUE(errorService);
   errorService->RegisterErrorStringBundle(NS_ERROR_MODULE_NETWORK,
                                           NECKO_MSGS_URL);
 
   InitializeCaptivePortalService();
@@ -234,16 +244,17 @@ nsresult nsIOService::Init() {
   nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
   if (observerService) {
     observerService->AddObserver(this, kProfileChangeNetTeardownTopic, true);
     observerService->AddObserver(this, kProfileChangeNetRestoreTopic, true);
     observerService->AddObserver(this, kProfileDoChange, true);
     observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true);
     observerService->AddObserver(this, NS_NETWORK_LINK_TOPIC, true);
     observerService->AddObserver(this, NS_WIDGET_WAKE_OBSERVER_TOPIC, true);
+    observerService->AddObserver(this, NS_PREFSERVICE_READ_TOPIC_ID, true);
   } else
     NS_WARNING("failed to get observer service");
 
   Preferences::AddBoolVarCache(&sIsDataURIUniqueOpaqueOrigin,
                                "security.data_uri.unique_opaque_origin", false);
   Preferences::AddBoolVarCache(
       &sBlockToplevelDataUriNavigations,
       "security.data_uri.block_toplevel_data_uri_navigations", false);
@@ -351,16 +362,156 @@ already_AddRefed<nsIOService> nsIOServic
     if (NS_SUCCEEDED(ios->Init())) {
       MOZ_ASSERT(gIOService == ios.get());
       return ios.forget();
     }
   }
   return do_AddRef(gIOService);
 }
 
+class SocketProcessListenerProxy : public SocketProcessHost::Listener {
+ public:
+  SocketProcessListenerProxy() = default;
+  void OnProcessLaunchComplete(SocketProcessHost *aHost, bool aSucceeded) {
+    if (!gIOService) {
+      return;
+    }
+
+    gIOService->OnProcessLaunchComplete(aHost, aSucceeded);
+  }
+
+  void OnProcessUnexpectedShutdown(SocketProcessHost *aHost) {
+    if (!gIOService) {
+      return;
+    }
+
+    gIOService->OnProcessUnexpectedShutdown(aHost);
+  }
+};
+
+nsresult nsIOService::LaunchSocketProcess() {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (XRE_GetProcessType() != GeckoProcessType_Default) {
+    return NS_OK;
+  }
+
+  if (mSocketProcess) {
+    return NS_OK;
+  }
+
+  if (!Preferences::GetBool("network.process.enabled", true)) {
+    LOG(("nsIOService skipping LaunchSocketProcess because of the pref"));
+    return NS_OK;
+  }
+
+  Preferences::RegisterPrefixCallbacks(
+      PREF_CHANGE_METHOD(nsIOService::NotifySocketProcessPrefsChanged),
+      gCallbackPrefsForSocketProcess, this);
+
+  // The subprocess is launched asynchronously, so we wait for a callback to
+  // acquire the IPDL actor.
+  mSocketProcess = new SocketProcessHost(new SocketProcessListenerProxy());
+  LOG(("nsIOService::LaunchSocketProcess"));
+  if (!mSocketProcess->Launch()) {
+    NS_WARNING("Failed to launch socket process!!");
+    DestroySocketProcess();
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+void nsIOService::DestroySocketProcess() {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (XRE_GetProcessType() != GeckoProcessType_Default || !mSocketProcess) {
+    return;
+  }
+
+  Preferences::UnregisterPrefixCallbacks(
+      PREF_CHANGE_METHOD(nsIOService::NotifySocketProcessPrefsChanged),
+      gCallbackPrefsForSocketProcess, this);
+
+  mSocketProcess->Shutdown();
+  mSocketProcess = nullptr;
+}
+
+bool nsIOService::SocketProcessReady() {
+  return mSocketProcess && mSocketProcess->IsConnected();
+}
+
+void nsIOService::NotifySocketProcessPrefsChanged(const char *aName) {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!XRE_IsParentProcess()) {
+    return;
+  }
+
+  dom::Pref pref(nsCString(aName), /* isLocked */ false, null_t(), null_t());
+  Preferences::GetPreference(&pref);
+  auto sendPrefUpdate = [pref]() {
+    Unused << gIOService->mSocketProcess->GetActor()->SendPreferenceUpdate(pref);
+  };
+  CallOrWaitForSocketProcess(sendPrefUpdate);
+}
+
+void nsIOService::OnProcessLaunchComplete(SocketProcessHost *aHost,
+                                          bool aSucceeded) {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  LOG(("nsIOService::OnProcessLaunchComplete aSucceeded=%d\n", aSucceeded));
+
+  mSocketProcessLaunchComplete = true;
+
+  if (mShutdown || !SocketProcessReady()) {
+    return;
+  }
+
+  if (!mPendingEvents.IsEmpty()) {
+    nsTArray<std::function<void()>> pendingEvents;
+    mPendingEvents.SwapElements(pendingEvents);
+    for (auto& func : pendingEvents) {
+      func();
+    }
+  }
+}
+
+void nsIOService::CallOrWaitForSocketProcess(const std::function<void()>& aFunc) {
+  MOZ_ASSERT(NS_IsMainThread());
+  if (IsSocketProcessLaunchComplete() && SocketProcessReady()) {
+    aFunc();
+  } else {
+    mPendingEvents.AppendElement(aFunc);  // infallible
+  }
+}
+
+bool nsIOService::IsSocketProcessLaunchComplete() {
+  MOZ_ASSERT(NS_IsMainThread());
+  return mSocketProcessLaunchComplete;
+}
+
+void nsIOService::OnProcessUnexpectedShutdown(SocketProcessHost *aHost) {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  LOG(("nsIOService::OnProcessUnexpectedShutdown\n"));
+  DestroySocketProcess();
+}
+
+RefPtr<MemoryReportingProcess> nsIOService::GetSocketProcessMemoryReporter() {
+  // Check the prefs here again, since we don't want to create
+  // SocketProcessMemoryReporter for some tests.
+  if (!Preferences::GetBool("network.process.enabled") || !SocketProcessReady()) {
+    return nullptr;
+
+  }
+
+  return new SocketProcessMemoryReporter();
+}
+
 NS_IMPL_ISUPPORTS(nsIOService, nsIIOService, nsINetUtil, nsISpeculativeConnect,
                   nsIObserver, nsIIOServiceInternal, nsISupportsWeakReference)
 
 ////////////////////////////////////////////////////////////////////////////////
 
 nsresult nsIOService::RecheckCaptivePortal() {
   MOZ_ASSERT(NS_IsMainThread(), "Must be called on the main thread");
   if (!mCaptivePortalService) {
@@ -1254,31 +1405,37 @@ nsIOService::Observe(nsISupports *subjec
       SetOffline(true);
     }
   } else if (!strcmp(topic, kProfileChangeNetRestoreTopic)) {
     if (mOfflineForProfileChange) {
       mOfflineForProfileChange = false;
       SetOffline(false);
     }
   } else if (!strcmp(topic, kProfileDoChange)) {
-    if (data && NS_LITERAL_STRING("startup").Equals(data)) {
+    if (!data) {
+      return NS_OK;
+    }
+    if (NS_LITERAL_STRING("startup").Equals(data)) {
       // Lazy initialization of network link service (see bug 620472)
       InitializeNetworkLinkService();
       // Set up the initilization flag regardless the actuall result.
       // If we fail here, we will fail always on.
       mNetworkLinkServiceInitialized = true;
 
       // And now reflect the preference setting
       PrefsChanged(MANAGE_OFFLINE_STATUS_PREF);
 
       // Bug 870460 - Read cookie database at an early-as-possible time
       // off main thread. Hence, we have more chance to finish db query
       // before something calls into the cookie service.
       nsCOMPtr<nsISupports> cookieServ =
           do_GetService(NS_COOKIESERVICE_CONTRACTID);
+    } else if (NS_LITERAL_STRING("xpcshell-do-get-profile").Equals(data)) {
+      // xpcshell doesn't read user profile.
+      LaunchSocketProcess();
     }
   } else if (!strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
     // Remember we passed XPCOM shutdown notification to prevent any
     // changes of the offline status from now. We must not allow going
     // online after this point.
     mShutdown = true;
 
     if (!mHttpHandlerAlreadyShutingDown && !mOfflineForProfileChange) {
@@ -1288,24 +1445,29 @@ nsIOService::Observe(nsISupports *subjec
 
     SetOffline(true);
 
     if (mCaptivePortalService) {
       static_cast<CaptivePortalService *>(mCaptivePortalService.get())->Stop();
       mCaptivePortalService = nullptr;
     }
 
+    DestroySocketProcess();
   } else if (!strcmp(topic, NS_NETWORK_LINK_TOPIC)) {
     OnNetworkLinkEvent(NS_ConvertUTF16toUTF8(data).get());
   } else if (!strcmp(topic, NS_WIDGET_WAKE_OBSERVER_TOPIC)) {
     // coming back alive from sleep
     // this indirection brought to you by:
     // https://bugzilla.mozilla.org/show_bug.cgi?id=1152048#c19
     nsCOMPtr<nsIRunnable> wakeupNotifier = new nsWakeupNotifier(this);
     NS_DispatchToMainThread(wakeupNotifier);
+  } else if (!strcmp(topic, NS_PREFSERVICE_READ_TOPIC_ID)) {
+    // Launch socket process after we load user's pref. This is to make sure
+    // that socket process can get the latest prefs.
+    LaunchSocketProcess();
   }
 
   return NS_OK;
 }
 
 // nsINetUtil interface
 NS_IMETHODIMP
 nsIOService::ParseRequestContentType(const nsACString &aTypeHeader,
@@ -1396,17 +1558,17 @@ nsIOService::SetManageOfflineStatus(bool
 NS_IMETHODIMP
 nsIOService::GetManageOfflineStatus(bool *aManage) {
   *aManage = mManageLinkStatus;
   return NS_OK;
 }
 
 // input argument 'data' is already UTF8'ed
 nsresult nsIOService::OnNetworkLinkEvent(const char *data) {
-  if (IsNeckoChild()) {
+  if (IsNeckoChild() || IsSocketProcessChild()) {
     // There is nothing IO service could do on the child process
     // with this at the moment.  Feel free to add functionality
     // here at will, though.
     return NS_OK;
   }
 
   if (mShutdown) {
     return NS_ERROR_NOT_AVAILABLE;
--- a/netwerk/base/nsIOService.h
+++ b/netwerk/base/nsIOService.h
@@ -40,19 +40,22 @@ static const char gForcedExternalSchemes
 
 class nsINetworkLinkService;
 class nsIPrefBranch;
 class nsIProtocolProxyService2;
 class nsIProxyInfo;
 class nsPISocketTransportService;
 
 namespace mozilla {
+class MemoryReportingProcess;
 namespace net {
 class NeckoChild;
 class nsAsyncRedirectVerifyHelper;
+class SocketProcessHost;
+class SocketProcessMemoryReporter;
 
 class nsIOService final : public nsIIOService,
                           public nsIObserver,
                           public nsINetUtil,
                           public nsISpeculativeConnect,
                           public nsSupportsWeakReference,
                           public nsIIOServiceInternal {
  public:
@@ -108,16 +111,31 @@ class nsIOService final : public nsIIOSe
   void IncrementCacheWonRequestNumber() { mCacheWon++; }
   uint32_t GetCacheWonRequestNumber() { return mCacheWon; }
   void IncrementNetWonRequestNumber() { mNetWon++; }
   uint32_t GetNetWonRequestNumber() { return mNetWon; }
 
   // Used to trigger a recheck of the captive portal status
   nsresult RecheckCaptivePortal();
 
+  void OnProcessLaunchComplete(SocketProcessHost* aHost, bool aSucceeded);
+  void OnProcessUnexpectedShutdown(SocketProcessHost* aHost);
+  bool SocketProcessReady();
+  void NotifySocketProcessPrefsChanged(const char* aName);
+
+  bool IsSocketProcessLaunchComplete();
+
+  // Call func immediately if socket process is launched completely. Otherwise,
+  // |func| will be queued and then executed in the *main thread* once socket
+  // process is launced.
+  void CallOrWaitForSocketProcess(const std::function<void()>& aFunc);
+
+  friend SocketProcessMemoryReporter;
+  RefPtr<MemoryReportingProcess> GetSocketProcessMemoryReporter();
+
  private:
   // These shouldn't be called directly:
   // - construct using GetInstance
   // - destroy using Release
   nsIOService();
   ~nsIOService();
   nsresult SetConnectivityInternal(bool aConnectivity);
 
@@ -157,30 +175,35 @@ class nsIOService final : public nsIIOSe
                                                    uint32_t aProxyFlags,
                                                    nsILoadInfo* aLoadInfo,
                                                    nsIChannel** result);
 
   nsresult SpeculativeConnectInternal(nsIURI* aURI, nsIPrincipal* aPrincipal,
                                       nsIInterfaceRequestor* aCallbacks,
                                       bool aAnonymous);
 
+  nsresult LaunchSocketProcess();
+  void DestroySocketProcess();
+
  private:
   bool mOffline;
   mozilla::Atomic<bool, mozilla::Relaxed> mOfflineForProfileChange;
   bool mManageLinkStatus;
   bool mConnectivity;
   // If true, the connectivity state will be mirrored by IOService.offline
   // meaning if !mConnectivity, GetOffline() will return true
   bool mOfflineMirrorsConnectivity;
 
   // Used to handle SetOffline() reentrancy.  See the comment in
   // SetOffline() for more details.
   bool mSettingOffline;
   bool mSetOfflineValue;
 
+  bool mSocketProcessLaunchComplete;
+
   mozilla::Atomic<bool, mozilla::Relaxed> mShutdown;
   mozilla::Atomic<bool, mozilla::Relaxed> mHttpHandlerAlreadyShutingDown;
 
   nsCOMPtr<nsPISocketTransportService> mSocketTransportService;
   nsCOMPtr<nsICaptivePortalService> mCaptivePortalService;
   nsCOMPtr<nsINetworkLinkService> mNetworkLinkService;
   bool mNetworkLinkServiceInitialized;
 
@@ -209,16 +232,23 @@ class nsIOService final : public nsIIOSe
   // change has happened shortly before.
   mozilla::Atomic<PRIntervalTime> mLastOfflineStateChange;
   mozilla::Atomic<PRIntervalTime> mLastConnectivityChange;
   mozilla::Atomic<PRIntervalTime> mLastNetworkLinkChange;
 
   // Time a network tearing down started.
   mozilla::Atomic<PRIntervalTime> mNetTearingDownStarted;
 
+  SocketProcessHost* mSocketProcess;
+
+  // Events should be executed after the socket process is launched. Will
+  // dispatch these events while socket process fires OnProcessLaunchComplete.
+  // Note: this array is accessed only on the main thread.
+  nsTArray<std::function<void()>> mPendingEvents;
+
  public:
   // Used for all default buffer sizes that necko allocates.
   static uint32_t gDefaultSegmentSize;
   static uint32_t gDefaultSegmentCount;
 };
 
 /**
  * Reference to the IO service singleton. May be null.
--- a/netwerk/base/nsIURI.idl
+++ b/netwerk/base/nsIURI.idl
@@ -176,16 +176,27 @@ interface nsIURI : nsISupports
 
     /**
      * An optimization to do scheme checks without requiring the users of nsIURI
      * to GetScheme, thereby saving extra allocating and freeing. Returns true if
      * the schemes match (case ignored).
      */
     boolean schemeIs(in string scheme);
 
+    /*
+     * Infallible version of SchemeIs for C++ callers.
+     */
+    %{C++
+     bool SchemeIs(const char* aScheme) {
+       bool ret;
+       mozilla::Unused << SchemeIs(aScheme, &ret);
+       return ret;
+     }
+    %}
+
     /**
      * This method resolves a relative string into an absolute URI string,
      * using this URI as the base. 
      *
      * NOTE: some implementations may have no concept of a relative URI.
      */
     AUTF8String resolve(in AUTF8String relativePath);
 
--- a/netwerk/base/nsNetUtil.cpp
+++ b/netwerk/base/nsNetUtil.cpp
@@ -2819,10 +2819,70 @@ bool InScriptableRange(int64_t val) {
 bool InScriptableRange(uint64_t val) { return val <= kJS_MAX_SAFE_UINTEGER; }
 
 nsresult GetParameterHTTP(const nsACString &aHeaderVal, const char *aParamName,
                           nsAString &aResult) {
   return nsMIMEHeaderParamImpl::GetParameterHTTP(aHeaderVal, aParamName,
                                                  aResult);
 }
 
+bool SchemeIsHTTP(nsIURI *aURI) {
+  MOZ_ASSERT(aURI);
+  return aURI->SchemeIs("http");
+}
+
+bool SchemeIsHTTPS(nsIURI *aURI) {
+  MOZ_ASSERT(aURI);
+  return aURI->SchemeIs("https");
+}
+
+bool SchemeIsJavascript(nsIURI *aURI) {
+  MOZ_ASSERT(aURI);
+  return aURI->SchemeIs("javascript");
+}
+
+bool SchemeIsChrome(nsIURI *aURI) {
+  MOZ_ASSERT(aURI);
+  return aURI->SchemeIs("chrome");
+}
+
+bool SchemeIsAbout(nsIURI *aURI) {
+  MOZ_ASSERT(aURI);
+  return aURI->SchemeIs("about");
+}
+
+bool SchemeIsBlob(nsIURI *aURI) {
+  MOZ_ASSERT(aURI);
+  return aURI->SchemeIs("blob");
+}
+
+bool SchemeIsFile(nsIURI *aURI) {
+  MOZ_ASSERT(aURI);
+  return aURI->SchemeIs("file");
+}
+
+bool SchemeIsData(nsIURI *aURI) {
+  MOZ_ASSERT(aURI);
+  return aURI->SchemeIs("data");
+}
+
+bool SchemeIsWYCIWYG(nsIURI *aURI) {
+  MOZ_ASSERT(aURI);
+  return aURI->SchemeIs("wyciwyg");
+}
+
+bool SchemeIsViewSource(nsIURI *aURI) {
+  MOZ_ASSERT(aURI);
+  return aURI->SchemeIs("view-source");
+}
+
+bool SchemeIsResource(nsIURI *aURI) {
+  MOZ_ASSERT(aURI);
+  return aURI->SchemeIs("resource");
+}
+
+bool SchemeIsFTP(nsIURI *aURI) {
+  MOZ_ASSERT(aURI);
+  return aURI->SchemeIs("ftp");
+}
+
 }  // namespace net
 }  // namespace mozilla
--- a/netwerk/base/nsNetUtil.h
+++ b/netwerk/base/nsNetUtil.h
@@ -893,12 +893,29 @@ bool InScriptableRange(uint64_t val);
  * @param  aParamName        the name of a MIME header parameter (e.g.
  *                           filename, name, charset). If empty or nullptr,
  *                           returns the first (possibly) _unnamed_ 'parameter'.
  * @return the value of <code>aParamName</code> in Unichar(UTF-16).
  */
 nsresult GetParameterHTTP(const nsACString &aHeaderVal, const char *aParamName,
                           nsAString &aResult);
 
+/**
+ * Convenience functions for verifying nsIURI schemes. These functions simply
+ * wrap aURI->SchemeIs(), but specify the protocol as part of the function name.
+ */
+
+bool SchemeIsHTTP(nsIURI *aURI);
+bool SchemeIsHTTPS(nsIURI *aURI);
+bool SchemeIsJavascript(nsIURI *aURI);
+bool SchemeIsChrome(nsIURI *aURI);
+bool SchemeIsAbout(nsIURI *aURI);
+bool SchemeIsBlob(nsIURI *aURI);
+bool SchemeIsFile(nsIURI *aURI);
+bool SchemeIsData(nsIURI *aURI);
+bool SchemeIsWYCIWYG(nsIURI *aURI);
+bool SchemeIsViewSource(nsIURI *aURI);
+bool SchemeIsResource(nsIURI *aURI);
+bool SchemeIsFTP(nsIURI *aURI);
 }  // namespace net
 }  // namespace mozilla
 
 #endif  // !nsNetUtil_h__
--- a/netwerk/base/nsSimpleURI.cpp
+++ b/netwerk/base/nsSimpleURI.cpp
@@ -518,18 +518,18 @@ bool nsSimpleURI::EqualsInternal(nsSimpl
               (!mIsRefValid || mRef == otherUri->mRef));
   }
 
   return result;
 }
 
 NS_IMETHODIMP
 nsSimpleURI::SchemeIs(const char *i_Scheme, bool *o_Equals) {
-  NS_ENSURE_ARG_POINTER(o_Equals);
-  if (!i_Scheme) return NS_ERROR_NULL_POINTER;
+  MOZ_ASSERT(i_Scheme);
+  MOZ_ASSERT(o_Equals, "null pointer");
 
   const char *this_scheme = mScheme.get();
 
   // mScheme is guaranteed to be lower case.
   if (*i_Scheme == *this_scheme || *i_Scheme == (*this_scheme - ('a' - 'A'))) {
     *o_Equals = PL_strcasecmp(this_scheme, i_Scheme) ? false : true;
   } else {
     *o_Equals = false;
--- a/netwerk/base/nsStandardURL.cpp
+++ b/netwerk/base/nsStandardURL.cpp
@@ -2199,16 +2199,17 @@ nsresult nsStandardURL::EqualsInternal(
   // same file, so they are different.
   *result = false;
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsStandardURL::SchemeIs(const char *scheme, bool *result) {
+  MOZ_ASSERT(scheme);
   MOZ_ASSERT(result, "null pointer");
 
   *result = SegmentIs(mScheme, scheme);
   return NS_OK;
 }
 
 /* virtual */ nsStandardURL *nsStandardURL::StartClone() {
   nsStandardURL *clone = new nsStandardURL();
--- a/netwerk/ipc/NeckoCommon.h
+++ b/netwerk/ipc/NeckoCommon.h
@@ -90,16 +90,21 @@ inline bool IsNeckoChild() {
   if (!didCheck) {
     didCheck = true;
     amChild = (XRE_GetProcessType() == GeckoProcessType_Content) &&
               !recordreplay::IsMiddleman();
   }
   return amChild;
 }
 
+inline bool IsSocketProcessChild() {
+  static bool amChild = (XRE_GetProcessType() == GeckoProcessType_Socket);
+  return amChild;
+}
+
 namespace NeckoCommonInternal {
 extern bool gSecurityDisabled;
 extern bool gRegisteredBool;
 }  // namespace NeckoCommonInternal
 
 // This should always return true unless xpcshell tests are being used
 inline bool UsingNeckoIPCSecurity() {
   return !NeckoCommonInternal::gSecurityDisabled;
--- a/netwerk/ipc/NeckoParent.cpp
+++ b/netwerk/ipc/NeckoParent.cpp
@@ -23,16 +23,18 @@
 #include "mozilla/net/AltDataOutputStreamParent.h"
 #include "mozilla/Unused.h"
 #include "mozilla/net/FileChannelParent.h"
 #include "mozilla/net/DNSRequestParent.h"
 #include "mozilla/net/ChannelDiverterParent.h"
 #include "mozilla/net/IPCTransportProvider.h"
 #include "mozilla/net/RequestContextService.h"
 #include "mozilla/net/TrackingDummyChannelParent.h"
+#include "mozilla/net/SocketProcessParent.h"
+#include "mozilla/net/PSocketProcessBridgeParent.h"
 #ifdef MOZ_WEBRTC
 #include "mozilla/net/StunAddrsRequestParent.h"
 #include "mozilla/net/WebrtcProxyChannelParent.h"
 #endif
 #include "mozilla/dom/ChromeUtils.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/TabContext.h"
 #include "mozilla/dom/TabParent.h"
@@ -69,17 +71,17 @@ using mozilla::ipc::PrincipalInfo;
 using mozilla::net::PTCPServerSocketParent;
 using mozilla::net::PTCPSocketParent;
 using mozilla::net::PUDPSocketParent;
 
 namespace mozilla {
 namespace net {
 
 // C++ file contents
-NeckoParent::NeckoParent() {
+NeckoParent::NeckoParent() : mSocketProcessBridgeInited(false) {
   // Init HTTP protocol handler now since we need atomTable up and running very
   // early (IPDL argument handling for PHttpChannel constructor needs it) so
   // normal init (during 1st Http channel request) isn't early enough.
   nsCOMPtr<nsIProtocolHandler> proto =
       do_GetService("@mozilla.org/network/protocol;1?name=http");
 
   // only register once--we will have multiple NeckoParents if there are
   // multiple child processes.
@@ -949,10 +951,46 @@ mozilla::ipc::IPCResult NeckoParent::Rec
 bool NeckoParent::DeallocPTrackingDummyChannelParent(
     PTrackingDummyChannelParent* aActor) {
   RefPtr<TrackingDummyChannelParent> c =
       dont_AddRef(static_cast<TrackingDummyChannelParent*>(aActor));
   MOZ_ASSERT(c);
   return true;
 }
 
+mozilla::ipc::IPCResult NeckoParent::RecvInitSocketProcessBridge(
+    InitSocketProcessBridgeResolver&& aResolver) {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  Endpoint<PSocketProcessBridgeChild> invalidEndpoint;
+  if (NS_WARN_IF(mSocketProcessBridgeInited)) {
+    aResolver(std::move(invalidEndpoint));
+    return IPC_OK();
+  }
+
+  SocketProcessParent* parent = SocketProcessParent::GetSingleton();
+  if (NS_WARN_IF(!parent)) {
+    aResolver(std::move(invalidEndpoint));
+    return IPC_OK();
+  }
+
+  Endpoint<PSocketProcessBridgeParent> parentEndpoint;
+  Endpoint<PSocketProcessBridgeChild> childEndpoint;
+  if (NS_WARN_IF(NS_FAILED(PSocketProcessBridge::CreateEndpoints(
+          parent->OtherPid(), Manager()->OtherPid(), &parentEndpoint,
+          &childEndpoint)))) {
+    aResolver(std::move(invalidEndpoint));
+    return IPC_OK();
+  }
+
+  if (NS_WARN_IF(!parent->SendInitSocketProcessBridgeParent(
+          Manager()->OtherPid(), std::move(parentEndpoint)))) {
+    aResolver(std::move(invalidEndpoint));
+    return IPC_OK();
+  }
+
+  aResolver(std::move(childEndpoint));
+  mSocketProcessBridgeInited = true;
+  return IPC_OK();
+}
+
 }  // namespace net
 }  // namespace mozilla
--- a/netwerk/ipc/NeckoParent.h
+++ b/netwerk/ipc/NeckoParent.h
@@ -82,16 +82,18 @@ class NeckoParent : public PNeckoParent 
                                nsICancelable**) override;
 
    protected:
     PNeckoParent* mNeckoParent;
     TabId mNestedFrameId;
   };
 
  protected:
+  bool mSocketProcessBridgeInited;
+
   virtual PHttpChannelParent* AllocPHttpChannelParent(
       const PBrowserOrId&, const SerializedLoadContext&,
       const HttpChannelCreationArgs& aOpenArgs) override;
   virtual mozilla::ipc::IPCResult RecvPHttpChannelConstructor(
       PHttpChannelParent* aActor, const PBrowserOrId& aBrowser,
       const SerializedLoadContext& aSerialized,
       const HttpChannelCreationArgs& aOpenArgs) override;
   virtual bool DeallocPHttpChannelParent(PHttpChannelParent*) override;
@@ -241,14 +243,17 @@ class NeckoParent : public PNeckoParent 
 
   virtual bool DeallocPTrackingDummyChannelParent(
       PTrackingDummyChannelParent* aChild) override;
 
   virtual mozilla::ipc::IPCResult RecvPTrackingDummyChannelConstructor(
       PTrackingDummyChannelParent* aActor, nsIURI* aURI, nsIURI* aTopWindowURI,
       const nsresult& aTopWindowURIResult,
       const OptionalLoadInfoArgs& aLoadInfo) override;
+
+  virtual mozilla::ipc::IPCResult RecvInitSocketProcessBridge(
+      InitSocketProcessBridgeResolver&& aResolver) override;
 };
 
 }  // namespace net
 }  // namespace mozilla
 
 #endif  // mozilla_net_NeckoParent_h
--- a/netwerk/ipc/PNecko.ipdl
+++ b/netwerk/ipc/PNecko.ipdl
@@ -23,16 +23,17 @@ include protocol PDataChannel;
 include protocol PSimpleChannel;
 include protocol PTransportProvider;
 include protocol PChildToParentStream; //FIXME: bug #792908
 include protocol PParentToChildStream; //FIXME: bug #792908
 include protocol PStunAddrsRequest;
 include protocol PFileChannel;
 include protocol PTrackingDummyChannel;
 include protocol PWebrtcProxyChannel;
+include protocol PSocketProcessBridge;
 
 include IPCStream;
 include URIParams;
 include NeckoChannelParams;
 include PBrowserOrId;
 include protocol PAltDataOutputStream;
 
 using class IPC::SerializedLoadContext from "SerializedLoadContext.h";
@@ -138,16 +139,19 @@ parent:
   async PWebrtcProxyChannel(PBrowserOrId browser);
 
   /**
    * WebExtension-specific remote resource loading
    */
   async GetExtensionStream(URIParams uri) returns (nsIInputStream stream);
   async GetExtensionFD(URIParams uri) returns (FileDescriptor fd);
 
+  async InitSocketProcessBridge()
+    returns (Endpoint<PSocketProcessBridgeChild> endpoint);
+
 child:
   /*
    * Bring up the http auth prompt for a nested remote mozbrowser.
    * NestedFrameId is the id corresponding to the PBrowser.  It is the same id
    * that was passed to the PBrowserOrId param in to the PHttpChannel constructor
    */
   async AsyncAuthPromptForNestedFrame(TabId nestedFrameId, nsCString uri,
                                       nsString realm, uint64_t callbackId);
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/PSocketProcess.ipdl
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include MemoryReportTypes;
+include protocol PSocketProcessBridge;
+include protocol PProfiler;
+include PrefsTypes;
+
+using mozilla::dom::NativeThreadId from "mozilla/dom/TabMessageUtils.h";
+using mozilla::Telemetry::HistogramAccumulation from "mozilla/TelemetryComms.h";
+using mozilla::Telemetry::KeyedHistogramAccumulation from "mozilla/TelemetryComms.h";
+using mozilla::Telemetry::ScalarAction from "mozilla/TelemetryComms.h";
+using mozilla::Telemetry::KeyedScalarAction from "mozilla/TelemetryComms.h";
+using mozilla::Telemetry::ChildEventData from "mozilla/TelemetryComms.h";
+using mozilla::Telemetry::DiscardedData from "mozilla/TelemetryComms.h";
+using base::ProcessId from "base/process.h";
+
+namespace mozilla {
+namespace net {
+
+protocol PSocketProcess
+{
+parent:
+  async InitCrashReporter(Shmem shmem, NativeThreadId threadId);
+  async AddMemoryReport(MemoryReport aReport);
+  async FinishMemoryReport(uint32_t aGeneration);
+  // Messages for sending telemetry to parent process.
+  async AccumulateChildHistograms(HistogramAccumulation[] accumulations);
+  async AccumulateChildKeyedHistograms(KeyedHistogramAccumulation[] accumulations);
+  async UpdateChildScalars(ScalarAction[] actions);
+  async UpdateChildKeyedScalars(KeyedScalarAction[] actions);
+  async RecordChildEvents(ChildEventData[] events);
+  async RecordDiscardedData(DiscardedData data);
+
+child:
+  async PreferenceUpdate(Pref pref);
+  async RequestMemoryReport(uint32_t generation,
+                            bool anonymize,
+                            bool minimizeMemoryUsage,
+                            MaybeFileDesc DMDFile);
+  async SetOffline(bool offline);
+  async InitSocketProcessBridgeParent(ProcessId processId, Endpoint<PSocketProcessBridgeParent> endpoint);
+  async InitProfiler(Endpoint<PProfilerChild> aEndpoint);
+};
+
+} // namespace net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/PSocketProcessBridge.ipdl
@@ -0,0 +1,30 @@
+/* -*- 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 PBackground;
+
+namespace mozilla {
+namespace net {
+
+/**
+  * PSocketProcessBridge is the IPC protocol between content process and
+  * socket process. This protocol allows socket process to send data to
+  * content process bypassing parent process.
+  * Once created, PSocketProcessBridgeChild is the actor that lives in
+  * content process and PSocketProcessBridgeParent lives in
+  * socket process.
+  */
+nested(upto inside_cpow) sync protocol PSocketProcessBridge
+{
+parent:
+  async InitBackground(Endpoint<PBackgroundParent> aEndpoint);
+
+both:
+  async Test();
+};
+
+}
+}
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/SocketProcessBridgeChild.cpp
@@ -0,0 +1,137 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SocketProcessBridgeChild.h"
+#include "SocketProcessLogging.h"
+
+#include "mozilla/net/NeckoChild.h"
+#include "nsIObserverService.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+namespace net {
+
+StaticRefPtr<SocketProcessBridgeChild>
+    SocketProcessBridgeChild::sSocketProcessBridgeChild;
+
+NS_IMPL_ISUPPORTS(SocketProcessBridgeChild, nsIObserver)
+
+// static
+bool SocketProcessBridgeChild::Create(
+    Endpoint<PSocketProcessBridgeChild>&& aEndpoint) {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  sSocketProcessBridgeChild =
+      new SocketProcessBridgeChild(std::move(aEndpoint));
+  if (sSocketProcessBridgeChild->Inited()) {
+    return true;
+  }
+
+  sSocketProcessBridgeChild = nullptr;
+  return false;
+}
+
+// static
+already_AddRefed<SocketProcessBridgeChild>
+SocketProcessBridgeChild::GetSingleton() {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!sSocketProcessBridgeChild) {
+    return nullptr;
+  }
+
+  RefPtr<SocketProcessBridgeChild> child = sSocketProcessBridgeChild.get();
+  return child.forget();
+}
+
+// static
+void SocketProcessBridgeChild::EnsureSocketProcessBridge(
+    std::function<void()>&& aOnSuccess, std::function<void()>&& aOnFailure) {
+  MOZ_ASSERT(IsNeckoChild() && gNeckoChild);
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!gNeckoChild) {
+    aOnFailure();
+    return;
+  }
+
+  if (sSocketProcessBridgeChild) {
+    aOnSuccess();
+    return;
+  }
+
+  gNeckoChild->SendInitSocketProcessBridge()->Then(
+      GetMainThreadSerialEventTarget(), __func__,
+      [onSuccess = std::move(aOnSuccess), onFailure = std::move(aOnFailure)](
+          Endpoint<PSocketProcessBridgeChild>&& aEndpoint) {
+        if (aEndpoint.IsValid()) {
+          if (SocketProcessBridgeChild::Create(std::move(aEndpoint))) {
+            onSuccess();
+            return;
+          }
+        }
+        onFailure();
+      },
+      [onFailure = std::move(aOnFailure)](
+          const mozilla::ipc::ResponseRejectReason) { onFailure(); });
+}
+
+SocketProcessBridgeChild::SocketProcessBridgeChild(
+    Endpoint<PSocketProcessBridgeChild>&& aEndpoint)
+    : mShuttingDown(false) {
+  LOG(("CONSTRUCT SocketProcessBridgeChild::SocketProcessBridgeChild\n"));
+
+  mInited = aEndpoint.Bind(this);
+  if (!mInited) {
+    MOZ_ASSERT(false, "Bind failed!");
+    return;
+  }
+
+  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+  if (os) {
+    os->AddObserver(this, "content-child-shutdown", false);
+  }
+
+  mSocketProcessPid = aEndpoint.OtherPid();
+}
+
+SocketProcessBridgeChild::~SocketProcessBridgeChild() {
+  LOG(("DESTRUCT SocketProcessBridgeChild::SocketProcessBridgeChild\n"));
+}
+
+mozilla::ipc::IPCResult SocketProcessBridgeChild::RecvTest() {
+  LOG(("SocketProcessBridgeChild::RecvTest\n"));
+  return IPC_OK();
+}
+
+void SocketProcessBridgeChild::ActorDestroy(ActorDestroyReason aWhy) {
+  LOG(("SocketProcessBridgeChild::ActorDestroy\n"));
+  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+  if (os) {
+    os->RemoveObserver(this, "content-child-shutdown");
+  }
+  MessageLoop::current()->PostTask(
+      NewRunnableMethod("net::SocketProcessBridgeChild::DeferredDestroy", this,
+                        &SocketProcessBridgeChild::DeferredDestroy));
+  mShuttingDown = true;
+}
+
+NS_IMETHODIMP
+SocketProcessBridgeChild::Observe(nsISupports* aSubject, const char* aTopic,
+                                  const char16_t* aData) {
+  if (!strcmp(aTopic, "content-child-shutdown")) {
+    PSocketProcessBridgeChild::Close();
+  }
+  return NS_OK;
+}
+
+void SocketProcessBridgeChild::DeferredDestroy() {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  sSocketProcessBridgeChild = nullptr;
+}
+
+}  // namespace net
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/SocketProcessBridgeChild.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_net_SocketProcessBridgeChild_h
+#define mozilla_net_SocketProcessBridgeChild_h
+
+#include <functional>
+#include "mozilla/net/PSocketProcessBridgeChild.h"
+#include "nsIObserver.h"
+
+namespace mozilla {
+namespace net {
+
+// The IPC actor implements PSocketProcessBridgeChild in content process.
+// This is allocated and kept alive by NeckoChild. When "content-child-shutdown"
+// topic is observed, this actor will be destroyed.
+class SocketProcessBridgeChild final : public PSocketProcessBridgeChild,
+                                       public nsIObserver {
+ public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+
+  static bool Create(Endpoint<PSocketProcessBridgeChild>&& aEndpoint);
+  static already_AddRefed<SocketProcessBridgeChild> GetSingleton();
+  static void EnsureSocketProcessBridge(std::function<void()>&& aOnSuccess,
+                                        std::function<void()>&& aOnFailure);
+
+  mozilla::ipc::IPCResult RecvTest() override;
+  void ActorDestroy(ActorDestroyReason aWhy) override;
+  void DeferredDestroy();
+  bool IsShuttingDown() const { return mShuttingDown; };
+  bool Inited() const { return mInited; };
+  ProcessId SocketProcessPid() const { return mSocketProcessPid; };
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SocketProcessBridgeChild);
+  explicit SocketProcessBridgeChild(
+      Endpoint<PSocketProcessBridgeChild>&& aEndpoint);
+  virtual ~SocketProcessBridgeChild();
+
+  static StaticRefPtr<SocketProcessBridgeChild> sSocketProcessBridgeChild;
+  bool mShuttingDown;
+  bool mInited = false;
+  ProcessId mSocketProcessPid;
+};
+
+}  // namespace net
+}  // namespace mozilla
+
+#endif  // mozilla_net_SocketProcessBridgeChild_h
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/SocketProcessBridgeParent.cpp
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SocketProcessBridgeParent.h"
+#include "SocketProcessLogging.h"
+
+#include "mozilla/ipc/BackgroundParent.h"
+#include "SocketProcessChild.h"
+
+namespace mozilla {
+namespace net {
+
+SocketProcessBridgeParent::SocketProcessBridgeParent(
+    ProcessId aId, Endpoint<PSocketProcessBridgeParent>&& aEndpoint)
+    : mId(aId) {
+  LOG((
+      "CONSTRUCT SocketProcessBridgeParent::SocketProcessBridgeParent mId=%d\n",
+      mId));
+  MOZ_COUNT_CTOR(SocketProcessBridgeParent);
+  DebugOnly<bool> ok = aEndpoint.Bind(this);
+  MOZ_ASSERT(ok);
+}
+
+SocketProcessBridgeParent::~SocketProcessBridgeParent() {
+  LOG(("DESTRUCT SocketProcessBridgeParent::SocketProcessBridgeParent\n"));
+  MOZ_COUNT_DTOR(SocketProcessBridgeParent);
+}
+
+mozilla::ipc::IPCResult SocketProcessBridgeParent::RecvTest() {
+  LOG(("SocketProcessBridgeParent::RecvTest\n"));
+  Unused << SendTest();
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SocketProcessBridgeParent::RecvInitBackground(
+    Endpoint<PBackgroundParent>&& aEndpoint) {
+  LOG(("SocketProcessBridgeParent::RecvInitBackground mId=%d\n", mId));
+  if (!ipc::BackgroundParent::Alloc(nullptr, std::move(aEndpoint))) {
+    return IPC_FAIL(this, "BackgroundParent::Alloc failed");
+  }
+
+  return IPC_OK();
+}
+
+void SocketProcessBridgeParent::ActorDestroy(ActorDestroyReason aWhy) {
+  LOG(("SocketProcessBridgeParent::ActorDestroy mId=%d\n", mId));
+
+  MessageLoop::current()->PostTask(
+      NewRunnableMethod("net::SocketProcessBridgeParent::DeferredDestroy", this,
+                        &SocketProcessBridgeParent::DeferredDestroy));
+}
+
+void SocketProcessBridgeParent::DeferredDestroy() {
+  SocketProcessChild::GetSingleton()->DestroySocketProcessBridgeParent(mId);
+}
+
+}  // namespace net
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/SocketProcessBridgeParent.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_net_SocketProcessBridgeParent_h
+#define mozilla_net_SocketProcessBridgeParent_h
+
+#include "mozilla/net/PSocketProcessBridgeParent.h"
+
+namespace mozilla {
+namespace net {
+
+// The IPC actor implements PSocketProcessBridgeParent in socket process.
+// This is allocated and kept alive by SocketProcessChild. When |ActorDestroy|
+// is called, |SocketProcessChild::DestroySocketProcessBridgeParent| will be
+// called to destroy this actor.
+class SocketProcessBridgeParent final : public PSocketProcessBridgeParent {
+ public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SocketProcessBridgeParent)
+
+  explicit SocketProcessBridgeParent(
+      ProcessId aId, Endpoint<PSocketProcessBridgeParent>&& aEndpoint);
+
+  mozilla::ipc::IPCResult RecvTest() override;
+  mozilla::ipc::IPCResult RecvInitBackground(
+      Endpoint<PBackgroundParent>&& aEndpoint) override;
+
+  void ActorDestroy(ActorDestroyReason aWhy) override;
+  void DeferredDestroy();
+
+ private:
+  ~SocketProcessBridgeParent();
+
+  ProcessId mId;
+};
+
+}  // namespace net
+}  // namespace mozilla
+
+#endif  // mozilla_net_SocketProcessBridgeParent_h
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/SocketProcessChild.cpp
@@ -0,0 +1,161 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SocketProcessChild.h"
+#include "SocketProcessLogging.h"
+
+#include "base/task.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/dom/MemoryReportRequest.h"
+#include "mozilla/ipc/CrashReporterClient.h"
+#include "mozilla/ipc/ProcessChild.h"
+#include "mozilla/Preferences.h"
+#include "nsDebugImpl.h"
+#include "nsThreadManager.h"
+#include "ProcessUtils.h"
+#include "SocketProcessBridgeParent.h"
+
+#ifdef MOZ_GECKO_PROFILER
+#include "ChildProfilerController.h"
+#endif
+
+namespace mozilla {
+namespace net {
+
+using namespace ipc;
+
+static SocketProcessChild* sSocketProcessChild;
+
+SocketProcessChild::SocketProcessChild() {
+  LOG(("CONSTRUCT SocketProcessChild::SocketProcessChild\n"));
+  nsDebugImpl::SetMultiprocessMode("Socket");
+
+  MOZ_COUNT_CTOR(SocketProcessChild);
+  sSocketProcessChild = this;
+}
+
+SocketProcessChild::~SocketProcessChild() {
+  LOG(("DESTRUCT SocketProcessChild::SocketProcessChild\n"));
+  MOZ_COUNT_DTOR(SocketProcessChild);
+  sSocketProcessChild = nullptr;
+}
+
+/* static */ SocketProcessChild* SocketProcessChild::GetSingleton() {
+  return sSocketProcessChild;
+}
+
+bool SocketProcessChild::Init(base::ProcessId aParentPid,
+                              const char* aParentBuildID, MessageLoop* aIOLoop,
+                              IPC::Channel* aChannel) {
+  if (NS_WARN_IF(NS_FAILED(nsThreadManager::get().Init()))) {
+    return false;
+  }
+  if (NS_WARN_IF(!Open(aChannel, aParentPid, aIOLoop))) {
+    return false;
+  }
+  // This must be sent before any IPDL message, which may hit sentinel
+  // errors due to parent and content processes having different
+  // versions.
+  MessageChannel* channel = GetIPCChannel();
+  if (channel && !channel->SendBuildIDsMatchMessage(aParentBuildID)) {
+    // We need to quit this process if the buildID doesn't match the parent's.
+    // This can occur when an update occurred in the background.
+    ProcessChild::QuickExit();
+  }
+
+  // Init crash reporter support.
+  CrashReporterClient::InitSingleton(this);
+
+  SetThisProcessName("Socket Process");
+  return true;
+}
+
+void SocketProcessChild::ActorDestroy(ActorDestroyReason aWhy) {
+  LOG(("SocketProcessChild::ActorDestroy\n"));
+
+  if (AbnormalShutdown == aWhy) {
+    NS_WARNING("Shutting down Socket process early due to a crash!");
+    ProcessChild::QuickExit();
+  }
+
+#ifdef MOZ_GECKO_PROFILER
+  if (mProfilerController) {
+    mProfilerController->Shutdown();
+    mProfilerController = nullptr;
+  }
+#endif
+
+  CrashReporterClient::DestroySingleton();
+  XRE_ShutdownChildProcess();
+}
+
+void SocketProcessChild::CleanUp() {
+  LOG(("SocketProcessChild::CleanUp\n"));
+
+  NS_ShutdownXPCOM(nullptr);
+}
+
+IPCResult SocketProcessChild::RecvPreferenceUpdate(const Pref& aPref) {
+  Preferences::SetPreference(aPref);
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SocketProcessChild::RecvRequestMemoryReport(
+    const uint32_t& aGeneration, const bool& aAnonymize,
+    const bool& aMinimizeMemoryUsage, const MaybeFileDesc& aDMDFile) {
+  nsPrintfCString processName("SocketProcess");
+
+  mozilla::dom::MemoryReportRequestClient::Start(
+      aGeneration, aAnonymize, aMinimizeMemoryUsage, aDMDFile, processName,
+      [&](const MemoryReport& aReport) {
+        Unused << GetSingleton()->SendAddMemoryReport(aReport);
+      },
+      [&](const uint32_t& aGeneration) {
+        return GetSingleton()->SendFinishMemoryReport(aGeneration);
+      });
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SocketProcessChild::RecvSetOffline(
+    const bool& aOffline) {
+  LOG(("SocketProcessChild::RecvSetOffline aOffline=%d\n", aOffline));
+
+  nsCOMPtr<nsIIOService> io(do_GetIOService());
+  NS_ASSERTION(io, "IO Service can not be null");
+
+  io->SetOffline(aOffline);
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SocketProcessChild::RecvInitSocketProcessBridgeParent(
+    const ProcessId& aContentProcessId,
+    Endpoint<mozilla::net::PSocketProcessBridgeParent>&& aEndpoint) {
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!mSocketProcessBridgeParentMap.Get(aContentProcessId, nullptr));
+
+  mSocketProcessBridgeParentMap.Put(
+      aContentProcessId,
+      new SocketProcessBridgeParent(aContentProcessId, std::move(aEndpoint)));
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SocketProcessChild::RecvInitProfiler(
+    Endpoint<PProfilerChild>&& aEndpoint) {
+#ifdef MOZ_GECKO_PROFILER
+  mProfilerController =
+      mozilla::ChildProfilerController::Create(std::move(aEndpoint));
+#endif
+  return IPC_OK();
+}
+
+void SocketProcessChild::DestroySocketProcessBridgeParent(ProcessId aId) {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  mSocketProcessBridgeParentMap.Remove(aId);
+}
+
+}  // namespace net
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/SocketProcessChild.h
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_net_SocketProcessChild_h
+#define mozilla_net_SocketProcessChild_h
+
+#include "mozilla/net/PSocketProcessChild.h"
+#include "nsRefPtrHashtable.h"
+
+namespace mozilla {
+class ChildProfilerController;
+}
+
+namespace mozilla {
+namespace net {
+
+class SocketProcessBridgeParent;
+
+// The IPC actor implements PSocketProcessChild in child process.
+// This is allocated and kept alive by SocketProcessImpl.
+class SocketProcessChild final : public PSocketProcessChild {
+ public:
+  SocketProcessChild();
+  ~SocketProcessChild();
+
+  static SocketProcessChild* GetSingleton();
+
+  bool Init(base::ProcessId aParentPid, const char* aParentBuildID,
+            MessageLoop* aIOLoop, IPC::Channel* aChannel);
+
+  void ActorDestroy(ActorDestroyReason aWhy) override;
+
+  mozilla::ipc::IPCResult RecvPreferenceUpdate(const Pref& aPref) override;
+  mozilla::ipc::IPCResult RecvRequestMemoryReport(
+      const uint32_t& generation, const bool& anonymize,
+      const bool& minimizeMemoryUsage, const MaybeFileDesc& DMDFile) override;
+  mozilla::ipc::IPCResult RecvSetOffline(const bool& aOffline) override;
+  mozilla::ipc::IPCResult RecvInitSocketProcessBridgeParent(
+      const ProcessId& aContentProcessId,
+      Endpoint<mozilla::net::PSocketProcessBridgeParent>&& aEndpoint) override;
+  mozilla::ipc::IPCResult RecvInitProfiler(
+      Endpoint<mozilla::PProfilerChild>&& aEndpoint) override;
+
+  void CleanUp();
+  void DestroySocketProcessBridgeParent(ProcessId aId);
+
+ private:
+  // Mapping of content process id and the SocketProcessBridgeParent.
+  // This table keeps SocketProcessBridgeParent alive in socket process.
+  nsRefPtrHashtable<nsUint32HashKey, SocketProcessBridgeParent>
+      mSocketProcessBridgeParentMap;
+
+#ifdef MOZ_GECKO_PROFILER
+  RefPtr<ChildProfilerController> mProfilerController;
+#endif
+};
+
+}  // namespace net
+}  // namespace mozilla
+
+#endif  // mozilla_net_SocketProcessChild_h
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/SocketProcessHost.cpp
@@ -0,0 +1,345 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SocketProcessHost.h"
+
+#include "nsAppRunner.h"
+#include "nsIObserverService.h"
+#include "SocketProcessParent.h"
+
+#ifdef MOZ_GECKO_PROFILER
+#include "ProfilerParent.h"
+#endif
+
+namespace mozilla {
+namespace net {
+
+#define NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC "ipc:network:set-offline"
+
+class OfflineObserver final : public nsIObserver {
+ public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  explicit OfflineObserver(SocketProcessHost* aProcessHost)
+      : mProcessHost(aProcessHost) {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+    if (obs) {
+      obs->AddObserver(this, NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC, false);
+      obs->AddObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID, false);
+    }
+  }
+
+  void Destroy() {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+    if (obs) {
+      obs->RemoveObserver(this, NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC);
+    }
+    mProcessHost = nullptr;
+  }
+
+ private:
+  // nsIObserver implementation.
+  NS_IMETHOD
+  Observe(nsISupports* aSubject, const char* aTopic,
+          const char16_t* aData) override {
+    if (!mProcessHost) {
+      return NS_OK;
+    }
+
+    if (!strcmp(aTopic, NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC)) {
+      NS_ConvertUTF16toUTF8 dataStr(aData);
+      const char* offline = dataStr.get();
+      if (!mProcessHost->IsConnected() ||
+          mProcessHost->GetActor()->SendSetOffline(
+              !strcmp(offline, "true") ? true : false)) {
+        return NS_ERROR_NOT_AVAILABLE;
+      }
+    } else if (!strcmp(aTopic, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID)) {
+      nsCOMPtr<nsIObserverService> obs =
+          mozilla::services::GetObserverService();
+        obs->RemoveObserver(this, NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC);
+        obs->RemoveObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID);
+    }
+
+    return NS_OK;
+  }
+  virtual ~OfflineObserver() = default;
+
+  SocketProcessHost* mProcessHost;
+};
+
+NS_IMPL_ISUPPORTS(OfflineObserver, nsIObserver)
+
+SocketProcessHost::SocketProcessHost(Listener* aListener)
+    : GeckoChildProcessHost(GeckoProcessType_Socket),
+      mListener(aListener),
+      mTaskFactory(this),
+      mLaunchPhase(LaunchPhase::Unlaunched),
+      mShutdownRequested(false),
+      mChannelClosed(false) {
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_COUNT_CTOR(SocketProcessHost);
+}
+
+SocketProcessHost::~SocketProcessHost() {
+  MOZ_COUNT_DTOR(SocketProcessHost);
+  if (mOfflineObserver) {
+    RefPtr<OfflineObserver> observer = mOfflineObserver;
+    NS_DispatchToMainThread(
+        NS_NewRunnableFunction("SocketProcessHost::DestroyOfflineObserver",
+                               [observer]() { observer->Destroy(); }));
+  }
+}
+
+bool SocketProcessHost::Launch() {
+  MOZ_ASSERT(mLaunchPhase == LaunchPhase::Unlaunched);
+  MOZ_ASSERT(!mSocketProcessParent);
+  MOZ_ASSERT(NS_IsMainThread());
+
+  std::vector<std::string> extraArgs;
+
+  nsAutoCString parentBuildID(mozilla::PlatformBuildID());
+  extraArgs.push_back("-parentBuildID");
+  extraArgs.push_back(parentBuildID.get());
+
+  SharedPreferenceSerializer prefSerializer;
+  if (!prefSerializer.SerializeToSharedMemory()) {
+    return false;
+  }
+
+  // Formats a pointer or pointer-sized-integer as a string suitable for passing
+  // in an arguments list.
+  auto formatPtrArg = [](auto arg) {
+    return nsPrintfCString("%zu", uintptr_t(arg));
+  };
+
+#if defined(XP_WIN)
+  // Record the handle as to-be-shared, and pass it via a command flag. This
+  // works because Windows handles are system-wide.
+  HANDLE prefsHandle = prefSerializer.GetSharedMemoryHandle();
+  AddHandleToShare(prefsHandle);
+  AddHandleToShare(prefSerializer.GetPrefMapHandle().get());
+  extraArgs.push_back("-prefsHandle");
+  extraArgs.push_back(formatPtrArg(prefsHandle).get());
+  extraArgs.push_back("-prefMapHandle");
+  extraArgs.push_back(
+      formatPtrArg(prefSerializer.GetPrefMapHandle().get()).get());
+#else
+  // In contrast, Unix fds are per-process. So remap the fd to a fixed one that
+  // will be used in the child.
+  // XXX: bug 1440207 is about improving how fixed fds are used.
+  //
+  // Note: on Android, AddFdToRemap() sets up the fd to be passed via a Parcel,
+  // and the fixed fd isn't used. However, we still need to mark it for
+  // remapping so it doesn't get closed in the child.
+  AddFdToRemap(prefSerializer.GetSharedMemoryHandle().fd, kPrefsFileDescriptor);
+  AddFdToRemap(prefSerializer.GetPrefMapHandle().get(), kPrefMapFileDescriptor);
+#endif
+
+  // Pass the lengths via command line flags.
+  extraArgs.push_back("-prefsLen");
+  extraArgs.push_back(formatPtrArg(prefSerializer.GetPrefLength()).get());
+  extraArgs.push_back("-prefMapSize");
+  extraArgs.push_back(formatPtrArg(prefSerializer.GetPrefMapSize()).get());
+
+  mLaunchPhase = LaunchPhase::Waiting;
+  if (!GeckoChildProcessHost::LaunchAndWaitForProcessHandle(extraArgs)) {
+    mLaunchPhase = LaunchPhase::Complete;
+    return false;
+  }
+
+  return true;
+}
+
+void SocketProcessHost::OnChannelConnected(int32_t peer_pid) {
+  MOZ_ASSERT(!NS_IsMainThread());
+
+  GeckoChildProcessHost::OnChannelConnected(peer_pid);
+
+  // Post a task to the main thread. Take the lock because mTaskFactory is not
+  // thread-safe.
+  RefPtr<Runnable> runnable;
+  {
+    MonitorAutoLock lock(mMonitor);
+    runnable = mTaskFactory.NewRunnableMethod(
+        &SocketProcessHost::OnChannelConnectedTask);
+  }
+  NS_DispatchToMainThread(runnable);
+}
+
+void SocketProcessHost::OnChannelError() {
+  MOZ_ASSERT(!NS_IsMainThread());
+
+  GeckoChildProcessHost::OnChannelError();
+
+  // Post a task to the main thread. Take the lock because mTaskFactory is not
+  // thread-safe.
+  RefPtr<Runnable> runnable;
+  {
+    MonitorAutoLock lock(mMonitor);
+    runnable =
+        mTaskFactory.NewRunnableMethod(&SocketProcessHost::OnChannelErrorTask);
+  }
+  NS_DispatchToMainThread(runnable);
+}
+
+void SocketProcessHost::OnChannelConnectedTask() {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (mLaunchPhase == LaunchPhase::Waiting) {
+    InitAfterConnect(true);
+  }
+}
+
+void SocketProcessHost::OnChannelErrorTask() {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (mLaunchPhase == LaunchPhase::Waiting) {
+    InitAfterConnect(false);
+  }
+}
+
+void SocketProcessHost::InitAfterConnect(bool aSucceeded) {
+  MOZ_ASSERT(mLaunchPhase == LaunchPhase::Waiting);
+  MOZ_ASSERT(!mSocketProcessParent);
+  MOZ_ASSERT(NS_IsMainThread());
+
+  mLaunchPhase = LaunchPhase::Complete;
+
+  if (aSucceeded) {
+    mSocketProcessParent = MakeUnique<SocketProcessParent>(this);
+    DebugOnly<bool> rv = mSocketProcessParent->Open(
+        GetChannel(), base::GetProcId(GetChildProcessHandle()));
+    MOZ_ASSERT(rv);
+
+    nsCOMPtr<nsIIOService> ioService(do_GetIOService());
+    MOZ_ASSERT(ioService, "No IO service?");
+    bool offline = false;
+    DebugOnly<nsresult> result = ioService->GetOffline(&offline);
+    MOZ_ASSERT(NS_SUCCEEDED(result), "Failed getting offline?");
+
+#ifdef MOZ_GECKO_PROFILER
+    Unused << GetActor()->SendInitProfiler(
+        ProfilerParent::CreateForProcess(GetActor()->OtherPid()));
+#endif
+
+    Unused << GetActor()->SendSetOffline(offline);
+
+    mOfflineObserver = new OfflineObserver(this);
+  }
+
+  if (mListener) {
+    mListener->OnProcessLaunchComplete(this, aSucceeded);
+  }
+}
+
+void SocketProcessHost::Shutdown() {
+  MOZ_ASSERT(!mShutdownRequested);
+  MOZ_ASSERT(NS_IsMainThread());
+
+  mListener = nullptr;
+  if (mOfflineObserver) {
+    mOfflineObserver->Destroy();
+    mOfflineObserver = nullptr;
+  }
+
+  if (mSocketProcessParent) {
+    // OnChannelClosed uses this to check if the shutdown was expected or
+    // unexpected.
+    mShutdownRequested = true;
+
+    // The channel might already be closed if we got here unexpectedly.
+    if (!mChannelClosed) {
+      mSocketProcessParent->Close();
+    }
+
+    return;
+  }
+
+  DestroyProcess();
+}
+
+void SocketProcessHost::OnChannelClosed() {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  mChannelClosed = true;
+
+  if (!mShutdownRequested && mListener) {
+    // This is an unclean shutdown. Notify our listener that we're going away.
+    mListener->OnProcessUnexpectedShutdown(this);
+  } else {
+    DestroyProcess();
+  }
+
+  // Release the actor.
+  SocketProcessParent::Destroy(std::move(mSocketProcessParent));
+  MOZ_ASSERT(!mSocketProcessParent);
+}
+
+static void DelayedDeleteSubprocess(GeckoChildProcessHost* aSubprocess) {
+  XRE_GetIOMessageLoop()->PostTask(
+      MakeAndAddRef<DeleteTask<GeckoChildProcessHost>>(aSubprocess));
+}
+
+void SocketProcessHost::DestroyProcess() {
+  {
+    MonitorAutoLock lock(mMonitor);
+    mTaskFactory.RevokeAll();
+  }
+
+  MessageLoop::current()->PostTask(NewRunnableFunction(
+      "DestroySocketProcessRunnable", DelayedDeleteSubprocess, this));
+}
+
+//-----------------------------------------------------------------------------
+// SocketProcessMemoryReporter
+//-----------------------------------------------------------------------------
+
+bool SocketProcessMemoryReporter::IsAlive() const {
+  MOZ_ASSERT(gIOService);
+
+  if (!gIOService->mSocketProcess) {
+    return false;
+  }
+
+  return gIOService->mSocketProcess->IsConnected();
+}
+
+bool SocketProcessMemoryReporter::SendRequestMemoryReport(
+    const uint32_t& aGeneration, const bool& aAnonymize,
+    const bool& aMinimizeMemoryUsage, const dom::MaybeFileDesc& aDMDFile) {
+  MOZ_ASSERT(gIOService);
+
+  if (!gIOService->mSocketProcess) {
+    return false;
+  }
+
+  SocketProcessParent* actor = gIOService->mSocketProcess->GetActor();
+  if (!actor) {
+    return false;
+  }
+
+  return actor->SendRequestMemoryReport(aGeneration, aAnonymize,
+                                        aMinimizeMemoryUsage, aDMDFile);
+}
+
+int32_t SocketProcessMemoryReporter::Pid() const {
+  MOZ_ASSERT(gIOService);
+
+  if (!gIOService->mSocketProcess) {
+    return 0;
+  }
+
+  if (SocketProcessParent* actor = gIOService->mSocketProcess->GetActor()) {
+    return (int32_t)actor->OtherPid();
+  }
+  return 0;
+}
+
+}  // namespace net
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/SocketProcessHost.h
@@ -0,0 +1,131 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_net_SocketProcessHost_h
+#define mozilla_net_SocketProcessHost_h
+
+#include "mozilla/UniquePtr.h"
+#include "mozilla/ipc/GeckoChildProcessHost.h"
+#include "mozilla/MemoryReportingProcess.h"
+#include "mozilla/ipc/TaskFactory.h"
+
+namespace mozilla {
+namespace net {
+
+class OfflineObserver;
+class SocketProcessParent;
+
+// SocketProcessHost is the "parent process" container for a subprocess handle
+// and IPC connection. It owns the parent process IPDL actor, which in this
+// case, is a SocketProcessParent.
+// SocketProcessHost is allocated and managed by nsIOService in parent process.
+class SocketProcessHost final : public mozilla::ipc::GeckoChildProcessHost {
+  friend class SocketProcessParent;
+
+ public:
+  class Listener {
+   public:
+    NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Listener)
+
+    // Called when the process of launching the process is complete.
+    virtual void OnProcessLaunchComplete(SocketProcessHost* aHost,
+                                         bool aSucceeded) = 0;
+
+    // Called when the channel is closed but Shutdown() is not invoked.
+    virtual void OnProcessUnexpectedShutdown(SocketProcessHost* aHost) = 0;
+
+   protected:
+    virtual ~Listener() = default;
+  };
+
+ public:
+  explicit SocketProcessHost(Listener* listener);
+  ~SocketProcessHost();
+
+  // Launch the socket process asynchronously.
+  // The OnProcessLaunchComplete listener callback will be invoked
+  // either when a connection has been established, or if a connection
+  // could not be established due to an asynchronous error.
+  bool Launch();
+
+  // Inform the socket process that it should clean up its resources and shut
+  // down. This initiates an asynchronous shutdown sequence. After this method
+  // returns, it is safe for the caller to forget its pointer to the
+  // SocketProcessHost.
+  void Shutdown();
+
+  // Return the actor for the top-level actor of the process. Return null if
+  // the process is not connected.
+  SocketProcessParent* GetActor() const {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    return mSocketProcessParent.get();
+  }
+
+  bool IsConnected() const {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    return !!mSocketProcessParent;
+  }
+
+  // Called on the IO thread.
+  void OnChannelConnected(int32_t peer_pid) override;
+  void OnChannelError() override;
+
+ private:
+  // Called on the main thread.
+  void OnChannelConnectedTask();
+  void OnChannelErrorTask();
+
+  // Called on the main thread after a connection has been established.
+  void InitAfterConnect(bool aSucceeded);
+
+  // Called on the main thread when the mSocketParent actor is shutting down.
+  void OnChannelClosed();
+
+  void DestroyProcess();
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SocketProcessHost);
+
+  RefPtr<Listener> mListener;
+  mozilla::ipc::TaskFactory<SocketProcessHost> mTaskFactory;
+
+  enum class LaunchPhase { Unlaunched, Waiting, Complete };
+  LaunchPhase mLaunchPhase;
+
+  UniquePtr<SocketProcessParent> mSocketProcessParent;
+  // mShutdownRequested is set to true only when Shutdown() is called.
+  // If mShutdownRequested is false and the IPC channel is closed,
+  // OnProcessUnexpectedShutdown will be invoked.
+  bool mShutdownRequested;
+  bool mChannelClosed;
+
+  RefPtr<OfflineObserver> mOfflineObserver;
+};
+
+class SocketProcessMemoryReporter : public MemoryReportingProcess {
+ public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SocketProcessMemoryReporter, override)
+
+  SocketProcessMemoryReporter() = default;
+
+  bool IsAlive() const override;
+
+  bool SendRequestMemoryReport(const uint32_t& aGeneration,
+                               const bool& aAnonymize,
+                               const bool& aMinimizeMemoryUsage,
+                               const dom::MaybeFileDesc& aDMDFile) override;
+
+  int32_t Pid() const override;
+
+ protected:
+  virtual ~SocketProcessMemoryReporter() = default;
+};
+
+}  // namespace net
+}  // namespace mozilla
+
+#endif  // mozilla_net_SocketProcessHost_h
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/SocketProcessImpl.cpp
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SocketProcessImpl.h"
+
+#include "base/command_line.h"
+#include "base/shared_memory.h"
+#include "base/string_util.h"
+#include "mozilla/ipc/IOThreadChild.h"
+#include "mozilla/BackgroundHangMonitor.h"
+#include "mozilla/Preferences.h"
+
+using mozilla::ipc::IOThreadChild;
+
+namespace mozilla {
+namespace net {
+
+LazyLogModule gSocketProcessLog("socketprocess");
+
+SocketProcessImpl::SocketProcessImpl(ProcessId aParentPid)
+    : ProcessChild(aParentPid) {}
+
+SocketProcessImpl::~SocketProcessImpl() {}
+
+bool SocketProcessImpl::Init(int aArgc, char* aArgv[]) {
+#ifdef OS_POSIX
+  if (PR_GetEnv("MOZ_DEBUG_SOCKET_PROCESS")) {
+    printf_stderr("\n\nSOCKETPROCESSnSOCKETPROCESS\n  debug me @ %d\n\n",
+                  base::GetCurrentProcId());
+    sleep(30);
+  }
+#endif
+  char* parentBuildID = nullptr;
+  char* prefsHandle = nullptr;
+  char* prefMapHandle = nullptr;
+  char* prefsLen = nullptr;
+  char* prefMapSize = nullptr;
+
+  for (int i = 1; i < aArgc; i++) {
+    if (!aArgv[i]) {
+      continue;
+    }
+
+    if (strcmp(aArgv[i], "-parentBuildID") == 0) {
+      if (++i == aArgc) {
+        return false;
+      }
+
+      parentBuildID = aArgv[i];
+
+#ifdef XP_WIN
+    } else if (strcmp(aArgv[i], "-prefsHandle") == 0) {
+      if (++i == aArgc) {
+        return false;
+      }
+      prefsHandle = aArgv[i];
+    } else if (strcmp(aArgv[i], "-prefMapHandle") == 0) {
+      if (++i == aArgc) {
+        return false;
+      }
+      prefMapHandle = aArgv[i];
+#endif
+    } else if (strcmp(aArgv[i], "-prefsLen") == 0) {
+      if (++i == aArgc) {
+        return false;
+      }
+      prefsLen = aArgv[i];
+    } else if (strcmp(aArgv[i], "-prefMapSize") == 0) {
+      if (++i == aArgc) {
+        return false;
+      }
+      prefMapSize = aArgv[i];
+    }
+  }
+
+  SharedPreferenceDeserializer deserializer;
+  if (!deserializer.DeserializeFromSharedMemory(prefsHandle, prefMapHandle,
+                                                prefsLen, prefMapSize)) {
+    return false;
+  }
+
+  if (NS_FAILED(NS_InitXPCOM2(nullptr, nullptr, nullptr))) {
+    return false;
+  }
+
+  return mSocketProcessChild.Init(ParentPid(), parentBuildID,
+                                  IOThreadChild::message_loop(),
+                                  IOThreadChild::channel());
+}
+
+void SocketProcessImpl::CleanUp() { mSocketProcessChild.CleanUp(); }
+
+}  // namespace net
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/SocketProcessImpl.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_net_SocketProcessImpl_h
+#define mozilla_net_SocketProcessImpl_h
+
+#include "mozilla/ipc/ProcessChild.h"
+#include "SocketProcessChild.h"
+
+namespace mozilla {
+namespace net {
+
+// This class owns the subprocess instance of socket child process.
+// It is instantiated as a singleton in XRE_InitChildProcess.
+class SocketProcessImpl final : public mozilla::ipc::ProcessChild {
+ protected:
+  typedef mozilla::ipc::ProcessChild ProcessChild;
+
+ public:
+  explicit SocketProcessImpl(ProcessId aParentPid);
+  ~SocketProcessImpl();
+
+  bool Init(int aArgc, char* aArgv[]) override;
+  void CleanUp() override;
+
+ private:
+  SocketProcessChild mSocketProcessChild;
+  DISALLOW_COPY_AND_ASSIGN(SocketProcessImpl);
+};
+
+}  // namespace net
+}  // namespace mozilla
+
+#endif  // mozilla_net_SocketProcessImpl_h
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/SocketProcessLogging.h
@@ -0,0 +1,20 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_SocketProcessLogging_h
+#define mozilla_SocketProcessLogging_h
+
+#include "mozilla/Logging.h"
+
+namespace mozilla {
+namespace net {
+extern LazyLogModule gSocketProcessLog;
+}
+}  // namespace mozilla
+
+#define LOG(msg) MOZ_LOG(gSocketProcessLog, mozilla::LogLevel::Debug, msg)
+#define LOG_ENABLED() MOZ_LOG_TEST(gSocketProcessLog, mozilla::LogLevel::Debug)
+
+#endif  // mozilla_SocketProcessLogging_h
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/SocketProcessParent.cpp
@@ -0,0 +1,149 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SocketProcessParent.h"
+
+#include "SocketProcessHost.h"
+#include "mozilla/ipc/CrashReporterHost.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/TelemetryIPC.h"
+
+namespace mozilla {
+namespace net {
+
+static SocketProcessParent* sSocketProcessParent;
+
+SocketProcessParent::SocketProcessParent(SocketProcessHost* aHost)
+    : mHost(aHost) {
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mHost);
+
+  MOZ_COUNT_CTOR(SocketProcessParent);
+  sSocketProcessParent = this;
+}
+
+SocketProcessParent::~SocketProcessParent() {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  MOZ_COUNT_DTOR(SocketProcessParent);
+  sSocketProcessParent = nullptr;
+}
+
+/* static */ SocketProcessParent* SocketProcessParent::GetSingleton() {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  return sSocketProcessParent;
+}
+
+mozilla::ipc::IPCResult SocketProcessParent::RecvInitCrashReporter(
+    Shmem&& aShmem, const NativeThreadId& aThreadId) {
+  mCrashReporter = MakeUnique<CrashReporterHost>(GeckoProcessType_Content,
+                                                 aShmem, aThreadId);
+
+  return IPC_OK();
+}
+
+void SocketProcessParent::ActorDestroy(ActorDestroyReason aWhy) {
+  if (aWhy == AbnormalShutdown) {
+    if (mCrashReporter) {
+      mCrashReporter->GenerateCrashReport(OtherPid());
+      mCrashReporter = nullptr;
+    }
+  }
+
+  if (mHost) {
+    mHost->OnChannelClosed();
+  }
+}
+
+bool SocketProcessParent::SendRequestMemoryReport(
+    const uint32_t& aGeneration, const bool& aAnonymize,
+    const bool& aMinimizeMemoryUsage, const MaybeFileDesc& aDMDFile) {
+  mMemoryReportRequest = MakeUnique<dom::MemoryReportRequestHost>(aGeneration);
+  Unused << PSocketProcessParent::SendRequestMemoryReport(
+      aGeneration, aAnonymize, aMinimizeMemoryUsage, aDMDFile);
+  return true;
+}
+
+mozilla::ipc::IPCResult SocketProcessParent::RecvAddMemoryReport(
+    const MemoryReport& aReport) {
+  if (mMemoryReportRequest) {
+    mMemoryReportRequest->RecvReport(aReport);
+  }
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SocketProcessParent::RecvFinishMemoryReport(
+    const uint32_t& aGeneration) {
+  if (mMemoryReportRequest) {
+    mMemoryReportRequest->Finish(aGeneration);
+    mMemoryReportRequest = nullptr;
+  }
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SocketProcessParent::RecvAccumulateChildHistograms(
+    InfallibleTArray<HistogramAccumulation>&& aAccumulations) {
+  TelemetryIPC::AccumulateChildHistograms(Telemetry::ProcessID::Socket,
+                                          aAccumulations);
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SocketProcessParent::RecvAccumulateChildKeyedHistograms(
+    InfallibleTArray<KeyedHistogramAccumulation>&& aAccumulations) {
+  TelemetryIPC::AccumulateChildKeyedHistograms(Telemetry::ProcessID::Socket,
+                                               aAccumulations);
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SocketProcessParent::RecvUpdateChildScalars(
+    InfallibleTArray<ScalarAction>&& aScalarActions) {
+  TelemetryIPC::UpdateChildScalars(Telemetry::ProcessID::Socket,
+                                   aScalarActions);
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SocketProcessParent::RecvUpdateChildKeyedScalars(
+    InfallibleTArray<KeyedScalarAction>&& aScalarActions) {
+  TelemetryIPC::UpdateChildKeyedScalars(Telemetry::ProcessID::Socket,
+                                        aScalarActions);
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SocketProcessParent::RecvRecordChildEvents(
+    nsTArray<mozilla::Telemetry::ChildEventData>&& aEvents) {
+  TelemetryIPC::RecordChildEvents(Telemetry::ProcessID::Socket, aEvents);
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SocketProcessParent::RecvRecordDiscardedData(
+    const mozilla::Telemetry::DiscardedData& aDiscardedData) {
+  TelemetryIPC::RecordDiscardedData(Telemetry::ProcessID::Socket,
+                                    aDiscardedData);
+  return IPC_OK();
+}
+
+// To ensure that IPDL is finished before SocketParent gets deleted.
+class DeferredDeleteSocketProcessParent : public Runnable {
+ public:
+  explicit DeferredDeleteSocketProcessParent(
+      UniquePtr<SocketProcessParent>&& aParent)
+      : Runnable("net::DeferredDeleteSocketProcessParent"),
+        mParent(std::move(aParent)) {}
+
+  NS_IMETHODIMP Run() override { return NS_OK; }
+
+ private:
+  UniquePtr<SocketProcessParent> mParent;
+};
+
+/* static */ void SocketProcessParent::Destroy(
+    UniquePtr<SocketProcessParent>&& aParent) {
+  NS_DispatchToMainThread(
+      new DeferredDeleteSocketProcessParent(std::move(aParent)));
+}
+
+}  // namespace net
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/SocketProcessParent.h
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_net_SocketProcessParent_h
+#define mozilla_net_SocketProcessParent_h
+
+#include "mozilla/UniquePtr.h"
+#include "mozilla/net/PSocketProcessParent.h"
+
+namespace mozilla {
+
+namespace dom {
+class MemoryReport;
+class MemoryReportRequestHost;
+}  // namespace dom
+
+namespace ipc {
+class CrashReporterHost;
+}  // namespace ipc
+
+namespace net {
+
+class SocketProcessHost;
+
+// IPC actor of socket process in parent process. This is allocated and managed
+// by SocketProcessHost.
+class SocketProcessParent final : public PSocketProcessParent {
+ public:
+  friend class SocketProcessHost;
+
+  explicit SocketProcessParent(SocketProcessHost* aHost);
+  ~SocketProcessParent();
+
+  static SocketProcessParent* GetSingleton();
+
+  mozilla::ipc::IPCResult RecvInitCrashReporter(
+      Shmem&& aShmem, const NativeThreadId& aThreadId) override;
+  mozilla::ipc::IPCResult RecvAddMemoryReport(
+      const MemoryReport& aReport) override;
+  mozilla::ipc::IPCResult RecvFinishMemoryReport(
+      const uint32_t& aGeneration) override;
+  mozilla::ipc::IPCResult RecvAccumulateChildHistograms(
+      InfallibleTArray<HistogramAccumulation>&& aAccumulations) override;
+  mozilla::ipc::IPCResult RecvAccumulateChildKeyedHistograms(
+      InfallibleTArray<KeyedHistogramAccumulation>&& aAccumulations) override;
+  mozilla::ipc::IPCResult RecvUpdateChildScalars(
+      InfallibleTArray<ScalarAction>&& aScalarActions) override;
+  mozilla::ipc::IPCResult RecvUpdateChildKeyedScalars(
+      InfallibleTArray<KeyedScalarAction>&& aScalarActions) override;
+  mozilla::ipc::IPCResult RecvRecordChildEvents(
+      nsTArray<ChildEventData>&& events) override;
+  mozilla::ipc::IPCResult RecvRecordDiscardedData(
+      const DiscardedData& aDiscardedData) override;
+
+  void ActorDestroy(ActorDestroyReason aWhy) override;
+  bool SendRequestMemoryReport(const uint32_t& aGeneration,
+                               const bool& aAnonymize,
+                               const bool& aMinimizeMemoryUsage,
+                               const MaybeFileDesc& aDMDFile);
+
+ private:
+  SocketProcessHost* mHost;
+  UniquePtr<ipc::CrashReporterHost> mCrashReporter;
+  UniquePtr<dom::MemoryReportRequestHost> mMemoryReportRequest;
+
+  static void Destroy(UniquePtr<SocketProcessParent>&& aParent);
+};
+
+}  // namespace net
+}  // namespace mozilla
+
+#endif  // mozilla_net_SocketProcessParent_h
--- a/netwerk/ipc/moz.build
+++ b/netwerk/ipc/moz.build
@@ -6,33 +6,47 @@
 
 EXPORTS.mozilla.net += [
     'ChannelEventQueue.h',
     'NeckoChild.h',
     'NeckoCommon.h',
     'NeckoMessageUtils.h',
     'NeckoParent.h',
     'NeckoTargetHolder.h',
+    'SocketProcessBridgeChild.h',
+    'SocketProcessBridgeParent.h',
+    'SocketProcessChild.h',
+    'SocketProcessHost.h',
+    'SocketProcessImpl.h',
+    'SocketProcessParent.h',
 ]
 
 UNIFIED_SOURCES += [
     'ChannelEventQueue.cpp',
     'NeckoChild.cpp',
     'NeckoCommon.cpp',
     'NeckoParent.cpp',
     'NeckoTargetHolder.cpp',
+    'SocketProcessBridgeChild.cpp',
+    'SocketProcessBridgeParent.cpp',
+    'SocketProcessChild.cpp',
+    'SocketProcessHost.cpp',
+    'SocketProcessImpl.cpp',
+    'SocketProcessParent.cpp',
 ]
 
 IPDL_SOURCES = [
     'NeckoChannelParams.ipdlh',
     'PChannelDiverter.ipdl',
     'PDataChannel.ipdl',
     'PFileChannel.ipdl',
     'PNecko.ipdl',
     'PSimpleChannel.ipdl',
+    'PSocketProcess.ipdl',
+    'PSocketProcessBridge.ipdl'
 ]
 
 # needed so --disable-webrtc builds work (yes, a bit messy)
 if not CONFIG['MOZ_WEBRTC']:
   IPDL_SOURCES += [
       '../../media/mtransport/ipc/PStunAddrsRequest.ipdl',
       '../../media/mtransport/ipc/PWebrtcProxyChannel.ipdl',
   ]
@@ -46,12 +60,13 @@ include('/ipc/chromium/chromium-config.m
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     '/caps',
     '/dom/base',
     '/modules/libjar',
     '/netwerk/base',
     '/netwerk/protocol/http',
+    '/xpcom/threads'
 ]
 
 # Add libFuzzer configuration directives
 include('/tools/fuzzing/libfuzzer-config.mozbuild')
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -1112,16 +1112,20 @@ uint32_t nsHttpHandler::MaxSocketCount()
 }
 
 void nsHttpHandler::PrefsChanged(const char *pref) {
   nsresult rv = NS_OK;
   int32_t val;
 
   LOG(("nsHttpHandler::PrefsChanged [pref=%s]\n", pref));
 
+  if (pref) {
+    gIOService->NotifySocketProcessPrefsChanged(pref);
+  }
+
 #define PREF_CHANGED(p) ((pref == nullptr) || !PL_strcmp(pref, p))
 #define MULTI_PREF_CHANGED(p) \
   ((pref == nullptr) || !PL_strncmp(pref, p, sizeof(p) - 1))
 
   // If a security pref changed, lets clear our connection pool reuse
   if (MULTI_PREF_CHANGED(SECURITY_PREFIX)) {
     LOG(("nsHttpHandler::PrefsChanged Security Pref Changed %s\n", pref));
     if (mConnMgr) {
--- a/old-configure.in
+++ b/old-configure.in
@@ -607,19 +607,17 @@ case "$target" in
 #error not iOS
 #endif],
                                    [],
                                    ac_cv_ios_target="yes",
                                    ac_cv_ios_target="no")])
     if test "$ac_cv_ios_target" = "yes" -a -z $MOZ_IOS; then
        AC_MSG_ERROR([targeting iOS but not using an iOS SDK?])
     fi
-    if test -n "$MOZ_IOS"; then
-        direct_nspr_config=1
-    else
+    if test -z "$MOZ_IOS"; then
         # The ExceptionHandling framework is needed for Objective-C exception
         # logging code in nsObjCExceptions.h. Currently we only use that in debug
         # builds.
         MOZ_DEBUG_LDFLAGS="$MOZ_DEBUG_LDFLAGS -framework ExceptionHandling";
     fi
 
     dnl DTrace and -dead_strip don't interact well. See bug 403132.
     dnl ===================================================================
@@ -1022,16 +1020,20 @@ case "${OS_TARGET}" in
 Darwin)
   ;;
 *)
   STL_FLAGS="-I${DIST}/stl_wrappers"
   WRAP_STL_INCLUDES=1
   ;;
 esac
 
+if test "$MOZ_BUILD_APP" = "tools/crashreporter"; then
+    WRAP_STL_INCLUDES=
+fi
+
 dnl Checks for header files.
 dnl ========================================================
 AC_HEADER_DIRENT
 case "$target_os" in
 bitrig*|dragonfly*|freebsd*|openbsd*)
 # for stuff like -lXshm
     CPPFLAGS="${CPPFLAGS} ${X_CFLAGS}"
     ;;
@@ -1840,39 +1842,43 @@ MOZ_ARG_HEADER(Toolkit Options)
 
 dnl ========================================================
 dnl = Enable the toolkit as needed                         =
 dnl ========================================================
 
 case "$MOZ_WIDGET_TOOLKIT" in
 
 cocoa)
-    LDFLAGS="$LDFLAGS -framework Cocoa -lobjc"
+    LDFLAGS="$LDFLAGS -framework Cocoa"
     # Use -Wl as a trick to avoid -framework and framework names from
     # being separated by AC_SUBST_LIST.
     TK_LIBS='-Wl,-framework,Foundation -Wl,-framework,CoreFoundation -Wl,-framework,CoreLocation -Wl,-framework,QuartzCore -Wl,-framework,Carbon -Wl,-framework,CoreAudio -Wl,-framework,CoreVideo -Wl,-framework,AudioToolbox -Wl,-framework,AudioUnit -Wl,-framework,AddressBook -Wl,-framework,OpenGL -Wl,-framework,Security -Wl,-framework,ServiceManagement -Wl,-framework,CoreServices -Wl,-framework,ApplicationServices -Wl,-framework,AppKit'
     TK_CFLAGS=""
     CFLAGS="$CFLAGS $TK_CFLAGS"
     CXXFLAGS="$CXXFLAGS $TK_CFLAGS"
     MOZ_USER_DIR="Mozilla"
     MOZ_FS_LAYOUT=bundle
     ;;
 
 uikit)
-    LDFLAGS="$LDFLAGS -framework UIKit -lobjc"
+    LDFLAGS="$LDFLAGS -framework UIKit"
     TK_CFLAGS=""
     TK_LIBS='-Wl,-framework,Foundation -Wl,-framework,CoreFoundation -Wl,-framework,CoreGraphics -Wl,-framework,CoreText -Wl,-framework,AVFoundation -Wl,-framework,AudioToolbox -Wl,-framework,CoreMedia -Wl,-framework,CoreVideo -Wl,-framework,OpenGLES -Wl,-framework,QuartzCore'
     CFLAGS="$CFLAGS $TK_CFLAGS"
     CXXFLAGS="$CXXFLAGS $TK_CFLAGS"
     MOZ_USER_DIR="Mozilla"
     MOZ_FS_LAYOUT=bundle
     ;;
 
 esac
 
+if test "$OS_TARGET" = Darwin; then
+    LDFLAGS="$LDFLAGS -lobjc"
+fi
+
 dnl there are a lot of tests on MOZ_ENABLE_GTK below, that are more convenient
 dnl to keep that way than testing against MOZ_WIDGET_TOOLKIT
 case "$MOZ_WIDGET_TOOLKIT" in
 gtk*)
     MOZ_ENABLE_GTK=1
     ;;
 esac
 
@@ -2162,132 +2168,20 @@ dnl = Universalchardet
 dnl ========================================================
 MOZ_ARG_DISABLE_BOOL(universalchardet,
 [  --disable-universalchardet
                           Disable universal encoding detection],
   MOZ_UNIVERSALCHARDET=,
   MOZ_UNIVERSALCHARDET=1 )
 
 dnl ========================================================
-dnl = ANGLE OpenGL->D3D translator for WebGL
-dnl = * only applies to win32 (and then, not MINGW)
+dnl Gamepad support
 dnl ========================================================
 
-MOZ_ANGLE_RENDERER=
-MOZ_D3D_CPU_SUFFIX=
-MOZ_HAS_WINSDK_WITH_D3D=
-MOZ_D3DCOMPILER_VISTA_DLL=
-MOZ_D3DCOMPILER_VISTA_DLL_PATH=
-
 if test "$COMPILE_ENVIRONMENT" ; then
-case "$target_os" in
-*mingw*)
-    MOZ_ANGLE_RENDERER=1
-    ;;
-esac
-
-# The DirectX SDK libraries are split into x86 and x64 sub-directories
-case "${target_cpu}" in
-i*86)
-  MOZ_D3D_CPU_SUFFIX=x86
-  ;;
-x86_64)
-  MOZ_D3D_CPU_SUFFIX=x64
-  ;;
-aarch64)
-  MOZ_D3D_CPU_SUFFIX=arm
-  ;;
-esac
-
-dnl ========================================================
-dnl D3D compiler DLL
-dnl ========================================================
-MOZ_FOUND_D3D_COMPILERS=
-
-if test -n "$MOZ_ANGLE_RENDERER"; then
-  if test -z "$MOZ_D3D_CPU_SUFFIX"; then
-    AC_MSG_ERROR([Couldn't determine MOZ_D3D_CPU_SUFFIX.])
-  fi
-
-  ######################################
-  # Find _46+ for use by Vista+.
-
-  # Find a D3D compiler DLL in a Windows SDK.
-  MOZ_D3DCOMPILER_VISTA_DLL=
-  if test "$OS_ARCH" != "$HOST_OS_ARCH"; then
-    MOZ_D3DCOMPILER_VISTA_DLL=d3dcompiler_47.dll
-    AC_MSG_RESULT([Assuming D3D compiler will be in fxc2.])
-  else
-    case "$MOZ_WINSDK_MAXVER" in
-    0x0603*|0x0A00*)
-      MOZ_D3DCOMPILER_VISTA_DLL=d3dcompiler_47.dll
-      AC_MSG_RESULT([Found D3D compiler in Windows SDK.])
-    ;;
-    esac
-  fi
-
-  if test -n "$MOZ_D3DCOMPILER_VISTA_DLL"; then
-    # We have a name, now track down the path.
-    if test -n "$WINDOWSSDKDIR"; then
-      MOZ_D3DCOMPILER_VISTA_DLL_PATH="$WINDOWSSDKDIR/Redist/D3D/$MOZ_D3D_CPU_SUFFIX/$MOZ_D3DCOMPILER_VISTA_DLL"
-      if test -f "$MOZ_D3DCOMPILER_VISTA_DLL_PATH"; then
-        AC_MSG_RESULT([Found MOZ_D3DCOMPILER_VISTA_DLL_PATH: $MOZ_D3DCOMPILER_VISTA_DLL_PATH])
-        MOZ_HAS_WINSDK_WITH_D3D=1
-      elif test "$target_cpu" = "aarch64" -a "$MOZ_D3DCOMPILER_VISTA_DLL" = "d3dcompiler_47.dll"; then
-        AC_MSG_RESULT([AArch64 Windows includes d3dcompiler DLLs])
-        MOZ_D3DCOMPILER_VISTA_DLL_PATH=
-      else
-        AC_MSG_RESULT([MOZ_D3DCOMPILER_VISTA_DLL_PATH doesn't exist: $MOZ_D3DCOMPILER_VISTA_DLL_PATH])
-        AC_MSG_ERROR([Windows SDK at "$WINDOWSSDKDIR" appears broken. Try updating to MozillaBuild 1.9 final or higher.])
-        MOZ_D3DCOMPILER_VISTA_DLL_PATH=
-      fi
-    else
-      MOZ_D3DCOMPILER_VISTA_DLL_PATH="$(dirname $FXC)/$MOZ_D3DCOMPILER_VISTA_DLL"
-      if test -f "$MOZ_D3DCOMPILER_VISTA_DLL_PATH"; then
-        AC_MSG_RESULT([Found MOZ_D3DCOMPILER_VISTA_DLL_PATH: $MOZ_D3DCOMPILER_VISTA_DLL_PATH])
-      else
-        AC_MSG_RESULT([MOZ_D3DCOMPILER_VISTA_DLL_PATH doesn't exist: $MOZ_D3DCOMPILER_VISTA_DLL_PATH])
-        AC_MSG_ERROR([fxc2 or "$MOZ_D3DCOMPILER_VISTA_DLL" seem to be missing from the expected location.])
-        MOZ_D3DCOMPILER_VISTA_DLL_PATH=
-      fi
-    fi
-  else
-    if test "$OS_ARCH" = "$HOST_OS_ARCH"; then
-      AC_MSG_ERROR([Couldn't find Windows SDK 8.1 or higher needed for ANGLE.])
-    else
-      AC_MSG_ERROR([We should need "$MOZ_D3DCOMPILER_VISTA_DLL" for ANGLE in Linux MinGW build, but we didn't look for it.])
-    fi
-  fi
-
-  if test -z "$MOZ_D3DCOMPILER_VISTA_DLL_PATH"; then
-    MOZ_D3DCOMPILER_VISTA_DLL=
-  fi
-